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.
4.0 KiB
layer, status, code, updated, decisions
| layer | status | code | updated | decisions | |||
|---|---|---|---|---|---|---|---|
| as-is | implemented |
|
2026-08-23 |
|
Provisioning
A module states what it needs. The mesh makes it exist, generates the credential, records the grant, and puts the values where the module will read them. Nobody writes a credential and nobody writes a topology.
This is the mesh's core concern rather than its plumbing — the property everything else is built on.
The declaration
A provider declares the resource type it can create and the network on which that resource is reachable from a container.
A consumer declares, per requirement: the provider module, the resource type, optionally a name and a target node, and a mapping from the resource's connection fields onto its own environment variables.
The consumer must also declare those variables as empty in its environment section. A mapped field with no declared variable is dropped — the value is produced and then discarded, because the generator only emits variables the manifest knows about.
What happens
Inside the pipeline the sequence is orchestrated by the coordinator and is not optional:
- The provisioner creates the resource and its credential, records the grant, and writes the mapped values as database overrides.
- The synchroniser regenerates the module's environment from those overrides.
- Only then is the service started.
Each step waits for the previous one's event. A module that declares no requirements skips the first step entirely — which is correct, and means the absence of provisioning is indistinguishable from provisioning that did not run.
Outside the pipeline the same two steps exist as direct operations, for debugging. They are not the normal path.
What can be provisioned
Providers exist for relational databases of two kinds, an object store, a cache, message-broker virtual hosts and access, an identity provider's clients, and a download category. Each returns the connection fields a consumer maps from — host, port, user, password, and whatever else the resource type implies.
A provider with no registered provisioner still records a grant, with an empty connection. This is deliberate and easy to misread: the grant exists, so the requirement looks satisfied, and nothing was created.
Cross-node grants
A requirement may name the node whose provider should satisfy it. The grant records consumer node and provider node separately, so a module on one node holding a database on another is the ordinary case rather than a special one.
For a containerised consumer, the provisioner returns the provider's routable name rather than a loopback address — the value has to be correct from inside a container on another machine.
Where this hurts
Rotation has no fan-out. A resource whose credential is shared by several consumers can be rotated by provisioning a new one, and the peers holding the old credential are not told. This has locked the mesh out of its own broker, and has caused a node's adoption to rotate a live shared password without informing anything that held it. Granting is easy; regranting is not modelled.
A frozen password outlives its generation. A generated password is written once. If the resource's persistent data directory already exists from an earlier initialisation, the stored credential and the generated one diverge, and the symptom is an authentication failure that looks like a configuration error.
Migrations against a provisioned resource run on the consumer's node, not the build node, and read the deployed artifact rather than the source tree. Both facts were wrong in the implementation for a period during which new provisioning migrations silently never ran.
A grant is not a check. The record says a resource was provisioned. Nothing verifies it still exists, still has the recorded credential, or is reachable from where the consumer runs.