Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 5 additions & 22 deletions .github/workflows/release-bundle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,9 @@ jobs:
# job downloads this job's artifact and verifies SHA256SUMS before any OIDC
# token is requested.
#
# persist-credentials is NOT set to false here: actions/checkout's persisted
# token is required for fetch-depth:0 on a private repository. This job has
# no write/OIDC permission, so the persisted token cannot be abused. (The
# prior attempt at this split failed because persist-credentials:false
# stripped the token the private-repo deep fetch needs — commit 6863c0f.)
# The estate's own fleet builds the estate's own releases. The consumer binds
# the attestation to this repository, this workflow path, this source commit
# and this ref against the estate's trusted root; the hosting provider adds
# nothing to that identity, and requiring a GitHub-hosted one rejected valid
# releases built here.
# This repository is public, so GitHub-hosted runners are unmetered and a
# fork's pull request cannot reach anything of ours. All three jobs must keep
# ONE provider between them: provenance describes the environment of the run
# that produced it, so a mixed chain attests an environment the build did not
# happen in (core/releasebuilder asserts this).
# This public repository builds only on GitHub-hosted runners. The build,
# attestation and publication jobs keep the same provider, as required by
# the release workflow contract. Permissions remain separate per job.
runs-on: ubuntu-latest
timeout-minutes: 75
permissions:
Expand Down Expand Up @@ -188,11 +176,7 @@ jobs:
# tools, and only then requests OIDC tokens for attestation. All semantic
# release validation and candidate execution belongs to the unprivileged
# build job above; this job treats its output as inert data.
# OIDC signing runs on the fleet with the rest of the chain. Splitting it
# across providers would make the provenance describe an environment the
# build did not happen in, which is worse than either provider alone.
# Interim; see the org-rollout note on the build job. Must equal the other
# two: the contract test compares these lines verbatim.
# Keep this job on the same hosted provider as build and publication.
runs-on: ubuntu-latest
timeout-minutes: 75
permissions:
Expand Down Expand Up @@ -277,8 +261,7 @@ jobs:
# This job can create the GitHub Release but cannot request an OIDC token or
# create attestations. It consumes only inert artifacts produced by the
# preceding jobs and never checks out or executes candidate source.
# Interim; see the org-rollout note on the build job. Must equal the other
# two: the contract test compares these lines verbatim.
# Keep the same hosted provider as build and attestation.
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
Expand Down
74 changes: 32 additions & 42 deletions docs/runbooks/release-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,49 +42,39 @@ sequence already accepted in the consumer ledger. Repository transfer or
republication never resets that ledger, and `github.run_number` is local to one
workflow lineage, so it is not a release sequence.

The whole release chain runs on GitHub-hosted runners. The consumer
does not constrain the runner environment: `gh attestation verify` binds the
attestation to this repository, this reusable-workflow path, this source commit
and this ref, checked against the estate's own trusted root. An owner-controlled
fleet is exactly as authoritative for that identity as an owner-controlled
repository secret, and the previous `--deny-self-hosted-runners` restriction
rejected the estate's own valid releases while adding nothing to it.

Do not split the chain across providers. Provenance describes the environment of
the run that produced it, so building on one provider and attesting on another
yields a claim about an environment the build did not happen in — worse than
either provider used consistently.

The repository is private (ADR 0033) and owned by
the example-org organization, so `actions/attest` provenance and SBOM
attestation is an available release path — keyless Sigstore attestation works
for private repositories as long as the workflow holds `id-token: write`.
The whole release chain runs on GitHub-hosted runners. The unprivileged build
job executes the exact source and release gates with `contents: read`; a
separate attestation job holds OIDC/attestation authority and treats build
outputs as inert files; publication has release-write authority and no OIDC.
The consumer checks repository, workflow, source ref/commit, digests and the
independently trusted signing root. The current source contract keeps all three
jobs on the same hosted runner provider.

This repository is public. GitHub Free, Pro and Team support artifact
attestations for public repositories; private/internal attestations require
GitHub Enterprise Cloud. A permission such as `id-token: write` does not establish
plan eligibility. See [GitHub artifact attestation availability](https://docs.github.com/en/actions/how-tos/secure-your-work/use-artifact-attestations/use-artifact-attestations).

Canary may omit active-seven evidence only as explicitly provisional and cannot
auto-promote. Stable/frozen verify the aggregate signature, every isolated
record, exact executable/module/root versions, GDS profile and bridge digests,
freshness (maximum 72 hours), and the complete active set. Only signed artifacts
and public trust material enter workflow inputs; private signing keys never do.

The producer workflow is defined in the supporting `example-harnesses`
repository at `docs/gds-runtime-evidence.md`. Its deterministic flat archive is
the value encoded for `harness_evidence_archive_base64`; the independently
distributed public policy is encoded for
`harness_evidence_trust_policy_base64`. The policy identity needs both
`harness-evidence` and `harness-evidence-aggregate` roles. Before dispatch,
decode both into a temporary directory and run a local stable
`gds-release-builder` build against them; producer self-consistency is not a
substitute for compatibility with the real GDS verifier.

The gate was exercised end to end historically: run `30046936069` built the
canary-channel bundle from `refs/heads/main` (2026-07-23), and run
`30064955206` built, attested, and published the stable `gds-v0.1.0` bundle
from `refs/tags/gds-v0.1.0` (2026-07-24T10:11:01Z) with keyless Sigstore SLSA
build provenance and an SBOM attestation. Releases through `gds-v0.3.6` were
signed on self-hosted runners; they were previously unusable as bootstrap
candidates because the consumer rejected any self-hosted signer, and that
restriction is gone, so they are ordinary releases judged on their own contents.
A prior successful dispatch is still not an approval: every bootstrap release
requires its own exact `A5` approval.
auto-promote. Stable/frozen require an exact version tag and verify the aggregate
signature, every isolated record, anchored module/root identity, GDS profile and
bridge digests, freshness (maximum 72 hours), and the complete active set. This
gate is enforced independently of individual `runtime_tests.required` profile
settings. Only signed records and public trust material enter workflow inputs;
private signing keys never do.

The evidence producer is independently managed. Its deterministic flat archive
is encoded in `harness_evidence_bundle_base64`; the independently distributed
public policy is encoded in `harness_evidence_trust_policy_base64`. The policy
identity needs both `harness-evidence` and `harness-evidence-aggregate` roles.
Before dispatch, decode both into a temporary directory and exercise the local
`gds-release-builder` against the exact target source/tag and evidence. Producer
self-consistency is not a substitute for compatibility with the GDS verifier.

Published releases and past workflow runs are historical evidence. They do not
prove that a new source commit, sequence, channel or active-seven record set is
eligible, and do not authorize a later publication. Bind publication approval to
the concrete release identity through checkpoint `A5`.

## Read-only verification

Expand Down
204 changes: 87 additions & 117 deletions docs/runbooks/release-promotion-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,123 +2,93 @@

## Scope

This runbook consolidates the release, channel-promotion, and consumer-pin
governance that is otherwise spread across `release-lifecycle.md`, the
`gds-release-control-plane` skill, and the completion-plan checkpoint table. It
defines *when* an artifact may advance from build to a channel to a consumer
pin. It does not restate the lifecycle commands (see `release-lifecycle.md`) and
authorizes no external mutation.

## Status

Policy defined; **the first external immutable release has been executed.**
`gds-v0.1.0` (source commit `bace996`) was built, attested, and published on
2026-07-24T10:11:01Z by `.github/workflows/release-bundle.yml` through the
`release` gate, with keyless Sigstore SLSA build provenance and an SBOM
attestation over the six-file release directory. Artifact attestation is
available because the repository is private (ADR 0033) but keyless Sigstore
attestation works for private repositories as long as the workflow holds
`id-token: write`, and the repository is owned by the example-org
organization. Harness runtime proof is delegated out of the release gate (every
`harnesses/*/profile.yaml` sets `runtime_tests.required: false`), so the
`codex`/`zcode` records being `not-proven` did not block publication.

Still `NOT_PROVEN` and therefore still gating downstream promotion: harness
runtime evidence itself (`C6`), Linux consumer rehearsal, canary and estate
rollout adoption (`C11`/`C12`), live GitHub App evidence, and restore/recovery
rehearsal. Publishing an artifact is not promoting it. Do not weaken any gate to
work around these boundaries.

Authority: `docs/contracts/authority-and-change-protocol-v1.md`. Release mechanics:
`docs/runbooks/release-lifecycle.md`. Bundle contract:
`docs/contracts/bundle-release-v1.md`.
This runbook connects source verification, immutable publication, installation
and consumer adoption. The lifecycle commands are in
[release-lifecycle.md](release-lifecycle.md); the artifact shape is in
[the bundle contract](../contracts/bundle-release-v1.md). This document records
no private estate topology, deployed versions or current acceptance status.

Ordinary source integration follows the repository's selected development
policy. Release integrity remains required. A GitHub check is evidence for its
exact commit and outcome; source integration alone does not establish a release
or installed runtime.

## Release identity

`release.mode: bundle` (`.gds/repository.yaml`). A release identity is coherent
only when all of these agree and are recorded together:

- source commit (fully tracked clean worktree, reproducible `go1.26.7` build);
- monotonic release sequence (the anti-rollback floor);
- artifact digests over the exact six-file release directory;
- SPDX SBOM and Sigstore provenance in the offline evidence directory;
- SemVer label and changelog entry;
- selected-harness runtime evidence for the target device set, for every profile
that declares `runtime_tests.required: true`.

A version file, a Git tag, and a changelog line are not independently
authoritative. None of them promotes an artifact; the recorded release identity
does.

## Channels and promotion order

Channels advance in one direction only; an artifact never skips a stage:

1. **build** — reproduced byte-identical in two isolated environments; digests
compared. Not installable.
2. **canary** — installed on a canary control-plane device
(`rollout_ring: canary-control-plane`). Requires the read-only
`gds release verify` = `success` and one proven rollback to the prior
immutable bundle on that canary (stage `C11`).
3. **stable** — the estate default channel (`default_bundle_channel: stable`).
Promotion requires the canary acceptance above plus green required checks.
4. **frozen** — an accepted identity retained for rollback targeting; never
mutated or rewritten.

`rollout.mutation_mode: pull-request` and `default_ring: standard` are the
current controlled posture. Only managed NDDev source repositories are
eligible, and this policy does not bypass exact signed approval, one-shot
enablement, fresh provider evidence, mutation-capability scope, or the device
kill switch. All observe-only assignments remain non-mutable.

## Approval checkpoints

Two checkpoints gate the externally-visible transitions (completion plan §9):

- **A5** — tag / release / artifact / SBOM / distribution. Required before any
hosted publication.
- **A6** — canary branches/PRs and rollback. Required before a canary rollout
or an authorized downgrade.

Every apply that crosses a boundary carries an exact `approval:*` reference; a
rollback apply approval must equal its authorization approval reference exactly.

## Consumer pin advancement

A consumer (this control plane's module gitlinks, and any runtime-dependency
edge) advances its pin **only to a promoted identity**, never to a raw
default-branch commit chosen for convenience. This rule exists because the
estate today carries three independent pin sources that have already drifted:

- GDS submodule gitlinks (`macos-ubuntu-bootstrap`, `ci-workflows`); the exact
current pins are recorded in
`docs/version-ledger.md`, which `scripts/validate_version_ledger.py` checks
against the tree, and are not restated here;
- the OS bootstrap contract's runtime-clone pins (`codex`, `zcode`) — currently
behind;
- the harness registry expected heads (`config/repositories.json`) — currently
ahead.

Until a single canonical estate graph equality-checks these sources (tracked as
the estate-graph canonicalization work), pin advances must be reconciled by
hand against a promoted identity, and a stale consumer pin is a governance
defect, not a cosmetic lag. No pin advance is valid while its target's
promotion evidence is `pending` or `NOT_PROVEN`.

## Rollback

Rollback is the only sanctioned exception to the monotonic sequence floor and
follows the authorized-rollback section of `release-lifecycle.md`: name the
exact lower target sequence and artifact digest, the `InstallScopeDigest`, a
bounded reason, an exact approval reference, and a short expiry. After
verification, issue a corrective release at a new higher sequence; never lower
the durable acceptance floor and never rewrite an existing release.

## Stop conditions

Stop without promotion on any of: a build that is not byte-identical on rebuild;
a missing or non-attested SBOM/provenance; a `NOT_PROVEN` runtime record for a
selected harness whose profile declares `runtime_tests.required: true`; a
consumer pin pointed at an unpromoted identity; a channel skip; a rollback
lacking an exact matching approval reference; or any publication attempt while
the hosted-workflow preconditions in `release-lifecycle.md` are unmet.
The public GDS engine selects `release.mode: bundle`. A release binds:

- a clean exact source commit and permitted source ref;
- SemVer, channel and a monotonic release sequence;
- the supported Go toolchain and pinned dependency inputs;
- byte-identical builds and the exact six-file release directory;
- artifact digests, SPDX SBOM and Sigstore provenance;
- independently distributed consumer trust and offline verification material;
- signed active-seven harness evidence when the channel requires it.

The sequence must exceed the applicable consumer acceptance floor. A repository
transfer, workflow run number, version label or fresh tag does not reset that
floor. Conflicting published identities must not be overwritten.

## Channel requirements

| Channel | Source ref | Harness evidence | Meaning |
| --- | --- | --- | --- |
| canary | `refs/heads/main` or exact `refs/tags/gds-v<version>` | May be absent only as provisional | Candidate for bounded evaluation; no automatic promotion |
| stable | Exact `refs/tags/gds-v<version>` | Signed complete active-seven set | Nonprovisional release; consumer acceptance is still separate |
| frozen | Exact `refs/tags/gds-v<version>` | Signed complete active-seven set | Immutable identity retained under the consumer's rollback policy |

The builder verifies `antigravity-cli`, `claude-code`, `codex`, `cursor-cli`,
`grok-build`, `opencode` and `pi`, including aggregate/record signatures, anchored
producer/module identities, profile and bridge digests, and at most 72-hour
freshness. Individual profile flags do not waive stable/frozen aggregate proof.
`HARNESS_EVIDENCE_TRUST_POLICY_DIGEST` binds the workflow input to its independent
public trust policy. See `core/releasebuilder/harness_evidence.go` and
`core/harnessevidence` for the executable contract.

Publishing an immutable artifact does not install it. A consumer's rollout
policy decides eligible channels/rings, canary and rollback evidence, and later
promotion. It must not treat a provisional canary as a verified stable release
or mutate a published artifact to change its channel.

## Publication and installation

Checkpoint `A5` covers the exact tag, artifact, SBOM and publication identity.
Checkpoint `A6` covers an explicitly scoped canary rollout and rollback.
Use the existing owner authorization and signed operation contract; a previous
release or this runbook does not independently authorize a new provider write.

Before publication, require `scripts/validate_release.sh`, the builder's
independent rebuild and directory verification, and the target channel's signed
evidence. Build, attest and publish run on GitHub-hosted runners with separate
permissions. The repository is public; do not infer private attestation support
from OIDC permissions alone. [GitHub documents the availability boundary](https://docs.github.com/en/actions/how-tos/secure-your-work/use-artifact-attestations/use-artifact-attestations).

Before installation, `gds release verify` must succeed against independent local
trust and exact offline materials. Plan/apply/verify then binds the target,
existing installation, acceptance ledger and approval. Runtime and rollback
acceptance are observed by the owning consumer; they are never inferred from a
release page or source commit.

## Module consumer pins

Consumer gitlinks are governed by each module's declared pin policy. GDS supports
`default-branch-commit` and explicit verified versioned-artifact transactions;
a global rule forbidding every raw main commit would contradict the first mode.
For a version-tag consumer, an advanced main branch does not satisfy the release
contract: verify the selected tag, source and required immutable assets before
advancing the gitlink. Do not change the pin policy to hide missing release
proof. The private estate owns its current pins and observations; this public
repository must not duplicate them in a version ledger.

## Rollback and refusal

Rollback is an explicit exception to monotonic installation order. It binds the
lower installed sequence and artifact digest, canonical install scope, bounded
reason, exact approval reference and expiry. Preserve the durable acceptance
floor and follow rollback with a new higher-sequence corrective release.

Refuse publication or promotion on dirty/unverified source, failed release
checks, nonreproducible artifacts, missing/mismatched attestations or SBOM,
stale/incomplete required harness evidence, conflicting sequence/tag identity,
unapproved provider writes, or a consumer target lacking its required trust and
acceptance evidence. Keep failures and missing evidence explicit.