This is the full developer documentation for antil # more workers. one machine. > Tunnels for apps, chambers for resources, seals for proof. antil gives every worker its own running app, shares the heavy resources, and is building seals that reuse tests only when their code and environment still match. [![](/docs/art/b-share-lanes.webp)](/docs/tunnels/) [**tunnels**](/docs/tunnels/) [apps & urls](/docs/tunnels/) [Every worker gets its own running app, ready only when it answers.](/docs/tunnels/) [![](/docs/art/b-share-store.webp)](/docs/chambers/) [**chambers**](/docs/chambers/) [shared resources](/docs/chambers/) [Run the heavy server once. Give every worker its own room inside.](/docs/chambers/) [![](/docs/art/b-proof-trail.webp)](/docs/seals/) [**seals**coming](/docs/seals/) [proof & cache](/docs/seals/) [Same code and environment: reuse the proof. Changed or unknown: run.](/docs/seals/) ## start in 5 minutes [Section titled “start in 5 minutes”](#start-in-5-minutes) Run a shop with a Vite storefront, a Node API, Postgres, Redis, and checkout tests. Give two agents separate worktrees and open both shops side by side. [run the shop →](/docs/quickstart/) You need macOS, Git, Node.js, Bun, Docker Desktop or OrbStack, and the [antil binary](/docs/start-here/install/). Allow extra time for the first container download. ## what works today [Section titled “what works today”](#what-works-today) **Tunnels:** local HTTP addresses, separate ports, and checked readiness. **Chambers:** one Postgres engine with a private database per worker, or an explicitly shared seeded database. **Seals:** coming; named test commands run today, but every new test request runs again. Recipes for Redis, object stores, and other resources are [coming](/docs/chambers/recipes/). The shop uses Docker Compose to share Redis manually. Private-network HTTPS is [coming](/docs/tunnels/https/). A worker means you or an agent working in one git worktree. “Private database” means separate working data for cooperative workers; antil is not a security sandbox for untrusted code. ## one foundation [Section titled “one foundation”](#one-foundation) All three parts rely on the same [foundation](/docs/foundation/): one JSON contract for agents, work that survives a daemon restart, and no automatic repetition when an earlier command’s outcome is unknown. ## free here. cloud across machines. [Section titled “free here. cloud across machines.”](#free-here-cloud-across-machines) The local tool is free and open source, for you and your agents. **antil cloud is paid and in early access.** Sharing proof, trust, or resources across people and machines belongs to cloud and is not available yet. Local seals are planned as part of the free tool. [See what comes next](/docs/roadmap/). # page not found > Find your way back to the antil field manual. This trail ends here. Return to [start here](/docs/) or use search to find a command, concept or guide. # chambers > Shared resources: run the heavy server once, keep each worker’s data separate. **Shared resources.** Two shop agents need databases, but they do not need two Postgres servers. Chambers share the server while giving each worker its own working database. *One store beneath the nest, with a separate room for each worker.* ![Separate chambers connect to one shared underground store.](/docs/art/b-share-store.webp) share the heavy bits. keep your own working data. One Postgres engine contains the catalog and checkout databases. Each starts with the products seed; an order in one stays out of the other. ## share the engine, separate the orders [Section titled “share the engine, separate the orders”](#share-the-engine-separate-the-orders) The catalog worker and checkout worker use the same Postgres container. Each gets a different database, seeded with the same products. An order placed in one shop does not appear in the other. The database connection reaches the API through a private file; JSON shows a reference rather than the password. You can explicitly share a seeded database when workers need the same mutable data. That choice shares writes too. A schema change belongs in a private fork so everyone else can keep working. Today, choose private databases up front or start a fresh private database from the seed (a fresh seeded fork, without current orders); copying a live database into a fork is not available yet. ## a recipe for every resource [Section titled “a recipe for every resource”](#a-recipe-for-every-resource) Chambers are moving to **recipes**: descriptions of how to start a resource, make a worker’s room, and check it. That will make the same pattern work for caches, object stores, and other services. **Today antil manages Postgres only.** Recipes are [coming](/docs/chambers/recipes/). The shop shares Redis manually through Compose, with a separate key prefix for each worktree. [Tunnels](/docs/tunnels/) connect apps to these resources. [Seals](/docs/seals/) will account for the environment a test used. All three rely on the [foundation](/docs/foundation/). [Give workers private Postgres databases](/docs/chambers/postgres/) · [change a schema privately](/docs/chambers/schema-change/) · [share Redis today](/docs/chambers/redis/) # give each worker its own database > Share one Postgres server and seed private shop databases. In the [shop](/docs/quickstart/), keep this resource definition: ```yaml resources: db: kind: postgres seed: file: seed.sql validate: "SELECT count(*) = 2 FROM products" ``` Map the connection into the API: ```yaml services: api: env: DATABASE_URL: {resource: db, output: url} ``` These are excerpts from the starter’s `antil.yaml`; keep the API command, port, and readiness settings. Start each worktree after installing its dependencies and preparing its cache: ```sh bun install bun run prepare:cache antil session start --json ``` Compare the database names and engine identifiers under `data.resources.db` in the two results. The database names differ; the engine identifier matches. Credentials stay in `outputs.url.secret_ref` and are passed to the API at launch. If you need deliberately shared writes, end the affected workers and add `sharing: seed` and `claim: {access: shared, permissions: [read, write]}` to their `db` definition before starting them. Keep identical seed inputs. Do not choose this for workers that need independent orders or schema changes. When the work is finished, end each session. Data is retained; review [prune](/docs/reference/cli/#antil-prune) before deleting it. # bring any resource > Coming: recipes for databases, caches, and object stores. coming The shop should describe Redis the same way it describes Postgres: run the heavy server once, give every worker a separate room, and check that it is ready. An object store could give each worker a bucket; a cache could give each worker a namespace. **Chambers: shared resources** will use recipes to describe those steps. A resource will not need a dedicated integration in antil to fit the model. When a worker needs to change shared structure, a recipe will describe how to make a private fork and validate it. Today only Postgres is managed by antil. There is no recipe schema to configure yet. Use [private Postgres databases](/docs/chambers/postgres/) and the shop’s [manual Redis helper](/docs/chambers/redis/). # share Redis between the shop workers > Use Compose and separate key prefixes while Redis recipes are coming. In each [shop worktree](/docs/quickstart/), run: ```sh bun run prepare:cache ``` The starter’s helper uses the shared Git directory to choose one Compose project. It starts one Redis server on a Docker-assigned loopback port, then writes `.antil/redis-url` in that worktree. Keep `.antil/` gitignored. The API receives that file through: ```yaml REDIS_URL: {secret: .antil/redis-url} ``` The starter prefixes catalog cache keys with a hash of the worktree path. Keep that prefix when adding cached queries; two workers must not overwrite each other’s results. Run the helper in every new worktree. Stop Redis only after both workers finish: ```sh bun run stop:cache ``` This is a Compose helper in the shop, not built-in Redis management. [Chamber recipes](/docs/chambers/recipes/) will make resources beyond Postgres part of antil’s lifecycle. # change a schema privately > Start a fresh private shop database without changing another worker’s data. Use a private database for the worker changing the shop schema. The starter already uses private databases. This procedure starts from the seed; it does not copy current orders. End only the worker that needs the new schema: ```sh antil session end ``` Replace `seed.sql` in that worktree with a catalog that includes a SKU: ```sql CREATE TABLE products (id integer PRIMARY KEY, name text NOT NULL, price_cents integer NOT NULL, sku text NOT NULL); INSERT INTO products (id, name, price_cents, sku) VALUES (1, 'Field notebook', 1200, 'NOTE-01'), (2, 'Canvas tool roll', 3500, 'ROLL-01'); CREATE TABLE orders (id serial PRIMARY KEY, product_id integer REFERENCES products(id)); ``` Keep the other worktree’s files untouched. Keep `sharing: private` on `resources.db` (the default). Start the worker again: ```sh antil session start --json ``` Read its new database name and wait for the API’s `/health` check. Run the shop tests in this worktree, then in the other worktree: ```sh antil run test --json ``` Both should report `status: ok`, a succeeded command, and exit code `0`. Follow each result’s log action; both test reports should show two passes. Open both storefronts and place an order in each. In the unchanged worktree, `antil status --json` should still show its original database name and ready apps. The old database from the changed worktree remains retained until you prune it. For an existing shared database, take this fresh private route before changing its schema. Live database copying and recipes are [coming](/docs/chambers/recipes/); antil does not automatically move an already running worker when you edit SQL. Commands you launch outside antil are not prevented from changing a shared database. # antil cloud > Paid, early access: proof, trust, and resources across people and machines. cloud coming **antil cloud is a paid early-access offering. The workflows on this page are planned and unavailable in this build.** “Early access” is not a switch that enables shared seals or CI reuse today. On your machine, tunnels, chambers, and future local seals belong to the free, open-source tool. No cloud account is needed to run the shop or coordinate local workers. Cloud begins when proof, trust, or resources cross people and machines. A teammate’s seal needs an identity and a trust decision. CI needs to check that its inputs still match. A shared remote resource needs coordination beyond one laptop. Cloud will provide those checks and shared coordination, along with team reporting. There is no published price, cloud CLI setup, or CI gate to enable today. [CI and seals](/docs/seals/ci/) describes the intended workflow. # next to your tools > Where antil fits beside Tilt, Compose, devcontainers, task caches, and agent harnesses. If your team runs one environment per developer, you may already have what you need. antil is for the point where one developer has several workers in separate git worktrees, and those workers need separate apps and data at the same time. Its three parts are **tunnels: apps & urls**, **chambers: shared resources**, and **seals: proof & cache**. Tunnels and Postgres chambers work locally today. Seals are coming. ## tilt [Section titled “tilt”](#tilt) [Tilt](https://docs.tilt.dev/) gives a developer a complete development environment, with live updates and a UI for services and logs, mostly around Kubernetes and Docker Compose. It can run local processes too. That is a good fit when your team wants a fast edit-and-refresh loop for its environment. antil’s unit of work is a worker in a git worktree: many isolated stacks on one machine, sharing heavy resources, with output an agent can act on. For the shop, two agents get separate Vite/API pairs and separate databases inside one Postgres server. You can build multiple environments with Tilt. antil makes per-worktree identity, resource sharing, and agent handoff its starting point. Reusable test proof is a planned difference, not a feature to switch on today. ## docker compose [Section titled “docker compose”](#docker-compose) [Compose](https://docs.docker.com/compose/) defines and runs container services, networks, and volumes. Separate Compose project names can isolate several stacks; dynamic ports and health checks can be configured too. If one Compose project per worktree works for your team, keep it. You maintain the naming, port discovery, per-worker data, and any deliberate sharing. antil coordinates those worker boundaries and gives agents one status contract. It still uses Docker for Postgres; the shop even uses Compose for its manually shared Redis while recipes are coming. ## devcontainers [Section titled “devcontainers”](#devcontainers) [Development containers](https://containers.dev/) define the containerized development environment and tools a contributor uses. They help teammates start with the same toolchain. antil manages the apps and resource access of parallel workers. These solve different problems; a standardized toolchain does not by itself separate two workers’ database writes. ## turborepo and nx [Section titled “turborepo and nx”](#turborepo-and-nx) [Turborepo](https://turborepo.com/docs/crafting-your-repository/caching) and [Nx](https://nx.dev/docs/features/cache-task-results) cache task results from declared inputs. Keep them for builds, lint, and suitable tests. Correct cache inputs matter for either tool. A shop integration test also depends on a running API and database state. Seals will include the environment a test actually used, and run again when that environment changes or cannot be established. Today antil can launch your existing runner but adds no test cache. ## agent harnesses [Section titled “agent harnesses”](#agent-harnesses) Keep Codex, Claude Code, T3 Code, or the harness you use to edit code, delegate work, and open a browser. antil supplies the environment beneath each worker. An agent reads a ready URL and opens it with its existing browser tool. There is no special browser integration to install. ## when antil earns its place [Section titled “when antil earns its place”](#when-antil-earns-its-place) Use it when parallel agents collide on app ports, invalidate each other’s data, or cannot tell what a previous worker left running. For a single app with no shared state, a package script may be enough. The local tool is free and open source. Sharing proof, trust, or resources across people and machines will use **antil cloud, paid and in early access**. Neither cloud coordination nor local seals is available yet. [What works now and what comes next](/docs/roadmap/). # for agents > Inspect the current shop worker, follow next actions, and use your own browser. Start in the shop worktree you own. Read the CLI’s guidance and current state: ```sh antil skill print antil status --json ``` If there is no session, inspect `antil.yaml`, install the app’s dependencies, prepare its resources, and start it with `antil session start --json`. Follow the [quickstart](/docs/quickstart/) for the shop’s exact setup. Do not choose app ports yourself. ## read the result before acting [Section titled “read the result before acting”](#read-the-result-before-acting) Check the top-level `status` and process exit code. Inspect `blockers`, `conflicts`, and `next_actions` before issuing another mutation. Run a returned action as an argument array, after checking its preconditions; do not concatenate it into a shell command. For an app, require `data.services.web.state` to be `ready`, then open `data.services.web.url` in your harness’s browser. Read its direct loopback URL if your client cannot resolve the named localhost address. For resources, use the provided environment mapping. Do not copy database credentials into chat or logs. For test commands, inspect the command’s exit status and exact log; a running app is not a passing test. ## survive retries and handoffs [Section titled “survive retries and handoffs”](#survive-retries-and-handoffs) For commands that accept `--idempotency-key` (session lifecycle, workspace creation, named commands, and resource changes), give each request one stable key. Check the command help for support. Reuse that key if delivery is uncertain. A different key means new work, including another test run. If a request is still running, follow it. If its outcome is unknown, inspect it before choosing an explicit retry. Record a handoff to the next agent: ```sh antil status --takeover checkout-agent --json ``` `status --takeover` and `session touch` do not accept an idempotency key. Repeating them refreshes liveness; it does not start another app or test. A stale mark does not release a worker’s data or ports. End the worker deliberately when it is finished. ## the three parts [Section titled “the three parts”](#the-three-parts) Use **tunnels: apps & urls** for ready app addresses, **chambers: shared resources** for data, and, when available, **seals: proof & cache** for reusable test proof. Today chambers manage Postgres only and seals are coming. [The foundation](/docs/foundation/) is shared by all three. ## machine-readable docs [Section titled “machine-readable docs”](#machine-readable-docs) Read [llms.txt](/docs/llms.txt) for the page index or [llms-full.txt](/docs/llms-full.txt) for the whole manual. Every page also has **view markdown** and **copy page**. The [JSON reference](/docs/reference/json/) and [CLI reference](/docs/reference/cli/) describe the literal contract. # the foundation > One contract for agents, crash-safe work, and no uncertain automatic replay. A **worker** is you or an agent in a Git worktree. A **session** is the running apps and resource access antil remembers for that worktree, even when the agent leaves. Tunnels, chambers, and seals need the same answer to a basic question: what is really running, and what can happen next? *The nest keeps its state when a worker leaves.* An agent asks for the shop state. antil keeps the request and observed result. A new agent reads the same state; an unknown result is inspected, never automatically repeated. ## an agent can arrive halfway through [Section titled “an agent can arrive halfway through”](#an-agent-can-arrive-halfway-through) The catalog agent starts the shop, then loses its conversation. The next agent reads `status --json` in the same worktree. It sees the apps, addresses, data references, known problems, and concrete next commands. It does not need the first agent’s terminal history or a different API for every harness. ## a lost response does not start the work again [Section titled “a lost response does not start the work again”](#a-lost-response-does-not-start-the-work-again) antil records requests before acting on them. Retrying the same request key returns that request. If a daemon restarts while a shop app is running, antil checks the existing worker rather than launching a duplicate app. If a migration or test might already have run, antil keeps that uncertainty visible. It never automatically repeats arbitrary work whose outcome it cannot establish. The agent must inspect it and choose an explicit next step. Crash-safe does not mean every external command can be undone. ## the boundary is explicit [Section titled “the boundary is explicit”](#the-boundary-is-explicit) antil coordinates work launched through its CLI. A command run directly in a terminal can still modify a database. It cannot protect shared data from tools that bypass it. Ending a worker keeps its data until you explicitly delete it. This foundation supports local tunnels and Postgres chambers today; [seals](/docs/seals/) are coming. [Use antil from an agent](/docs/for-agents/). # two agents. two shops. > Run a real shop stack in two worktrees, make an order, and run tests. In about five minutes, you will run a shop in two worktrees: one agent changes the catalog while another works on checkout. Each gets its own Vite storefront, Node API, and Postgres database. They share one Postgres engine and one Redis server. Tests place real orders. Use **macOS, Git, Node.js 22.12 or newer, Bun, a running Docker Desktop or OrbStack, and [antil on your PATH](/docs/start-here/install/)**. Git needs your usual commit identity. The first image download can take longer. ## 1. unpack the shop [Section titled “1. unpack the shop”](#1-unpack-the-shop) The [shop starter](/docs/examples/shop.tar.gz) contains all the code used throughout this manual. Use a new directory: ```sh mkdir -p /tmp/antil-shop-lesson cd /tmp/antil-shop-lesson export DOCS_URL="${DOCS_URL:-https://antil.dev/docs}" curl -fsSL "$DOCS_URL/examples/shop.tar.gz" -o shop.tar.gz tar -xzf shop.tar.gz cd shop git init -b main bun install git add . git commit -m "Add shop stack" ``` The shop has `index.html` and `vite.config.mjs` for the storefront, `api/server.mjs`, `seed.sql`, `compose.yaml` for Redis, `tests/shop.test.mjs`, and `antil.yaml`. ## 2. prepare the shared cache [Section titled “2. prepare the shared cache”](#2-prepare-the-shared-cache) ```sh bun run prepare:cache ``` After Docker’s progress messages: ```text shared Redis ready; connection saved in .antil/redis-url ``` Redis support through antil recipes is coming. For now this helper uses Compose, saves the connection in a gitignored file, and gives each worktree its own cache key prefix. Postgres is managed by antil. Use separate state and ports for this lesson. Keep these exports in the same shell for every step: ```sh export ANTIL_HOME=/tmp/antil-shop-lesson/state export ANTIL_PROXY_PORT=1376 export ANTIL_PORT_RANGE=45100-45199 ``` ## 3. start the catalog worker [Section titled “3. start the catalog worker”](#3-start-the-catalog-worker) ```sh antil session start --json antil status ``` Here are the app lines from a real run. IDs, ports, paths, and times vary: ```text status: ok session: ses_8cf7db2b7f27 (active, stack default) api ready http://api.ses-8cf7db2b7f27.prj-695192eb9055.localhost:1376 log: /private/tmp/antil-docs/shop-home/logs/run_8cf7db2b7f27_2.log last probe: ready (2026-10-31T16:32:54Z) web ready http://web.ses-8cf7db2b7f27.prj-695192eb9055.localhost:1376 log: /private/tmp/antil-docs/shop-home/logs/run_8cf7db2b7f27_5.log last probe: ready (2026-10-31T16:32:56Z) ``` Both apps are `ready`. The API’s `/health` checks Postgres and Redis before it answers successfully. The storefront waits for the API. Open the `web` URL from **your** output in your browser. You will see “field supplies”, a Field notebook for €12, and a Canvas tool roll for €35. Click **order a field notebook**. An order number appears, stored in this worker’s database. ## 4. start the checkout worker [Section titled “4. start the checkout worker”](#4-start-the-checkout-worker) Keep the first shop running. In the same shell: ```sh antil workspace create checkout --path /tmp/antil-shop-lesson/shop-checkout cd /tmp/antil-shop-lesson/shop-checkout bun install bun run prepare:cache antil session start --json antil status ``` The second worker gets different addresses: ```text status: ok session: ses_2e26319326f0 (active, stack default) api ready http://api.ses-2e26319326f0.prj-695192eb9055.localhost:1376 log: /private/tmp/antil-docs/shop-home/logs/run_2e26319326f0_2.log last probe: ready (2026-10-31T16:32:58Z) web ready http://web.ses-2e26319326f0.prj-695192eb9055.localhost:1376 log: /private/tmp/antil-docs/shop-home/logs/run_2e26319326f0_5.log last probe: ready (2026-10-31T16:32:59Z) ``` Open its `web` URL beside the first. Ordering here writes to the checkout worker’s database. Both workers share the database engine; their orders stay separate. Their Redis keys stay separate too. ## 5. run the checkout tests [Section titled “5. run the checkout tests”](#5-run-the-checkout-tests) ```sh antil run test --json ``` A successful result contains these lines, trimmed from a real run: ```text "apiVersion": "antil/v1alpha1", "status": "ok", "operation": { "id": "op_dcd6b4b0cb0b", "kind": "run", "state": "succeeded" }, "data": { ``` The tests check the seeded catalog and place an order. In `next_actions`, `argv` lists the words of the log command. Type `antil logs --run` followed by the run ID from your result. The report includes: ```text ℹ tests 2 ℹ suites 0 ℹ pass 2 ℹ fail 0 ``` These are ordinary test runs. **Seals are coming:** antil does not yet reuse a passing result in another worktree. ## 6. stop the lesson [Section titled “6. stop the lesson”](#6-stop-the-lesson) End both workers, delete only the lesson’s retained database data, and stop its shared Redis: ```sh antil session end cd /tmp/antil-shop-lesson/shop antil session end antil prune --json ``` `prune` prints a preview; it deletes nothing. Read the listed targets. The `apply` action gives an `argv` array: `antil`, `prune`, `--apply`, your preview revision, `--json`. Type those five entries in order, separated by spaces. Use the revision from your own preview. Both lesson databases and their unused engine should be in that preview. You used a separate `ANTIL_HOME`, so it contains only this lesson’s work. Finish cleanup: ```sh bun run stop:cache antil daemon shutdown --stop-sessions ``` The worktree files remain. You have two working shops with separate addresses and data, sharing the expensive servers. Continue with [tunnels: apps & urls](/docs/tunnels/), [chambers: shared resources](/docs/chambers/), or [seals: proof & cache](/docs/seals/). If a start fails, read `blockers` and the log path in its result. Check that Docker is running and `bun run prepare:cache` succeeded in that worktree. After fixing the cause, end that session and start it again. [Recover a worker](/docs/tunnels/recover/) has the next steps. # cli > Literal command help captured from the real binary. Captured from `/tmp/antil-docs/antil` built from the working checkout. Help is literal; internal process roles are included for completeness. No `seal` or recipe command exists in this binary. ## antil [Section titled “antil”](#antil) ```text Isolated-but-shared dev stacks for parallel agents Usage: antil [command] Available Commands: check Observe changed paths and required Resource permissions claim Manage the current Session's Resource Claims completion Generate the autocompletion script for the specified shell config Inspect the effective configuration daemon Run the antil daemon in the foreground (normally auto-started) help Help about any command init Scaffold antil.yaml from a package script for review logs Print a service log or an exact Run log operation Follow durable Operations prune Preview retained resources; explicitly apply an exact revision to delete idle unclaimed data resource Inspect and reconcile retained resource ownership run Run a named OperationDefinition under the Session runner session Manage the Session of this workspace skill Tool-agnostic agent guidance status Show Session state, evidence and handoff actions workspace Create or explicitly adopt git worktrees Flags: -h, --help help for antil --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope Use "antil [command] --help" for more information about a command. ``` ## antil check [Section titled “antil check”](#antil-check) ```text Observe changed paths and required Resource permissions Usage: antil check [flags] Flags: -h, --help help for check Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil claim upgrade [Section titled “antil claim upgrade”](#antil-claim-upgrade) ```text Add Resource permissions with atomic holder checks Usage: antil claim upgrade [flags] Flags: --access string upgrade shared to exclusive; DDL defaults to exclusive --expected-revision string exact Claim/Binding revision from status or a Remedy -h, --help help for upgrade --idempotency-key string retry-safe key; returns the original accepted receipt --permission strings permissions to add: read, write, ddl (repeatable or comma-separated) --wait duration maximum time to wait after admission (default 1m0s) Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil claim [Section titled “antil claim”](#antil-claim) ```text Manage the current Session's Resource Claims Usage: antil claim [command] Available Commands: upgrade Add Resource permissions with atomic holder checks Flags: -h, --help help for claim Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope Use "antil claim [command] --help" for more information about a command. ``` ## antil config explain [Section titled “antil config explain”](#antil-config-explain) ```text Show a value and its source Usage: antil config explain [flags] Flags: -h, --help help for explain Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil config [Section titled “antil config”](#antil-config) ```text Inspect the effective configuration Usage: antil config [command] Available Commands: explain Show a value and its source Flags: -h, --help help for config Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope Use "antil config [command] --help" for more information about a command. ``` ## antil daemon shutdown [Section titled “antil daemon shutdown”](#antil-daemon-shutdown) ```text Stop the daemon; with --stop-sessions also stop every Session's processes Usage: antil daemon shutdown [flags] Flags: -h, --help help for shutdown --stop-sessions also stop all session runners and their services Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil daemon [Section titled “antil daemon”](#antil-daemon) ```text Run the antil daemon in the foreground (normally auto-started) Usage: antil daemon [flags] antil daemon [command] Available Commands: shutdown Stop the daemon; with --stop-sessions also stop every Session's processes Flags: -h, --help help for daemon Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope Use "antil daemon [command] --help" for more information about a command. ``` ## antil init [Section titled “antil init”](#antil-init) ```text Scaffold antil.yaml from a package script for review Usage: antil init [flags] Flags: --exclude-local add .antil/ to git's local info/exclude -h, --help help for init --script string package script to run (default "dev") Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil logs [Section titled “antil logs”](#antil-logs) ```text Print a service log or an exact Run log Usage: antil logs [service] [flags] Flags: -h, --help help for logs --run string read the log of this exact Run in the Workspace --tail int last N lines (0 = all) (default 200) Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil operation cancel [Section titled “antil operation cancel”](#antil-operation-cancel) ```text Stop an exact named command without ending its Session Usage: antil operation cancel [flags] Flags: -h, --help help for cancel --idempotency-key string retry-safe key; returns the same control or execution --wait duration maximum time to wait for completion (default 1m0s) Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil operation get [Section titled “antil operation get”](#antil-operation-get) ```text Show an Operation's current state without waiting Usage: antil operation get [flags] Flags: -h, --help help for get Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil operation retry [Section titled “antil operation retry”](#antil-operation-retry) ```text Explicitly retry a confirmed stopped named command Usage: antil operation retry [flags] Flags: -h, --help help for retry --idempotency-key string retry-safe key; returns the same control or execution --wait duration maximum time to wait for completion (default 1m0s) Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil operation wait [Section titled “antil operation wait”](#antil-operation-wait) ```text Wait for an Operation to finish Usage: antil operation wait [flags] Flags: -h, --help help for wait --wait duration maximum time to wait (default 1m0s) Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil operation [Section titled “antil operation”](#antil-operation) ```text Follow durable Operations Usage: antil operation [command] Available Commands: cancel Stop an exact named command without ending its Session get Show an Operation's current state without waiting retry Explicitly retry a confirmed stopped named command wait Wait for an Operation to finish Flags: -h, --help help for operation Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope Use "antil operation [command] --help" for more information about a command. ``` ## antil prune [Section titled “antil prune”](#antil-prune) ```text Preview retained resources; explicitly apply an exact revision to delete idle unclaimed data Usage: antil prune [flags] Flags: --apply string apply the exact revision returned by a reviewed preview -h, --help help for prune --idempotency-key string retry-safe apply request key --retry string explicitly retry the remaining exact releases of a failed/unknown prune Operation --wait duration maximum wait (default 1m0s) Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil resource reconcile [Section titled “antil resource reconcile”](#antil-resource-reconcile) ```text Release a proven-quiescent resource reservation while retaining unknown schema Usage: antil resource reconcile [flags] Flags: --generation int exact retained Allocation generation (required) -h, --help help for reconcile --idempotency-key string retry-safe reconciliation key Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil resource [Section titled “antil resource”](#antil-resource) ```text Inspect and reconcile retained resource ownership Usage: antil resource [command] Available Commands: reconcile Release a proven-quiescent resource reservation while retaining unknown schema Flags: -h, --help help for resource Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope Use "antil resource [command] --help" for more information about a command. ``` ## antil run [Section titled “antil run”](#antil-run) ```text Run a named OperationDefinition under the Session runner Usage: antil run [flags] Flags: -h, --help help for run --idempotency-key string retry-safe key; returns the same execution --wait duration maximum time to wait for command completion (default 1m0s) Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil session end [Section titled “antil session end”](#antil-session-end) ```text Stop every process of this workspace's Session and end it (harmless to repeat) Usage: antil session end [flags] Flags: --abandon end without stopping processes antil can no longer reach -h, --help help for end --idempotency-key string retry-safe key; the same key returns the same Operation --wait duration how long to wait before returning status waiting (default 1m0s) Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil session resume [Section titled “antil session resume”](#antil-session-resume) ```text Start a suspended Session's services again on the same ports Usage: antil session resume [flags] Flags: -h, --help help for resume --idempotency-key string retry-safe key; the same key returns the same Operation --wait duration how long to wait before returning status waiting (default 1m0s) Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil session start [Section titled “antil session start”](#antil-session-start) ```text Start (or replay) the Session of this workspace Usage: antil session start [stack] [flags] Flags: -h, --help help for start --idempotency-key string retry-safe key; the same key returns the same Operation --wait duration how long to wait before returning status waiting (default 1m0s) Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil session suspend [Section titled “antil session suspend”](#antil-session-suspend) ```text Stop this workspace's services but keep their ports and data Usage: antil session suspend [flags] Flags: -h, --help help for suspend --idempotency-key string retry-safe key; the same key returns the same Operation --wait duration how long to wait before returning status waiting (default 1m0s) Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil session touch [Section titled “antil session touch”](#antil-session-touch) ```text Refresh liveness without changing ownership Usage: antil session touch [flags] Flags: -h, --help help for touch Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil session [Section titled “antil session”](#antil-session) ```text Manage the Session of this workspace Usage: antil session [command] Available Commands: end Stop every process of this workspace's Session and end it (harmless to repeat) resume Start a suspended Session's services again on the same ports start Start (or replay) the Session of this workspace suspend Stop this workspace's services but keep their ports and data touch Refresh liveness without changing ownership Flags: -h, --help help for session Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope Use "antil session [command] --help" for more information about a command. ``` ## antil skill print [Section titled “antil skill print”](#antil-skill-print) ```text Usage: antil skill print [flags] Flags: -h, --help help for print Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil skill [Section titled “antil skill”](#antil-skill) ```text Tool-agnostic agent guidance Usage: antil skill [command] Available Commands: print Flags: -h, --help help for skill Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope Use "antil skill [command] --help" for more information about a command. ``` ## antil status [Section titled “antil status”](#antil-status) ```text Show Session state, evidence and handoff actions Usage: antil status [flags] Flags: -h, --help help for status --takeover string record the successor controller and refresh liveness Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil workspace adopt [Section titled “antil workspace adopt”](#antil-workspace-adopt) ```text Usage: antil workspace adopt [flags] Flags: -h, --help help for adopt Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil workspace create [Section titled “antil workspace create”](#antil-workspace-create) ```text Usage: antil workspace create [flags] Flags: -h, --help help for create --idempotency-key string retry-safe request key --path string destination checkout path (required) --ref string commit or ref to start from (default "HEAD") --wait duration maximum wait (default 1m0s) Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil workspace [Section titled “antil workspace”](#antil-workspace) ```text Create or explicitly adopt git worktrees Usage: antil workspace [command] Available Commands: adopt create Flags: -h, --help help for workspace Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope Use "antil workspace [command] --help" for more information about a command. ``` ## antil help [Section titled “antil help”](#antil-help) ```text Help provides help for any command in the application. Simply type antil help [path to command] for full details. Usage: antil help [command] [flags] Flags: -h, --help help for help Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` ## antil runner [Section titled “antil runner”](#antil-runner) ```text Run a session runner (internal; started by the daemon) Usage: antil runner [flags] Flags: -h, --help help for runner --session string session id Global Flags: --home string antil home (default $ANTIL_HOME or ~/.local/state/antil) --json print the JSON envelope ``` # antil.yaml > Every accepted version 1 config key, its type, default and example. The effective config is committed `antil.yaml` overlaid by `.antil/local.yaml`. Unknown fields, duplicate keys, multiple YAML documents, invalid references and dependency cycles are rejected. Local config has the [same schema and defined merge rules](/docs/reference/local-configuration/). Names for Projects, Services, Resources, OperationDefinitions and Rules are lowercase slugs of 1–40 characters, using letters, digits and internal hyphens. ## top level [Section titled “top level”](#top-level) | key | type | default / requirement | example | | ------------ | -------------------------- | --------------------------------------------------- | ---------------------------- | | `version` | integer | required; only `1` | `version: 1` | | `project` | string | required; lowercase slug | `project: shop` | | `target` | string | `HEAD`; resolved and pinned at Session admission | `target: main` | | `services` | map of Service | required; at least one | see below | | `stacks` | map of StackDefinition | if absent or empty: `default` selects every Service | `default: {services: [web]}` | | `resources` | map of Resource | empty | `db: {kind: postgres}` | | `operations` | map of OperationDefinition | empty | see below | | `rules` | map of Rule | empty | see below | ## services [Section titled “services”](#services) `services.` accepts exactly these keys: | key | type | default / requirement | example | | ----------------- | --------------------------------- | ----------------------------------------------------- | ------------------------ | | `command` | string list (argv) | required, nonempty executable | `[node, api/server.mjs]` | | `dir` | string | Workspace root | `.` | | `env` | map of literal or typed env value | empty | `MODE: development` | | `port` | map | required | `{env: PORT}` | | `port.env` | string | required, nonempty | `PORT` | | `ready` | map | exactly one probe required | `{http: {path: /}}` | | `ready.http` | map | absent if TCP chosen | `{path: /health}` | | `ready.http.path` | string | required for HTTP, begins `/`; 2xx passes | `/` | | `ready.tcp` | empty map | absent if HTTP chosen; connection passes | `{}` | | `depends_on` | string list of Service names | empty | `[api]` | | `timeout` | Go duration string | `60s`; zero also defaults to `60s`; negatives invalid | `30s` | | `requires` | Resource name → permission list | empty | `{db: [read, write]}` | `command` executes argv without an implicit shell. Dependencies include transitive dependencies; cycles are invalid. Each Service gets one port. `dir` is joined to the Workspace root. ## environment values [Section titled “environment values”](#environment-values) These forms work in both Service and OperationDefinition `env`: | form / key | type | default / requirement | example | | ------------- | --------------------- | -------------------------------------------------------------------------- | ------------------------------------------- | | literal value | scalar | public config, not a secret | `MODE: development` | | `secret` | string file reference | nonempty, resolved at dispatch; relative to Workspace root unless absolute | `TOKEN: {secret: .antil/token}` | | `resource` | string Resource name | required with `output`; declared Resource must exist | `DATABASE_URL: {resource: db, output: url}` | | `output` | string | required with `resource`; `url`, `database`, `host`, `port` | `DB_NAME: {resource: db, output: database}` | `secret_env` and `resource_env` are internal JSON representations, not accepted YAML keys. Secret files are regular files of at most 1 MiB, nonempty after trailing CR/LF removal, with no NUL bytes. Resource `url` is a secret Output; status returns its reference. ### service output templates [Section titled “service output templates”](#service-output-templates) Templates expand in literal env values and argv strings. `$$` escapes a literal dollar sign. | template | value | | ---------------------- | ----------------------------- | | `${port}` | this Service’s allocated port | | `${services.api.port}` | `api`’s allocated port | | `${services.api.host}` | `127.0.0.1` | | `${services.api.url}` | `api`’s named proxy URL | A Service can reference itself or a transitive dependency. Named OperationDefinitions use explicit named Service references and require referenced Services to be ready; `${port}` has no own Service to resolve to there. ## stacks [Section titled “stacks”](#stacks) | key | type | default / requirement | example | | ------------------------ | ----------- | ----------------------------------------- | ------- | | `stacks..services` | string list | required, nonempty; every name must exist | `[web]` | Starting a selected Service also starts its transitive dependencies. If you declare a nonempty `stacks` map, name the desired StackDefinition at start or include a `default` entry for unnamed starts. ## resources [Section titled “resources”](#resources) `resources.` accepts exactly these keys: | key | type | default / requirement | example | | ------------------- | ---------------------------------- | ------------------------------------------------------------------------ | --------------------------------------- | | `kind` | string | required; only `postgres` | `postgres` | | `image` | string | `postgres:17-alpine` | `postgres:17-alpine` | | `sharing` | string enum | `private`; alternative `seed` | `seed` | | `on_conflict` | string enum | `reject`; alternatives `fork:empty`, `fork:seed` | `fork:empty` | | `claim` | map | defaults below | `{access: shared, permissions: [read]}` | | `claim.access` | string enum | `exclusive`; alternative `shared` | `shared` | | `claim.permissions` | string list | `[read, write]`; nonempty, no duplicates; choices `read`, `write`, `ddl` | `[read, write, ddl]` | | `schema` | string list of relative file paths | empty; contributes identity, does not execute SQL | `[seed.sql]` | | `seed` | map | absent; required for seed sharing or `fork:seed` | see below | | `seed.file` | relative file path | required when `seed` is set; SQL initialization | `seed.sql` | | `seed.validate` | string SQL query | required when `seed` is set; boolean validation | `SELECT count(*) = 2 FROM products` | Schema and seed inputs must be readable regular Workspace-relative files of at most 8 MiB each. They are exact file paths, not globs. Source hashes are checked again before use. Sharing also depends on relevant config and actual runtime image identity. Missing evidence prevents unsafe reuse. `wait` and `fork:snapshot` are not accepted conflict policies. Permissions coordinate antil-managed work; they do not configure database ACLs. ## operations [Section titled “operations”](#operations) `operations.` accepts the following keys. Named operations have no Service port or readiness probe. | key | type | default / requirement | example | | ---------- | ------------------------------- | --------------------------------------------------------- | ------------------------------------- | | `command` | string list (argv) | required, nonempty executable | `[node, --test, tests/shop.test.mjs]` | | `dir` | string | Workspace root | `.` | | `env` | map | empty; same literal / secret / Resource forms as Services | `{MODE: test}` | | `timeout` | Go duration string | `60s`; zero defaults to `60s`; negatives invalid | `30s` | | `requires` | Resource name → permission list | empty; declared nonempty lists use known permissions | `{db: [ddl]}` | ## rules [Section titled “rules”](#rules) | key | type | default / requirement | example | | ----------------------- | ------------------------------- | --------------------------------------------------- | ------------------- | | `rules..paths` | string list | required, nonempty; repository-relative patterns | `["migrations/**"]` | | `rules..requires` | Resource name → permission list | required, nonempty; known Resources and permissions | `{db: [ddl]}` | Supported glob operators: `*` and `?` do not cross `/`; `**` does; `**/` also matches zero directories. Absolute paths, parent segments and backslashes are invalid config. Bracket syntax is unsupported and evaluates as `unknown`, conservatively activating the Rule. Git observations that are incomplete or ambiguous also activate Rules. ## complete example [Section titled “complete example”](#complete-example) This is the runnable [shop configuration](/docs/quickstart/): ```yaml version: 1 project: shop services: api: command: [node, api/server.mjs] port: {env: PORT} env: DATABASE_URL: {resource: db, output: url} REDIS_URL: {secret: .antil/redis-url} ready: {http: {path: /health}} web: command: [bun, node_modules/vite/bin/vite.js, --host, 127.0.0.1, --port, "${port}", --strictPort] port: {env: PORT} depends_on: [api] env: API_URL: "http://127.0.0.1:${services.api.port}" ready: {http: {path: /}} resources: db: kind: postgres seed: file: seed.sql validate: "SELECT count(*) = 2 FROM products" operations: test: command: [node, --test, tests/shop.test.mjs] env: SHOP_API_URL: "http://127.0.0.1:${services.api.port}" ``` ## installation environment [Section titled “installation environment”](#installation-environment) These are environment variables read by the daemon at startup, not YAML keys. A running daemon keeps its settings until restarted. | variable | type / bounds | default | example | | ------------------- | ---------------------------------------- | ----------------------------------------------------- | ---------------------- | | `ANTIL_HOME` | directory path | `~/.local/state/antil`; CLI `--home` takes precedence | `/tmp/antil-docs/home` | | `ANTIL_PORT_RANGE` | inclusive `FROM-TO`, within `1024–65535` | `4000-4999` | `44100-44199` | | `ANTIL_PROXY_PORT` | integer `1–65535` | `1355` | `1365` | | `ANTIL_STALE_AFTER` | positive Go duration | `30m` | `1h` | Separate Installations do not coordinate ports. Use disjoint service ranges and different proxy ports. `ANTIL_RUNNER_TOKEN` is an internal runner credential, not user configuration. # blocker and conflict codes > Codes, readiness details and concrete recovery paths. The envelope’s `error.code`, persisted `blockers[].code`, and structured Conflicts are different surfaces. A readiness reason can appear inside a `service_failed` detail; it is not necessarily a separate Blocker code. Remedies below describe the action; use current `next_actions` for exact IDs and revisions. ## request and admission codes [Section titled “request and admission codes”](#request-and-admission-codes) | code | condition | remedy | | ------------------------ | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | | `invalid_config` | schema, YAML, reference or source-input validation fails | fix the reported path / file / line; explain the effective value | | `invalid_request` | arguments, checkout, unknown named action or control target invalid | correct the request from CLI help | | `session_exists` | Workspace has a non-ended Session | inspect it; end deliberately before a new Session | | `no_session` | request needs a registered Session / Project | start or adopt in the intended checkout | | `no_free_port` | allocation range cannot satisfy start | free ports or restart daemon with a suitable range | | `idempotency_key_reused` | key reused for incompatible intent | inspect original request; retain its key for retries | | `operation_in_progress` | mutation or retained impact prevents admission | inspect the referenced Operation; settle it before incompatible work | | `invalid_state` | lifecycle, resource ownership or other precondition invalid | inspect status and the specific error detail | | `config_changed` | effective config or declared source inputs differ from pinned evidence | review changes; end and start a new Session | | `permission_required` | a launch lacks a declared permission | use the current upgrade Remedy if compatible, or choose reviewed private allocation in a new Session | | `claim_conflict` | incompatible holder or Claim request | inspect holders; choose a private or explicit allocation-time fork policy | | `claim_changed` | Claim/Binding revision no longer matches the request | read fresh status and use a current revision | | `prune_changed` | deletion preview is stale | review a fresh prune preview | A permission Conflict additionally reports its `cause`, optional `rule`, Resource, matched paths, missing permissions, Allocation/Binding generations and Remedies. A Conflict can coexist with a running Session. ## persisted blockers [Section titled “persisted blockers”](#persisted-blockers) | code | meaning | remedy | | ---------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | `service_failed` | startup failed; detail includes readiness / exit cause and log | inspect log, fix the Service, end and start | | `service_exited` | previously ready Run exited | inspect exit/log; decide whether to restart the Session | | `run_unknown` | exact execution is not known | inspect runner and Run; never repeat on a guess | | `stop_unknown` | lifecycle cannot prove process-group stop | restore evidence or inspect before explicit abandon | | `runner_lost` | runner unreachable | inspect processes; recover using the same Installation | | `runner_incompatible` | runner cannot be trusted as the expected execution | inspect version / incarnation; do not signal unproven processes | | `command_failed` | named command failed, was refused before start, or exceeded execution timeout | inspect Run/log and stop evidence; use explicit linked retry when eligible | | `command_unknown` | arbitrary command execution or group completion uncertain | inspect/cancel the exact Run; retain its resource impact | | `command_stop_unknown` | explicit cancel cannot confirm exact group stop | preserve holds and inspect the existing execution | | `prune_failed` | an exact release failed or became uncertain | inspect the original prune; retry only its remaining releases | | `resource_failed` | provisioning/validation failed or became uncertain | inspect the exact Allocation; reconcile only proven-quiescent retained work after end | | `workspace_failed` | checkout creation failed | inspect the requested path and git worktree registry | | `workspace_unknown` | interrupted checkout creation cannot be established | inspect existing checkout before correction or retry | Launch refusals can also persist admission codes such as `permission_required` as Blockers. ## readiness detail reasons [Section titled “readiness detail reasons”](#readiness-detail-reasons) | reason | remedy | | -------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | `port_taken` | stop only the process you independently own or fix a server ignoring its assigned port; do not kill the foreign listener by guess | | `ipv6_only` | bind the server to `127.0.0.1` | | `ambiguous_listener` | resolve unverified listener ownership; a passing probe is insufficient | | `timeout` | inspect startup/logs and probe path; adjust Service timeout only after diagnosing | | `spawn_failed` | fix executable, work directory, secret reference or environment | | `exited` | inspect the exit code and log | ## runtime failures [Section titled “runtime failures”](#runtime-failures) Infrastructure errors also use literal diagnostic codes: `bad_request`, `bad_response`, `client_error`, `config_create_failed`, `config_unreadable`, `config_write_failed`, `daemon_unavailable`, `daemon_unreachable`, `exclude_failed`, `executor_halted`, `git_inspect`, `git_reference`, `git_unavailable`, `home_error`, `log_unreadable`, `package_unreadable`, `state_error`, `workspace_conflict`, `workspace_path`. These describe I/O or internal failure surfaces rather than a stable lifecycle transition. Read `error.message` and any `path`, preserve unknown ownership, and follow current Remedies. Do not edit `state.db` to bypass a Blocker. # file locations > Project config, per-Installation state, sockets, logs and credentials. ## project and workspace [Section titled “project and workspace”](#project-and-workspace) | path | purpose | | -------------------- | ---------------------------------------------------------------------- | | `antil.yaml` | committed project configuration | | `.antil/local.yaml` | optional uncommitted Workspace overlay | | `.antil/…` | recommended secret files; keep excluded from git | | git common directory | Project / worktree identity shared by checkouts | | git `info/exclude` | local exclusion modified only when requested by `init --exclude-local` | ## installation [Section titled “installation”](#installation) Home precedence: CLI `--home`, then `ANTIL_HOME`, then `~/.local/state/antil`. Home is canonicalized so symlink spellings use the same Installation lock and socket directory. | relative to home | purpose | | ----------------------- | ----------------------------------------------------------- | | `state.db` | SQLite state and durable event record | | `daemon.lock` | single daemon lock for this physical home | | `daemon.log` | daemon diagnostics | | `logs/run_….log` | managed Run output; exact path returned in JSON | | `logs/runner-ses_….log` | runner diagnostics | | `private/` | installation-private resource credential files, mode `0600` | The home and runtime directories are created with mode `0700`. Resource credentials remain references in projections. Do not edit SQLite or credential files as a recovery workflow. ## runtime sockets [Section titled “runtime sockets”](#runtime-sockets) Sockets live under `/tmp/antil--/` to fit Unix socket path limits. The derived directory uses a hash of the canonical home, mode `0700`. | path in runtime directory | purpose | | -------------------------- | --------------------- | | `daemon.sock` | CLI to daemon API | | `runner-.sock` | Session runner API | | `runner-.lock` | runner ownership lock | A Session end retains git worktrees and database data. Automatic TTL deletion is not implemented. See [explicit prune](/docs/reference/cli/#antil-prune). # json envelope > Fields, result projections, and next actions in antil/v1alpha1. Operational commands with `--json` use one envelope. CLI help and shell completions are utility text; human `skill print` returns guidance directly. | field | type | meaning | | -------------- | --------------- | ------------------------------------------------ | | `apiVersion` | string | `antil/v1alpha1` | | `status` | enum | [status and exit codes](/docs/reference/status/) | | `operation` | optional object | durable request `id`, `kind`, `state` | | `data` | optional value | command-specific result | | `error` | optional object | `code`, `message`, optional `path` | | `conflicts` | array | permission conflicts; empty when none | | `blockers` | array | persisted problems; empty when none | | `next_actions` | array | concrete commands and their preconditions | ## actual shop test result [Section titled “actual shop test result”](#actual-shop-test-result) Unedited output from `antil run test --json --idempotency-key shop-test-a`. IDs, paths, and times vary. ```json { "apiVersion": "antil/v1alpha1", "status": "ok", "operation": { "id": "op_dcd6b4b0cb0b", "kind": "run", "state": "succeeded" }, "data": { "operation_id": "op_dcd6b4b0cb0b", "definition": "test", "run": { "group_gone": true, "id": "run_6f6b813ee8b8", "state": "succeeded", "pid": 59894, "log_path": "/private/tmp/antil-docs/shop-home/logs/run_6f6b813ee8b8.log", "exit_code": 0 }, "config_revision": "cfg_3d2eca261455765f", "deadline": "2026-10-31T16:34:00.105105Z" }, "conflicts": [], "blockers": [], "next_actions": [ { "id": "command-log", "argv": [ "antil", "logs", "--run", "run_6f6b813ee8b8" ], "effect": "read the exact Run's redacted log" } ] } ``` ## session data [Section titled “session data”](#session-data) | field | contents | | ----------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `workspace` | immutable `id`, `project_id`, current `root` locator | | `session` | `id`, `stack`, `state`, `config_revision`, `liveness` | | `services` | map by Service name; `state`, `port`, `url`, `direct_url`, `run`, optional `observation` | | `config` | optional pinned vs current config: `pinned`, `current`, `changed`, optional `error` | | `resources` | optional map; Allocation ID/generation, Binding generation, database, engine, Claim, Outputs, applied schema and seed-baseline evidence | | `check` | optional last stored Rule report, not a new observation | | `commands` | optional list of named command Run projections | A Run contains `id`, `state`, `group_gone`, optional `pid`, `log_path`, `exit_code`. `group_gone` is completion evidence, not an inference from the leader’s exit alone. An Observation contains `detail` and `time`. Liveness contains `last_touched`, `expires_at`, `controller`, and `stale`. Resource Outputs contain a public `value` or `secret_ref`. Credential bytes are not part of these projections. ## other data projections [Section titled “other data projections”](#other-data-projections) | command | data | | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | `config explain` | effective `revision`, `path`, `value`, `origin` (`file`, `line`), optional per-value `values` origins | | `logs` | `run_id`, `service`, `log_path`, `content` | | `check` | report ID/time, pinned target/base, observed paths/revision/unknown reason, Rule evaluations, Claim revisions, Conflicts | | `run`, `operation retry` | Operation ID, definition, Run, config revision, optional deadline, retry source | | `operation cancel` | exact target Operation ID and Run with stop evidence | | `claim upgrade` | accepted Claim, resource/generations, current and previous revision, whether changed | | `prune` | preview revision, exact targets and retained reasons | | `workspace create`, `workspace adopt` | registered checkout ID, root, branch and pinned commit | | `init` | generated path, exclusion and review actions | | `skill print --json` | `content` string | ## blockers and remedies [Section titled “blockers and remedies”](#blockers-and-remedies) A Blocker contains `id`, `code`, `detail`, and optional `operation_id` and `run_id`. A Conflict identifies its Rule/cause, Resource, paths, missing permissions and exact generations, with Remedies. Each `next_actions` entry has `id`, argv array `argv`, optional `preconditions`, `effect`, and `expected_revision`. Treat argv as arguments, not shell source. Inspect preconditions before executing a destructive Remedy. A stored revision ties intent to the evidence observed; changed inputs can reject it. # .antil/local.yaml > Local overrides use the same keys, with precise merge and origin semantics. `.antil/local.yaml` is an optional Workspace-local overlay, read after `antil.yaml`. It accepts exactly the [version 1 schema](/docs/reference/configuration/), with no additional local-only YAML keys. Defaults are applied after merging. An overlay need not repeat required fields supplied by the project file. ## keys and defaults [Section titled “keys and defaults”](#keys-and-defaults) | key family | type | default in overlay | example | | ------------------------------ | ------------------------------------ | --------------------------------------------- | ------------------------------------ | | `version`, `project`, `target` | same scalars as project config | inherited | `target: main` | | `services` | map, full Service schema | inherited / empty when newly added | `web: {timeout: 30s}` | | `stacks` | map, `services` list per entry | inherited | `default: {services: [web]}` | | `resources` | map, full Resource schema | inherited / Resource defaults for new entries | `db: {sharing: private}` | | `operations` | map, full OperationDefinition schema | inherited | `test: {timeout: 30s}` | | `rules` | map, full Rule schema | inherited | `migrations: {paths: ["schema/**"]}` | All child keys, types and defaults are identical to those in the `antil.yaml` reference. ## merge semantics [Section titled “merge semantics”](#merge-semantics) | value | behavior | | ---------------------------------- | ------------------------------------------------------------------ | | map | merge by key | | scalar or list | replace the previous value; lists do not append | | `null` | delete the key, then validate the merged config and apply defaults | | Service `ready` or `port` | replace the whole definition | | one Service or operation env entry | replace the whole literal / secret / Resource reference | Duplicate keys are rejected in each input layer even if a later overlay would replace them. Unknown fields are invalid in the effective schema. Probe and port definitions do not partially merge. ## example [Section titled “example”](#example) ```yaml services: web: timeout: 30s env: TOKEN: {secret: .antil/token} MODE: development ``` A secret reference resolves at dispatch. `config explain` returns references and each effective value’s source file and line. Keep `.antil/` excluded from git. Local overlays participate in the effective config revision; they do not replan an existing Session. # status values and exit codes > Envelope outcomes, Session lifecycle, Runs and observations. ## envelope status [Section titled “envelope status”](#envelope-status) | status | process exit code | meaning | | ---------- | ----------------- | ------------------------------------------------------- | | `ok` | `0` | request succeeded | | `failed` | `1` | execution or internal failure | | `invalid` | `2` | invalid arguments or configuration | | `conflict` | `3` | required permission is not held | | `blocked` | `4` | a valid request cannot proceed under current conditions | | `waiting` | `5` | accepted Operation has not resolved within the wait | `waiting` is not evidence of failure. Follow the returned Operation. Envelope status describes the command result; it is separate from lifecycle and observation fields. ## session state [Section titled “session state”](#session-state) | value | meaning | | -------------- | ------------------------------------------------- | | `provisioning` | start or resume is preparing resources / Services | | `active` | Session started | | `suspending` | stop work is proceeding toward suspend | | `suspended` | Services stopped, ports / Claims retained | | `ending` | stop work is proceeding toward end | | `ended` | lifecycle finished; retained data may remain | | `failed` | Session start could not complete | ## service state [Section titled “service state”](#service-state) `pending`, `starting`, `ready`, `failed`, `stopped`, `unknown` describe the ServiceAllocation. `unknown` preserves missing evidence rather than declaring a process stopped. Run evidence can also be `succeeded` for a completed named command; named command terminal outcomes are `succeeded`, `failed`, `unknown`. ## operation state [Section titled “operation state”](#operation-state) `accepted`, `running`, `succeeded`, `failed`, `outcome_unknown`, `cancelled` describe the durable request. `outcome_unknown` does not authorize automatic replay. A cancelled request does not erase the target Run’s execution history. ## other observations [Section titled “other observations”](#other-observations) Rule match results are `match`, `no_match`, `unknown`; unknown conservatively activates the Rule. `liveness.stale` is a boolean independent of lifecycle and ownership. Resource `applied_schema: unknown` and `seed_baseline: false` prevent interpreting unverified data as a reusable seed baseline. # terms > Literal CLI and JSON terms and their relationships. | term | meaning | | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | worker | A human or agent working in one Git worktree. | | Project | The repository and its committed `antil.yaml`, with one project identity. | | Workspace | A registered Git checkout; usually one worktree. | | Session | One worker’s runtime in a Workspace: selected services, resource access, and remembered configuration. At most one non-ended Session per Workspace. | | Service | A declared app process such as `web` or `api`. Tunnels provide its port and ready URL. | | Resource | A declared dependency such as the shop’s `db`. Chambers manage it. | | InstanceAllocation | A running resource server, currently a Docker Postgres engine. | | SliceAllocation | A worker’s logical resource inside a server, currently a Postgres database. | | Binding | The current connection from a Session’s resource name to its concrete Allocation. | | Claim | A Session’s declared access and permissions for that resource. These coordinate antil commands; they are not database ACLs. | | OperationDefinition | A named command in `operations`, such as `test`. | | Operation | A recorded CLI request with an ID and progress. | | Run | One execution of a service or named command. | | generation | A counter distinguishing replacement versions of the same resource identity. | | fresh seeded fork | A new private database initialized from seed SQL. It does not include current rows from another worker. | | live snapshot fork | A copy of the current database contents. Not available in this build. | | seal | Planned reusable test proof tied to matching code and environment. Existing Run records are not seals. | A Project has Workspaces; each active Workspace has a Session. A Session starts Services and holds Claims on Resources through Bindings. Multiple database SliceAllocations can share one engine InstanceAllocation. [JSON fields](/docs/reference/json/) use these literal terms. # what works. what’s next. > Available now, next, later, and antil cloud in user terms. ## available now [Section titled “available now”](#available-now) The free local tool is in early access on macOS, built from source. * **Tunnels: apps & urls.** Run each worker’s native apps in its own git worktree, with separate ports and local HTTP URLs. Wait for a real readiness check before opening an app. Pause, resume, end, or hand a worker to another agent. * **Chambers: shared resources.** Run one Postgres engine for compatible workers. Give each worker a private database, initialize it from validated seed SQL, or explicitly share a seeded database. Retain data until you choose to prune it. * **Foundation.** Read one JSON contract with problems and next steps. Recover existing work after a daemon restart. Keep unknown command outcomes visible instead of automatically repeating them. * Run named commands such as the shop’s tests and read their exit status and logs. Every new test request runs again; these records are not seals. ## next coming [Section titled “next ”](#next-) * **Chambers:** describe any resource with a recipe, including worker-specific cache namespaces and object-store buckets. Make a private fork before changing shared structure. * **Tunnels:** open any worker’s app from your phone over HTTPS on your private network. * **Seals: proof & cache:** reuse a passing test only while the code and environment it proved still match. Run again when inputs change or are unknown. Local seals will be free. These features have no configuration switch or release date yet. ## later coming [Section titled “later ”](#later-) Follow a problem across app logs and test results without reconstructing several terminal histories. See what is running without visiting every worktree. Broaden platform support beyond the first macOS release. ## antil cloud cloud [Section titled “antil cloud ”](#antil-cloud-) **Paid and in early access; not available yet.** Share seals across people and machines, let CI check trusted proof before rerunning tests, share remote resources, and see team-wide activity. The free local tool does not require a cloud account. [Cloud scope](/docs/cloud/overview/) · [run the shop today](/docs/quickstart/) # seals > Coming: proof & cache tied to the code and environment a test actually used. coming **Proof & cache.** Five agents should not repeat the same passing checkout test against the same code and environment. Seals will let the next worker reuse what the first one proved. *Like a mark on a path already checked: it counts only while the path is the same.* ![A painted tunnel records the path already walked.](/docs/art/b-proof-trail.webp) prove it once. changed or unknown? run it. Coming: a passing shop test records its code and environment. A matching request reuses the seal; changed or unknown inputs run the test again. ## the environment is part of the proof [Section titled “the environment is part of the proof”](#the-environment-is-part-of-the-proof) A shop test depends on more than its source file. It also depends on the API it called, its configuration, and the database it used. A passing test will leave a seal of exactly those inputs. Same inputs: reuse. Changed inputs, or inputs antil cannot establish: run. For example, a checkout test that passed before a product price changed must not justify skipping a test against the changed data. The same applies to a stale Redis catalog entry or an external payment sandbox whose state cannot be checked: antil must run again when it cannot establish a match. These are intended rules, not a shipped cache contract; the exact supported input checks are not defined yet. [Tunnels](/docs/tunnels/) identify the running apps. [Chambers](/docs/chambers/) identify the resources behind them. The [foundation](/docs/foundation/) gives agents one way to inspect the result. ## what you can do today [Section titled “what you can do today”](#what-you-can-do-today) You can [run the shop tests through antil](/docs/seals/run-tests/) and inspect their exit status and logs. Every fresh request runs them again. Those records are **not seals**; local result reuse and automatic test selection are not available yet. Local seals are planned for the free, open-source tool. ## when proof crosses machines [Section titled “when proof crosses machines”](#when-proof-crosses-machines) A seal from a teammate or a developer laptop needs a trust check before CI can use it. [CI and shared seals](/docs/seals/ci/) belong to **antil cloud: paid, in early access, and not available yet**. # let ci check a seal > Coming with antil cloud: share trusted proof across people and machines. coming cloud Your shop’s checkout tests pass on a developer machine. CI will check who produced that seal and whether the code and environment still match. It will reuse an acceptable seal and run tests when proof is missing, changed, unknown, or untrusted. This is **antil cloud: paid and in early access**. Cross-machine seals and the CI gate are not available yet. There is no CI command or workflow file to install today. Local seals are also coming and will be part of the free tool. [Seals: proof & cache](/docs/seals/) · [cloud scope](/docs/cloud/overview/) # run the shop’s tests > Run catalog and checkout tests now; inspect their actual result. Start the [shop](/docs/quickstart/) in the current worktree. Run its named test command: ```sh antil run test --json ``` Check `status`, `operation.state`, and `data.run.exit_code`. For a successful run these are `ok`, `succeeded`, and `0`. Use the `antil logs --run …` command returned in `next_actions` to see the test report. If the response is `waiting`, use the returned action to follow the existing request. If execution is unknown, inspect that request before deciding to retry. Do not treat a successful HTTP response from the CLI as a passing test. To add a different test runner, change `operations.test.command` in `antil.yaml`, then end and start that worker. antil runs an argv list without an implicit shell. Keep the API address in `SHOP_API_URL` or use your runner’s equivalent. **Seals are coming.** A new test request runs the tests again; neither test caching nor CI reuse is enabled by this guide. [How seals will work](/docs/seals/). # install antil > Build the local CLI on macOS. This guide is for early-access users who already have an antil source checkout. There is no public clone-and-install path or packaged release documented for this build; get source access from the person who supplied your early-access checkout. Use that supplied revision. You need macOS, Git, and Go 1.26.5 or newer. The five-minute shop lesson starts **after** installation and its other prerequisites are ready. From the root of that checkout: ```sh mkdir -p /tmp/antil-docs CGO_ENABLED=0 go build -o /tmp/antil-docs/antil ./cmd/antil export PATH="/tmp/antil-docs:$PATH" antil --help ``` The help begins: ```text Isolated-but-shared dev stacks for parallel agents Usage: antil [command] ``` Keep the binary somewhere persistent if you want it to survive a system cleanup of `/tmp`. For the [shop quickstart](/docs/quickstart/), also install Node.js, Bun, and Docker Desktop or OrbStack, and start Docker. The first wave supports macOS; Linux is not a supported release target yet. # tunnels > Apps & urls: one running app per worker, ready only when it answers. **Apps & urls.** Git worktrees separate files. Tunnels give those worktrees separate running apps, so the checkout agent cannot take over the catalog agent’s port. *Like separate paths through a nest: each worker reaches its own app.* ![Parallel painted paths give each worker its own route to an app.](/docs/art/b-share-lanes.webp) each worker gets its own running app. Two shop worktrees start separate API and web processes. Each passes its health check before its own browser address opens. ## ready means you can use it [Section titled “ready means you can use it”](#ready-means-you-can-use-it) In the shop, each worker runs a Vite storefront and a Node API. antil picks their ports, starts the API first, and waits for its `/health` check. Then it starts the storefront. An app gets a live address only after antil verifies its process owns the port and its readiness check passes. This catches an easy mistake: a server that ignores its assigned port must not borrow a successful health response from somebody else’s server. ## an address belongs to a worker [Section titled “an address belongs to a worker”](#an-address-belongs-to-a-worker) Each app has a direct loopback address and a named local HTTP URL. Your browser or agent reads it from `status --json`. An ended or unready worker does not keep serving through its named URL. These addresses work on this machine; [HTTPS over your private network is coming](/docs/tunnels/https/). Tunnels use [chambers](/docs/chambers/) for data and will give [seals](/docs/seals/) the running environment behind a test. The [foundation](/docs/foundation/) keeps the same state available to a replacement agent. [Open the right app](/docs/tunnels/open-apps/) · [configure the shop](/docs/tunnels/configure-apps/) · [pause a worker](/docs/tunnels/pause/) # configure the shop’s apps > Wire the storefront to the API and wait for both to answer. Use the [shop starter](/docs/quickstart/) as the working repository. Keep the API listening on `process.env.PORT` at `127.0.0.1`. In `antil.yaml`, configure the two services: ```yaml version: 1 project: shop services: api: command: [node, api/server.mjs] port: {env: PORT} env: DATABASE_URL: {resource: db, output: url} REDIS_URL: {secret: .antil/redis-url} ready: {http: {path: /health}} web: command: [bun, node_modules/vite/bin/vite.js, --host, 127.0.0.1, --port, "${port}", --strictPort] port: {env: PORT} depends_on: [api] env: API_URL: "http://127.0.0.1:${services.api.port}" ready: {http: {path: /}} ``` The Vite configuration in the starter forwards `/api` to `process.env.API_URL`. Keep `--strictPort`: a port collision should fail rather than silently pick another address. Inspect the effective settings: ```sh antil config explain services.web --json ``` After editing a running worker’s config, end and start that worker to use it: ```sh antil session end antil session start --json ``` Check `data.services.api.state` and `data.services.web.state` are `ready`. If either fails, inspect its returned log. For every accepted field, see [configuration](/docs/reference/configuration/). # open a worker from your phone > Coming: private HTTPS addresses for your workers. coming Your checkout agent fixes a mobile layout. You will open that worker’s storefront on your phone over HTTPS, through your private network, without exposing a development server to the public internet. These will still be **tunnels: apps & urls**, with one address per worker and the same readiness checks. Today the shop’s HTTP addresses work only on the machine running antil. There is no HTTPS or tailnet configuration to enable yet. [Use local URLs today](/docs/tunnels/open-apps/) · [roadmap](/docs/roadmap/) # open the right worker’s app > Read the ready URL for the shop in your current worktree. In the running shop worktree, ask for its current apps: ```sh antil status --json > /tmp/shop-status.json node -e 'const s = require("/tmp/shop-status.json"); if (s.status !== "ok" || s.data.services.web.state !== "ready") process.exit(1); console.log(s.data.services.web.url)' ``` Open the printed URL in your browser. Read a fresh address after starting a new session; do not copy one from another worktree or from these docs. For a command-line client that cannot resolve `*.localhost`, use `data.services.web.direct_url` instead. Both addresses stay on this machine. If the named address fails but the direct one works, check your client’s localhost DNS support and proxy settings. For an agent, pass the URL to its own browser tool. [Agent workflow](/docs/for-agents/) describes the JSON fields and returned next steps. # pause and resume a worker > Free the shop’s app processes while keeping its ports and data. From the shop worktree you want to pause: ```sh antil session suspend ``` The apps stop. Their ports and database remain reserved for this worker. Other workers keep running. Bring that worker back: ```sh antil session resume ``` Wait for `ready` before reopening its app. Use `antil session end` when the worker is finished; its data remains until an explicit [prune](/docs/reference/cli/#antil-prune). # recover a worker > Find a failed shop service or hand an existing worker to another agent. Start in the affected shop worktree: ```sh antil status --json antil logs api --tail 20 ``` Read `blockers`, the service’s last check, and `next_actions`. If the API cannot connect, check Docker and run `bun run prepare:cache` in that worktree. If Vite is missing, run `bun install` there. Once you fix a known startup failure: ```sh antil session end antil session start --json ``` If a new agent is taking over an existing worker, record its name: ```sh antil status --takeover checkout-agent --json ``` A stale worker still owns its apps and data. Inspect it before ending it. If a named test command timed out or its result is unknown, follow the returned command to inspect that exact execution. Do not launch the same work under a new retry key just because your terminal lost the response. The [JSON reference](/docs/reference/json/) describes the result; the [CLI reference](/docs/reference/cli/) lists inspection, cancellation, and explicit retry commands.