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:
@@ -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
|
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
|
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
|
own secret nobody delivers as random bytes, and random bytes are not a certificate: issued, the
|
||||||
could not start, and no bed had raised it
|
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)).
|
([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
|
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`,
|
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
|
## How it is checked
|
||||||
|
|
||||||
The route-forwarding bed installs the authority, the proxy and a consumer from the catalogue and
|
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
|
asserts a routed name is served through the proxy. The proxy refuses to start on a bundle that is
|
||||||
gate fetched. The catalogue-wide manifest test parses both manifests.
|
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
|
## 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
|
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
|
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
|
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
|
run-once step before it starts. The step is run once per declaration: a root that changes
|
||||||
authority, the proxy and a consumer from the catalogue and asserts the routed name is served.
|
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
|
### 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
|
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
|
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
|
initialise from. An own secret nobody delivers is minted by the mesh as random bytes, and random
|
||||||
certificate: as written, the authority cannot initialise, and no bed has ever raised it — the
|
bytes are not a certificate: issued that way, the authority cannot initialise. It could be
|
||||||
whole-mesh bed that names it has not run since it was converted. Found while converting the
|
raised by an operator making a root with openssl and delivering all three through `secret
|
||||||
route-forwarding bed to the catalogue's proxy, which requires the authority beside it.
|
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
|
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
|
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.
|
real first node is.
|
||||||
- With the overlay's networking and the three modules in **one** push, the proxy's fetch of the
|
- 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
|
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
|
modules pushed after, it passes. The order between modules is not the cause: the controller
|
||||||
the filter closing the interface until it was derived was not isolated. A consumer whose first
|
applies a node's providers before its consumers. The overlay interface and the filter that
|
||||||
start dials a provider assumes the provider's network is already there; the bed makes it so.
|
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