Changelog#
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased#
0.8.0 - 2026-07-24#
Added#
Serve a first-party HTMX workbench at
/for preflight checks, environment solves, selected-package inspection, and exporter output.
Changed#
Serve the browser workbench instead of OpenAPI JSON at
/. API clients continue to use/openapi.json.
0.7.0 - 2026-07-24#
Added#
POST /repairsuggests single-spec relaxations for infeasible requests and returns only suggestions that solve on every requested platform.Register
conda-presto.serveras a manual, loopback-only conda-broker service.Require
conda-brokerand document how to run the local service.Run the Docker server in persistent-worker mode without starting conda-broker.
Add internal
conda --solver=presto, fixed to the rattler backend and served through the broker-managed loopback service.Cache successful
/solver/v1final states by serialized solver request fields and dependency versions, with repodata cache-file markers recorded by the worker.Record successful cacheable foreground
/solver/v1requests as cache-warming candidates.Check recorded solver requests and refresh missing or stale cache entries when no foreground solve is active or waiting.
Publish container SBOMs and GitHub-verifiable image attestations, and scan Python and GitHub Actions changes with CodeQL.
Changed#
Reorganize the documentation into tutorials, how-to guides, reference, and explanation pages covering the 0.7 service, solver, cache, and repair features.
Add package authorship, classifiers, keywords, and project URLs, and update release examples for 0.7.0.
Fixed#
Limit public HTTP access logs to request metadata and response status, and disable uvicorn’s raw access log so solve inputs and credentials are not recorded.
Restrict the private solver route to loopback JSON requests for the broker-assigned authority, reject browser origins, and serve OpenAPI as JSON without remotely loaded browser code.
Bound private solver channels and serialized state independently of the public API, return generic unexpected errors, and redact URL-shaped strings and conda
Current channels:blocks from application and dependency logs.Compare channel allowlists by resolved credential-free URLs so canonical names cannot authorize another host, protocol downgrade, or wildcard local-file access.
Apply
no-storeto successful mutable/resolvecache responses, reserve immutable caching for content-addressed permalinks, and skip retention when requests or produced package URLs contain detected credential patterns.Include effective conda settings, per-platform virtual-package records, stronger repodata file identity, and identifiable exporter provider callbacks and distribution versions in public result-cache keys.
Recheck repodata before returning public cache hits, and publish public and private results only when their before-and-after repodata markers are unchanged and safe.
Isolate HTTP file parsing in a terminable process, bound input structure before conda plugin object construction, reject YAML aliases, remove its temporary directory after process cleanup, and reject non-printable or longer-than-240-byte basenames.
Prevent HTTP lockfile uploads from fetching package URLs during parsing. Resolve and review paths reject operations that need package records, while
/transcodeuses a conda-presto compatibility path to reconstruct and serialize supported conda-lockfiles formats atomically inside the isolated parser process, reject incompatible or inconsistent records, and mark successful responsesno-store.Bound solver responses, individual persistent values, and recorded-request catalogs, expire persistent entries, remove invalid stored values, and perform best-effort file-store cleanup at startup and hourly.
Document required filesystem quotas and Redis
maxmemoryeviction because persistent TTL and per-value limits do not bound aggregate storage.Run releases only for final version tags, separate artifact builds from OIDC attestation, gate PyPI and GHCR publication with protected environments, and dispatch Docker publication from the released tag.
Build and scan server and CLI images on amd64 and arm64, require approval through the protected
ghcrenvironment before publication, publish full vulnerability reports, pin base images by digest, keep application and environment files outside writable cache and home paths read-only, and remove unnecessary setuid and setgid permissions.Pin
setup-pixiby commit and Pixi to 0.70.1, keep response bodies out of Action logs, require HTTPS except for loopback, disable curl configuration, accept only HTTP 2xx, and validate native JSON.Lock release build tooling, require setuptools 83 or newer, and isolate automated lockfile generation from its write-token job.
Keep conda filesystem locking enabled in the persistent Docker server.
Refresh loaded solver indexes when conda considers their repodata stale, and validate cached results against the repodata cache files used.
Restart failed persistent workers in Docker while conda-broker restarts its server process.
Bypass cached solver results when py-rattler or repodata cache-file markers change, skip retention across ambiguous metadata transitions, and replace an existing solver cache entry after refresh.
Publish the Docker CLI alias as
cli, keep CLI images from replacinglatest, add source-revision tags, and omit ambiguous major-zero aliases.
0.6.0 - 2026-07-10#
Added#
POST /preflightfor deterministic local input validation without solver or channel access.POST /difffor platform-aware resolved package comparisons, including direct lockfile reads when covered.POST /explainfor bounded dependency chains from requested specs to a selected package.
Fixed#
Return parser plugin errors as normal HTTP input errors instead of internal server errors.
0.5.1 - 2026-07-10#
Fixed#
Published Docker server images now include the Redis client required by the Redis result-cache backend.
Docker server builds now include the package README required by the distribution metadata.
0.5.0 - 2026-07-10#
Added#
Documentation site with reference, explanation, tutorial, and roadmap pages, published through GitHub Pages.
POST /transcodefor lockfile-to-lockfile conversion without solving when the requested platforms are already present in the input lockfile.Content-addressed resolve result cache with stable
Location: /r/{hash}permalinks for successful HTTP solve responses and an optional file-backed or Redis-backed persistent layer.
Changed#
Pixi environments and install docs now use stable conda-forge conda packages instead of the
conda-canary/label/devchannel.GitHub Action documentation and examples now focus on CI lockfile workflows.
Proposal documentation is now a concise roadmap page that links to GitHub issues for the detailed design records.
Consolidate the server and CLI container builds into one multi-target
docker/Dockerfile.
Fixed#
Result cache keys now include the
conda-lockfilespackage version so cached lockfile-format responses are invalidated when the exporter implementation changes.Docker release builds now publish the server
latesttag on release events, matching the documented image tag contract.
Removed#
Experimental external integration surface and tutorial.
Generated benchmark result snapshots and the no-signal benchmark CI job.
0.4.0 - 2026-04-16#
Added#
POST /resolvenow dispatches onContent-Type:application/json(or missing) keeps the existingResolveRequestenvelope behavior;application/yaml/application/x-yaml/text/yaml/application/toml/text/plaintreat the body as a raw environment file, parsed through conda’s env-spec plugin registry. Specs, channels, platforms, and format come from query params on the raw-body dispatch;?filename=picks the parser when Content-Type alone is ambiguous. Unsupported Content-Types return HTTP 400 with the list of accepted types. This makescurl --data-binary @environment.yml -H 'Content-Type: application/yaml' '.../resolve?format=pixi-lock-v6&platform=linux-64'a one-liner — nojqor JSON wrapping required.HTTP API
?format=<name>query parameter onGET/POST /resolveroutes the response through conda’s exporter plugin registry. The CLI (--format) and HTTP API share the samerender_envshelper so both surfaces expose the same set of formats. Unknown formats return HTTP 400 with the available-format list; solver failures on this path return HTTP 500 (exporters can’t represent per-platform errors).conda-lockfiles >=0.1.1is now a base dependency, soconda-lock-v1andrattler-lock-v6/pixi-lock-v6are available out of the box from both CLI and HTTP API.Windows virtual package override via
CONDA_PRESTO_WIN_VERSION(default0) so cross-platformwin-*solves get the same treatment as Linux/macOS.Per-request abuse/DoS caps:
CONDA_PRESTO_MAX_SPECS(default 200, returns 400),CONDA_PRESTO_MAX_PLATFORMS(default 8, returns 400), andCONDA_PRESTO_SOLVE_TIMEOUT_S(default 60s, returns 504).Process pool is shut down cleanly via Litestar’s
on_shutdownhook.
Changed#
HTTP API now returns
SolveResult/ResolvedPackagemsgspec.Structinstances directly; Litestar encodes them natively to JSON without an intermediateto_dict()conversion.CLI default output is now produced by
msgspec.json.encodeon the samelist[SolveResult]the HTTP API returns, pretty-printed viamsgspec.json.format(..., indent=2). CLI and HTTP emit identical JSON (modulo whitespace), including a per-platform"error"field (was previously absent from the CLI default). This is an additive change to the schema — correct JSON parsers continue to work.POST body fields override query params based on presence, not truthiness. An explicit empty array in the body (e.g.
{"platforms": []}) now overrides the corresponding query param, matching the documented behavior.Solver error responses surface detail only for an allow-list of known exception types (
UnsatisfiableError,PackagesNotFoundError); all other exceptions return a generic"Internal solver error"message. Full detail is still logged server-side.Environment-variable parsing is centralized through
env_intandenv_listhelpers: list values now strip whitespace and drop empty parts, and numeric values fail with a clear startup error instead of a rawValueErrorfromint().
Removed#
resolve-jsonconda exporter plugin and theconda_environment_exportershook entry. It duplicated theSolveResult/ResolvedPackagemsgspec shape for no good reason; the CLI now produces that shape natively. Minor user-visible consequences:conda presto --format resolve-json ...now errors with “Unknown format”. Fix: drop--format; the default output is the same JSON shape (plus a"error"field per platform).HTTP
GET/POST /resolve?format=resolve-jsonreturns HTTP 400 for the same reason. Drop the query parameter.conda env export --format resolve-json(invoked from plain conda on an installed prefix, with conda-presto installed) no longer works. This was never conda-presto’s intended use case.
ResolvedPackage.to_dictandSolveResult.to_dictmethods: no longer called anywhere; msgspec serializes the structs directly.
Fixed#
HTTP
Content-Typeon?format=responses is now derived from the exporter’sdefault_filenamesrather than a hand-coded format-name table. YAML-ish formats (environment-yaml,conda-lock-v1,rattler-lock-v6/pixi-lock-v6) are now served asapplication/yamlinstead oftext/plain.Tempfile lifetime bug in
parse_file_content:specsandchannelsare now extracted while the temp file is still open, preventing races with env-spec plugins that read the file lazily on attribute access.README documentation of the index-cache TTL was inaccurate. The on-disk repodata TTL (
CONDA_LOCAL_REPODATA_TTL) and the in-memoryRattlerIndexHelpercache (no TTL, process-lifetime) are now described separately.
0.3.0 - 2026-04-16#
Added#
Interactive API documentation (Scalar UI) at
/with auto-generated OpenAPI 3.1 schema at/openapi.json, replacing the hand-written ~190-line schema dictionary.Production middleware: brotli compression with gzip fallback, CORS (configurable via
CONDA_PRESTO_CORS_ORIGINS, default*), structured request logging, and rate limiting (configurable viaCONDA_PRESTO_RATE_LIMIT, default 300 req/min,0to disable).New environment variables:
CONDA_PRESTO_RATE_LIMIT,CONDA_PRESTO_CORS_ORIGINS,CONDA_PRESTO_LOG_LEVEL.Native request body size enforcement via Litestar’s
request_max_body_size(configurable viaCONDA_PRESTO_MAX_BODY_BYTES, default 1 MB).
Changed#
Migrated HTTP API framework from Starlette to Litestar. Typed request validation via
ResolveRequestdataclass replaces manual JSON parsing.Migrated
ResolvedPackageandSolveResultfromdataclassestomsgspec.Structfor faster serialization and lower memory. Litestar encodes these natively to JSON without intermediate dicts.Startup initialization uses Litestar’s
on_startuphook withapp.stateinstead of module-level globals.Application log level configurable via
CONDA_PRESTO_LOG_LEVEL(defaultINFO).Renamed project from
conda-resolvetoconda-presto(package, CLI subcommand, environment variables, Docker images).
0.2.1 - 2026-04-16#
Added#
Separate Docker image flavors: server (
latest) and CLI (cli), built fromdocker/server.Dockerfileanddocker/cli.Dockerfile. The CLI image excludes server dependencies (uvicorn) for a smaller footprint.
0.2.0 - 2026-04-16#
Added#
Configuration module (
config.py) with environment variable overrides for all operational settings:CONDA_PRESTO_CHANNELS,CONDA_PRESTO_PLATFORMS,CONDA_PRESTO_CONCURRENCY,CONDA_PRESTO_WORKERS,CONDA_PRESTO_MAX_BODY_BYTES,CONDA_PRESTO_HOST,CONDA_PRESTO_PORT,CONDA_PRESTO_GLIBC_VERSION,CONDA_PRESTO_LINUX_VERSION,CONDA_PRESTO_OSX_VERSION.Support for
.tomland.jsonfile extensions in the HTTP API file upload endpoint.Dynamic OpenAPI schema version derived from package metadata via
importlib.metadata.
Changed#
Default fallback channel changed from
defaultstoconda-forgein both CLI and HTTP API, configurable viaCONDA_PRESTO_CHANNELS.Unified HTTP API to single
/resolveendpoint supporting both GET (query params) and POST (JSON body). Removed old/solve,/solve/environment-yml, and/cache/clearendpoints.Request body uses
specsfield instead ofdependencies, andfile/filenamefields for environment file content instead of raw YAML body.POST body fields override query params when both are present.
Virtual package versions for cross-platform solving are now configurable via environment variables instead of hardcoded.
Process pool size and server host/port defaults are now configurable via environment variables.
Updated test suite to match the new
/resolveAPI.
0.1.1 - 2026-04-15#
Added#
Minimal, hardened Docker image using multi-stage build with
debian:bookworm-slimand non-root user.Docker release workflow publishing multi-arch images (linux/amd64, linux/arm64) to GitHub Container Registry on release and manual dispatch.
Dependabot configuration for Docker base image updates.
Automated pixi lockfile update workflow (monthly, via
pixi-diff-to-markdown).linux-aarch64platform support.
Fixed#
Default platform detection after server warmup. The warmup loop left a stale platform in
context._cache_, causing the server to default to the wrong platform. Now captured once at import time asNATIVE_SUBDIR.
0.1.0 - 2026-04-15#
Added#
Core solver module (
resolve.py) withResolvedPackageandSolveResultdataclasses usingslots=Truefor memory efficiency.Split architecture: CLI uses conda’s
Environmentmodel directly viasolve_environments(), server uses lightweight custom types viasolve()for fast JSON serialization.Shared
dispatch()helper for single- and multi-platform solving, with per-platform error handling for the server path.Multi-platform parallel solving via
ProcessPoolExecutorwith persistent worker pool for cross-request cache reuse.Cross-platform virtual package injection (
configure_platform()) usingcontext._cache_for thread-safe platform configuration with conservative defaults (glibc 2.17, linux 5.15, osx 11.0).Thread-safe solver invocation via
platform_lockto prevent concurrent requests from racing on process-global state.In-memory repodata index cache with
threading.Lock-guarded check-then-build for thundering herd protection, and explicitclear_index_cache()invalidation.HTTP API (
app.py) built on Starlette with/solve,/solve/environment-yml,/health, and/cache/clearendpoints.anyio.CapacityLimiterto cap concurrent solver threads andabandon_on_cancel=Truefor client disconnect handling.Request body size limit (1 MB), input type validation (both JSON and YAML endpoints), Content-Length header validation, and sanitized error responses (no internal stack traces leaked).
Repodata cache pre-warming on server startup via
warmup(), offloaded from the event loop withanyio.to_thread.run_sync.CLI with resolve as the default action and
--serveflag for the HTTP server. Uses conda’s environment specifier plugins for input and exporter plugins for output.Custom
resolve-jsonenvironment exporter providing full package metadata (sha256, md5, urls, sizes, dependencies) as the default CLI output format.Conda subcommand plugin (
conda presto) with lazy imports to keep plugin load under 1 ms.Lazy uvicorn import in
cmd_serve()to reduce CLI startup overhead by ~100 ms for non-server invocations.Performance tuning via pixi activation env:
CONDA_SOLVER=rattler,CONDA_CHANNEL_PRIORITY=strict,CONDA_NO_LOCK=true,CONDA_UNSATISFIABLE_HINTS=false,CONDA_NUMBER_CHANNEL_NOTICES=0,CONDA_AGGRESSIVE_UPDATE_PACKAGES="",CONDA_LOCAL_REPODATA_TTL=300,CONDA_JSON=true.Comprehensive test suite with pytest, pytest-benchmark, pytest-cov, and httpx for async API testing. 99% code coverage with a 95%
fail_underthreshold enforced on every run.Hyperfine benchmark fixtures (
benchmarks/) for end-to-end CLI performance tracking.GitHub Actions CI workflow with lint, test, and benchmark jobs.
Dependabot configuration for GitHub Actions version updates.
BSD 3-Clause license.