Files
hq/02-DECISIONS/0005-capabilities-are-provisioned-on-declaration.md
T
jschoubben c0b35652d0 The numbering is the flow: decisions are 02, design is 03
papa-hq reads 01 research -> 03 decision -> 02 design. The order is a
scar, not a choice: 02-DESIGN existed from its initial commit, and when
adr/ was finally promoted on 2026-07-13 it took the next free number
rather than its place in the sequence. By then design was too settled to
renumber.

hal-hq was three commits old, so it is not. adr/ becomes 02-DECISIONS and
02-DESIGN becomes 03-DESIGN, and following the folder numbers now walks
the process in the order it happens: research produces a decision, the
decision authorises a design.

00-GENESIS becomes 00-META, matching papa's rename from the same
restructure.

Every path reference rewritten across documents, frontmatter, playbooks
and skills. All links resolve; all 58 frontmatter blocks parse and their
path fields still point at files that exist.
2026-08-23 18:05:11 +02:00

3.1 KiB

status, date, deciders, reconstructed
status date deciders reconstructed
accepted 2026-04-06 jochen true

5. Capabilities are provisioned on declaration, not configured by hand

Reconstructed after the fact from the evidence cited below.

Context

Most modules need something another module holds — a database, a cache, a bucket, a message vhost, an identity client. Wiring that by hand means creating the resource, creating a user, generating a credential, putting it in the consumer's configuration, and repeating all of it on every node the consumer runs on.

Every step is a place to make a mistake that surfaces much later, and the credential ends up written somewhere it can be read.

Considered options

  1. Manual setup, documented. Rejected. Documentation of a manual procedure is a description of the mistakes people will make.
  2. A shared credential per resource type, distributed to all consumers. Rejected: no isolation, and rotation becomes a mesh-wide outage.
  3. Declared requirements, satisfied by the provider module. Chosen.

Decision

A module declares what it provides and what it requires. A requirement names the provider, the resource type, optionally a name and a target node, and a mapping from the resource's connection fields to the consumer's environment variables.

The mesh satisfies it: a provisioner belonging to the provider creates the resource and its credential, records the grant, and writes the mapped values as database overrides. The synchroniser from ADR 0004 then materialises them. Neither the credential nor the topology is ever written by hand.

A requirement may name a provider on another node. The grant records consumer and provider nodes separately, so cross-node wiring is the same declaration.

Consequences

  • Provisioning becomes a core concern of the mesh, not plumbing. A module asks for a capability; where it lives is the mesh's problem. This is the property ADR 0015 later builds the whole domain model around.
  • Credentials are never authored, so they are never authored badly, and they are never in the repository.
  • Each consumer gets its own credential, so revocation is per-consumer.
  • Rotation is where this bites. A shared secret rotated for a new consumer invalidates the peers holding the old one, and this has taken the mesh down. The declaration model makes granting easy and says nothing about fan-out.
  • A module with no requirements skips the stage entirely, which is correct and also means the absence of provisioning is indistinguishable from provisioning that did not run.

References

  • Remove shell/ helper library; split brain into independent workspaces, 2026-04-06 — the provisioner daemon becomes its own component.
  • Coordinator refactor: centralize pipeline orchestration, 2026-04-04 — the provision-then- environment-then-start sequence becomes the coordinator's.
  • Knowledge base: provisioning, provisioning/requires.
  • The rotation failure: troubleshooting/provision-rotation-invalidates-peers, troubleshooting/provision-adoption-rotates-live-credential.