ADR 0014: Distribution trust (one fail-closed policy)¶
- Status: Accepted
- Date: 2026-09-17
Context¶
The installer verified provenance only when Cosign happened to be present
with a retrievable bundle; otherwise a checksum from the same untrusted
path was the whole story (pre-publication audit F11; the note was not
published). Generated CI compiled
the toolchain in the customer job instead of using release binaries, and
the SDK resolved inside go.work but not as a published dependency (F07).
No complete provider download path existed (F02 distribution half). The
release pipeline built the provider from a deleted main path and emitted
lockfiles the loader refuses.
Decision¶
One trust policy over four surfaces: identity and content verified before execution, fail closed on missing or invalid material, no silent skip.
- Installer: bootstraps a pinned Cosign release (checksums embedded in the
script), requires the Sigstore bundle on
checksums.txt, verifies archive SHA-256. Trust root is the script content itself. - Updater: verifies bundle plus checksum (as before), keeps a same-directory backup, swaps atomically, self-checks the new binary, restores on failure.
- Core load: digest plus bundle per lockfile entry, beside the CLI or from
the user cache (
<user-cache>/magelift/providers/<id>/<version>/<os>_<arch>/). - Provider download:
magelift providers installresolves providers frommagelift.yaml(never flags), fetches over HTTPS-only URLs, verifies before atomic cache install.
Community alpha acquisition uses this same resolver and trust policy. A valid
project lock takes priority over the release lock; an invalid project lock is
an error. Only the first-party GCP provider may be acquired automatically on
activation. With no installed lock, the CLI release version selects the exact
release lock; development builds require an explicit lock or installation.
No mutable latest default or alternative provider is substituted.
Cache reuse verifies the digest and signature. Automatic acquisition refuses
corrupt entries; explicit providers install provides repair. Installs serialize
across processes and expose the verified binary and bundle together. A valid
legacy cache remains usable offline. Registration, help, version, doctor and
envelope validation remain free of automatic provider installation.
Provider blob verification passes an explicit public Sigstore trust-root snapshot to the pinned Cosign binary. This avoids Cosign 3.1.3 refreshing TUF during offline cache use. The snapshot was authenticated through Sigstore TUF on 2026-10-17; its embedded digest is checked before materialization and reuse. Corruption fails closed and explicit installation can repair it. Trust-root rotation requires a reviewed CLI update; arbitrary local roots are not loaded.
Tags are vX.Y.Z core, sdk/vX.Y.Z SDK, providers/<name>/vX.Y.Z
providers (ADR 0013), lockstep for alpha. Module tags push first, proxy
resolution is confirmed, the release tag pushes last and triggers the
signed release. The development path is build-from-source; no bypass flag
or variable exists.
Consequences¶
- First install needs no signing tooling and skips nothing.
- CI consumes verified release binaries; customer repos need no Go toolchain.
go mod tidyat root fails pre-tag by design (unpublished modules have no resolvable versions); the tag-then-require sequence clears it.- Release lockfiles always load: the generator shares the loader's validation.
Alternatives¶
- Require user-installed Cosign for install: rejected (first-install friction, version drift; the pinned bootstrap is deterministic).
- In-process Sigstore verification via Go libraries: rejected for alpha (large new dependency surface; the exec boundary stays explicit and fail-closed).
- Per-surface policies: rejected (four chances to drift; one table to audit).
Provenance¶
Pre-publication audit F02/F07/F11. The intent notes were not published. Code:
website/public/install.sh, internal/upgrade, internal/providerhost,
internal/cli/ci.go, cmd/genproviders, tests/distribution, ADRs
0005/0008/0011/0013.