Documentation
A summary of the documents in the Go repository. Where they differ, the repository is right.
Build with `make build` (Go 1.26+), then `./bin/dh dev up --dir ./devcluster` starts a real local cluster of 3 control-plane members and 4 hosts. For a real installation, follow docs/runbooks/install.md: dh init, dh-control on three machines, dh cp bootstrap, dh node invite, dh-noded --join-file.
Build
README.mdRequirements: Go 1.26 or newer. Optional: Docker (container runtime and the oom chaos scenario), Python 3 (the independent conformance implementation) and Postgres (evidence mirror).
`make build` produces five binaries in bin/: dh (operator CLI), dh-control (control-plane member), dh-noded (host agent), dh-conformance (vector generator and runner) and dh-beacon (the sample workload).
$ make build
Local cluster
README.md`dh dev up` starts 3 control-plane members and 4 hosts (one of them an edge) as real local processes bound to loopback, pushes the dh-beacon artifact, deploys a 3-replica app and prints a console URL with a session capability in its fragment. It serves the API without TLS unless you pass --tls. Flags: --cps, --hosts, --edges, --base (first port, default 17700).
$ ./bin/dh dev up --dir ./devcluster$ export DH_HOME=./devcluster/operator$ ./bin/dh dev status --dir ./devcluster$ ./bin/dh dev down --dir ./devcluster
Install: trust root
docs/runbooks/install.md §1`dh init` creates the cluster root key and root CA in $DH_HOME. The root key is the trust anchor every host pins: keep it offline or on a hardware-protected machine. You need it to bootstrap and change membership, create invites, and sign rosters, federation agreements and root rotations. Day-to-day operations use short-lived capabilities derived from it. TLS is on by default.
$ export DH_HOME=~/.dh$ dh init --cluster prod
Install: control-plane members
docs/runbooks/install.md §2–3Run dh-control on three machines. Before it has credentials a member serves a throwaway self-signed certificate and writes its fingerprint to bootstrap.fingerprint, next to the one-time bootstrap.code. Copy both to the operator machine over a channel you trust. The CLI pins that certificate while bootstrapping, so the code and credentials never travel in cleartext; afterwards members serve root-issued certificates and the CLI verifies them against root-ca.pem. Expected `dh cp status`: one leader, two followers, three voters.
$ dh-control --data /var/lib/dh-control --api 0.0.0.0:7700 --api-advertise cp1.example.net:7700 --raft 0.0.0.0:7800 --raft-advertise cp1.example.net:7800 --mesh 0.0.0.0:51900 --mesh-advertise cp1.example.net:51900 --tls$ dh cp bootstrap --api cp1.example.net:7700 --code-file ./cp1/bootstrap.code$ dh cp add-member --api cp2.example.net:7700 --code-file ./cp2/bootstrap.code$ dh cp add-member --api cp3.example.net:7700 --code-file ./cp3/bootstrap.code$ dh cp status
Install: hosts
docs/runbooks/install.md §4Invites are single-use; create one per host (add --roles edge for an edge host, --auto to skip approval). The token pins the root key and root CA, so the host speaks HTTPS from its first request. On first start the host writes policy.yaml — its sovereign policy — which the control plane can read but never change. Approve pending hosts with `dh node approve`.
$ dh node invite --out host-1.token$ dh-noded --data /var/lib/dh-noded --join-file host-1.token --name host-1 --region eu-west --zone a --host rack1-u12 --mesh 0.0.0.0:51820 --mesh-advertise host-1.example.net:51820$ dh get nodes$ dh node approve host-1
Push an executable into the cluster CAS (optionally attested with the root key), reference it by digest in a dh/v1 manifest, and apply it. Process workloads get their port in $PORT. For a container, use a digest-pinned image (ref@sha256:…) and the docker runtime is selected. For ingress, add an edge host and `ingress: [{host: web.example.net, port: http, tls: acme}]`; edge hosts take --edge-http, --edge-https, --acme-directory and --acme-email.
$ dh artifact push ./myservice --name myservice --sign$ dh apply -f web.yaml$ dh rollout status app web$ dh describe app web
Manifest (dh/v1)
pkg/manifestapiVersion: dh/v1, kind: Application, metadata.name, and a spec with: replicas; runtime (process or docker, inferred from the image); image (name@b3:<digest> or ref@sha256:<digest> — unpinned images are refused); command, args, env; resources (cpu, mem); placement (tiers, spread, antiAffinity, arch, features, preferRegion, federation); ports; volumes (name, size, mount, replicas, durability, snapshot every/retain); ingress (host, port, tls); health (http, interval, timeout, failureThreshold); update strategy (maxUnavailable).
Parsing is strict: unknown fields are rejected. The manifest is normalized and hashed, and the hash appears in `dh describe app`. durability.erasure accepts only `none`.
Console
docs/runbooks/install.md §6Every member serves the console. `dh console` prints https://<member>/#token=…; the session capability travels in the URL fragment, which browsers never send to servers. Use --read-only for a session that cannot change anything, and --ttl to shorten it. Over TLS, browsers must trust root-ca.pem, or you front the console with your own TLS terminator.
$ dh console --read-only --ttl 1h
Day-2 operations
docs/runbooks/operations.mdUpdates roll out a new generation within maxUnavailable; the new artifact is fetched and verified on each host before the old generation stops. Rolling back means re-applying the previous manifest — generations only increase. Drain hosts for maintenance; revoke a host you trust less (revocation blocks new admission, while work it already admitted may continue under its own policy); revoke a single suspect key; rotate host keys with a grace period.
If a leader is lost, a new one is elected in about 1–2 s and hosts keep running. If quorum is lost, hosts enter offline-hold. After total control-plane loss, bootstrap new members with the same operator home and run `dh cp restore`.
$ dh scale app web --replicas 5$ dh explain app web$ dh logs app web --replica 0$ dh node drain host-1$ dh node revoke host-1 --reason "…"$ dh cp backup --out cp.json && dh verify backup cp.json$ dh cp transfer-leadership
Incidents
docs/runbooks/operations.mdSuspected control-plane compromise: `dh freeze` — hosts hold admitted work and refuse all new work — then investigate with `dh audit verify` and `dh audit host`. A host reporting ledger-corrupt keeps running admitted work, refuses new work and never appends to the broken chain; after investigating, `dh-noded ledger-seal` keeps the corrupt file byte for byte and starts a new chain that records the break and your reason. CLOCK_SKEW holds work until NTP is fixed. Storage DEGRADED repairs from peers automatically, with every repaired object listed.
$ dh freeze$ dh audit host host-1$ dh-noded ledger-seal --data /var/lib/dh-noded --reason "…"$ dh unfreeze
Federation
docs/runbooks/operations.mdOne cluster grants another bounded capacity with a root-signed agreement. Placements are re-signed by the grantor, and hosts on the granting side accept federated work only if their own policy sets allowFederated: true. With TLS the agreement carries the grantor's root CA. Revoking the agreement stops placements under it.
$ dh federation root$ dh federation grant --to alpha --to-root <key> --tiers federated --max-replicas 3 --ttl 720h --out agreement.json$ dh federation accept agreement.json$ dh federation ls$ dh federation revoke <agreement-digest> --reason "…"
Verifying claims yourself
docs/runbooks/operations.mdYou do not have to trust this site. `dh audit verify` checks the control-plane ledger locally. `dh chaos run --scenario all` runs the 17 failure scenarios on disposable clusters and signs a report for each. `dh-conformance run -self` runs the protocol vectors against your build, and `-adapter` runs them against any other implementation. `dh evidence verify --dir evidence/REF-MAC-A03` re-hashes and verifies the signed validation record the site's status is based on.
$ dh audit verify$ dh chaos run --scenario all$ dh-conformance run -self$ dh evidence verify --dir evidence/REF-MAC-A03
CLI reference
Generated from dh help of the validated build. 68 commands.
dh applyapply a dh/v1 manifest: -f FILEdh artifact lslist artifactsdh artifact pushupload an executable into the cluster CAS: FILE --name NAME [--sign]dh artifact signattest an artifact digest with the root key: DIGEST --name NAMEdh audit hostfetch and verify a host's own ledger over the mesh: HOSTdh audit tailshow recent audit entries: [-n 30]dh audit verifyfetch the full ledger and verify chain + checkpoints locallydh chaos listlist chaos scenarios with their injection and invariantdh chaos runrun chaos scenarios on real disposable clusters: [--scenario a,b|all] [--out DIR] [--submit]dh chaos soaklong-duration randomized faults on one cluster under traffic: --duration 10m [--out DIR] [--submit]dh consoleprint a console URL with a session capability: [--read-only] [--ttl 12h]dh cp add-memberadd a control-plane member: --api ADDR --code CODE|--code-file FILEdh cp backupwrite a signed control-plane backup: --out FILE [--secrets]dh cp bootstrapbootstrap the first control-plane member: --api ADDR --code CODE|--code-file FILEdh cp remove-memberremove a control-plane member: --id MEMBERdh cp restorerestore a verified backup into a freshly bootstrapped cluster: FILEdh cp rotate-rootrotate the cluster root key (old root signs the rotation)dh cp snapshotforce a raft snapshot on the leaderdh cp statusshow raft membership, leader and each member's healthdh cp transfer-leadershipask the leader to hand leadership to another voterdh delete appremove an application (volumes are retained): APPdh describe appshow every replica: desired, admitted, observed, admission checks: APPdh describe nodeshow a host: identity, policy, observation, mesh, storagedh dev downstop a local cluster and its workloads: --dir DIRdh dev statusshow processes of a local cluster: --dir DIRdh dev uprun a real local multi-process cluster: --dir DIR [--cps 3] [--hosts 3] [--edges 1] [--base 17700] [--sample]dh evidence digestprint the source-tree digest a validation record pins: [--src DIR]dh evidence envprint this machine's measured environment: [--data DIR]dh evidence packwrite exactly the digested source tree as a deterministic .tar.gz: --out FILE [--src DIR]dh evidence runrun and record one validation step: --dir EVID --name STEP [--retries N] -- COMMAND...dh evidence sealsign a validation record over an evidence dir: --dir EVID --id EVIDENCE-ID --stage ID --claim TEXT --scope TEXT --require step,... [--parent ID] [--limitation T ...] [--exclude T ...] [--infra-failure REASON] [--machine env.json ...]dh evidence verifyverify a validation record and re-hash its files: --dir EVIDdh exec apprun a command in a replica (needs host policy allowExec): APP [--replica 0] -- ARGV...dh explain appshow the scheduler plan for an app: APPdh exportwrite a signed, secret-free export of the installation: --out FILEdh federation acceptaccept an agreement a peer granted this cluster: FILEdh federation grantgrant a peer cluster capacity here: --to NAME --to-root KEY [--tiers federated] [--max-replicas 3] [--max-cpu 1] [--max-mem 1Gi] [--runtimes process,docker] [--ttl 720h] --out FILEdh federation lslist agreements and placements in both directionsdh federation revokerevoke an agreement this cluster granted: AGREEMENT-DIGEST [--reason R]dh federation rootprint this cluster's name and root key (share it with a peer that will grant you capacity)dh federation withdrawwithdraw this cluster's app from a peer: APPdh freezefreeze the control plane: hosts hold admitted work, refuse new workdh get appslist applications with desired / admitted / observeddh get nodeslist hostsdh get volumesalias of volume lsdh importverify an export and re-apply its desired state here: FILEdh initcreate a cluster trust root (root key + root CA) in --homedh logs apptail a replica's output over the mesh: APP [--replica 0] [--tail 100]dh mesh doctorcheck bindings, handshakes and gossip for inconsistenciesdh mesh peersshow peer handshakes, traffic and measured RTT: [HOST]dh mesh pingmeasure RTT from the control plane to a host over the mesh: HOSTdh mesh statusshow every host's WireGuard device and gossip viewdh node approveapprove a pending host: HOSTdh node draindrain a host (replicas are rescheduled): HOSTdh node invitecreate a single-use join token: [--roles edge] [--auto] [--ttl 24h] [--out FILE]dh node joinprint how to join a host with a token (runs on the host): TOKENdh node revokerevoke a host (blocks new work; admitted work may continue): HOST [--reason R]dh node revoke-keyemergency-revoke one host key: HOST --pub KEY [--reason R]dh node undrainreturn a drained host to service: HOSTdh publishers setset artifact publisher keys (root is always included): KEY...dh rollout status appwait until every replica runs the current generation: APP [--timeout 2m]dh scale appchange replica count: APP --replicas Ndh tokenprint an operator bearer capability: [--read-only] [--ttl 1h]dh unfreezeresume signing new workdh verify backupverify a control-plane backup file offline: FILEdh verify exportverify an export file offline: FILEdh volume exportdownload a committed snapshot and verify it: VOLUME --out DIR [--snapshot ID]dh volume lslist volumes with replica evidence