diff --git a/02-DECISIONS/0098-a-fact-a-provider-makes-at-first-start-is-fetched-from-it.md b/02-DECISIONS/0098-a-fact-a-provider-makes-at-first-start-is-fetched-from-it.md new file mode 100644 index 0000000..6d0b729 --- /dev/null +++ b/02-DECISIONS/0098-a-fact-a-provider-makes-at-first-start-is-fetched-from-it.md @@ -0,0 +1,58 @@ +--- +topic: the tiers +status: accepted +date: 2026-09-21 +deciders: jochen +reconstructed: false +extends: 02-DECISIONS/0085-a-secret-is-a-provision.md +--- + +# 98. A fact a provider makes at first start is fetched from it, not carried in its manifest + +## Context + +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 +([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`, +which is written in the manifest before anything runs. + +## Considered Options + +1. **A secret the module makes**, with the mesh taking custody once the file exists. Rejected + for now: a node would have to send a value up to the mesh, which no channel does today, and + a root key is the one thing the mesh has no reason to hold. +2. **A served fact the provider contributes at run time.** Rejected for now: the same new + channel, for a fact that is not secret at all. +3. **The consumer fetches it from the provider**, over the mesh network, through a gate before + the thing that needs it starts. Adopted. + +## Decision + +A provider's `serves` names where a fact made at first start can be fetched — the authority +serves its root at a path beside its ACME directory — and a consumer fetches it in a `run-once` +step declared before the resource that needs it, from the provider's bound address. The mesh +network is where the fetch happens, which is what makes fetching without a prior trust +acceptable: it is the network the mesh itself authenticates. The mesh mints only what it can +make: the authority's password. The root key stays where it was made. + +## Consequences + +The catalogue's authority starts, and the proxy that requires it trusts what it fetched. What +got harder: a consumer of such a fact carries one more resource, the gate that fetches it, and +a fact that changes after first start is refetched only when the declaration changes. + +## 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. + +## References + +- [issue 076](../04-ISSUES/076-a-served-fact-made-at-first-start-cannot-be-served/00-report.md) +- [ADR 0053](0053-a-step-that-runs-on-a-schedule.md), [ADR 0085](0085-a-secret-is-a-provision.md) +- [`03-DESIGN/01-to-be/08-connectivity.md`](../03-DESIGN/01-to-be/08-connectivity.md) diff --git a/02-DECISIONS/README.md b/02-DECISIONS/README.md index 3b098db..63d6133 100644 --- a/02-DECISIONS/README.md +++ b/02-DECISIONS/README.md @@ -115,6 +115,7 @@ python3 00-META/checks/index.py fail if stale - **0092** — [An operator delivers a pair credential, and the mesh never replaces it](0092-an-operator-delivers-a-pair-credential.md) - **0094** — [A module may hold several secrets from one provider, each a pair of its own](0094-a-module-may-hold-several-secrets-from-one-provider.md) - **0095** — [The control plane is the way to ask a module](0095-the-control-plane-is-the-way-to-ask-a-module.md) +- **0098** — [A fact a provider makes at first start is fetched from it, not carried in its manifest](0098-a-fact-a-provider-makes-at-first-start-is-fetched-from-it.md) ### What runs on them, and how it gets there diff --git a/03-DESIGN/01-to-be/08-connectivity.md b/03-DESIGN/01-to-be/08-connectivity.md index e5871da..9badb7e 100644 --- a/03-DESIGN/01-to-be/08-connectivity.md +++ b/03-DESIGN/01-to-be/08-connectivity.md @@ -7,8 +7,9 @@ code: - mesh-controller internal/identity/authority.go - mesh-host internal/identity/serving.go - mesh-host internal/apply (the service that reflects a rule set) -updated: 2026-09-09 +updated: 2026-09-21 decisions: + - 02-DECISIONS/0098-a-fact-a-provider-makes-at-first-start-is-fetched-from-it.md - 02-DECISIONS/0005-the-node-host.md - 02-DECISIONS/0004-a-node-and-how-it-joins.md - 02-DECISIONS/0007-connectivity.md @@ -557,6 +558,15 @@ fingerprint in its token ([ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-j so nothing needs the CA before membership. It certifies internal names afterwards, and that is all it does. +**The internal authority makes its own root at first start, and a consumer fetches it** +([ADR 0098](../../02-DECISIONS/0098-a-fact-a-provider-makes-at-first-start-is-fetched-from-it.md)). +The mesh mints the authority's password and nothing else of its: a root certificate and its key +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. + ### What was built *2026-08-31.* diff --git a/04-ISSUES/074-a-mesh-test-wears-a-catalogue-modules-name/01-diagnosis.md b/04-ISSUES/074-a-mesh-test-wears-a-catalogue-modules-name/01-diagnosis.md index 7d1437f..f2313f7 100644 --- a/04-ISSUES/074-a-mesh-test-wears-a-catalogue-modules-name/01-diagnosis.md +++ b/04-ISSUES/074-a-mesh-test-wears-a-catalogue-modules-name/01-diagnosis.md @@ -30,3 +30,6 @@ route-forwarding, which needs the certificate authority beside the proxy, and th *Route-forwarding's conversion is blocked:* the catalogue's authority cannot be raised as written ([issue 076](../076-a-served-fact-made-at-first-start-cannot-be-served/00-report.md)). + +*Later the same day.* Route-forwarding converted, with the authority beside the proxy +(ADR 0098). One bed remains declared: the large mesh test, with its three fixtures. diff --git a/04-ISSUES/076-a-served-fact-made-at-first-start-cannot-be-served/00-report.md b/04-ISSUES/076-a-served-fact-made-at-first-start-cannot-be-served/00-report.md index 4494234..a39ae2b 100644 --- a/04-ISSUES/076-a-served-fact-made-at-first-start-cannot-be-served/00-report.md +++ b/04-ISSUES/076-a-served-fact-made-at-first-start-cannot-be-served/00-report.md @@ -1,9 +1,9 @@ --- -status: located +status: resolved opened: 2026-09-21 -located-in: [mesh-catalog modules/step-ca, mesh-controller internal/catalogue (serves)] -fixed-by: -amended-design: +located-in: [mesh-catalog modules/step-ca, mesh-catalog modules/route-proxy] +fixed-by: ADR 0098; mesh-catalog multiple-fixes (the authority makes its own root and serves it; the proxy fetches it through a gate); proven by the route-forwarding bed +amended-design: 03-DESIGN/01-to-be/08-connectivity.md --- # A served fact made at first start cannot be served, so the catalogue's authority cannot start diff --git a/04-ISSUES/076-a-served-fact-made-at-first-start-cannot-be-served/01-diagnosis.md b/04-ISSUES/076-a-served-fact-made-at-first-start-cannot-be-served/01-diagnosis.md new file mode 100644 index 0000000..b066938 --- /dev/null +++ b/04-ISSUES/076-a-served-fact-made-at-first-start-cannot-be-served/01-diagnosis.md @@ -0,0 +1,11 @@ +# Diagnosis — 2026-09-21 + +1. The certificate bed already raised the same authority image with no root supplied, and it made + its own root and issued within a second. The manifest's three minted "secrets" were not needed + by the authority; they were needed by the consumer, which was handed the root as a served fact. +2. Of the three ways to get a fact made at first start to a consumer, two need a channel from a + node up to the mesh that does not exist. The third needs nothing new: the provider serves the + fact at a path, and the consumer fetches it over the mesh network in a gate before it starts. + +**Located in:** the two manifests. Decided in +[ADR 0098](../../02-DECISIONS/0098-a-fact-a-provider-makes-at-first-start-is-fetched-from-it.md).