Skip to content

The trust model

doze up downloads and executes binaries. That sentence is why this page exists: a tool that fetches executables owes you a story about why you can trust what it runs — especially a dev tool, because dev machines hold source code, credentials, and production access.

Two supply chains meet on your machine, and every link is verified:

Modules (the plugins that provide each engine) come from a signed registry:

  1. Each publisher namespace (doze/, or a third party’s) has an ed25519 keypair. The public key is served at <registry>/<ns>/keys.json and pinned on first use into your doze.lock. After that, a changed key is a hard error — a compromised registry cannot silently rotate identity.
  2. Each module’s index is signed as a whole — not just its artifacts. The index carries the metadata that selects what you run: which releases exist, which engine versions each supports, which plugin protocol it speaks, where the channel points. Signing the index means a compromised CDN can’t claim false compatibility or roll the channel back to a vulnerable release.
  3. Each archive’s checksum is signed individually. The archive host (GitHub releases) is untrusted by design — tampered bytes fail verification no matter who serves them.

Engine binaries (the actual Postgres, Valkey, …) are fetched by the module from the doze-binaries mirror: built from upstream source in public CI, published append-only (a released version is never rebuilt), and checksum-pinned in your lock.

doze.lock — committed next to your config — pins three layers:

engines: # engine binaries: version = 16 -> 16.14.0 + per-platform SHA-256
modules: # per engine: module release, plugin protocol, supported
# engine versions, per-platform hashes
keys: # each namespace's publisher key (trust-on-first-use)

The consequences are the guarantees:

  • Reproducibility. A teammate’s clone and your CI resolve to byte-identical software — verified, not hoped.
  • No drift. A moving registry can’t change what a locked project runs. Warm caches resolve fully offline.
  • Explicit change. Pins move only via doze modules upgrade (and the updated lock goes through code review like anything else).
  • Immutability upstream. Published (version, platform) artifacts are never rebuilt — the tooling hard-errors on changed bytes — so a lock committed today resolves forever.

You declare the engine version (version = 18) — the software your app talks to. The module version (the plugin’s own release) is selected automatically: newest release compatible with your doze’s plugin protocol and every engine version you declared, then locked. You never write it; you meet it only in doze modules output and in error messages that name their own fix (run 'doze modules upgrade postgres').

This split is what makes evolution safe: a module can ship a new config argument or support a new Postgres major without touching what any locked project runs, and your project adopts it in one reviewed commit.

Honesty section: signatures prove who published, not that the code is benign — a malicious publisher signs malware happily. The official doze/ namespace is built in public CI from public source; for third-party modules, the trust decision is yours, and the tooling’s job is to make identity unforgeable and changes visible (doze modules info shows provenance; doze modules docs shows exactly what config it accepts). Verification also starts at first fetch — the initial key pin is trust-on-first-use, the same model as SSH.

For operating your own end of this — hosting a registry, mirroring binaries, air-gapping — see the operator guide.