dozeGitHubGet doze
Registry
Check against your doze

Which doze do you run?

doze 0.1.1newest · 2 Jul 2026doze 0.1.02 Jul 2026Stop checkingKept in this browser only. Nothing is sent anywhere.

Publish a module

A module teaches doze to run one more engine. Every module in the registry is written in doze-modules, arrives as a pull request, and is built and signed by CI after the merge. Here is that path, with the rules the registry enforces.

Status, honestly

doze-dev publishes every module in the registry. Contributions arrive as pull requests to doze-modules.

doze has one registry and one namespace, doze, and loads modules from nowhere else. Any other namespace is refused: module namespace "acme": doze has one registry, and its modules are all under "doze"

Two ways to run an engine
module

Contribute it to doze-modules. A pull request to doze-modules; once merged, CI builds it and the registry signs it as an official module.

service

Anything else runs as your own service. service "mailpit" { run = "mailpit" } in doze.hcl runs any command you can start from a shell; give it a port and it sleeps until something connects.

01

What a module is

A plugin binary, written in Go with doze-sdk, that speaks the doze plugin protocol over gRPC. doze is an engine-blind host: proxy, supervisor, config, lockfile. Everything engine-specific lives in the module: how to find the engine binary, set up its data directory, start it, tell when it is ready, and what its config block accepts. This is the Terraform provider model, with one difference: every module comes from the one signed registry.

Self-contained

The plugin is the server. doze AWS and doze Kafka work this way. No version =.

Real upstream engine

The module fetches a real binary (Postgres, Valkey) from doze-binaries and declares the engine majors it supports.

02

Write it in doze-modules

A module is not its own repo: it is a package under modules/<name>/ in doze-modules and a line in modules.yaml. Copy the smallest one, modules/valkey/. Before writing code, open a "Propose a module" issue: the engine, why, its licence, where its binaries come from, and who will maintain it.

What a module is made of
modules/<name>/<name>.go         # the driver: Type, Resolve, Provision, Plan, …
modules/<name>/config.go         # the HCL block it decodes
modules/<name>/describe.go       # Describe(): docs and engine support, from code
modules/<name>/config_test.go    # decode tests
modules/<name>/acceptance_test.go # boots the real backend (-tags acceptance)
modules/<name>/plugin/main.go    # the plugin binary: dozeplugin.Main(Driver, &Config)
first

The backend in doze-binaries

Users download engine binaries from doze-binaries, and so does the release gate. A new engine needs a build recipe there, for all three platforms, before its module can release.

register

Four lines elsewhere

The version in modules.yaml, the driver in cmd/dzm's describers, a backend in scripts/backend-env.sh, and the module in the acceptance workflow's matrix.

03

Describe: what your page shows comes from code

Hand-written module docs rot, so everything a user sees is generated from your driver's Describe(). doze-modules' build tool refuses to package a module without it, and its tests fail when one is incomplete: no title, no documented config, no example block, no engine versions.

On the module pageFromNote
Name, one line, paragraphTitle · Tagline · DescriptionShown on the card in search and in the header.
Category and portCategory · Portdatabase, cache, queue, storage, workflow, other.
Engines, and the gateVersionsSigned. Becomes the index's engines; a user declaring an unlisted major is stopped before your code runs.
The example blockExample · ExampleLabelMust run as written: it is the first thing people paste.
Reference tabConfig · BlocksSince/Until render "engine ≥ 18" badges and need a matching check in your decoder.
Start, recipes, env, connect[Start · Recipes · Env · Connect]new Snippets for the Overview tab: first start, common setups, the variables it exports, how to connect. Not fields in engine.Description yet.
04

Testing, against the real engine

Three layers, each catching what the one before cannot. Run the first two on your machine before you open the pull request; CI runs all three.

every pull request

Decode tests

Defaults, error messages, version gates, that an unknown attribute is an error, and that every Describe() is complete. On Linux and macOS.

-tags acceptance

enginetest

Boots a real engine from your driver and checks that each declared option is applied in the running server. Skips without DOZE_<ENGINE>_BINDIR.

before release

The release gate

After the merge, CI runs acceptance again against the backend users download from doze-binaries. No version publishes without it.

Unit tests, as the pull request runs them
$ go vet ./...
$ go test -race ./...
Acceptance, with the published backend
$ scripts/backend-env.sh valkey aarch64-apple-darwin > /tmp/valkey.env   # needs curl, tar, yq
$ set -a; . /tmp/valkey.env; set +a                                      # DOZE_VALKEY_BINDIR=…
$ go test -tags acceptance -v -timeout 20m ./modules/valkey/...
05

Releasing: archives, hashes, signatures

You don't release anything. On the merge, doze-modules' CI builds the archives with dzm (a thin loop over the SDK's modtool) and uploads them to the module's GitHub release. The registry signs them, because the key lives there.

What CI builds for valkey 0.2.2
dist/valkey/
  valkey-plugin-0.2.2-aarch64-apple-darwin.tar.gz
  valkey-plugin-0.2.2-aarch64-unknown-linux-gnu.tar.gz
  valkey-plugin-0.2.2-x86_64-unknown-linux-gnu.tar.gz
  index.yaml   # protocol, engines, doze, sha256 per archive; unsigned
  meta.yaml    # the docs, from Describe()

Bump for every change

Users' locks pin releases. A fix, a new argument, a new engine major: each is a new version in modules.yaml. Only a changed version releases.

Never rebuild a release

Rebuilding a published version with different bytes is refused:

valkey 0.2.2 (aarch64-apple-darwin) is already published with a different sha256 — bump the module version instead of rebuilding it

Every old release stays

CI merges the published index.yaml before building, so last year's locks and older doze versions keep resolving. The registry's CI refuses to drop one.

06

Declaring the oldest doze your release needs

The plugin protocol number says the wire is the same. It does not say which doze has everything your module leans on. Each release can carry a doze field: the oldest doze that runs it.

index.yaml · one release
releases:
  "0.2.5":
    protocol: 1
    engines: ["14", "15", "16", "17", "18"]
    doze: "0.2.0"        # oldest doze that runs it
    artifacts: …

Where the value comes from

You don't type it. modtool stamps it from the SDK you built with (proto.MinHost). doze-sdk v0.11.0 and later stamp "0.2.0": modules built with it leave fetching engine binaries to doze, which only doze 0.2.0 and later do.

Format is three numbers. npm run validate rejects anything else: doze must be the oldest doze version that runs it, as "0.2.0"

How doze uses it

choosing a release

Releases that need a newer doze are skipped; an older release that fits is chosen instead. If none fits, doze says so:

postgres 17 needs doze 0.3.0 or newer; this doze is 0.2.0. Update doze (in the app: Settings › About), then run the command againdoze 0.2 and later. The 0.3.0 release is an example, to show the shape.
reading doze.lock

The pin keeps the field, so a teammate on an older doze is told why rather than failing at launch:

module doze/postgres 0.3.1 (pinned in doze.lock) needs doze 0.3.0 or newer; this doze is 0.2.0. Update doze (in the app: Settings › About); the lock was written by a newer dozedoze 0.2 and later. doze 0.1.1 cannot read the field: update doze before doze modules upgrade.

When to raise it

  • doze-modules moves to a doze-sdk whose MinHost is higher: it rises on its own. Say so in the pull request.
  • The module starts using a newer protocol feature or SDK field that an older doze ignores or lacks. [An override in modtool.Module for this case does not exist yet] new
  • Never lower it on a published release; it is part of the release, like its bytes.

postgres 0.2.5 and the other 3 Nov 2026 releases are the first to carry it ("0.2.0"); earlier releases have no field and run on any doze. Today it sits outside the signed part of the index; the design moves it inside (known gaps).

07

The review checklist

Each module page shows these as ticks. Some are enforced by tooling today; the rest are checked by a maintainer in review until CI can do it.

CheckHowToday
SignedEvery archive and the index verify against the registry key. npm run validate on every PR and push.enforced
Never rebuilt, never droppedmodtool refuses changed bytes; validate --against refuses a removed or edited release.enforced
Three platformsmacOS arm64, Linux arm64, Linux x86_64. Built by default; not yet a gate in validate.built, not gated
Conformance on each engine majorAcceptance tests green before every release, and weekly. Today on one engine major per module, not yet every major in Versions.gated, one major
Examples parseEvery example on the page parsed by the current doze in CI.new
Describe is completedoze-modules' tests: every module has a well-formed Describe().enforced
Licencedoze-modules is Apache-2.0; the engine's own licence must let doze download and run it.review
SourcePublic source in doze-modules, linked from the page [to the exact commit of each release: new].review
08

From pull request to registry

Nothing after the merge is manual. Nobody outside CI holds the signing key, and doze-dev never signs a module by hand.

  1. ProposeA "Propose a module" issue on doze-modules: engine, why, licence, backend, maintainer.
  2. BackendThe engine's binaries published in doze-binaries for all three platforms.
  3. Pull requestThe module under modules/<name>/, its version in modules.yaml, the pull request checklist ticked.
  4. CI testsgo vet and go test -race on Linux and macOS; a maintainer reviews.
  5. Merge, then the gateAcceptance against the published backend; then dzm builds and uploads the release.
  6. Signed and livedoze-registry's CI signs it with the doze key, validates it and deploys. doze modules upgrade picks it up.
09
Print me

Pull request checklist