Configuration reference
A repository’s environments live in one file, replicator.config.ts, which also declares the secrets it reads. This page lists every option of
it. Environments is the guide with the reasoning; Examples has
complete files; The command line has the verbs that run them.
import { replicator } from "@clankernet/replicator";
export default replicator.app({
name: "acme",
stages: { staging: {}, production: {} },
run(ctx) {
return { stage: ctx.stage };
},
});
The file imports the framework and nothing underneath it; the engine reads it with its own copy of the framework, so the repository installs nothing.
replicator.app({ … })
| Option | Type | Required | Meaning |
|---|---|---|---|
name | string | yes | Lowercase letters, digits and dashes, at most 40, starting with a letter. The state is kept under it: never change it after the first deploy. |
stages | { [stage]: StageOptions } | yes | The environments, at least one. A stage name follows the same rule as name. |
run | (ctx) => outputs | yes | Builds one stage’s resources and returns its outputs (names and addresses, never a secret). May be async. |
engine | string | no | The engine versions this file was written for (>=1.25.0 <2.0.0). An engine outside the range refuses the file. A repository without one gets a doctor warning. |
state | { accountId?: string } | no | The account that holds the state and the stored secrets, 32 hex characters. It must equal the account the runner’s token belongs to. |
preview | PreviewOptions | no | One environment per pull request. Absent: no previews. |
plain | string[] | no | Stored values that are settings, not credentials (a zone id, an account id). The output check skips these names. |
dependsOn | OutputRef[] | no | The other apps’ outputs this one reads with ctx.output. |
Stages
stages maps a stage name to its options:
| Option | Type | Default | Meaning |
|---|---|---|---|
protect | boolean | true for production, else false | Keep this stage’s data (buckets, databases, sites) when the program drops it. A protected stage cannot be removed without --allow-destroy '*'. |
Previews
| Option | Type | Default | Meaning |
|---|---|---|---|
pattern | string | — | The stage name, with {number} for the pull request number, exactly once, after literal text: pr-{number}. |
ttl | string | 7d | How long a preview is meant to live: hours or days (12h, 7d), at most 30d. |
A preview is never protected and holds nothing that outlives it. The pattern may not be named
like production or a protected stage, and no listed stage may match it. Each refusal names its
rule: preview-pattern, preview-ttl, preview-protected or preview-collision.
dependsOn
Each entry names one plain output of another app, so the engine can read it before the stage runs:
| Option | Type | Required | Meaning |
|---|---|---|---|
app | string | yes | The producing app’s name. It cannot be this app. |
stage | string | yes | The producing app’s stage. |
key | string | yes | The output’s name: letters, digits and underscores, starting with a letter. |
accountId | string | no | The producer’s state account. The only legal value is this app’s own; another account is another trust domain and is refused. |
The context: ctx
run(ctx) receives:
| Member | Meaning |
|---|---|
ctx.app | The app’s name. |
ctx.stage | The stage being built (staging, production, a preview’s name). |
ctx.protect | Whether this stage’s data is protected. Never true for a preview. |
ctx.preview | Whether this stage is a preview. |
ctx.secret(name) | A secret stored for this stage (or for every stage); fails naming the missing one. |
ctx.optionalSecret(name) | The same, or undefined. |
ctx.output(app, stage, key) | A plain output another app published. The edge must be in dependsOn. |
run may return the reserved key __env, a flat record of environment names to strings: it is
written to the file infra deploy --env-out names and is never an output.
replicator.hostnames(zone, stage, rules?)
Returns a function host(sub?) that gives each stage its names inside one zone.
| Argument / option | Type | Default | Meaning |
|---|---|---|---|
zone | string | — | A DNS zone (acme.example). |
stage | string | — | The stage to name. |
rules.production | string | production | The stage that owns the apex and plain subdomains (api.acme.example). |
rules.labels | { [stage]: string } | the stage | The label another stage adds, flat-dashed: api-next.acme.example, never a second level. |
host() is the apex or the stage’s own name; host("api") adds one DNS label.
Presets
Plain functions you call at the top of run(ctx); each applies to every resource declared after it.
| Preset | What it does |
|---|---|
presets.labels({ key: "value" }) | Labels every server, firewall and login key. A label a resource sets itself wins. |
presets.stageProtection(ctx) | Protects buckets and sites on a protected stage and on no other, whatever a component’s default says. |
presets.site(ctx, conventions, name, args) | A static site on the zone’s conventions: the production branch, a tagged record per domain, the stage’s protection. conventions is { accountId, zoneId, productionBranch? } (default main); args is { project, domains }. |
replicator.transform(type, edit) is the primitive underneath: type is a provider’s type token or
a regular expression, and edit({ type, name, props, opts }) returns the props or opts to use
instead, or nothing. A rule lives for one stage’s run only.
Components
Every component takes a logical name first: new replicator.Bucket("uploads", { … }). The logical
name keys the resource in the stage’s state, so renaming it replaces the resource.
StaticSite
| Option | Type | Default | Meaning |
|---|---|---|---|
accountId | string | — | The account that holds the site. |
project | string | — | The hosting project’s name. |
productionBranch | string | — | The branch whose deploys are production. |
domains | string[] | none | Custom domains to attach. |
dns | { zoneId, app, stage } | none | When set, each domain also gets a tagged record in that zone. |
protect | boolean | true | Keep the project when the stack drops it. |
Output: url. The site’s content is published from the config’s static block, not by this component.
Worker
| Option | Type | Default | Meaning |
|---|---|---|---|
accountId | string | — | The account that holds the Worker. |
scriptName | string | — | The Worker’s name on the account. Never change it after the first deploy. |
module | string | — | The finished ES module, uploaded as it is. A relative path is read from the repository root (the folder that holds .replicator/). It must exist when the command runs. |
compatibilityDate | string | — | The runtime date the script was written for (2025-01-01). |
compatibilityFlags | string[] | none | Runtime flags (nodejs_compat). |
bindings | see below | none | What the script can reach. |
durableObjects | { migrations, adoptedTag? } | none | The history of the Worker’s durable-object classes, in order. adoptedTag is the tag a live script already reports. |
routes | { zoneId, pattern }[] | none | Path patterns on an existing zone (api.acme.example/*). |
domains | { zoneId, hostname }[] | none | Hostnames the Worker owns outright. |
protect | boolean | true with durable objects | Keep the script when the stack drops it. |
bindings, each as binding name: what it points at:
| Binding | Value | Meaning |
|---|---|---|
vars | text | Plain text variables. |
secrets | text, usually ctx.secret("name") | Sent as a secret, never shown in a plan, never an output. |
secretsStore | { storeId, secretName } | A secret that lives in a store (see SecretsStore). |
r2 | bucket name | An object bucket. |
hyperdrive | configuration id | A pooled database connection. |
kv | namespace id | A key-value namespace. |
services | script name | Another Worker. |
durableObjects | class name, or { className, scriptName } | A class this Worker exports (it must be in a migration), or another Worker’s. |
A migration is { tag, newClasses?, newSqliteClasses?, deletedClasses?, renamedClasses?: { from, to }[] }.
Add one to the end whenever the exported classes change; never edit or reorder the ones already
there. A binding name used twice is an error. Output: scriptName.
Bucket
| Option | Type | Default | Meaning |
|---|---|---|---|
accountId | string | — | The account. |
bucket | string | — | The bucket’s name. |
location | WNAM, ENAM, WEUR, EEUR, APAC, OC | WNAM | Location hint. Immutable after create. |
protect | boolean | true | Keep the bucket when the stack drops it. |
Output: name.
DnsRecord
| Option | Type | Default | Meaning |
|---|---|---|---|
zoneId | string | — | The zone. |
name | string | — | The record’s full name. |
type | A, AAAA, CNAME, TXT | — | The record type. |
content | string | — | The record’s value. |
proxied | boolean | true except for TXT | Pass traffic through the edge network. |
ttl | number | 300 when not proxied | Seconds. A proxied record’s is automatic. |
app, stage | string | — | Written into the record’s tag, so a sweep can tell ours from anyone else’s. |
deleteBeforeReplace | boolean | true | Two records of one name cannot coexist, so a replace deletes first. |
GeneratedSecret
| Option | Type | Default | Meaning |
|---|---|---|---|
length | number | 48 | Characters of the value. |
alphanumeric | boolean | true | Letters and digits only (some consumers refuse symbols). |
Member: value, a secret other components may read. It is never an output.
SecretsStore
| Option | Type | Default | Meaning |
|---|---|---|---|
accountId | string | — | The account. |
secretName | string | — | The secret’s name inside the store. |
value | string | — | The key. Stored as a secret, never an output. |
storeName | string | the component’s name | The store’s name. |
scopes | string[] | ["workers"] | Who may read it. |
comment | string | none | A note on the secret. |
existingStoreId | string | none | Put the key into a store another stack owns (an account may hold only one). This component then never creates or deletes the store. |
protect | boolean | true | Keep the store and the key: losing the key loses what it wraps. |
Outputs: storeId, secretName.
Hyperdrive
| Option | Type | Default | Meaning |
|---|---|---|---|
accountId | string | — | The account. |
origin | { host, port?, database, user, password } | port 5432 | The database to connect to. The password is stored as a secret and is never an output. |
configName | string | the component’s name | The configuration’s name. |
caching | { disabled?, maxAge?, staleWhileRevalidate? } | caching off | Query caching. |
originConnectionLimit | number | the provider’s own | The most connections opened to the database at once. |
protect | boolean | true | Keep the configuration. |
Outputs: id, name.
Database
The Postgres component: one project with its first database and role, protected by default.
| Option | Type | Default | Meaning |
|---|---|---|---|
apiKey | string | — | The database provider’s key (a stored secret). |
project | string | — | The project’s name. |
region | string | aws-eu-central-1 | Immutable. |
orgId | string | none | The organisation that owns the project, when the key spans several. |
pgVersion | number | 16 | Immutable. |
database | string | app | The first database. Immutable. |
role | string | app | The first role. Immutable. |
minCu, maxCu | number | 0.25, 1 | Autoscaling floor and ceiling, in compute units. |
suspendSeconds | number | the plan’s own | Seconds idle before it suspends. |
protect | boolean | true | Keep the project. |
Plain members: host, pooledHost, database, user. Secret members, never to be returned from
run: password, uri.
Server
| Option | Type | Default | Meaning |
|---|---|---|---|
sshPublicKey | string | — | The machine’s login key, public half only. |
cloudflareRanges | string[] | — | The edge network’s address ranges allowed to reach ports 80 and 443. Required and never empty. |
serverType | string | — | The machine’s size. |
location | string | — | The data centre. |
ssh | boolean | false | Also open port 22 to everyone. |
image | string | ubuntu-24.04 | The operating-system image. |
userData | string | none | First-boot script. Changing it replaces the machine. |
labels | { [key]: string } | none | Extra labels; managed-by is always set. |
hostname | string | the component’s name | The machine’s name at the provider. |
protect | boolean | true | Keep the machine. |
Output: ip.
The secrets block
secrets in replicator.app declares the stored values the environments read. replicator infra secrets check lists the required names a stage lacks; replicator infra secrets ensure stores
them; infra diff and infra deploy refuse a stage that lacks one; replicator doctor checks the
block against what the config reads, and ctx.secret of a name outside the block is an error. An
app without a secrets block is not checked up front, and any name may be read.
export default replicator.app({
name: "acme",
stages: { staging: {}, production: {} },
secrets: {
sessionKey: { description: "Signs sessions", generate: "random-hex-32" },
zoneId: { description: "The DNS zone's id" },
analyticsKey: { required: false },
},
run(ctx) {
/* … */
},
});
| Key | Meaning |
|---|---|
| (the name) | Letters, digits and underscores, starting with a letter. |
description | Printed beside the name when it is missing. Never put a value here. |
required | Default true: a stage cannot run without it. false for a value read with ctx.optionalSecret. |
generate | The runner can mint the value: random-hex-16, random-hex-32, random-base64url-32 or uuid. ensure generates it and never prints it. |
An unknown key is an error (a typo would otherwise be ignored), and a generated value cannot also be optional.
An older repository may declare the same list in secrets.schema.json (a JSON Schema with
required, x-generate and x-optional) beside its config. It is still read when the config has
no secrets block; with a block, replicator doctor says the file is ignored. Move the names into
the block and delete the file.
The values file for ensure --values-file <file> holds NAME=value lines (blank lines and
lines starting with # are skipped). The file must be readable by its owner only: a file that
group or others can read, or a link, is refused before any of it is read. A name the block does
not declare, a repeated name and an empty value are errors, reported by line number and never by
value. A name the stage already holds is kept, so running ensure again never rotates a key.
The host owner’s policy
The host owner’s fleet file may carry a policy: block, checked on every infra diff, infra deploy and infra drift that is given --policy <fleet file>. Each refusal names its rule.
| Option | Type | Rule | Meaning |
|---|---|---|---|
allowedRegions | string[] | region | The only locations a server, database or bucket may be created in (compared case-insensitively). |
requiredLabels | { key: value } | label | Labels every labelled resource must carry; a value of "" requires only the key. |
protectedKinds | bucket, database, dns, site, secrets, server | protected-kind | Kinds never deleted or replaced on any stage. --allow-destroy does not lift it. |
maxStages | number, 1 to 50 | stage-count | The most stages one repository’s file may declare. |
Without the block only the built-in rules apply: no data resource or DNS record is deleted or
replaced unless named with --allow-destroy, and on a protected stage nothing is deleted at all.