Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Safety model

NXD is built so that the destructive step is the one that is hardest to reach by accident. Every safety property below is a refusal in code, not a convention.

The core separation

CommandReads live stateWrites a proposalMutates infrastructure
planyesyesno
applyyesnoyes, for exactly one persisted plan
verifyyesnono

apply never re-plans. It executes one plan that already exists on disk, which is what makes the reviewed artifact and the executed artifact the same object.

Why approval binds to a digest

A plan is written to disk and hashed. Approval is recorded as evidence bound to that hash, so authorization cannot drift from what was reviewed.

apply refuses when any of these hold:

  • The plan expired. Plans carry expiresAtUnix. Applying past it fails with the expiry and current time, and the fix is a new plan — expiry is not overridable.
  • The digest does not match. Evidence names a specific plan digest. Evidence for a different plan is rejected.
  • The approval summary does not match the actions. approvalRequirements is recomputed from the plan’s own actions and install mode, and compared. A summary that was edited, stripped, or forged after planning is refused with “plan approvalRequirements do not match its actions; recreate and review the plan”. The summary is convenience data, never an independent authority.
  • Evidence is missing on a destructive plan with non-interactive stdin. CI cannot silently inherit a human’s approval. The refusal prints the digest, the expiry, every requirement with its risk and resources, and the two commands needed to proceed.
  • Evidence is supplied for a plan that needs none. Mismatched expectations are an error in both directions.

Approval evidence has its own constraints: the principal must be a non-empty operator identity under 128 characters with no control characters, expiry must be in the future, and evidence may not outlive the plan it approves. Evidence defaults to a one-hour TTL, clamped to the plan’s own expiry.

Risk classification

Each action carries one of five risk levels, ordered lowest to highest:

RiskMeaning
ReadOnlyObservation and verification assertions; changes nothing
ReversibleMutates state that can be restored without data loss
ServiceImpactingActivates profiles, reboots, or mutates running workloads
DestructiveRemoves or overwrites state that is not recoverable from NXD
IdentityCriticalModifies cryptographic identity or credential bindings

Evidence records the highest risk present in the plan, so approving a low-risk plan never authorizes a high-risk one.

Default plan output groups work by target and lists destructive and identity-critical actions first, naming the exact resources touched. --verbose renders the full graph with identifiers and dependency edges.

Secrets

Secrets appear in configuration only as bindings and are resolved at runtime.

A secret value must never appear in Nix expressions, canonical JSON, plans, logs, events, argv, URLs, or committed documentation. argv matters specifically because process arguments are readable by other users on the same host — secrets reach subprocesses through the environment or 0600 files, never as command-line arguments.

Enrollment continuation

Interactive durable enrollment (deploy, switch --reenroll, and install or convert intents) asks once for the enrollment plan plus a displayed, bounded same-session continuation across the selected targets.

The continuation is not pre-authorized work. It is freshly planned, persisted, digested, and validated against the authorized scope before any approval evidence is written. Any change to source, target, action, provider, secret, dependency, or risk stops for explicit review.

The split exists because minting is decided by reading the current persisted binding and matching it against live inventory, and minting mutates an input that the installing plan’s own planning reads. One plan cannot honestly contain both. Non-interactive and separate plan/apply workflows still require evidence per digest.

Plan retention

nxd clean is a dry run unless --apply is given. Even then it only archives expired plans that carry no approval, journal, or pin evidence — it never deletes reviewed evidence.

  • --pin <plan> protects a plan through a long review.
  • --unpin <plan> releases it.
  • --recover <archived-plan> restores an archived plan.

Recovery restores the artifact, not its authority: expiry, compatibility, and approval checks all still apply.

What this does not protect against

Stated plainly, because a safety model that implies more than it delivers is worse than none:

  • An operator who approves a plan without reading it. Approval proves what was authorized, not that anyone understood it.
  • Anything outside NXD’s model — a change made directly on a host, or through a provider’s own UI, is drift NXD can only detect at the next plan.
  • Correctness of your desired state. NXD converges infrastructure to what you declared; it has no opinion on whether that is what you wanted.