|
|
|
@@ -6,34 +6,39 @@ deciders: jochen
|
|
|
|
|
reconstructed: false
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
# 113. The vault makes every secret, a provider makes resources and data, and the mesh carries both
|
|
|
|
|
# 113. The vault makes every shared secret, a provider makes resources and data, and the mesh carries both
|
|
|
|
|
|
|
|
|
|
## Context
|
|
|
|
|
|
|
|
|
|
**A secret comes into being seven different ways today**, counted across the catalogue and the
|
|
|
|
|
**A shared secret comes into being many different ways today**, counted across the catalogue and the
|
|
|
|
|
controller on 2026-09-25:
|
|
|
|
|
|
|
|
|
|
| kind | made by | used by |
|
|
|
|
|
|---|---|---|
|
|
|
|
|
| a credential between a consumer and a provider | the controller | 19 modules |
|
|
|
|
|
| a module's own secret (`own-secrets`) | the controller, as a random value nothing owns | 54 modules |
|
|
|
|
|
| a broker account | the controller, but only when a person runs a separate command; otherwise the random value above, which cannot work ([issue 095](../04-ISSUES/095-a-module-assigned-after-genesis-has-no-broker-account/00-report.md)) | 49 modules |
|
|
|
|
|
| a module's broker account | the controller, but only when a person runs a separate command; otherwise the random value above, which cannot work ([issue 095](../04-ISSUES/095-a-module-assigned-after-genesis-has-no-broker-account/00-report.md)) | 49 modules |
|
|
|
|
|
| a node's and the builder's broker accounts | the controller, each in its own code path | every node, the builder |
|
|
|
|
|
| an enrolment token | the controller | every node joining |
|
|
|
|
|
| a `secret` from the vault | the controller mints it, and the vault only records it ([ADR 0085](0085-a-secret-is-a-provision.md), as amended) | 6 modules |
|
|
|
|
|
| a value an operator accepts | a person ([ADR 0092](0092-an-operator-delivers-a-pair-credential.md)) | where accepted |
|
|
|
|
|
| a licence for model access | a separate controller context with its own store | model consumers |
|
|
|
|
|
| the mesh's root secrets | genesis, sealed to the operator key | the foundation |
|
|
|
|
|
| the foundation's root secrets | genesis, sealed to the operator key | the foundation |
|
|
|
|
|
|
|
|
|
|
**The vault was built to end the second row, and did not.** ADR 0085 says a module's own secret
|
|
|
|
|
*"stops being a generated value that nothing owns"*. 54 modules still use one, and 6 use the vault.
|
|
|
|
|
The replacement was added and the old path was never retired. The same has happened to the broker
|
|
|
|
|
account, a special case of the second row that fails silently when the separate command is forgotten.
|
|
|
|
|
The replacement was added and the old path was never retired.
|
|
|
|
|
|
|
|
|
|
**Rotation has its own gaps.** To-be 13 makes rotation one command, all-or-nothing, and states the
|
|
|
|
|
window in which a consumer cannot authenticate: the provider has taken the new password, and the
|
|
|
|
|
consumer has not yet restarted with it. A consumer restarts only if its definition remembered to say
|
|
|
|
|
so, and a container fed by an env-file is not recreated when that file changes, so it keeps the old
|
|
|
|
|
value ([issue 103](../04-ISSUES/103-a-container-is-not-recreated-when-a-file-it-reads-changes/00-report.md)).
|
|
|
|
|
Nothing confirms that the new secret works.
|
|
|
|
|
**ADR 0085 considered and rejected making the vault the only maker**, because *"the controller must
|
|
|
|
|
mint in order to deliver any provision — the vault's own credential among them"*: the vault cannot
|
|
|
|
|
make the credentials that exist before it does. That objection is real, and this record has to answer
|
|
|
|
|
it rather than step around it.
|
|
|
|
|
|
|
|
|
|
**Rotation has gaps.** To-be 13 makes rotation one command, all-or-nothing, with a stated window in
|
|
|
|
|
which a consumer cannot authenticate. A consumer restarts only if its definition remembered to say so;
|
|
|
|
|
a container fed by an env-file is not recreated when that file changes
|
|
|
|
|
([issue 103](../04-ISSUES/103-a-container-is-not-recreated-when-a-file-it-reads-changes/00-report.md));
|
|
|
|
|
and some secrets are read only when a service first initialises, where a restart changes nothing.
|
|
|
|
|
|
|
|
|
|
**And providers cannot answer with data.** [ADR 0048](0048-a-provider-creates-the-credential-the-mesh-minted.md)
|
|
|
|
|
left *"delivering provider-generated data back to a consumer"* to a separate decision. The analytics
|
|
|
|
@@ -41,86 +46,112 @@ provider's site id and the DNS provider's record have no way back, and say so in
|
|
|
|
|
|
|
|
|
|
## Considered Options
|
|
|
|
|
|
|
|
|
|
**1. Keep the controller minting, and tidy the seven paths.** Rejected. The paths are the problem:
|
|
|
|
|
each is made, kept, rotated and audited differently, and tidying keeps all seven.
|
|
|
|
|
**1. Keep the controller minting, and tidy the paths.** Rejected. The paths are the problem: each is
|
|
|
|
|
made, kept, rotated and audited differently, and tidying keeps them all.
|
|
|
|
|
|
|
|
|
|
**2. Every provider mints its own secrets, with one shared function in the SDK.** Rejected. It makes
|
|
|
|
|
generation uniform and leaves custody scattered: every provider's machine holds secrets the vault
|
|
|
|
|
never sees, so rotation, audit and the operator's break-glass copies cover only some of them. The
|
|
|
|
|
function would also need a conforming implementation in every language a provider is written in.
|
|
|
|
|
**2. Every provider mints its own secrets, with one shared function in the SDK.** Rejected. Generation
|
|
|
|
|
becomes uniform, but custody stays spread over every provider's machine, so rotation, audit and the
|
|
|
|
|
operator's break-glass copies cover only some secrets. Each SDK language needs its own implementation.
|
|
|
|
|
|
|
|
|
|
**3. The vault makes every secret, and a provider that needs one requires it, like any consumer.**
|
|
|
|
|
Chosen. A provider serving a consumer requires a secret for that consumer from the vault. Secrets
|
|
|
|
|
become provisioning all the way down, with one maker at the bottom.
|
|
|
|
|
**3. Raise the vault first at genesis, so it makes even the first secrets.** Rejected. The vault is
|
|
|
|
|
built on the shared runtime base, which the installation makes only after the store, the broker and
|
|
|
|
|
the controller exist, and the vault learns what to answer from the controller over the bus. Running
|
|
|
|
|
it first means reordering the whole installation and giving the vault a second way of being asked.
|
|
|
|
|
|
|
|
|
|
**4. The vault makes every shared secret; genesis delivers the first ones to it.** Chosen. It answers
|
|
|
|
|
0085's objection with a mechanism the mesh already has: a value delivered to the vault.
|
|
|
|
|
|
|
|
|
|
## Decision
|
|
|
|
|
|
|
|
|
|
**The vault makes every secret in the mesh.** Generation, to a secret's contract, exists in the vault
|
|
|
|
|
and nowhere else. The controller mints nothing.
|
|
|
|
|
**There are two kinds of secret, and each has one rule.**
|
|
|
|
|
|
|
|
|
|
**A provider that needs a secret for a consumer requires it from the vault.** A provision's contract
|
|
|
|
|
declares it: *for each consumer, one secret*. Resolution expands that into one requirement per
|
|
|
|
|
consumer, named for the consumer. So gitea requiring a database makes the database's provider require
|
|
|
|
|
a secret named for gitea, and the vault answers it. The provider's own code does not change. It is
|
|
|
|
|
handed a login and a password, as it is today.
|
|
|
|
|
- **A shared secret** is a value more than one party must hold: a password, a token, an API key. **The
|
|
|
|
|
vault makes every one.** Nothing else in the mesh generates a shared secret.
|
|
|
|
|
- **A private key** is made where it is used and never leaves: a node's sealing key, the operator's
|
|
|
|
|
key, the mesh's certificate authority. This is not a second way of making secrets. A private key any
|
|
|
|
|
other party ever held would no longer be private.
|
|
|
|
|
|
|
|
|
|
**A secret has holders, and the vault delivers to each.** The database credential has two: the
|
|
|
|
|
provider, which creates the login with it, and the consumer, which presents it. The vault hands it to
|
|
|
|
|
the mesh, which delivers it to each holder sealed to that holder's node. Plaintext exists in the
|
|
|
|
|
vault while it is made and on each holder's machine, and nowhere else. The controller carries sealed
|
|
|
|
|
values it cannot open.
|
|
|
|
|
**Every shared secret is a `secret` requirement, answered by the vault:**
|
|
|
|
|
|
|
|
|
|
**Every other secret takes the same path:**
|
|
|
|
|
- a **credential between a consumer and a provider**. A provision's contract declares *for each
|
|
|
|
|
consumer, one secret*, and resolution expands it into one requirement per consumer. So gitea
|
|
|
|
|
requiring a database makes the database's provider require a secret for gitea, and the vault
|
|
|
|
|
answers it. The provider's own code does not change: it is handed a login and a password, as today;
|
|
|
|
|
- a module's **own secret**. `own-secrets` is retired;
|
|
|
|
|
- every **broker account**: a module's, a node's, the builder's. The broker delivers `amqp` through
|
|
|
|
|
its seat ([ADR 0110](0110-a-seat-is-a-module-assignment-from-a-closed-set.md)), and the broker's own
|
|
|
|
|
provisioner creates each account from the vault's secret, like any provider. The controller no
|
|
|
|
|
longer creates accounts, and there is no separate command to forget;
|
|
|
|
|
- an **enrolment token**;
|
|
|
|
|
- a **secret operator value**, such as an external API key, which the operator delivers to the vault
|
|
|
|
|
([ADR 0092](0092-an-operator-delivers-a-pair-credential.md)). A licence's credential is one of these.
|
|
|
|
|
What the licences context adds, refreshing a token, is provider behaviour, decided in its own record.
|
|
|
|
|
|
|
|
|
|
- a module's **own secret** is a `secret` requirement the vault answers. `own-secrets` is retired;
|
|
|
|
|
- a **broker account** is the module's identity on the bus. Its name is the mesh's, its password is a
|
|
|
|
|
secret the vault makes, and the controller creates the account with it, as it creates accounts
|
|
|
|
|
today. There is no separate command to forget;
|
|
|
|
|
- an **operator's value** that is secret is handed to the vault, which provides it like any other
|
|
|
|
|
secret. It is never replaced by rotation ([ADR 0092](0092-an-operator-delivers-a-pair-credential.md));
|
|
|
|
|
- a **licence's** credential is an operator's value the vault keeps. What the licences context adds,
|
|
|
|
|
refreshing a token and a manager holding the refresh credential, is provider behaviour, decided in
|
|
|
|
|
its own record.
|
|
|
|
|
**Only the vault may provide `secret`.** A module providing it must hold the `mesh-vault` seat, and
|
|
|
|
|
the parser refuses one that does not. A pin cannot route a `secret` requirement anywhere else, because
|
|
|
|
|
there is nowhere else.
|
|
|
|
|
|
|
|
|
|
**Genesis is the vault's first answer, not an exception.** Genesis raises the vault before anything
|
|
|
|
|
else and asks it for the foundation's secrets: the store's superuser, the broker's admin and its hashed
|
|
|
|
|
form, and the vault's own broker account. The vault answers with the same code it always uses, before
|
|
|
|
|
the bus exists. Genesis mints nothing itself.
|
|
|
|
|
**A secret has recipients, and the vault delivers to each.** The database credential has two: the
|
|
|
|
|
provider, which *applies* it by creating the login, and the consumer, which *presents* it. The vault
|
|
|
|
|
hands the value to the mesh sealed to each recipient's node. The controller and the broker carry sealed
|
|
|
|
|
values they cannot open.
|
|
|
|
|
|
|
|
|
|
**A provider makes resources and data, and the mesh carries data back.** A provider answers with its
|
|
|
|
|
contract's non-secret fields: a site id, a registered name. The mesh delivers them to the consumer as
|
|
|
|
|
resolved values. Who a consumer is stays the mesh's: a provider makes what a consumer is *given*,
|
|
|
|
|
never what it is *called* ([ADR 0049](0049-a-consumers-identity-fits-the-tightest-backend.md)).
|
|
|
|
|
**Genesis delivers, and the vault adopts.** The foundation's first shared secrets exist before the
|
|
|
|
|
vault can run: the store's superuser, the broker's admin in the hashed form the broker needs, the bus
|
|
|
|
|
accounts of the temporary controller and of the vault itself, and the first enrolment token. Genesis
|
|
|
|
|
generates these, seals them to the operator key as today, and **delivers them to the vault when the
|
|
|
|
|
vault is installed**, through the same path an operator's value takes. From then on the vault holds,
|
|
|
|
|
audits and rotates them. Unlike an operator's external key, the vault can make their replacements, so
|
|
|
|
|
they are delivered but replaceable. Genesis is the only thing besides the vault that ever generates a
|
|
|
|
|
shared secret, once, before the vault exists, and it hands them over.
|
|
|
|
|
|
|
|
|
|
**A provider makes resources and data, and the mesh carries data back.** A provider's adapter may
|
|
|
|
|
answer with its contract's non-secret fields: a site id, a registered name. The mesh delivers them to
|
|
|
|
|
the consumer as resolved values. Who a consumer is stays the mesh's: a provider makes what a consumer
|
|
|
|
|
is *given*, never what it is *called* ([ADR 0049](0049-a-consumers-identity-fits-the-tightest-backend.md)).
|
|
|
|
|
|
|
|
|
|
### Rotation
|
|
|
|
|
|
|
|
|
|
**It is asked of the vault**, by an operator or by the vault's own policy, such as the maximum age a
|
|
|
|
|
secret's contract sets.
|
|
|
|
|
**It is asked of the vault**, by an operator or by the vault's policy, such as a maximum age in the
|
|
|
|
|
secret's contract. A delivered value the vault cannot replace, such as an external API key, is not
|
|
|
|
|
rotated by the vault: rotating it means an operator delivering a new one.
|
|
|
|
|
|
|
|
|
|
**It is provider-first.** The vault delivers the new value first to the holders that *accept* it,
|
|
|
|
|
such as the database, and waits for each to confirm it has applied it. Only then does it release the
|
|
|
|
|
value to the holders that *present* it, such as gitea. A consumer is never sent a value its provider
|
|
|
|
|
has not accepted, so the window shrinks to the consumer's own restart. A provider that does not
|
|
|
|
|
confirm holds the rotation: the consumer keeps the old value, which still works, and `status` shows the
|
|
|
|
|
rotation as waiting on that provider. It is never half-done and never silently abandoned.
|
|
|
|
|
**A secret's contract says how each recipient takes a new value:**
|
|
|
|
|
|
|
|
|
|
**Restarts are derived, not declared.** The mesh knows which process reads which secret, because the
|
|
|
|
|
definition reads it through its requirement ([to-be 27](../03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md)).
|
|
|
|
|
When a secret changes, the host restarts every process that reads it, and recreates a container whose
|
|
|
|
|
env-file carries it. No definition has to remember `restart-on` for a secret.
|
|
|
|
|
| recipient takes it by | example | what happens on rotation |
|
|
|
|
|
|---|---|---|
|
|
|
|
|
| **applying** it | a provider creating the login; the broker's provisioner updating an account; the store's own provisioner changing its superuser | its provisioner applies the new value; it is never restarted for it |
|
|
|
|
|
| **reading it at start** | a consumer reading its password when it starts | the host restarts it, or recreates a container whose env-file carries it |
|
|
|
|
|
|
|
|
|
|
**It is confirmed.** A rotated secret is shown as unconfirmed until each consumer has restarted with
|
|
|
|
|
it and, where its definition declares a health check, passed it. Delivered is not the same as working,
|
|
|
|
|
and the mesh says which one it knows.
|
|
|
|
|
A secret read only when a service first initialises cannot be rotated by a restart. Its contract marks
|
|
|
|
|
it applied, and a provisioner makes the change. The host derives which recipients read a secret at
|
|
|
|
|
start from the requirement their definition reads it through, so no definition declares a restart for
|
|
|
|
|
a secret.
|
|
|
|
|
|
|
|
|
|
**It is applier-first.** The vault delivers the new value first to the recipients that apply it. Each
|
|
|
|
|
applies it, verifies that the new value authenticates and the old one no longer does, and confirms.
|
|
|
|
|
**It repeats that confirmation on every reconcile pass until the vault acknowledges it**, so a lost
|
|
|
|
|
message costs one pass. Only after every applier has confirmed does the vault release the value to the
|
|
|
|
|
recipients that read it at start.
|
|
|
|
|
|
|
|
|
|
**The remaining window is stated.** If an applier applies the new value and its provisioner stops
|
|
|
|
|
before confirming, the recipients that present the secret are locked out until the provisioner runs
|
|
|
|
|
again, because the old value no longer works and they have not been sent the new one. A provisioner is
|
|
|
|
|
supervised and restarted when it exits, so the window is bounded by that restart. The mesh shows the
|
|
|
|
|
rotation as waiting on that applier for as long as it lasts, never as done.
|
|
|
|
|
|
|
|
|
|
**It is confirmed.** A rotation is shown as unconfirmed until every applier has confirmed, and every
|
|
|
|
|
recipient that reads at start has restarted with the new value and passed its health check, where its
|
|
|
|
|
definition declares one.
|
|
|
|
|
|
|
|
|
|
| step | who |
|
|
|
|
|
|---|---|
|
|
|
|
|
| asks | an operator, or the vault's policy |
|
|
|
|
|
| makes the value | the vault |
|
|
|
|
|
| carries it | the controller, sealed, provider first |
|
|
|
|
|
| applies it on the provider | the provider's provisioner, which confirms |
|
|
|
|
|
| applies it on the consumer | the host, restarting or recreating what reads it |
|
|
|
|
|
| confirms it works | the consumer's restart and health check, shown in `status` |
|
|
|
|
|
| carries it | the controller, sealed, appliers first |
|
|
|
|
|
| applies it | each applier's provisioner, which verifies and confirms on every pass until acknowledged |
|
|
|
|
|
| takes it at start | the host, restarting or recreating what reads it |
|
|
|
|
|
| confirms it | applier confirmations, then restarts and health checks, shown in `status` |
|
|
|
|
|
|
|
|
|
|
## What this changes in earlier records
|
|
|
|
|
|
|
|
|
@@ -129,53 +160,61 @@ On acceptance, each of these is superseded or amended by this record, not edited
|
|
|
|
|
- [ADR 0048](0048-a-provider-creates-the-credential-the-mesh-minted.md) is superseded: the controller
|
|
|
|
|
no longer mints a provider's credential; the vault makes it. That a provider is handed its
|
|
|
|
|
credential and seals nothing stands.
|
|
|
|
|
- [ADR 0085](0085-a-secret-is-a-provision.md) is amended: the vault makes a module's own secret rather
|
|
|
|
|
than recording one the controller minted, own secrets are retired, and genesis asks the vault for
|
|
|
|
|
the root secrets. "The vault stores no plaintext, ever" stands.
|
|
|
|
|
- [ADR 0085](0085-a-secret-is-a-provision.md) is amended: the vault makes every shared secret, own
|
|
|
|
|
secrets are retired, and its rejection of vault-only minting is answered by genesis delivering the
|
|
|
|
|
first secrets. "The vault stores no plaintext, ever" stands.
|
|
|
|
|
- [ADR 0092](0092-an-operator-delivers-a-pair-credential.md) is amended: an operator delivers a secret
|
|
|
|
|
to the vault.
|
|
|
|
|
- [To-be 13](../03-DESIGN/01-to-be/13-credentials-and-their-rotation.md) and
|
|
|
|
|
[to-be 24](../03-DESIGN/01-to-be/24-the-secrets-vault.md) are amended: rotation is provider-first,
|
|
|
|
|
derived and confirmed, and the vault is the only maker.
|
|
|
|
|
to the vault, and genesis delivers the foundation's first secrets the same way.
|
|
|
|
|
- [ADR 0043](0043-a-module-broker-account-is-scoped-by-emits-and-consumes.md) is amended: a broker
|
|
|
|
|
account is created by the broker's provisioner, not the controller. Its scoping stands.
|
|
|
|
|
- [To-be 13](../03-DESIGN/01-to-be/13-credentials-and-their-rotation.md),
|
|
|
|
|
[to-be 21](../03-DESIGN/01-to-be/21-the-installation-in-full.md) and
|
|
|
|
|
[to-be 24](../03-DESIGN/01-to-be/24-the-secrets-vault.md) are amended: rotation is applier-first,
|
|
|
|
|
derived and confirmed; genesis delivers its secrets to the vault; the vault is the only maker.
|
|
|
|
|
- [Issue 103](../04-ISSUES/103-a-container-is-not-recreated-when-a-file-it-reads-changes/00-report.md)
|
|
|
|
|
becomes a prerequisite: rotation cannot be trusted while a changed env-file leaves a container on
|
|
|
|
|
its old value.
|
|
|
|
|
|
|
|
|
|
## Consequences
|
|
|
|
|
|
|
|
|
|
- **The vault is on the path of every new or rotated secret.** Today the controller is, and both run
|
|
|
|
|
on the control-node, so no new single point of failure appears. It is stated rather than implied.
|
|
|
|
|
- **The vault is on the path of every new or rotated shared secret.** Today the controller holds that
|
|
|
|
|
place, on the same node. A secret can no longer be made while the vault is down.
|
|
|
|
|
- Resolution expands per-consumer requirements from a provision's contract. The contract declares
|
|
|
|
|
them, never the provider's code, so what a provider requires stays predictable from the catalogue.
|
|
|
|
|
- The SDK's provider loop gains one thing: confirming that a rotation was applied. Adapters are
|
|
|
|
|
unchanged.
|
|
|
|
|
- The mesh gains the way back from provider to consumer, for confirmations and for data.
|
|
|
|
|
- 54 modules move from own secrets to vault requirements, and the broker account stops needing a
|
|
|
|
|
separate command.
|
|
|
|
|
- **What got harder:** a secret can no longer be made when the vault is down, where today the
|
|
|
|
|
controller makes one regardless. And a rotation waits for its provider. Both move failures from
|
|
|
|
|
late and silent to early and visible, which is the trade this record makes throughout.
|
|
|
|
|
- The SDK's provider loop gains repeated confirmation of an applied rotation. A credential provider's
|
|
|
|
|
adapter is unchanged. A data provider's adapter gains a return value.
|
|
|
|
|
- The broker's provisioner gains every bus account, and the controller loses five separate places it
|
|
|
|
|
generates a secret today.
|
|
|
|
|
- 54 modules move from own secrets to vault requirements.
|
|
|
|
|
- **What got harder:** a rotation waits for its appliers, and an applier that stops mid-rotation locks
|
|
|
|
|
presenters out until it restarts. Both are shown, not hidden, and the second is bounded by a
|
|
|
|
|
supervised restart rather than by someone noticing.
|
|
|
|
|
|
|
|
|
|
## How it is checked
|
|
|
|
|
|
|
|
|
|
| Rule | Checked by |
|
|
|
|
|
|---|---|
|
|
|
|
|
| Only the vault makes secrets | A controller test: no code path mints a secret. A vault test: the one generation function is the only one, and genesis reaches it through the vault. |
|
|
|
|
|
| A provider's per-consumer secret comes from the vault | A resolution test: a consumer requiring a database expands to a secret requirement named for it, answered by the vault. |
|
|
|
|
|
| A secret reaches each holder sealed to its node | A controller test: each holder's copy opens with that holder's node key and with no other, the controller's included. |
|
|
|
|
|
| Only the vault generates a shared secret after genesis | A controller test: no code path generates a shared secret. An installer test: genesis generates exactly the foundation's first secrets and delivers them to the vault. |
|
|
|
|
|
| A private key is made where it is used | A test per key: a node's sealing key never leaves the node, and the operator's private key never enters the mesh. |
|
|
|
|
|
| Only the vault provides `secret` | The parser refuses a module providing `secret` without holding `mesh-vault`, and resolution refuses a pin on a `secret` requirement. |
|
|
|
|
|
| A provider's per-consumer secret comes from the vault | A resolution test: a consumer requiring a database expands to a secret requirement for it, answered by the vault and delivered to both recipients. |
|
|
|
|
|
| Values are carried sealed | A controller test: each recipient's copy opens with that recipient's node key and no other; neither the controller nor a message on the broker can open one. |
|
|
|
|
|
| Own secrets are retired | A catalogue test: no definition declares an own secret, with a declared list of exceptions that shrinks to empty. |
|
|
|
|
|
| A broker account needs no separate command | A resolution test: assigning a module that speaks on the bus yields its account. |
|
|
|
|
|
| Rotation is provider-first | A rotation test: the consumer is not sent the new value until the provider confirms; a provider that never confirms leaves the consumer on the old value, shown as waiting. |
|
|
|
|
|
| Restarts are derived | A host test: changing a secret restarts every process that reads it, and recreates a container whose env-file carries it, with no `restart-on` declared. |
|
|
|
|
|
| Rotation is confirmed | A rotation test: the secret shows as unconfirmed until the consumer restarted and passed its health check. |
|
|
|
|
|
| A provider answers data back | A resolution test: the analytics provider's site id reaches its consumer as a resolved value. |
|
|
|
|
|
| Bus accounts come from the broker's provisioner | A resolution test: assigning a module that speaks on the bus yields its account, created by the broker's provisioner with no separate command. |
|
|
|
|
|
| An irreplaceable delivered value is not rotated by the vault | A vault test: a rotation request on an operator's external key is refused, naming the operator as its source. |
|
|
|
|
|
| Rotation is applier-first and re-confirmed | A rotation test: presenters are not sent the new value until every applier confirms, and a confirmation lost in transit is repeated on the next pass. |
|
|
|
|
|
| Restarts are derived from how a secret is read | A host test: a secret read at start restarts its reader, and recreates a container whose env-file carries it; an applied secret restarts nothing. |
|
|
|
|
|
| Rotation is confirmed | A rotation test: an applier confirms only after the new value authenticates and the old one does not, and the rotation shows unconfirmed until every reader restarted and passed its health check. |
|
|
|
|
|
| A provider answers data back | A lab test with a consumer requiring analytics: the provider's site id reaches it as a resolved value. |
|
|
|
|
|
|
|
|
|
|
## References
|
|
|
|
|
|
|
|
|
|
- [ADR 0048](0048-a-provider-creates-the-credential-the-mesh-minted.md): the decision this supersedes,
|
|
|
|
|
and the return path it left open
|
|
|
|
|
- [ADR 0085](0085-a-secret-is-a-provision.md), [ADR 0092](0092-an-operator-delivers-a-pair-credential.md),
|
|
|
|
|
[ADR 0049](0049-a-consumers-identity-fits-the-tightest-backend.md): the vault, the operator, and identity
|
|
|
|
|
- [ADR 0085](0085-a-secret-is-a-provision.md): the vault, and the objection this record answers
|
|
|
|
|
- [ADR 0092](0092-an-operator-delivers-a-pair-credential.md), [ADR 0049](0049-a-consumers-identity-fits-the-tightest-backend.md),
|
|
|
|
|
[ADR 0043](0043-a-module-broker-account-is-scoped-by-emits-and-consumes.md): delivered values,
|
|
|
|
|
identity, and broker accounts
|
|
|
|
|
- [ADR 0112](0112-a-module-definition-names-no-node-mesh-or-path.md), [to-be 27](../03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md):
|
|
|
|
|
everything a module needs is a requirement
|
|
|
|
|
- [Issue 095](../04-ISSUES/095-a-module-assigned-after-genesis-has-no-broker-account/00-report.md),
|
|
|
|
|