Place the two new records in the reading order, and fix a link the merge moved
Both carried a topic outside the six the index knows, so neither had a place to be read in — 0067 had none at all. Both are 'the tiers', beside 0036 (bootstrap ends at a usable mesh) and 0007 (connectivity), which is what they extend. 0067 cited 0041 for tier 0's property; on this trunk 0041 is events, and the record it meant is 0005. A citation that resolves to the wrong record reads as corroboration, which is worse than a dead link. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
---
|
||||
topic: routing and names
|
||||
topic: the tiers
|
||||
status: proposed
|
||||
date: 2026-09-09
|
||||
deciders: jochen
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
topic: the tiers
|
||||
status: proposed
|
||||
date: 2026-09-10
|
||||
deciders: jochen
|
||||
@@ -121,7 +122,8 @@ machine auditable. An installer connects to plenty. Same tier, same delivery, di
|
||||
|
||||
- [ADR 0006 — the substrate and the control plane](0006-the-substrate-and-the-control-plane.md),
|
||||
which pinned images by digest and named what a first node fetches.
|
||||
- [ADR 0041 — the host depends on nothing that must be installed first](0041-the-host-depends-on-nothing.md),
|
||||
the tier 0 property this keeps true by not putting the installer inside the host.
|
||||
- [ADR 0005 — the node host](0005-the-node-host.md), which makes tier 0 the one thing installed by
|
||||
hand and the only thing that changes a machine — the property this keeps true by shipping the
|
||||
installer beside the host rather than inside it.
|
||||
- [`04-ISSUES/029`](../04-ISSUES/029-the-artifact-store-cannot-be-delivered-by-the-artifact-store/00-report.md),
|
||||
the same cycle one layer down, and the rule that a registry module is named and never built.
|
||||
|
||||
@@ -98,6 +98,8 @@ python3 00-META/checks/index.py fail if stale
|
||||
- **0031** — [The control plane authenticates nobody, so identity is a module](0031-the-control-plane-authenticates-nobody.md)
|
||||
- **0033** — [The substrate is a store and a broker](0033-the-substrate-is-a-store-and-a-broker.md)
|
||||
- **0036** — [Bootstrap ends at a usable mesh, and the first credential comes from a person](0036-bootstrap-ends-at-a-usable-mesh.md)
|
||||
- **0066** — [Public routing is name-agnostic, its names are resolved inside the mesh, and an internal authority can certify them](0066-public-routing-is-name-agnostic.md) *(proposed)*
|
||||
- **0067** — [Genesis is a pivot: a temporary control plane installs the registry that makes it permanent](0067-genesis-is-a-pivot.md) *(proposed)*
|
||||
|
||||
### What runs on them, and how it gets there
|
||||
|
||||
@@ -121,6 +123,7 @@ python3 00-META/checks/index.py fail if stale
|
||||
- **0050** — [Model access is vendor-agnostic, and a vendor is an adapter](0050-model-access-is-vendor-agnostic.md)
|
||||
- **0051** — [Shared data is the operator's, and a module is granted access to it](0051-shared-data-is-the-operators.md)
|
||||
- **0052** — [An init step is a container run once to completion, gating what follows](0052-a-step-that-runs-once-before-a-container.md)
|
||||
- **0053** — [A scheduled step is a container run on a recurring schedule](0053-a-step-that-runs-on-a-schedule.md)
|
||||
|
||||
### How it is built
|
||||
|
||||
@@ -149,4 +152,9 @@ python3 00-META/checks/index.py fail if stale
|
||||
- **0032** — [The local account owns the mesh; a surface delegates to a module](0032-the-local-account-owns-the-mesh.md) *(superseded)*
|
||||
- **0034** — [The local account owns the mesh, and a web application's login is not that](0034-the-local-account-owns-the-mesh.md)
|
||||
|
||||
### Unfiled
|
||||
|
||||
- **0054** — [Model usage is a vendor-neutral record, produced by the adapter, at two grains](0054-model-usage-is-recorded-at-two-grains.md) — `topic:` is 'model access', which is not one of building it, checking it, how we work, the mesh, the tiers, what runs on it
|
||||
- **0055** — [Model access is answered by a licence, or by a node that hosts the model](0055-model-access-is-answered-by-a-licence-or-a-node.md) — `topic:` is 'model access', which is not one of building it, checking it, how we work, the mesh, the tiers, what runs on it
|
||||
|
||||
<!-- index:end -->
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
status: open
|
||||
opened: 2026-09-10
|
||||
located-in: []
|
||||
fixed-by:
|
||||
amended-design:
|
||||
---
|
||||
|
||||
# 041 — A credential the mesh took care to seal ends up in the process environment
|
||||
|
||||
## Symptom
|
||||
|
||||
The mesh generates a module's own secret, **seals it to the machine and discards the plaintext** —
|
||||
it cannot read the value back even if asked. The host unseals it into a file the module names, at
|
||||
`0600`.
|
||||
|
||||
Then the module hands it to its container as an environment variable, and the runtime puts it
|
||||
where anything on that machine that can talk to the runtime can read it: `docker inspect` prints
|
||||
it, and `/proc/<pid>/environ` holds it for the life of the process.
|
||||
|
||||
Observed while making the control plane an ordinary module. Its **store** connections were moved to
|
||||
files, read by a `…_FILE` variable naming the path. Its **broker** credentials have no such variable,
|
||||
so they are still delivered through an env-file — which the runtime turns into exactly the
|
||||
environment above. Same credential handling, same machine, two different exposures, decided by
|
||||
whether the program that reads it happens to accept a path.
|
||||
|
||||
## Why this matters
|
||||
|
||||
**The care taken elsewhere is what makes this stand out.** Sealing to a machine and discarding the
|
||||
plaintext is expensive and deliberate: it exists so that a credential is readable only where it is
|
||||
used. Handing that same value to the runtime as an environment variable gives it back to anything
|
||||
that can run `inspect` — and `inspect` is a routine operation. It lands in support output, in
|
||||
captured logs, in a screenshot of a terminal, and in any tooling that dumps container state.
|
||||
|
||||
**It is not a module's mistake.** Nothing in the manifest format is being misused: placing a secret
|
||||
into an env-file with `${secret:…}` is a supported shape and other modules use it. So each module is
|
||||
correct on its own, and the property — *a sealed credential is not readable by everything on the
|
||||
machine* — holds or fails per variable, by accident of what each program accepts.
|
||||
|
||||
**And the two halves now disagree inside one module.** The control plane reads its store connection
|
||||
from a file and its broker credential from the environment. A reader cannot tell from the manifest
|
||||
which secrets are protected from `inspect` and which are not, because the manifest looks the same
|
||||
either way.
|
||||
|
||||
## Open questions
|
||||
|
||||
- Should every program the mesh runs accept a path for anything secret — a `…_FILE` twin as a
|
||||
convention rather than a thing each program decides? That is a small change in several programs
|
||||
and a large one in what the manifest can promise.
|
||||
- Should the manifest layer **refuse** `${secret:…}` inside a container's `env`, or inside an
|
||||
`env-file`, once a path-shaped alternative exists? A rule nothing enforces is the shape this
|
||||
repository keeps finding.
|
||||
- Is there a case where the environment is genuinely the only channel — a program that cannot be
|
||||
changed and reads no file? If so, what should the mesh say about that module, out loud, rather
|
||||
than treating it as equivalent?
|
||||
- What is the actual reach of the exposure on a node — which identities can talk to the container
|
||||
runtime, and is that set smaller than "anything running as the operator"? The answer decides
|
||||
whether this is a hardening item or something sharper.
|
||||
Reference in New Issue
Block a user