Development record
abcd never runs a foreign executable under its own name, and no code that writes records is loaded at run time
Context
People asked for abcd to be extended by turning each verb into a plug-in or module.
The verbs are not independent features. They are views over one record store, and that store has shared rules: the lint gates, the release cut and the reservation fence read what capture, intent and spec write. So a verb and the gate that judges its output have to read every field the same way.
None of the ways to load code at run time keeps that guarantee or abcd's trust chain intact:
- Go's
pluginpackage. The plug-in must be built with exactly the same toolchain, and exactly the same source for every shared dependency. A loaded plug-in cannot be closed, and the race detector supports it poorly (https://pkg.go.dev/plugin). - Executables found by name on PATH (
git-<name>,gh-<name>). Their hosts verify no provenance; GitHub says outright that extensions are not verified, signed or endorsed (https://cli.github.com/manual/gh_extension). - Subprocess RPC (HashiCorp's go-plugin). Its handshake "is not a security measure", and checksum checks are opt-in (https://github.com/hashicorp/go-plugin).
- WebAssembly sandboxes. By default they deny the file and process access that a verb needs.
SLSA provenance covers only the artefacts its build produced (https://slsa.dev/spec/v1.0/threats). abcd updates itself only from builds whose provenance is signed by its own release workflow (iss-2609231050273096), and it promotes its cache to PATH only through a home-scoped attestation (adr-2609151706587280), and it fetches tools only from downloads pinned in its own release (adr-2610241057124151).
Decision
We will keep the verbs compiled into the one attested binary (adr-21). No code that writes records is loaded at run time, and abcd never runs a foreign executable under its own name.
abcd is extended at capability seams instead (adr-22):
- A repository declares, in data, which external program stands behind a seam, pinned by checksum and signer.
- The person admits that program once from their home before it runs on their machine. The admission is bound to the repository's identity (its root commit), the program's checksum and signer, and the exact invocation and configuration the repository declares for it, and the configuration is bound by a digest of its contents (and of any file it points to), so a yes given for one repository is not reused by an unrelated one and a changed argument or config asks again; a fork shares its root commit, so a fork declaring the identical program, invocation and configuration bytes reuses the yes, which admits nothing the person has not already admitted. Admitting is a person-act: the shell guard refuses it from an agent, and no sequence may contain it.
- The core's joins see the adapter's result unchanged: build, drain, lint, launch, the guard and the board.
- The first seam is the project check of itd-62.
A verb-shaped addition for a repository's product lives in the host's own plugin format or in a script, and reaches abcd through the MCP front door's structured results (itd-113).
Alternatives Considered
- Each verb a Go
plugin. Rejected because of the exact-build rule: every plug-in would need rebuilding for each abcd release. That suits modules built alongside abcd, and those are already Go packages. abcd-<name>executables on PATH, as git and gh do it. Rejected. Nothing verifies them, they sit outside every join the core makes, and they would duplicate the host's own plugin mechanism under abcd's name.- Subprocess RPC or WebAssembly verbs. Rejected. The first adds a dependency and a process on every hook event. The second denies what verbs need. Both still need a trust chain per plug-in.
- Capability seams declared in pinned data. Chosen. The joins stay in the core, and only the backend varies.
Consequences
- An extension never changes what a record means, so every repository's ledger stays readable by
embark,lintand the release cut. - Each new seam is built only when a consumer needs it. The project check of itd-62 is first.
- The person's one-time admission needs a home-scoped store keyed on the repository's root commit, the program's checksum and signer, and its declared invocation and configuration, as the existing one-time allows are keyed on the root commit. It is asked again when any of them changes.
- A wish to add a verb becomes a contribution to the one release, a host plugin, or a script. abcd itself never provides a plug-in channel.
Frontmatter
| id | adr-2611011813195749 |
|---|---|
| type | adr |
| status | proposed |
| date | 2026-11-01 |
.abcd/development/decisions/adrs/2611011813195749-abcd-never-runs-a-foreign-executable.md
open on GitHub ↗ · commit history ↗
References5
- adr-21Rebuild abcd as a Go binaryrelated
- adr-22Bundled dependencies become pluggable adapters over a native defaultrelated
- adr-2610241057124151abcd installs the tools it needs from downloads pinned in its own releaserelated
- itd-62abcd Routes The Invisible Risks An Amateur Cannot See To A Real, Fail-Closed Gate That Wraps A Trusted Scannerrelated
- itd-113The MCP Front Door Opens — abcd's Core Verbs From Any MCP-Capable Harnessrelated
mentions2
- adr-2609151706587280The cache reaches PATH only through a home-scoped attestation the environment cannot write
- iss-2609231050273096abcd update verifies a downloaded binary only against the checksums.txt published in the same GitHub release (internal/core/update/update.go), so it catches corruption in transit but not a release page whose binary and checksums.txt were both replaced; the binary needs an independent check next cycle, such as a digest committed in the repository's history the way the plugin catalog now pins the plugin archive (adr-2609231048308186), or verification of the release's signed build-provenance attestation (a new verification dependency needs sign-off first). Ruled by the product thinker on 2026-09-23 (E5, 10:27Z): harden next cycle; this release keeps the same-page check.