<!-- Generated from product/twins.html at build time. Do not edit. -->

> Canonical: https://antifailure.dev/product/twins

> A temporary copy of the application stack for every risky change, created per pull request and destroyed with it.

# A disposable production twin for every risky change.

Build the change, branch a sanitized database, isolate the network, replace production credentials, journal every resource as it comes up, and tear all of it down when the report is done.

Built candidate code.

Production-shaped, not production.

Runs inside the twin.

Restored for this run only.

Live credentials are replaced.

External effects are local or simulated.

Rows, traces, invariant results, and cleanup proof leave the boundary. The twin does not.

- 01 Build

- 02 Restore

- 03 Validate

- 04 Destroy

Topology of one disposable twin: candidate code and sanitized state enter an isolated run boundary, live production routes are blocked, evidence leaves, and the environment is destroyed.

## Every transition is idempotent and recoverable. A resource is journaled the moment it exists, not after the run succeeds.

Four normal events; failure can terminate the same journaled run.

- 0 1 env.creating Plan + provision Lock held; resources journaled.

- 0 2 env.ready Containment verified Candidate can accept workload.

- 0 3 env.destroying Reverse replay Journal drives teardown.

- 0 4 env.destroyed Terminal success Cleanup count is complete.

Failure is emitted where the run stops. The existing journal remains the recovery source for teardown.

Environment state machine showing the successful lifecycle from creating to destroyed, plus env.failed as a terminal event reachable from any earlier state.

- Plan Read the manifest, take the environment lock, write the plan.

- Provision Build candidate, restore safe state, replace credentials, verify containment.

- Run Agents drive the declared workflows. Invariants are asked of the data.

- Close Evidence attaches to the pull request. The journal is replayed in reverse.

Illustrative These are the lifecycle events one run emits, so a reader can run af up and af down and watch each of them arrive in the log. The last two are where a run stops: it was torn down, or it failed, which is emitted from any point before it. The run identifier is invented.

## Isolation is a spec, not a hope. An unresolved secret fails closed and stops the run.

These seven are in force today on the Docker runtime, which passes all thirty-two runtime conformance behaviours against a real daemon. The Kubernetes runtime is written and not yet proven to the same standard, and no cloud runtime exists. Convenience must not silently override containment.

Synthetic but production-shaped.

Can only see twin-scoped dependencies.

- 01 credentials creds replaced No production write credentials

- 02 database prod-db cut No production database route

- 03 network egress deny-by-default No default internet route

- 04 dns dns clone-local Separate DNS policy

- 05 secrets namespace twin-scoped Separate secrets namespace

- 06 ownership run_08f2 One label scheme, on everything

- 07 teardown Down_TouchesOnlyItsOwnEnvironment Touches only what it made

Containment boundary showing clone-local DNS, twin-scoped secrets, safe Postgres and a simulator gateway, with production database, live credentials and default internet routes explicitly blocked.

## A preview URL is not the product. The twin exists to answer whether the deployment is safe, then it is destroyed.

The output is a pass or a fail on the pull request, with the rows and the trace behind it. Not a dataset, and not an address somebody has to remember to shut down.

- $ af up create Build, restore, isolate, verify.

- $ af ci judge Run workloads and invariants.

- $ af down destroy Replay the resource journal.

CI decision flow in which af up creates the twin, af ci collects rows and traces into a pull-request gate, and af down destroys the environment; the preview URL is explicitly temporary.

## Cleanup is a first-class safety property. Resource deletion is not a background convenience.

Every resource is written to the journal as it is created, so a run that dies halfway still has a list of what it made. Teardown replays that journal in reverse and counts what it removed. A continuous integration step counts the managed containers and networks afterwards and fails the build if any are left.

Illustrative A teardown of one run. The journal, the reverse replay and the count of what was removed are real; the resource names and the timestamps are written.

af env prune removes environments older than a cutoff you pass.

There is no automatic time-to-live and no independent reaper yet. The sweep is a command a person or a schedule runs.

- 01 isolated network network

- 02 database state volume

- 03 candidate app container

- 04 side-effect simulator container

- 04 side-effect simulator delete

- 03 candidate app delete

- 02 database state delete

- 01 isolated network delete

Teardown can enumerate only the containers, network, and volume created for this run.

af env prune --before <cutoff>

Cleanup proof showing resources appended to a journal in creation order, filtered by the environment label, deleted in reverse order, and verified by zero-resource assertions.

- Safe State What gets restored into the twin. Read

- Side-Effect Firewall How side effects are contained. Read

- Load Traffic shaped like production's access log. Read

## Know what happens before you deploy.

Create a disposable production twin for every risky change. Catch migration failures before they reach customers.
