Skip to content
Alpha: Odal Node is in active development. APIs, schemas and docs will change before 1.0.

Production deployment

The recommended shape is the bundled compose stack on one machine per operator, behind a TLS proxy. One machine per operator keeps the isolation between operators physical, keeps the disk your signing key lives on under your control, and scales by adding machines rather than redesigning.

Internet
│ :443
┌──────▼──────┐
│ TLS proxy │
└──┬───────┬──┘
api.<your-domain> dpp.<your-domain>
│ │
┌────▼───┐ ┌─────▼────┐
│ node │ │ resolver │ postgres · redis · nats (internal only)
└────────┘ └──────────┘
  • api.<your-domain>: the node’s API, for you and your systems. DID_WEB_BASE_URL must be the public HTTPS origin that serves /.well-known/did.json here.
  • dpp.<your-domain>: the resolver, the address printed on your products. This is RESOLVER_BASE_URL.

SSH keys only, a firewall allowing only 22, 80 (for certificate issuance) and 443, automatic security updates, and a non-root user in the docker group. Bind the node’s and resolver’s ports to loopback (127.0.0.1) in the compose file, so they are reachable only through the proxy.

Any reverse proxy works. With Caddy, certificates are automatic:

api.example.com { reverse_proxy 127.0.0.1:8001 }
dpp.example.com { reverse_proxy 127.0.0.1:8003 }

The ready-made images are not public yet, so the machine runs a clone of the engine repository and builds the node from it. Start from that clone’s .env.example; every variable is on the configuration reference. Then:

  • generate the database passwords and the key-store passphrase, and store the passphrase as Backup, restore and key custody describes;
  • set ADMIN_USERNAME and ADMIN_PASSWORD only for odal bootstrap, then unset them;
  • run a release, not the latest source: check out a release tag in the clone (for example v1.4.2) and set ODAL_VERSION to the same number without the leading v (1.4.2), never latest;
  • chmod 600 .env.
  1. Trust posture: call the authenticated GET /vault/api/v1/node/state with an API key and confirm the profile, the trust tier of each service and the ruleset version. The public /health answers {"status":"ok"} and nothing else, so a probe on it proves the node is up and nothing more.
  2. Create and publish a test passport.
  3. Resolve it through the resolver’s public address, as HTML and as JSON.
  4. Generate its evidence dossier and verify it with odal verify.
  • From outside: a simple uptime probe on the resolver’s /health, and on a known passport.
  • On the machine: the metrics endpoint (loopback only) exports a trust_mode gauge per service: 0 stand-in, 1 sandbox, 2 real. Alert when any service drops below what you expect.