Introduction
NXD is a pre-release infrastructure reconciler for Nix-authored desired state. It evaluates consumer-owned Nix modules into canonical JSON, plans changes, applies persisted plans, and verifies results through typed providers.
Current verified gates include:
- standalone extraction;
- core canonical contracts and fixture reconciliation;
- built-in PVE
BackupJobreconciliation; - a synthetic external-provider protocol trial.
Production use is not generally supported yet. Follow the compatibility matrix and feature evidence before relying on any behavior.
Quick start with disposable fixtures
This tutorial uses checked-in synthetic fixtures only. It does not contact a real provider.
nix develop
cargo run -- validate --config-json tests/fixtures/canonical/minimal-site.json --format json
cargo run -- plan synthetic/example --config-json tests/fixtures/canonical/minimal-site.json --format json
For the Gate C backup-job fixture:
cargo run -- validate --config-json tests/fixtures/infrastructure/backup-job-site.json --format json
For Nix-authored sites, use the shared model and let NXD project canonical resources:
{
nxd = {
stack.name = "example";
site.guests.web = {
cluster = "lab";
node = "pve-a";
vmid = 120;
tags = [ "production" ];
};
providers.pve.lab = {
endpoint = "https://pve.example.invalid:8006";
credentialBinding = "secret/pve-api";
};
secrets.bindings.pve-api = {
resolver = "sops-age";
document = "sites/example/pve.yaml";
key = "token";
};
operations.production.includeTags = [ "production" ];
};
}
Export it through nxd.lib.evalConfiguration as
nxdConfigurations.<site>, then run nxd validate --source .#nxdConfigurations.<site>. Validation does not contact provider endpoints or
resolve secret values.
Do not substitute production endpoints into fixture files. Real endpoint runs require an accepted runbook and explicit owner authorization.
Validate configuration
Use validate to parse and validate canonical JSON without contacting a
provider:
nxd validate --config-json tests/fixtures/infrastructure/backup-job-site.json --format json
Validation proves local shape and reference constraints. It does not prove live provider compatibility, credentials, trust material, or target identity.
Architecture overview
NXD follows this boundary:
Nix modules -> canonical JSON -> validation -> plan -> persisted apply -> verify
Consumer repositories own topology, inventory, policy, and reviewed state. NXD owns schemas, planning, provider execution, reports, and reusable contracts.
See docs/nxd-integration-design.md for the full accepted design.
Safety model
NXD separates planning from mutation:
planobserves state and writes a reviewable proposal;applyexecutes exactly one persisted plan;verifychecks live postconditions without producing mutation actions.
Secrets are represented by bindings in configuration and resolved only at runtime. Secret values must not appear in Nix, canonical JSON, plans, logs, events, argv, URLs, or committed documentation.
Plan cleanup is explicit and recoverable. nxd clean is a dry run unless
--apply is supplied, and even then it archives only expired plans without
approval, journal, or pin evidence. It never deletes reviewed evidence. Use
--pin <plan> before long-lived review, --unpin <plan> when that protection
is no longer needed, and --recover <archived-plan> to restore an archived
plan. Recovery does not bypass plan expiry, compatibility, or approval checks.
Interactive durable enrollment workflows (deploy, switch --reenroll, and install/convert intents) ask once for the exact enrollment plan and a displayed, bounded same-session continuation across selected targets. The continuation is freshly planned, persisted, digested, and validated against the authorized scope before approval evidence is written. Any changed source, target, action, provider, secret, dependency, or risk stops for explicit review. Non-interactive and separate plan/apply workflows continue to require approval evidence for each plan digest.
Default plan output and interactive approval prompts render a target-grouped summary of pending work, listing destructive and identity-critical actions first and naming exact resources touched. --verbose renders the full action graph with identifiers and dependency edges.
CLI reference
Generated from the released Clap command model. Do not edit by hand.
| Command | Purpose | Arguments and options |
|---|---|---|
nxd | Lifecycle reconciler for Nix-authored infrastructure | --debug, --verbose, --profile |
nxd clean | Preview or archive expired disposable plans in a consumer .nxd workspace | --root, --apply, --pin, --unpin, --recover |
nxd bootstrap | Serve one validated, bounded PVE bootstrap plan | |
nxd bootstrap serve | plan | |
nxd info | Show deployment-target metadata or its provider-observed endpoint | target, --ip, --wait, --source, --config-json, --format |
nxd exec | Execute a reviewed argv vector through the configured target provider | --target, --hosts, --source, --config-json, --format, --plan-only, --out, --parallel, command |
nxd validate | selections, --source, --config-json, --format | |
nxd show | ||
nxd show config | selections, --source, --config-json, --format | |
nxd show plan | path, --format | |
nxd show run | run_id, --format | |
nxd plan | Create a reviewed plan; --auto-apply continues through the shared mutation workflow | selections, --source, --config-json, --format, --intent, --install-mode, --enrollment-strategy, --reactivate, --reenroll, --convert-from, --offline, --host-identity, --out, --auto-apply, --approval, --parallel |
nxd apply | plan, --approval, --run-id, --parallel | |
nxd verify | selections, --source, --config-json, --format | |
nxd switch | Ergonomic plan --intent switch --auto-apply target workflow | target, --hosts, --source, --config-json, --format, --plan-only, --out, --parallel, --reactivate, --reenroll |
nxd deploy | Ergonomic plan --intent install --auto-apply target workflow | target, --hosts, --source, --config-json, --format, --plan-only, --out, --parallel, --reinstall, --replace, --enrollment-strategy |
nxd build | Ergonomic plan --intent build-only --auto-apply target workflow | target, --hosts, --source, --config-json, --format, --plan-only, --out, --parallel |
nxd boot | Ergonomic plan --intent boot --auto-apply target workflow | target, --hosts, --source, --config-json, --format, --plan-only, --out, --parallel |
nxd test | Ergonomic plan --intent test --auto-apply target workflow | target, --hosts, --source, --config-json, --format, --plan-only, --out, --parallel |
nxd capture | selections, --source, --config-json, --format, --out | |
nxd artifact | ||
nxd artifact build | selector, --source, --config-json, --format, --out, --parallel | |
nxd artifact verify | manifest, --format | |
nxd approval | Create or validate digest-bound approval evidence (never mutates targets) | |
nxd approval create | Create digest-bound approval evidence for a reviewed plan (no mutation) | --plan, --out, --principal, --expires-at-unix, --format |
nxd approval validate | Import and validate approval evidence against a plan without applying | --plan, --approval, --format |
nxd dashboard | Browse selected configuration, reviewed plans, and run history locally | --source, --config-json, --port, --open |
nxd monitor | run_id, --format | |
nxd cancel | run_id, --format | |
nxd completions | shell |
Provider resource reference
Generated from the released linked-provider metadata. Do not edit by hand.
| Provider | Kind | Schema digest | Required fields | Fields |
|---|---|---|---|---|
digitalocean | digitaloceanDroplet | sha256:2bac8f359929fc0b3bb8d72719e53f8daff773f226f52a72140225772a77f0d3 | kind, id, provider, secretBinding, hostname, region, size, image | dependsOn, deploymentTarget, hostname, id, image, kind, provider, region, secretBinding, size, state |
headscale | headscaleNode | sha256:8c4d7fa77b908e9ea49bf17f66a5afabb8b4d2ee2f423d0eda595a194157cc90 | kind, id, provider, hostname, user, state | dependsOn, deploymentTarget, hostname, id, kind, provider, state, user |
headscale | headscalePreauthKey | sha256:b2e25273a98294a26e6fbb4f6d470f83b09c9319ec308e139cf3af5f1f2ce4e7 | kind, id, provider, hostname, user, secretBinding | aclTags, dependsOn, deploymentTarget, ephemeral, expirationSeconds, hostname, id, kind, provider, reusable, secretBinding, user |
headscale | headscaleUser | sha256:873d90b4d429c0e8622e7175a86b16c29a0ec88c72801e9586afd5ac54dc38c6 | kind, id, provider, namespace | dependsOn, id, kind, namespace, provider |
identity | sshHostIdentity | sha256:3f9d9b7a4f109a86a0c0957baff576051f35733da3c9db612b7b56593030b648 | kind, id, provider, algorithm, state, secretBinding, publicBinding | algorithm, dependsOn, deploymentTarget, id, kind, provider, publicBinding, secretBinding, state |
identity | trustAnchor | sha256:28944458af3f8c288b1e796e06f7e0153852f74aa51bb1d1b18aca6a77df9d64 | kind, id, provider, anchorType, publicContent, digest | anchorType, dependsOn, digest, id, kind, provider, publicContent |
pbs | accessGrant | sha256:6c3d9ddd27ce418c8ae5331cc3653be4ea4b7c99c1756ad00600bd81f78e8371 | kind, id, provider, principal, path, role | dependsOn, deploymentTarget, id, kind, labels, path, principal, propagate, provider, role, state |
pbs | accessPrincipal | sha256:d35f4551152e10635d68d4571ba60948aaa9390e068d49ab99b749fcdb8adc49 | kind, id, provider, backupServer, principalId | backupServer, dependsOn, deploymentTarget, id, installBootstrap, kind, labels, principalId, principalType, provider, state, tokenSecretBinding |
pbs | backupNamespace | sha256:c12e1e6a652f4885601639bb230ca9407e973ee3fef8bc10fc74bdb77d6266d3 | kind, id, provider, datastore, namespace | datastore, default, dependsOn, deploymentTarget, id, kind, labels, namespace, operatorAccess, provider, state |
pbs | backupRemote | sha256:bc37e30edf353b53dd6df80ee5e3dc773ada8e813a9865d324641ec3677c722b | kind, id, provider, backupServer, remoteId | address, authId, backupServer, certificateFingerprintBinding, dependsOn, deploymentTarget, fingerprint, host, id, kind, labels, provider, remoteId, state, tokenSecretBinding |
pbs | backupServer | sha256:71a30cc93dc5089f70c4c2a1442c0074a706766a38e7732bbf1e018421ce55b8 | kind, id, provider, address | address, apiPort, dependsOn, deploymentTarget, guest, id, installAppliance, kind, labels, provider, pveNode, state |
pbs | backupSnapshot | sha256:434f23ab1e565f636dd30d5c466a07b391e46768a10f1385df5344ae8e805ad1 | kind, id, provider, datastore, namespace, backupType, backupId, maxAgeSeconds | backupId, backupType, datastore, dependsOn, deploymentTarget, id, kind, labels, maxAgeSeconds, namespace, proof, provider, state |
pbs | datastore | sha256:df0a681c7796065549c3b2277c4435b55e79b1903bdf875add8604140f18a439 | kind, id, provider, backupServer, datastoreId | backingMount, backupServer, datastoreId, dependsOn, deploymentTarget, garbageCollectionSchedule, id, kind, labels, path, provider, state |
pbs | pbsNotificationMatcher | sha256:4b81a411f930fa5513df085fe8a780a552c1eb68fee3c35cc252afccd4806beb | kind, id, provider, server, matcherId, severities, targets | dependsOn, deploymentTarget, disableDefaultMatcher, id, kind, labels, matchMode, matcherId, provider, server, severities, state, targets |
pbs | pbsNotificationTarget | sha256:c77895993ed855c4fe7754e7b4f06cfff581e20916e0fd97e6618f39863d0375 | kind, id, provider, server, targetId, smtpServer, port, smtpMode, username, fromAddress, mailto, smtpSecretBinding | author, dependsOn, deploymentTarget, fromAddress, id, kind, labels, mailto, port, provider, server, smtpMode, smtpSecretBinding, smtpServer, state, targetId, username |
pbs | prunePolicy | sha256:0cd7cbca858852080bd0363b37bfc63473053b8c254ad522e7dbe005193080b2 | kind, id, provider, policyId, datastore, schedule | datastore, dependsOn, deploymentTarget, id, keepDaily, keepLast, keepMonthly, keepWeekly, keepYearly, kind, labels, maxDepth, namespace, policyId, provider, schedule, state |
pbs | syncJob | sha256:56ff65c9bb94f972d84e3014ff278b47ac5057d42c59bd9b66c7a3da81ff1f3a | kind, id, provider, remote, targetDatastore | dependsOn, deploymentTarget, groupFilter, id, kind, labels, maxDepth, owner, provider, remote, remoteDatastore, remoteNamespace, removeVanished, schedule, state, targetDatastore, targetNamespace |
pbs | verificationPolicy | sha256:1d70c9bc0a456593a2ab14d5e193576c20e6fadd283de5aff5b4f1bfe0d3117a | kind, id, provider, policyId, datastore, schedule | datastore, dependsOn, deploymentTarget, id, ignoreVerified, kind, labels, maxDepth, namespace, outdatedAfterDays, policyId, provider, schedule, state |
pve | backupJob | sha256:fdacd8c718efb1a79610e1f8f598d2b357a8f0a3e77395cbe80005368a6c5643 | kind, id, provider, targetStorage, guests, schedule | compression, dependsOn, deploymentTarget, enabled, guests, id, kind, labels, mode, provider, schedule, state, targetStorage |
pve | guest | sha256:4165126114ca5b6390ddc34c35fdc16bd2ee9573387d5c71aef980e9a7a872d9 | kind, id, provider, guestType, vmid | dependsOn, deploymentTarget, endpointDiscovery, guestType, id, kind, labels, name, networks, node, onBoot, provider, provisioning, restoreSmoke, startupDown, startupOrder, startupUp, state, tags, vmid |
pve | networkAttachment | sha256:d8da16b89b7ed6365a25339fc164f1985af0eaf15e9c62ed669a99b41083c06a | kind, id, provider, node, iface | bridgePorts, dependsOn, deploymentTarget, id, iface, kind, labels, node, provider, state, vlanAware |
pve | pveAcl | sha256:7a5b916370029e823284d18f802c45de7e52f51c1a510205a9f695517f1c0ade | kind, id, provider, path, role, principal | dependsOn, deploymentTarget, id, kind, labels, path, principal, propagate, provider, role, state |
pve | pveCluster | sha256:2bf535cda2a2c5ba4112170930928e51a4200c5431c5854840a78caedb309ce7 | kind, id, provider, clusterId | apiPreference, clusterId, dependsOn, deploymentTarget, id, kind, labels, provider, qdeviceNodeId, qdeviceState, qnetEndpoint, qnetHostIdentity, qnetHostKey, state |
pve | pveHostBackup | sha256:5c15ff152d4596be75a783442608cc65100a3f3806d38b0b4ff7545415d2d278 | kind, id, provider, nodeId, sources, backupServer, accessPrincipal, pbsServer, pbsDatastore, pbsNamespace, pbsTokenId, pbsPasswordBinding, pbsServerCertificatePem, pbsTrustMode, schedule, maximumAge, state | accessPrincipal, ageRecipients, ageRecipientsBinding, backupServer, dependsOn, deploymentTarget, id, kind, labels, maximumAge, nodeId, pbsDatastore, pbsFingerprint, pbsNamespace, pbsPasswordBinding, pbsServer, pbsServerCertificatePem, pbsTokenId, pbsTrustMode, provider, schedule, sources, state |
pve | pveHostState | sha256:47142ba7f1aa2d1c7d9e9ca80fd5363196ad776c1a2dbd73ecb1bc74bd354b42 | kind, id, provider, nodeId, hostname, bootMode, files | bootMode, captureDirectories, dependsOn, deploymentTarget, files, hostname, id, kind, labels, nodeId, provider, state |
pve | pveNode | sha256:53acee0a63dd15d9ca1957d25320867d43bc18cd03163555d45c2962cc00644a | kind, id, provider, nodeId, proxmoxNodeName, cluster, address | address, cluster, defaultDiscoverySubnets, defaultDiskStorage, defaultGateway, defaultIsoStorage, defaultNetwork, dependsOn, deploymentTarget, id, kind, labels, nodeId, provider, proxmoxNodeName, state |
pve | pveNotificationMatcher | sha256:4bbea484840765c148c8e8be2b7cd4d85e99d3477f265f615fd0e0096c2f8e26 | kind, id, provider, matcherId, severities, targets | dependsOn, deploymentTarget, disableDefaultMatcher, id, kind, labels, matchMode, matcherId, provider, severities, state, targets |
pve | pveNotificationTarget | sha256:f1a0168848cc8da50a1accf2926c5d5728472f4d3a0fb220bab469c515b4bbcf | kind, id, provider, targetId, targetType, server, port, smtpMode, username, fromAddress, mailto, smtpSecretBinding | author, dependsOn, deploymentTarget, fromAddress, id, kind, labels, mailto, port, provider, server, smtpMode, smtpSecretBinding, state, targetId, targetType, username |
pve | pveRole | sha256:32fbeda88006826f0344772f95aa1326d066965aba38f2fbd1854d333f5122df | kind, id, provider, roleId, privileges | dependsOn, deploymentTarget, id, kind, labels, privileges, provider, roleId, state |
pve | storageAttachment | sha256:e18768864307d886f6cbab72615018eff360881fad91db5294dbf37e3f55af31 | kind, id, provider, storageId | content, dependsOn, deploymentTarget, id, kind, labels, nodes, path, pbsDatastore, pbsFingerprint, pbsFingerprintBinding, pbsNamespace, pbsPasswordBinding, pbsServer, pbsUsername, provider, state, storageId, storageType |
vmware | vmwareVm | sha256:569d121c6d0346ce7b04781f6c76aa8a7adafc6f394da04b0b5005158b3303e3 | kind, id, provider, vmxPath, name, architecture, cores, memoryMiB, diskGiB, installer | architecture, cores, dependsOn, deploymentTarget, diskGiB, id, installer, kind, labels, memoryMiB, name, provider, state, vmxPath |
wsl | wslDistribution | sha256:47407025f3757b41b12b41b25ccffb3f41849f7ae2a2845d58dd48a1c55c9f56 | kind, id, provider, windowsConnection, distribution, installRoot, archive, guestTransport, bootstrapUser | archive, bootstrapUser, dependsOn, deploymentTarget, distribution, guestHost, guestTransport, id, installRoot, kind, provider, state, windowsConnection, windowsPublicKey |
Shared Nix option reference
Generated from nix/modules/authoring/. Do not edit by hand.
| Namespace | Purpose |
|---|---|
nxd.site | Typed topology, lifecycle, identity, PVE, PBS, Headscale, recovery, notification, and retention declarations |
nxd.providers.<kind>.<name> | Linked-provider endpoint, credential binding, CA, dependencies, pinned arguments, and provider configuration |
nxd.operations.<name> | Explicit resource IDs or tags, lifecycle intent, and artifact-set selection |
nxd.secrets.bindings.<name> | One logical reference or one structured resolver/document/key reference |
nxd.secrets.publicBindings.<name> | A public whole-document or structured binding emitted as public/<name> |
NXD derives canonical IDs, provider resources, associations, operation sets, and allowlisted PVE host-state files. It refuses duplicate IDs, unknown or unused reserved operation tags, unsupported host-state paths, and empty operation policy.
Compatibility matrix
See docs/compatibility.md in the repository source. Released manuals will
include a generated compatibility matrix from release metadata.
Incident response
See docs/runbooks/incident-response.md in the repository source.
Do not include secrets, private endpoints, raw production journals, or unredacted infrastructure captures in public reports.
Release readiness
See docs/release/release-policy.md and docs/release/supply-chain.md.
NXD is pre-release. Release signing, SBOM, and provenance automation are planned pre-alpha work.
Security policy
See SECURITY.md in the repository root for current reporting instructions.
Common failures
Validation fails
Check for unknown fields, duplicate resource IDs, unresolved references, and invalid provider configuration.
Apply refuses a plan
Apply may refuse expired, stale, wrong-provider, incompatible, or changed-input
plans. Re-run plan after investigating the refusal.
Provider operation is ambiguous
Mutation disconnects and timeouts are ambiguous. Do not blindly retry mutation. Observe current state and replan.
Contributing
Read CONTRIBUTING.md, AGENTS.md, and docs/README.md before proposing a
change.
Non-trivial work requires accepted artifacts, tests, evidence, and review.