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

Architecture overview

NXD keeps three concerns apart: the consumer repository owns what the infrastructure should be, the core engine owns what must change and in what order, and providers own how a specific system is actually mutated.

The boundaries are what make the engine testable and the providers replaceable. Each layer may only consume what the previous one produced.

Ownership boundaries

┌──────────────────────────────────────────────────────────────┐
│ Consumer repository                                          │
│ Topology, inventory, addresses, storage, site ACLs           │
│ SOPS/age encrypted secrets (references only, never values)   │
└───────────────────────────┬──────────────────────────────────┘
                            │ evalConfiguration  (offline)
                            ▼
┌──────────────────────────────────────────────────────────────┐
│ Core engine — nxd-core                                       │
│ Canonical JSON parsing and validation                        │
│ Action graph scheduling and risk classification              │
│ Plan persistence, approval evidence, journaling              │
└───────────────────────────┬──────────────────────────────────┘
                            │ provider port protocol
                            ▼
┌──────────────────────────────────────────────────────────────┐
│ Providers — crates/providers/                                │
│ nix         NixOS and Darwin build, transfer, activation     │
│ pve         Proxmox VE API, PXE assets, QDevice witness      │
│ pbs         Proxmox Backup Server API                        │
│ headscale   Tailnet control plane: users, keys, nodes, ACLs  │
│ identity    SOPS/age key sink and host trust                 │
│ vmware      VMware Fusion and ESXi VMX substrate             │
│ digitalocean, wsl                                            │
└──────────────────────────────────────────────────────────────┘

The core never speaks a provider’s protocol, and a provider never decides ordering. A provider that learned to schedule its own work would break the one property the whole design exists to provide: that ordering across provider boundaries is decided in one place.

Evaluation and canonical JSON

nxd.lib.evalConfiguration (or evalConfigurationUnstable for in-development schemas) evaluates consumer modules into a canonical JSON specification.

  • Offline. Evaluation performs no network I/O, contacts no API, and decrypts no secret.
  • Target-scoped. lib.selectTargetInventory extracts the resource subgraph for the selected targets, so planning one host does not evaluate every host in the site.

Offline evaluation is a correctness property, not a performance one: if evaluation could read live state, the plan would depend on the thing it is planning against, and two runs against a drifting endpoint could disagree.

Transport — nxd-transport

One shared SSH layer serves every provider.

  • Multiplexed by default. ControlMaster=yes with ControlPersist=10m reuses a single connection instead of paying a TCP and SSH handshake per remote command.
  • Host keys pinned. Every connection verifies the target’s key against the managed identity record. There is no prompt and no fallback.
  • Jump hosts. Targets behind a bastion route through SSH proxy-jump without ambient credentials.

These are one decision, not two. Before the transport was shared, twelve non-test modules across four providers invoked SSH and exactly one multiplexed — which meant host key handling was also inconsistent. Consolidating fixed both.

Provider port

Providers implement four handlers, mirroring the pipeline:

HandlerResponsibilityMutates
observe()Report current live stateno
plan()Diff desired against observed, emit actionsno
apply()Execute approved actionsyes
verify()Assert live postconditions holdno

Only apply() mutates. plan() emitting an action is a proposal, not a commitment — nothing runs until a plan carrying that action is persisted, hashed, and approved.

Where to go next