Last updated: September 25, 2026

Documentation

A summary of the documents in the Go repository. Where they differ, the repository is right.

How do I get started?

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.

Source: README.md, docs/runbooks/install.md

Requirements: 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).

shell
$ 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).

shell
$ ./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.

shell
$ export DH_HOME=~/.dh
$ dh init --cluster prod

Install: control-plane members

docs/runbooks/install.md §2–3

Run 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.

shell
$ 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

Invites 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`.

shell
$ 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.

shell
$ 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/manifest

apiVersion: 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`.

Every 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.

shell
$ dh console --read-only --ttl 1h

Updates 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`.

shell
$ 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

Suspected 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.

shell
$ dh freeze
$ dh audit host host-1
$ dh-noded ledger-seal --data /var/lib/dh-noded --reason "…"
$ dh unfreeze

One 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.

shell
$ 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.md

You 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.

shell
$ 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 FILE
dh artifact lslist artifacts
dh artifact pushupload an executable into the cluster CAS: FILE --name NAME [--sign]
dh artifact signattest an artifact digest with the root key: DIGEST --name NAME
dh audit hostfetch and verify a host's own ledger over the mesh: HOST
dh audit tailshow recent audit entries: [-n 30]
dh audit verifyfetch the full ledger and verify chain + checkpoints locally
dh chaos listlist chaos scenarios with their injection and invariant
dh 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 FILE
dh cp backupwrite a signed control-plane backup: --out FILE [--secrets]
dh cp bootstrapbootstrap the first control-plane member: --api ADDR --code CODE|--code-file FILE
dh cp remove-memberremove a control-plane member: --id MEMBER
dh cp restorerestore a verified backup into a freshly bootstrapped cluster: FILE
dh cp rotate-rootrotate the cluster root key (old root signs the rotation)
dh cp snapshotforce a raft snapshot on the leader
dh cp statusshow raft membership, leader and each member's health
dh cp transfer-leadershipask the leader to hand leadership to another voter
dh delete appremove an application (volumes are retained): APP
dh describe appshow every replica: desired, admitted, observed, admission checks: APP
dh describe nodeshow a host: identity, policy, observation, mesh, storage
dh dev downstop a local cluster and its workloads: --dir DIR
dh dev statusshow processes of a local cluster: --dir DIR
dh 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 EVID
dh exec apprun a command in a replica (needs host policy allowExec): APP [--replica 0] -- ARGV...
dh explain appshow the scheduler plan for an app: APP
dh exportwrite a signed, secret-free export of the installation: --out FILE
dh federation acceptaccept an agreement a peer granted this cluster: FILE
dh 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 FILE
dh federation lslist agreements and placements in both directions
dh 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: APP
dh freezefreeze the control plane: hosts hold admitted work, refuse new work
dh get appslist applications with desired / admitted / observed
dh get nodeslist hosts
dh get volumesalias of volume ls
dh importverify an export and re-apply its desired state here: FILE
dh initcreate a cluster trust root (root key + root CA) in --home
dh logs apptail a replica's output over the mesh: APP [--replica 0] [--tail 100]
dh mesh doctorcheck bindings, handshakes and gossip for inconsistencies
dh mesh peersshow peer handshakes, traffic and measured RTT: [HOST]
dh mesh pingmeasure RTT from the control plane to a host over the mesh: HOST
dh mesh statusshow every host's WireGuard device and gossip view
dh node approveapprove a pending host: HOST
dh node draindrain a host (replicas are rescheduled): HOST
dh 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): TOKEN
dh 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: HOST
dh 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 N
dh tokenprint an operator bearer capability: [--read-only] [--ttl 1h]
dh unfreezeresume signing new work
dh verify backupverify a control-plane backup file offline: FILE
dh verify exportverify an export file offline: FILE
dh volume exportdownload a committed snapshot and verify it: VOLUME --out DIR [--snapshot ID]
dh volume lslist volumes with replica evidence

Source documents