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.
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”| 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.<name> 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”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”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”| key | type | default / requirement | example |
|---|---|---|---|
stacks.<name>.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.<name> 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.<name> 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]} |
| key | type | default / requirement | example |
|---|---|---|---|
rules.<name>.paths |
string list | required, nonempty; repository-relative patterns | ["migrations/**"] |
rules.<name>.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”This is the runnable shop configuration:
version: 1project: shopservices: 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”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.