ADR 0098; issue 076 opened and resolved; ADR 0097's base refusal live; ADR 0096 proven against the public hub; 074 down to one bed #69
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user