Pre-release • v0.1.0

One graph for the OS and everything under it

NixOS made your hosts reproducible. The hypervisors, private network, and backup servers beneath them stayed manual. NXD closes that gap: one Nix-authored description of the whole environment, planned as one dependency-ordered graph, applied only against a digest you approved.

nxd switch medo
$ nxd switch medo --source ".#nxdConfigurations.lamt"
evaluating selected target outputs...
evaluated selected target outputs in 4s
approval required for switch plan .nxd/plans/switch-4592a2f32894.plan.json
plan digest: sha256:4592a2f32894b465309c87f2052ab9b97ee19799898d1cfc5ec5b69cb446a3ea  (3 actions, 1 target)

PLAN ACTIONS:
  1. deployment-target/medo      build -> nixos-rebuild switch    [ServiceImpacting]
  2. secret/hosts/medo/ssh-host  stage SOPS decrypted key         [IdentityCritical]
  3. deployment-target/medo      verify active system closure     [ReadOnly]

✓ [medo] build placement configured=auto builder=deploy@utils
✓ [medo] realized closure /nix/store/jpz7lcmwjzjymdr38kxh428rwa2arr04-nixos-system-medo
✓ [medo] active system verified: desiredSystemPath == activeSystemPath (delta +0)

How a change reaches a host

01 Evaluate

nxd.lib.evalConfiguration reads your Nix modules and emits a canonical JSON specification. Offline: no endpoint is contacted, no secret is decrypted.

02 Plan

nxd plan computes a dependency-ordered action graph across every provider, classifying each action by risk from ReadOnly up to IdentityCritical.

03 Approve

The plan is persisted and hashed. Applying requires approval of that exact digest, so the plan you reviewed is provably the plan that runs.

04 Apply & verify

Typed Rust providers mutate, then live state is re-read. Success means desiredSystemPath == activeSystemPath, not a zero exit code.

Why it is built this way

🧩

Ordering that crosses providers

A Tailscale auth key must exist before the guest that consumes it boots. That is an edge in the graph, not a step in a runbook — one scheduler orders identity, network, hypervisor, and OS together.

🔒

Plans are artifacts with identities

Approval binds to a plan digest, not a session. If configuration changes between planning and applying, the digest stops matching and the apply refuses rather than executing something you never read.

🔑

Secrets never reach the store

The Nix store is world-readable. Configuration carries binding references only; SOPS/age resolves them at apply time into memory or 0600 staging. Git holds ciphertext, the store holds nothing sensitive.

🛡️

One transport, pinned and multiplexed

Every provider shares nxd-transport, which enforces exact Ed25519 host key pinning with ControlMaster multiplexing by default. Host key verification and connection reuse are one decision, enforced in one place.

⚖️

Build placement is a decision

Closures build on a builder, the orchestrator, or the target, resolved per host. A low-memory node the orchestrator cannot realize for gets the reviewed derivation realized in its own mounted store instead.

🤖

Machine-readable end to end

Canonical JSON specs, --format json on plan and verify, typed provider contracts, explicit postconditions. Agents inspect real state and get empirical confirmation instead of parsing shell output.

Providers

ProviderResource kindsTransportMaturity
NixdeploymentTarget, nixSystemSSH multiplex, Nix remote buildActive
Proxmox VEpveCluster, pveVm, pvePxeAssetPVE REST, QDevice over SSHActive
Proxmox BackuppbsAppliance, pbsDatastore, pbsUserPBS RESTActive
Tailscale / HeadscaleheadscaleUser, headscaleNode, preauthKeySelf-hosted Headscale control planeActive
IdentitysshHostIdentity, publicArtifactSOPS/age key sinkActive
VMwarevmwareVmFusion / ESXi VMX substrateActive
DigitalOceandigitalOceanDropletDigitalOcean RESTEarly
WSLwslDistributionwsl.exe lifecycleEarly

Common invocations

Run NXD from your consumer repository against a pinned revision:

# Inspect the action graph before anything moves
nxd plan medo --source ".#nxdConfigurations.lamt"

# Switch the system closure on a live host
nxd switch medo --source ".#nxdConfigurations.lamt"

# Re-enroll into the tailnet, overriding the endpoint out of band
nxd switch medo=203.0.113.48 --reenroll --source ".#nxdConfigurations.lamt"

# Machine-readable output for agents and CI
nxd plan medo --source ".#nxdConfigurations.lamt" --format json

NXD is pre-release and under active development. It also runs the homelab it was built for. Star the repository to follow along, or read the technical manual for the full architecture.