Changelog

Notable changes to Ant.

Ant is pre-alpha. Every push to main is tagged vX.Y.Z (a patch bump from the previous release) and published to the download host.

No tagged releases yet.

Unreleased

Added

  • Rollback: ant trail rollback [deployment] [--to RELEASE] [--steps N] redeploys a previously released image without rebuilding, with optional rollback.pre_hook / rollback.post_hook; rollback.keep_releases is the fallback for image retention.
  • Audit log: a tamper-evident, hash-chained log of privileged RPCs on each machine, read with ant nest audit (rotates at 8 MiB; admin-only).
  • State backup/restore: ant nest state export | import (owner-only) backs up and restores a machine's roster and invites.
  • Cleanup: ant nest doctor --cleanup removes client leftovers (pre-v2 config backup, world-readable deploy logs, empty ~/.ant/local trees) and, on a machine, stored projects whose workload is gone.
  • Machine decommission: ant nest machines decommission NAME --host … --yes reverses provisioning over SSH (--purge, --keep-record).
  • Tunnels: ant nest tunnel --container NAME --port P forwards a local port to a container port on a machine over iroh; the dashboard's remote machine page has the same action on a running container's row (the listener stays on the dashboard's host).
  • Secret providers: a secret may name from: op://…, cmd:…, or env:…, resolved at deploy time so the value is never stored.
  • Dashboard password: ant ui passwd sets the password required for a non-loopback bind; a successful login issues a signed session cookie.
  • Project templates: ant nest templates list | search | show | add | sync and ant nest new, backed by the Dokploy blueprint catalog (bundled snapshot plus live fetch).
  • Image signing: opt-in cosign verification via deploy.image_signing; the worker verifies on the machine before pulling and running, not just the local CLI.
  • Branch-optional naming: naming.branch: false keeps one set of resources per project+env across branches.
  • Dashboard service: ant ui install | status | start | stop | restart | recreate | logs | passwd | run | uninstall runs the console as a background service (a systemd user unit on Linux, a launchd agent on macOS).
  • Domains are opt-in: ant never adds a hostname. Each deployments.<env>.domains entry names its routing target (a compose service/port, a path, or a bare port) and is added with ant nest edit domain add or the dashboard's Domains tab.
  • Group base domains: a group carries a list of base domains; a deployment adds a subdomain or path, uses one as-is, or pins one with group_domain.
  • Machine-wide network: opt in per project with deploy.use_machine_network; the machine's worker ensures and attaches it.
  • Docker volume management: ant nest volume list | rm, a GET /api/volumes and DELETE /api/volumes/{name} endpoint, and a Volumes section on a machine's dashboard page. Volumes report whether a container mounts them.
  • Colony profiles: edit a member's name, email, or linked unix account (ant colony users update, ant colony profile).
  • Remote machines: ant-worker (from cmd/agent) with per-RPC authorization, signed single-use (and revocable) invites, and remote deploy over the iroh transport.
  • Web dashboard parity: the roster backend is shared between the CLI and the dashboard, so users, roles, and deploys agree in both.
  • Deploy-time registry credentials: with ANT_REGISTRY_PASSWORD set, the pull credential travels with the deploy (deploy.run, compose.apply, stack.apply; ANT_REGISTRY_USERNAME covers a registry-qualified --image) as a short-lived worker-side DOCKER_CONFIG, so private images need no stored docker login on the machine.
  • Handover Caddyfile: decommission --handover exports caddy/Caddyfile.ant-handover with the routes ant programmed, so domains keep resolving after the takeover.

Changed

  • ~/.ant layout: a worker's state moved under ~/.ant/worker/ (identity, state, audit log, remote-compose) and the managed local Caddy's PID under ~/.ant/caddy/; a default-path worker adopts pre-existing files on first run, and explicit --identity/--state flags are untouched.
  • Dashboard look: the console wears the ant palette (warm paper in light, warm charcoal in dark, the logo's wood amber as the single interactive accent); the home stat cards became one status strip, and the theme control follows the applied theme, system included.
  • Project page and settings: the project header groups its actions by intent (environment, Build → Deploy, a joined Restart|Stop, quiet Terminal and settings icons, and a menu for rollback/destroy); page tabs are one underlined rail on the project, group, machine, and settings pages; and settings/detail cards share one titled section pattern.
  • Machine and project details: remote machines can be renamed from the machine page; the project overview lists what each compose service runs (or that it builds on deploy); volume rows use Key/Value plus the container path; and editing an environment shows the compose file it uses, overridden or inherited.

Changed (breaking)

  • CLI surface cleanup. Machine resources moved to nest (ant nest containers, ant nest volume) and ant trail ps was removed; colony users promote became colony users update --role; the hidden ant forage alias was retired. Config-target flags use --target-machine now, so --machine is routing-only and local-only commands fail fast on a remote.
  • Wire shape: deploy payloads. Deploy-path payloads are shared types on both sides, and route.apply carries a route spec (service + port) the worker resolves. The pre-release protocol version stays v1; upgrade the CLI and every worker together, since mixed versions fail closed.
  • Wire shape: compose profiles. Compose profiles and external-network semantics ride on the deploy payloads.

Fixed

  • Project page environment scoping: the environment picker scopes the header status, badges, image, and Monitoring (containers, logs, disk usage on that environment's machine) to the selected deployment; a remote environment reads its containers from its own machine instead of showing the local deployment's running state.
  • Local container secrets. The default local run: container path dropped provider-resolved secrets entirely; it now passes them like the other paths.
  • Cancelled deploys. A cancelled running probe no longer leaves the app stopped; ContainerRunning errors abort the swap.
  • Branch naming works when the project lives in a subdirectory and the default relative --config ant.yaml is used.
  • Swap leftovers (-staging/-previous) are removed only when their app label matches, so a colliding resource name from another app is not deleted.
  • Zero-downtime ports preserve an explicit host IP (including loopback).
  • --machine: trail logs, trail rollback, and haul honor --machine, and an explicit --machine local fails fast when the config targets another machine.
  • nest edit computes env/secret/volume/network list mutations under the config lock and stores an absolute project root for groups.
  • Health checks with an invalid path return an error instead of panicking.
  • Archive extraction caps the entry count.
  • Provisioning keeps a corrupt machine identity instead of regenerating it, and removes the upload directory on every path.
  • History and logs: history --clear --machine keeps other machines' entries, remote logs resolve the deployment's run:, and haul records the machine it built on.
  • Managed Caddy uses the configured caddy.admin_listen, keeps its config in ~/.ant, stops a live managed process before starting another, and removes a PID file only when it names the exiting process.
  • Remote compose deploys. deploy.files layers are all resolved (previously only the first was shipped), deploy.profiles activate the same services remotely as locally, deploy.build_services: false no longer rebuilds anyway, and runtime secrets are no longer interpolated to empty (or baked) on the client before the worker's secrets.env applies.
  • Release ids. A remote source build without --release records the minted image tag, so history and rollback --to can address the deploy.
  • trail destroy no longer reports success when the container removal fails, and a non-path-safe app: value is refused instead of deleting a directory outside ~/.ant/local.
  • Caddy routes. Local deploys validate route paths (no wildcards), path routes are ordered before a host-only route, and conflict checks cover every host in a match set.
  • Config. Legacy group machine: values are normalized during migration, machine identity paths normalize their id, concurrent EditFile writes are serialized, deployment keys that normalize to the same name are rejected, a newer schema version is refused instead of silently downgraded, and config writes follow a symlinked config to its target.
  • Machine ops. A failed container-existence probe aborts a deploy instead of deleting the live app, the stop/park step survives a cancelled RPC, the reaper restores a parked container when the canonical one exists but is not running, and a bad network name no longer leaks a secret env file in /tmp.
  • Rollback no longer fails on an unavailable build-secret provider, compose config writes restore the previous file when the secrets file cannot be written, and nest init quotes generated YAML values.
  • Remote compose domains. A remote compose deploy now programs its domains after up instead of dropping them silently; a routing failure fails the deploy with the stack left running.
  • First-deploy health gate probes a first-time container (previously only staged replacements were gated), and a failed first deploy removes the half-created container.
  • Teardown clears routes on compose.down/containers.remove and local trail down/destroy, so a domain does not keep pointing at a removed app.
  • Buildpacks and railpack builds work: pack no longer gets an unsupported --label, and provisioning starts BuildKit when railpack is installed.
  • Provisioning installs the Docker Compose and buildx plugins when the distro ships them separately; a missing plugin now fails with a clear message.
  • Local Caddy listener changes: changing caddy.http_listen/https_listen now reaches a running managed Caddy: the next local deploy restarts it, and ant nest tools caddy restart applies it on demand. The old behavior silently kept the previous listeners.
  • Local Caddy without a PID file: ant nest tools caddy stop|restart now falls back to Caddy's admin API, so it works on a proxy started by an earlier ant invocation (or one whose PID file was cleaned).
  • Tool status: ant nest tools <tool> status [--json] reports any tool (installed, version, path, install hint; Docker also reports daemon reachability, so a down daemon is not shown as "not installed"). Only the one tool ant runs as a service, caddy, additionally gets ant nest tools caddy restart|stop; the builders are one-shot CLIs and the Docker daemon is host-managed, so they have no lifecycle verbs.
  • Per-deployment target and secrets: trail deploy, trail status, and the rollback history honor a deployment's server: override (and trail status --machine M targets M); deploy.publish merges per deployment; and editing secrets keeps each from: provider reference.
  • Local compose secrets are no longer written into a .ant-<app>.yml file in the project directory; the patch uses ${KEY} placeholders and the values pass through the compose process environment.
  • Interrupted-deploy reaper no longer mistakes a compose service whose container_name ends in -staging/-previous for a leftover swap container.
  • ant.yaml typos are refused instead of silently ignored: unknown keys and a newer version: fail the load, so a misspelled zero_downtime, health_check, or image_signing cannot disable the feature it names.
  • Zero-downtime cleanup runs on a detached context and reports failures as deploy warnings, so a cancelled RPC cannot leave the old container holding the canonical name unnoticed.
  • Deploy probes report a failed ContainerRunning as a probe error, abort cancellation promptly, and remove a container whose first-deploy health gate failed.
  • Docker reads on the worker keep a per-call timeout, so an unresponsive daemon cannot pin a container, image, or volume listing RPC.
  • Service units escape a literal % for systemd and write units/plists atomically; a corrupt deploy history is backed up instead of overwritten; malformed legacy config values (including group entries) are reported instead of dropped; and unknown secret providers no longer echo the full from: reference.
  • Remote nests in the dashboard no longer fail with "Remote worker: context deadline exceeded": the dashboard shares one iroh endpoint per process instead of dialing per request, remote probes get individual slices of a bounded page budget, and page polls never start a new request before the previous one settles.
  • Last owner: the machine's sole owner can no longer be demoted or removed (dashboard and daemon), and the settings page lists group scan roots next to the machine's own so group-registered projects are visible.
  • Compose volume edits keep mount modes beyond ro and preserve tmpfs / option-carrying long-syntax mounts verbatim; a volume row's Key and Value are mutually exclusive instead of silently dropping one.
  • Stack routing warns when a routed service publishes in mode: host on a multi-node swarm, where a manager-local Caddy cannot reach the task.

Security

  • Dashboard CSRF over GET. Cross-site requests to the dashboard's API are rejected for every method, GET included: a malicious page could otherwise trigger GET /api/projects/{name}/exec (which runs a command in a container). Opening the UI itself from another site (a top-level navigation) still works.
  • Container env passthrough. Runtime secrets (including multi-line values) travel only in the docker process environment on both local and remote container deploys; the temporary --env-file is gone.
  • Compose ~. The mount/read guards refuse a leading ~, which compose expands to the user's home directory.
  • Transport lifetimes. Per-connection idle deadlines, tunnel first-stream deadlines, and oldest-connection eviction bound connection-slot exhaustion; a split read/handler budget stops empty streams from blocking RPC processing.
  • Caddy routes are serialized in-process.
  • Denial audit has a global per-interval cap; invite redemptions and failed tunnel authorizations are recorded.
  • System accounts derive from 12 NodeID characters; duplicate links are refused.
  • Resource-name collisions are refused instead of replacing another app's container.
  • Cloud-init escapes values for the single-quoted systemd unit.
  • Build-context extraction. A symlink whose target traverses an earlier symlink is refused, and the remote Dockerfile write removes an existing symlink first, closing an arbitrary-file-write path.
  • Compose guard. env_file, build context, extends, and include paths are checked on the worker (client-side parent-relative paths stay allowed); a remote apply refuses build services outright.
  • Local container secrets are passed through the docker process environment instead of docker run -e KEY=VALUE, so they no longer appear in argv.
  • Worker bootstrap uploads into a private mktemp -d directory instead of predictable /tmp names a local user could pre-create.
  • trail prune --volumes removes only the project's unused volumes, scoped by its compose label, and refuses to run without a project; previously it removed every unused volume on the host.
  • Transport hardening. Per-peer connection caps and a first-stream watchdog bound connection-slot exhaustion; the endpoint no longer hangs forever waiting for a relay; an oversize error reply is truncated rather than dropped.
  • Managed Caddy PID file moved out of world-writable /tmp, and a PID file with no recorded binary is never signalled.
  • --machine on local-only trail commands (down, restart, destroy, env, exec) now fails fast, and trail deploy honors it as a routing override.
  • Denial audit entries are rate-limited per peer+method, so an unauthenticated caller cannot force log rotation.
  • App names are validated at config load, and unknown users.remove ops are rejected instead of deleting the roster row without a plan.
  • Build-context extraction can no longer be redirected outside the extraction root by a chained symlink.
  • Container-deploy mounts refuse relative sources (they resolved against the worker's working directory), ancestors of the docker socket, and compose volumes_from; the identity directory and ~/.ant are protected even when --state points elsewhere.
  • Audit log flooding is bounded: details are capped, unauthenticated request bodies are not logged, oversized lines are skipped on read, and two rotated segments are kept.
  • Route upstreams are verified against the named container's published ports, so a deploy cannot route the Caddy admin API or another local listener.
  • Role changes are re-checked against the state being mutated, closing the window where a demoted caller finished an in-flight privileged call.
  • Worker stream budgets stop a stalled or trickled request and bound every call's duration, so a hung command cannot pin a handler slot.
  • Mount-path resolution fails closed: a bind source that cannot be resolved (an untraversable parent, a symlink loop) is refused, since the root-owned docker daemon would follow it.
  • Invite revocation re-resolves the caller's role under the write lock, so a demoted member cannot finish an in-flight revoke with its stale role.

Changed

  • Caddy is required on a remote nest (optional locally); a worker with no reachable Caddy still starts (logging a warning) but refuses deploys until Caddy answers.
  • Config schema v2: project_dirs, groups, and caddy nest under machines.<id>; v1 configs migrate on load.
  • Resource naming: resources are named [<group>-]<app>-<env>[-<branch>] from one rule (branch can be dropped with naming.branch: false).
  • Machine resources moved: containers and volume live under ant nest; ant nest deploy is now ant trail deploy --image.
  • Mount guard: deploys that reach the worker's identity/state (including its ancestors), the docker socket, or the host root are refused.
  • Log color: CLI and dashboard logs share one heuristic: errors red, warns amber, success emerald, steps cyan, debug gray.

Removed

  • Project-wide expose: routing lives per deployment under domains:, so each hostname is paired with what it forwards to.

Pre-alpha

See the installation guide for release tarballs, checksums.sha256, and the rolling main build.

Copyright © 2026