Review corrections: 076 and ADR 0098 say what the authority could and could not do; issues 077 (a fetched fact is fetched once) and 078 (secret accept takes any name) opened

This commit is contained in:
2026-09-21 22:55:11 +02:00
parent 6d3cb60949
commit 6bc9df4b49
6 changed files with 93 additions and 13 deletions
@@ -13,8 +13,8 @@ extends: 02-DECISIONS/0085-a-secret-is-a-provision.md
The catalogue's certificate authority declared its root certificate, its root key and that key's
password as its own secrets, and told the container to initialise from them. The mesh mints an
own secret as random bytes, and random bytes are not a certificate: as written the authority
could not start, and no bed had raised it
own secret nobody delivers as random bytes, and random bytes are not a certificate: issued, the
authority could not start; only an operator hand-making its root could raise it
([issue 076](../04-ISSUES/076-a-served-fact-made-at-first-start-cannot-be-served/00-report.md)).
The authority can make its own root at first start. What it could not do then was tell the mesh
what that root is: a consumer was given `${bound:acme-ca:root}` from the provider's `serves`,
@@ -48,8 +48,11 @@ a fact that changes after first start is refetched only when the declaration cha
## How it is checked
The route-forwarding bed installs the authority, the proxy and a consumer from the catalogue and
asserts a routed name is served through the proxy; the proxy cannot start without the root its
gate fetched. The catalogue-wide manifest test parses both manifests.
asserts a routed name is served through the proxy. The proxy refuses to start on a bundle that is
not a certificate, so the name being served proves the gate fetched one; the gate itself refuses
a body that is not a certificate. That the proxy obtains a certificate from this authority through
that root is the certificate bed's proof, against the same authority with the same proxy. The
catalogue-wide manifest test parses both manifests.
## References
+5 -2
View File
@@ -564,8 +564,11 @@ The mesh mints the authority's password and nothing else of its: a root certific
are things only the authority can make, and a served fact written in a manifest cannot carry what
does not exist until the authority has run. So the authority serves its root at a path beside its
ACME directory, and the proxy that requires it fetches that root over the mesh network in a
run-once step before it starts. *How it is checked:* the route-forwarding bed installs the
authority, the proxy and a consumer from the catalogue and asserts the routed name is served.
run-once step before it starts. The step is run once per declaration: a root that changes
after first start is fetched again only when the declaration changes
([issue 077](../../04-ISSUES/077-a-fact-fetched-at-first-start-is-fetched-once/00-report.md)).
*How it is checked:* the route-forwarding bed installs the authority, the proxy and a consumer
from the catalogue and asserts the routed name is served.
### What was built
@@ -12,10 +12,13 @@ amended-design: 03-DESIGN/01-to-be/08-connectivity.md
The catalogue's certificate authority module declares its root certificate, its root key and
that key's password as its own secrets, and writes each into a file the container is told to
initialise from. The mesh mints an own secret as random bytes. Random bytes are not a
certificate: as written, the authority cannot initialise, and no bed has ever raised it — the
whole-mesh bed that names it has not run since it was converted. Found while converting the
route-forwarding bed to the catalogue's proxy, which requires the authority beside it.
initialise from. An own secret nobody delivers is minted by the mesh as random bytes, and random
bytes are not a certificate: issued that way, the authority cannot initialise. It could be
raised by an operator making a root with openssl and delivering all three through `secret
accept` — the whole-mesh bed did exactly that, and has not run since it was converted — but a
module that only starts once a person has hand-made its key material is not a module a mesh
can raise. Found while converting the route-forwarding bed to the catalogue's proxy, which
requires the authority beside it.
The authority can make its own root at first start — the certificate bed raises it that way and
it issues within a second. What it cannot do then is tell the mesh what that root is: a
@@ -20,6 +20,10 @@ taught, both about the bed rather than the decision:
real first node is.
- With the overlay's networking and the three modules in **one** push, the proxy's fetch of the
roots timed out at the private-network address; with the overlay converged first and the
modules pushed after, it passes. Whether that was the order of application within a push or
the filter closing the interface until it was derived was not isolated. A consumer whose first
start dials a provider assumes the provider's network is already there; the bed makes it so.
modules pushed after, it passes. The order between modules is not the cause: the controller
applies a node's providers before its consumers. The overlay interface and the filter that
admits it were not there yet, and the gate, as first written, tried once with no timeout — a
fetch that hangs holds the node's whole apply. The gate now retries with a timeout and refuses
a body that is not a certificate. A consumer whose first start dials a provider still assumes
the provider's network exists; a fact fetched once per declaration is
[issue 077](../077-a-fact-fetched-at-first-start-is-fetched-once/00-report.md).
@@ -0,0 +1,35 @@
---
status: open
opened: 2026-09-21
located-in: [mesh-host internal/apply (run-once marker), mesh-catalog modules/route-proxy]
---
# 077 — A fact fetched at first start is fetched once per declaration
## Symptom
A consumer fetches a fact its provider made at first start through a run-once step
([ADR 0098](../../02-DECISIONS/0098-a-fact-a-provider-makes-at-first-start-is-fetched-from-it.md)):
the route proxy fetches the certificate authority's root before it starts. The host runs a
run-once step once per declaration digest. When the authority is re-initialised — its state
wiped, or the module moved to another node, where it makes a new root — the proxy's declaration
is unchanged, so the step does not run again. The proxy keeps the old root, refuses the new
authority's certificates, and its own healing path, keyed on the root it holds, never fires.
Observed by reading the apply loop and the proxy, not from an incident. No bed re-keys an
authority.
## Why it matters beyond the instance
Any fact a provider makes at first start has the same shape: the consumer's declaration does not
change when the provider's fact does. A run-once step cannot say "again when the provider
changed", and a restart trigger is not allowed on a run-once step (ADR 0053), so there is no
declarative remedy today.
## What would close it
Either the run-once marker includes something of the provider's — the provider's declaration
digest, or an epoch the mesh raises when a provider is re-issued or moved — or the gate is not
run-once but a validator that runs before every start of the service and is cheap when nothing
changed. Decided, then proven by a bed that re-keys the authority and watches the proxy trust the
new root.
@@ -0,0 +1,32 @@
---
status: open
opened: 2026-09-21
located-in: [mesh-controller internal/inventory (secrets), mesh-controller cmd (secret accept)]
---
# 078 — A delivered secret is accepted under any name
## Symptom
`secret accept <node> <module> <name>` stores a value for a module under a name it does not
check against the module's manifest. A name the manifest no longer declares — an own secret that
became a requirement kept in the vault, or a name that never existed — is stored silently. The
row is dead: nothing reads it, the vault mints a value instead, and the operator believes they
delivered a secret the module is not using.
Found by review, not by a run: the whole-mesh bed delivered four such names after their modules
moved to the several-secrets vocabulary ([ADR 0094](../../02-DECISIONS/0094-a-module-may-hold-several-secrets-from-one-provider.md)),
and nothing said so.
## Why it matters beyond the instance
A silent acceptance is the shape of failure the mesh is built to refuse: an operator's action
that changes nothing and reports success. It hides every stale delivery, in beds and in operation
alike.
## What would close it
Acceptance is refused for a name the module's current manifest does not declare as an own
secret, with the names it does declare in the refusal. A unit test delivers under an undeclared
name and expects the refusal; the whole-mesh bed then fails loudly if a delivery goes stale
again.