The command line

replicator is one program with a verb per job. This page lists every verb, what it reads, its options and how it exits. The guides explain the ideas: Modular repositories for init, doctor and fleet bump, Environments for infra, The fleet file and Host specs for hosts and pods.

replicator <verb> [arguments] [--option value] [--flag]

Options are --name value, --name=value or a bare --flag. replicator help (or --help) prints the list; running it with no verb at all prints it and exits 2.

Exit codes

Every verb follows the same convention, so a workflow can tell a failure from a mistake:

CodeMeaning
0Done: the files are valid, the check passed, the change was made.
1The verb ran and said no: a validation error, a policy violation, skew between files, a missing secret, a failed run.
2The command was wrong: a missing or contradictory option, an unknown verb. Nothing was read or changed.

Two verbs add a meaning, and say so below: infra drift exits 2 for drift it found (a failure to run is still 1), and doctor --strict exits 1 for a warning. An unexpected error exits 1 with its stack on standard error.

What the verbs read

VariableUsed byMeaning
REPLICATOR_STATE_TOKENinfra, host, secretsThe one credential that opens the state and the stored secrets. Read from the environment, never from an argument.
REPLICATOR_STATE_ACCOUNTinfra, doctorThe account the token belongs to. A config naming another account is refused.
REPLICATOR_WORKDIRinfraWhere a run keeps its working files. Default: the runner’s temporary folder.
REPLICATOR_ENGINE_VERSIONevery verb that reads a rangeOverrides the engine’s own version, for a dry run against a repository’s declared range.
FACTORY_ROOT--host <id>The folder that holds hosts/<id>.yml. Default: the engine’s own checkout.
FACTORY_GITHUB_TOKENfleet bump, fleet readsA token of yours for private repositories (GITHUB_TOKEN is the fallback).

A host given as --host is a host id (hosts/<id>.yml), a path to a .yml file, or a host.ref (a repository reference).

Starting and checking a repository

init

replicator init [--dir <repo>] [--name <app>] [--zone <dns zone>]
                [--profile static-site|worker|site-and-worker]
                [--account <id>] [--ref <sha>] [--release <tag>]

Writes the files a repository owns and prints what to do next. It never overwrites: if any file it would write exists, it writes nothing, lists them and exits 1.

OptionMeaning
--dir <repo>The repository’s root. Default: the current folder.
--name <app>The app’s name; the state is kept under it. Default: the folder’s name, made lowercase and dashed.
--zone <zone>The DNS zone the stages’ names live in. Default: a placeholder doctor refuses.
--profile <name>What kind of repository: static-site (a site and its publishing contract), worker (an edge Worker) or site-and-worker (both). Without it, the bare config.
--staticThe same as --profile static-site. Refused beside a different profile.
--account <id>The account that holds the state, 32 hex characters. Default: a placeholder doctor refuses.
--ref <sha>The release commit the caller workflow pins, 40 hex characters. Default: a placeholder doctor refuses.
--release <tag>The release’s name, written as a comment beside each pin.

Every profile writes the dev-container file, replicator.config.ts (with its secrets block) and the caller workflow; static-site adds a static block to the config, worker adds worker/index.mjs, and site-and-worker adds both. Exit 0 written, 1 refused or invalid.

doctor

replicator doctor [--dir <repo>] [--fleet <fleet file>] [--strict] [--json]
replicator doctor [<fleet file>] [--host <id>] [--strict] [--json]

The first form checks that a repository’s files agree with each other, with the engine and with the state account; --fleet does the same for every repository of a fleet and reports which share an account. The second form is the skew report for a checkout: the engine’s pins, the caller’s pins, the base image, the gateway tag and the host. Both are read-only.

Each finding is ok, warn or SKEW. In the repository form the secrets are checked too: a name the environments read that the config’s secrets block does not declare, a name read with ctx.secret that the block marks required: false, and a declared name nothing reads are warnings.

OptionMeaning
--dir <repo>Check one repository, offline.
--fleet <file>Check a fleet file and each enabled repository at its pinned ref.
--host <id>The host to check against (the skew form).
--strictCount warnings as failures.
--jsonPrint the findings as JSON (findings, ok, and under --strict the warning count).

Exit 0 when there is no skew (and, under --strict, no warning), 1 otherwise, 2 for a missing path.

fleet bump

replicator fleet bump --to <release> [--fleet <file>] [--repos <owner/name,…>]
                      [--open-prs] [--widen-range] [--json]

Moves every repository’s caller workflows to a release’s commit. A dry run listing every file and pin unless --open-prs, which opens one branch and one pull request per repository with your own token (FACTORY_GITHUB_TOKEN or GITHUB_TOKEN). --widen-range raises an excluded upper bound of a repository’s engine range. Exit 0 when every repository was handled, 1 when one was blocked, unreachable or failed, 2 for a missing --to, a missing token under --open-prs, or --dry-run beside --open-prs.

Running a repository’s environments

Every infra verb needs --config <replicator.config.ts> --stage <stage> and the state token. The stage is a name the config lists, or a preview’s name such as pr-12.

VerbWhat it does
infra stagePrints what the stage name is (a listed stage or a preview, protected or not) as JSON. Reads the config only; no credential.
infra diffPrints every step a deploy would take and any policy violation. Never applies.
infra deployApplies the diff, only if it passes the policy. Publishes the stage’s plain outputs for other apps.
infra removeTears the stage down. A protected stage needs --allow-destroy '*'.
infra driftRefreshes the stage’s state from the cloud and compares it with the program. Never applies.
infra secrets set <NAME>Stores one secret of the stage; the value is read from standard input.
infra secrets get <NAME> --out <file>Writes one stored value to a file (mode 0600, masked in logs, never printed).
infra secrets checkLists the required secrets the stage lacks, by name only.
infra secrets ensureStores what the secrets block declares: given in a values file, else generated.

infra diff and infra deploy first check the declared secrets (when the config has a secrets block): if any required secret is missing, they stop before planning, name every missing secret at once and print the exact replicator infra secrets set NAME … line for each.

OptionVerbsMeaning
--config <file>allThe environments file.
--stage <stage>allThe stage to act on.
--allow-destroy <a,b,…>diff, deploy, remove, driftNames of the resources this run may delete or replace; * means every resource and works with remove alone.
--policy <fleet file>diff, deploy, driftAdds the host owner’s policy: rules (regions, labels, protected kinds, stage count).
--import <name=id,…>diff, deploy, driftAdopts live resources the program declares instead of creating them.
--env-out <file>deployWrites the reserved __env output to a private file; --env-format shell (default) or docker picks the line format.
--outputs-out <file>deployWrites the stage’s plain outputs as JSON.
--summary-out <file.md>driftWrites a short Markdown table for the job summary.
--if-absentsecrets setLeaves a stored value alone.
--all-stagessecrets set, ensureStores (or looks at) the app-wide value every stage reads until it stores its own.
--values-file <file>secrets ensureNAME=value lines. The file must be readable by its owner only (mode 0600); anything else is refused before a byte is read.
--jsonsecrets checkPrints ok, present, missing and optionalMissing as JSON — names only.

Exit codes: 0 done; 1 a policy violation, a missing required secret, a refused values file or a failed run; 2 wrong arguments. infra drift exits 0 when the stage matches its program, 2 when it found drift and 1 when the run itself failed, so a scheduled job can tell a finding from a failure.

Starting a host

Four verbs take a new host from nothing to a spec the engine accepts, with its secrets stored and its sign-in created, and two more write the files that name the repositories it runs. None of them creates or changes a server; host up does that.

init host

replicator init host --profile own-server|aws|hetzner --id <id> --zone <dns zone> [--gateway] [--private-images] [--dir <repo>]

Writes hosts/<id>.yml for one kind of host, checks it against the schema before writing, and prints what is left for a person: the secrets to store, by name, and the commands to run. It never overwrites.

ProfileThe hostAlso needs
own-serverA server you already run, on any cloud: the engine logs in over ssh, the server answers 80 and 443 itself, and you create one wildcard record. No vendor account is named.--address, --email; optional --ssh-user, --state local|aws|cloudflare
awsOne cloud account: the instance, its firewall, a static address, one wildcard record and the state.--region, --hosted-zone, --email; optional --instance-type
hetznerThe reference host: no public port, web traffic through an outbound tunnel, ssh over a private network.--account; optional --server-type, --location, --tag

--gateway adds the gateway: block: the sign-in service the host runs, which every public name needs (--gateway-tag <release tag> names the image release; the default is the release of the engine you run). On own-server and aws it also turns on app.serve: host, so the host serves the web app people sign in through, out of the same image. The list it prints then replaces the sign-in secrets with two commands: setup github-app creates the App and stores four of them, secrets rotate GATEWAY_BROKER_SECRET mints the fifth.

--private-images adds image.pull: the images the host pulls are private, so it stores a registry read token (ghcrPullToken, a read:packages token) and logs in to the registry with it only while it pulls.

--state says where an own-server host keeps its state: a folder on this machine (the default), or a bucket (aws with --region, cloudflare with --account).

init fleet

replicator init fleet --host <id> --repo <owner/name>[@<ref>] [--repo …]
                      (--users <login,login,…> | --org <organisation>)
                      [--workspace <owner/name>[@<ref>] …] [--default <owner/name>] [--dir <folder>]

Writes clanker.yml, the fleet file of a host that gives each engineer a pod, checks it against the schema before writing, and prints what is left for a person. It never overwrites.

OptionMeaning
--host <id>The host spec the file names, hosts/<id>.yml. The file need not exist yet; if it does not, the steps say how to write it.
--repo <owner/name>[@ref]A repository that gets a container. Repeat the option or give a comma list; at least one is required. The ref is a branch, main unless given (nothing is pinned).
--workspace <owner/name>[@ref]A repository that is cloned into every pod and gets no container (devcontainer: false). Repeats the same way.
--default <owner/name>The repository a fresh pod shows first; one of the --repo entries. Default: the first.
--users <login,…>Who may hold a pod, by login.
--org <organisation>Who may hold a pod: the members of this organisation. Exactly one of --users and --org is required.
--dir <folder>Where the file is written. Default: the current folder.

The ssh ports are not written: they follow the entries’ positions. The steps that follow are read from what was written: a dev-container file for each container repository (init devcontainer), the host spec if it is not there yet, the check with replicator validate (--offline reads no repository), and, when the host spec beside the file has a gateway: block, installing the host’s GitHub App on the repositories. Exit 0 written, 1 refused.

init devcontainer

replicator init devcontainer [--dir <repo>] [--name <repo name>] [--image <image>]
                             [--port <label>=<number>[:public][:<health path>] …]
                             [--postgres] [--engine <range>]

Writes .devcontainer/devcontainer.json for one repository: the standard file, with the block the engine reads under customizations.replicator (the dev-container file). It is read back through the same reader validate uses before anything is written, and it never overwrites.

OptionMeaning
--dir <repo>The repository’s folder. Default: the current folder.
--name <repo name>The repository’s short name: lowercase letters, digits and single dashes. Default: the folder’s name, lowercased and dashed.
--image <image>The image the container runs. Default: the base image, which holds none of the repository; replace it with the image the repository’s own build makes.
--port <label>=<number>[:public][:<health>]A forwarded port. Repeat for several; the first is the default port. public makes it reachable from a browser at its hostname; <health> (a path starting with /, the default port only) is probed after provision and must answer HTTP 200. Default: web=3000:public:/.
--postgresAdds the container’s own database (services.postgres).
--engine <range>The engine versions the file is written for. Default: the running engine’s major version. A range the running engine is outside of is refused.

Exit 0 written, 1 refused. The steps that follow are read from the ports and options given: what public and health mean for them, replacing image when none was given, and the validate --host check.

secrets import

replicator secrets import --host <id> --env-file <file> [--map <secret>=<VARIABLE>,…] [--dry-run] [--overwrite]

Stores every secret the host’s spec asks for that a private NAME=value file holds, so a credentials file another tool wrote reaches the host’s store without a value passing through a terminal. The file must be readable by you alone (chmod 600). A secret is looked up under its own name, its upper-case form (hetznerToken is HETZNER_TOKEN) and the usual variable names of its vendor; --map names any other. A secret already stored is kept unless --overwrite. Only names are printed. Exit 1 when a required secret is in neither the file nor the store.

setup github-app

replicator setup github-app --host <id> [--org <organisation>] [--name <app name>]
                            [--print | --code <code>] [--port <n>] [--github-url <url>]

Creates the one GitHub App the host’s gateway acts as, from the host spec, with one approval in a browser. It prints a link to a page served on this machine only; the page shows what the App will ask for and has one button. You confirm on GitHub, and the App’s id, private key and sign-in client are stored under the names the host’s gateway: block reads. Only names are printed.

The App asks for what the gateway hands out and nothing else: contents and workflows (a developer container pushes), actions and issues (its other scopes), metadata, and organisation members only on a host that admits people by organisation. It subscribes to no event and has no webhook. --org creates it in an organisation you administer; without it, in your own account.

Two things stay yours, and the command prints both: install the App on the repositories a developer container may push to, and, only if people sign in from a terminal, tick “Enable Device Flow” on the App’s settings page (there is no way to set that from a command).

FlagWhat it does
--printShows the App that would be created, where it would be created and the names it would store. No change.
--code <code>For a machine with no browser: take the code from the address GitHub sends you back to, and pass it in.
--name <name>The App’s name (GitHub requires it to be unique). Default: <host id>-replicator.
--github-url <url>A GitHub server of your own instead of github.com.

setup stripe-projects

replicator setup stripe-projects --id <id> [--zone <dns zone>] [--dir <folder>] [--bucket <name>]
                                 [--sign-in] [--apply] [--accept-tos] [--confirm-paid] [--json]

Opens the edge account a host can keep its state, its names and its tunnel in, through Stripe Projects: one sign-in, no console. It needs the Stripe command line with its Projects plugin, and a person signed in to it; an API key is not accepted, and one in the environment is kept from the tool.

  • A plan by default. Every step is asked of Stripe’s tool in its check-only mode, and each thing in the way is listed with what to do about it. Nothing is created.
  • --sign-in asks Stripe’s tool for a link and a short code, and prints them. A person opens the link and approves with the code; nothing is created. Run the plan again afterwards.
  • --apply creates the project and the resources, pulls the credentials into a private file in --dir (default .replicator-setup/<id>), then measures what the returned token can do with read-only requests and tells you which host that supports and the init host line to run.
  • It never accepts terms (--accept-tos), never confirms a paid service (--confirm-paid; a bucket is one) and never signs in on your behalf.

Stripe Projects sells accounts and services, not servers: the machine is yours, on any cloud, through init host. Keep --dir out of version control; it holds a credentials file.

Running hosts, pods and the fleet

These verbs act on the host owner’s files: a host spec (hosts/<id>.yml) and a fleet file (clanker.yml). The quickstart runs them from a workflow.

VerbWhat it does
validate [<file>] [--host <ref>] [--offline] [--json]Checks a fleet file or a dev-container file (which needs --host). --offline reads no repository.
plan [<file>] --user <login> [--host <ref>] [--entry <owner/name>] [--offline] [--json]Renders what a developer’s container or pod would be, without changing anything.
plan host <id> [--json]Previews the host’s resources behind the DNS gate. No apply.
host up|converge --host <id> [--fleet <file>] [--sysbox]Creates the host, or brings a running one up to date. With a gateway block, the fleet file says who may sign in.
host down --host <id> [--dry-run]Removes the host. --dry-run lists the steps.
host diagnostics --host <id>Read-only logs of the tunnel, the proxy and the gateway.
host outputs --host <id>Prints the stack’s outputs as JSON, never decrypted — what the outputs gate reads.
container provision|sync|teardown|purge --host <id> --user <login> [--repo <file>] [--entry <owner/name>]A developer’s container or, with a pod: block, their whole pod. purge alone removes volumes. sync takes --branch; teardown and purge take --by <admin>.
container access-check --host <id> --user <login> [--repo <file>] [--entry <owner/name>]A read-only preflight of the pod’s access to its repositories.
container select --host <id> --user <login> --repo <file> --entry <owner/name>Moves the pod’s public name and port 22 to one member. Restarts nothing.
fleet sync --host <id> [--fleet <file>]Converges every container on the host from the fleet file. Never restarts a running container.
fleet status [--host <id>] [--fleet <file>] [--offline] [--no-records] [--json]Every entry’s owner, kind, commit, and how many pods hold it. Reads only.
resources destroy --host <id> --name <resource>The only verb that empties and deletes a bucket.
secrets set <NAME> --host <id> [--developer <login>]Stores a host secret; the value is read from standard input. --developer stores it as that developer’s own.
secrets import --host <id> --env-file <file>Stores the host’s secrets from a private file (Starting a host).
setup github-app --host <id> [--org <organisation>]Creates the gateway’s GitHub App with one approval in a browser and stores it (Starting a host).
secrets list --host <id>The names the host’s store holds, never a value.
secrets rotate <NAME> --host <id> --yesMints a fresh gateway secret and stores it; never printed. Invalidates every pod’s broker capability, so it needs --yes.
tailnet cleanup --host <id> --names <a,b,…>Removes exactly the named device registrations from the private network.

Exit codes follow the table at the top: 1 for a refused or failed run, 2 for a missing --host, --user, --entry or --name.