diff --git a/.github/workflows/release-bundle.yml b/.github/workflows/release-bundle.yml index d9bf9d3..d36f996 100644 --- a/.github/workflows/release-bundle.yml +++ b/.github/workflows/release-bundle.yml @@ -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: @@ -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: @@ -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: diff --git a/docs/runbooks/release-lifecycle.md b/docs/runbooks/release-lifecycle.md index b10cccc..400482f 100644 --- a/docs/runbooks/release-lifecycle.md +++ b/docs/runbooks/release-lifecycle.md @@ -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 diff --git a/docs/runbooks/release-promotion-policy.md b/docs/runbooks/release-promotion-policy.md index ac82851..59d4927 100644 --- a/docs/runbooks/release-promotion-policy.md +++ b/docs/runbooks/release-promotion-policy.md @@ -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` | May be absent only as provisional | Candidate for bounded evaluation; no automatic promotion | +| stable | Exact `refs/tags/gds-v` | Signed complete active-seven set | Nonprovisional release; consumer acceptance is still separate | +| frozen | Exact `refs/tags/gds-v` | 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.