Files
hq/02-DECISIONS/0047-the-bundle-may-carry-actions-the-link-may-not.md
T
jschoubben 93470f6162 ADR 0047 — the bundle may carry actions the link may not
The bootstrap's sharpest open question, and the framing was wrong. "State on
this machine" was being read as the filesystem and the service manager. A
service running on this machine IS part of this machine — writing a file and
creating a database in a local store differ in mechanism, not in scope.

The real question was underneath: must the host learn what a database is? It
must not. Giving it a `database` resource type means tier 0 knows Postgres, then
a bucket, then a virtual host — the host acquiring the substrate's vocabulary
one service at a time, which is what ADR 0037 exists to stop.

So the bundle declares an ACTION and the host runs it and verifies it. What a
database means stays with the module that provides one; the host knows only how
to run a declared action against something local and check the result. Its
vocabulary grows by one shape rather than by one resource type per service.

Actions are permitted in the bundle and forbidden over the link, and the
asymmetry is deliberate. A bundle arrives WITH the binary: anyone able to put a
hostile action in it could equally have put it in the host itself, so refusing
actions there buys nothing and costs the bootstrap. The link is a separate
party, reachable separately, and an action there is the unbounded blast radius
ADR 0039 refuses. That decision stands unchanged.

And ongoing provisioning is not the host's at all — the control plane does it
once a mesh exists — so the asymmetry costs nothing.

Which dissolves the earlier worry about one mechanism with a tier boundary
inside it: there are two mechanisms, with different actors, scopes and trust
models, and that is the answer rather than a compromise.

Named rather than hidden: this is the escape hatch research 011 warned about,
arbitrary code in the place hardest to remove later. It is bounded by being
bundle-only and by every action having to declare how it verifies itself, and
that boundary is the whole defence.
2026-08-26 23:56:39 +02:00

5.2 KiB

status, date, deciders, reconstructed, extends
status date deciders reconstructed extends
accepted 2026-08-26 jochen false 0043-a-declaration-is-an-ordered-list-of-owned-resources.md

47. The bundle may carry actions the link may not

Context

The substrate is raised in five steps, and steps two and three happen before a mesh exists:

1  the host applies the bundle          the store runs; no mesh exists
2  a database is created in it          before there is anything to ask
3  the control plane's schema applied   a migration against that database
4  the control plane starts

That was recorded as the sharpest unresolved thing in the bootstrap path, on the grounds that creating a database inside a running store is not state on a machine.

That framing was wrong, and it was reading ADR 0043 too narrowly. State on this machine does not mean the filesystem and the service manager. A service running on this machine is part of this machine. Writing a file and creating a database in a local store differ in mechanism, not in scope.

The real question was underneath: must the host learn what a database is?

Considered options

  1. Give the host a database resource type. Then tier 0 knows Postgres — and next a bucket, a virtual host, a repository. The host acquires the substrate's vocabulary one service at a time, which is what ADR 0037 exists to stop. Rejected.
  2. Have the control plane do all provisioning, including the bootstrap. Rejected because it is not there yet: at step 2 the thing that would do the provisioning does not exist.
  3. The bundle declares an action; the host runs it and reads back. Chosen.

Decision

The host runs actions the bundle declares, and never learns what they mean.

A bundle step says run this, against this, and here is how to tell whether it worked. The host executes it and verifies. What a database is stays knowledge of the module that provides one; the host knows only how to run a declared action against something local and check the result.

Actions are permitted in the bundle and forbidden over the link.

ADR 0039 says what arrives over the link is declarations of known shape, never a command to run. That stands, unchanged. The bundle is a different trust path and the asymmetry is deliberate:

the bundle the link
where it comes from the installer somebody built and copied onto the machine a remote party
when it is fixed at build time, pinned and reviewable at any moment
what compromise means whoever built the installer, who also built the binary a control plane, which may be compromised separately
may contain an action yes no

The reasoning is that a bundle arrives with the binary. Anyone able to put a hostile action in it could equally have put it in the host itself, so refusing actions there buys nothing while costing the bootstrap. The link has no such property: it is a separate party, reachable separately, and an action there is the unbounded blast radius ADR 0039 refuses.

And ongoing provisioning is not the host's at all. Once a mesh exists, a consumer on one node granted a database on another is provisioned by the control plane. The host never performs a provisioning action from the link, so the asymmetry costs it nothing.

Consequences

  • There are two provisioning paths, and that is the answer rather than a problem. The host runs bootstrap actions locally from the bundle; the control plane provisions across the mesh afterwards. The earlier worry — one mechanism with a tier boundary inside it — dissolves, because they are two mechanisms with different actors, scopes and trust models.
  • The host's vocabulary grows by one shape, not one per service. action joins directory, file and service. Adding a substrate service does not add a host resource type.
  • An action must say how to verify itself. A step that runs and reports success without a read-back is the fault this project is about, and an action is the easiest place to reintroduce it — so the verification is part of the declaration rather than left to the applier.
  • This is the escape hatch research 011 warned about. Arbitrary code, in the one place it is hardest to remove later. It is bounded by being bundle-only and by requiring its own verification, and that boundary is the whole defence — it should be watched rather than trusted.
  • The bundle becomes something a person must be able to read. If it can run commands, the reason to keep it small and pinned stops being convenience and becomes review.

References