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
124 changes: 18 additions & 106 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
required: true
type: string
publish:
description: Publish and tag after every check passes
description: Publish and tag after direct package checks pass
required: true
default: false
type: boolean
Expand All @@ -20,8 +20,7 @@ concurrency:
jobs:
candidate:
runs-on: ubuntu-latest
timeout-minutes: 180
environment: release
timeout-minutes: 45
permissions:
contents: read
outputs:
Expand Down Expand Up @@ -55,29 +54,13 @@ jobs:
- name: Configure external release storage
run: echo "BRAID_RELEASE_ARTIFACT_ROOT=$RUNNER_TEMP/braid-release" >> "$GITHUB_ENV"

- name: Restore resumable release state
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: ${{ runner.temp }}/braid-release
key: braid-release-${{ inputs.commit }}-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
braid-release-${{ inputs.commit }}-

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Install pinned visual tools
run: |
sudo apt-get update
sudo apt-get install --yes fonts-dejavu-core imagemagick
AGG_URL="https://github.com/asciinema/agg/releases/download/v1.9.0/agg-x86_64-unknown-linux-gnu"
AGG_SHA256="f111e315cd71056b116302342553dd765b7297579ed511f111d0cedb442aeda6"
curl --fail --location --silent --show-error "$AGG_URL" --output "$RUNNER_TEMP/agg"
echo "$AGG_SHA256 $RUNNER_TEMP/agg" | sha256sum --check --strict
sudo install -m 0755 "$RUNNER_TEMP/agg" /usr/local/bin/agg
test "$(agg --version | head -1)" = "agg 1.9.0"

- name: Build and exercise one immutable candidate
- name: Run source checks once
run: pnpm check

- name: Build and use one immutable candidate
run: pnpm release:prepare

- name: Read candidate metadata
Expand All @@ -89,68 +72,12 @@ jobs:
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "archive=$ARCHIVE" >> "$GITHUB_OUTPUT"

- name: Run the complete release checks
env:
BRAID_LIVE_BRIDGE: '1'
BRAID_CLI_BRIDGE_BEARER: ${{ secrets.BRAID_CLI_BRIDGE_BEARER }}
BRAID_CLI_BRIDGE_URL: ${{ vars.BRAID_CLI_BRIDGE_URL }}
BRAID_EVAL_API_KEY: ${{ secrets.BRAID_EVAL_API_KEY }}
BRAID_EVAL_BASE_URL: ${{ vars.BRAID_EVAL_BASE_URL }}
BRAID_EVAL_MODEL: ${{ vars.BRAID_EVAL_MODEL }}
BRAID_TANGLE_API_KEY: ${{ secrets.BRAID_TANGLE_API_KEY }}
BRAID_TANGLE_ENDPOINT: ${{ vars.BRAID_TANGLE_ENDPOINT }}
BRAID_TANGLE_MODEL: ${{ vars.BRAID_TANGLE_MODEL }}
BRAID_TANGLE_PROVIDER: ${{ vars.BRAID_TANGLE_PROVIDER }}
BRAID_TANGLE_RUNNER: ${{ vars.BRAID_TANGLE_RUNNER }}
BRAID_TANGLE_SANDBOX_API_KEY: ${{ secrets.BRAID_TANGLE_SANDBOX_API_KEY }}
BRAID_TANGLE_SANDBOX_ENDPOINT: ${{ vars.BRAID_TANGLE_SANDBOX_ENDPOINT }}
BRAID_TANGLE_SANDBOX_MODEL: ${{ vars.BRAID_TANGLE_SANDBOX_MODEL }}
BRAID_TANGLE_SANDBOX_PROVIDER: ${{ vars.BRAID_TANGLE_SANDBOX_PROVIDER }}
BRAID_TANGLE_SANDBOX_RUNNER: ${{ vars.BRAID_TANGLE_SANDBOX_RUNNER }}
BRAID_ANALYSIS_API_KEY: ${{ secrets.BRAID_ANALYSIS_API_KEY }}
BRAID_ANALYSIS_ENDPOINT: ${{ vars.BRAID_ANALYSIS_ENDPOINT }}
BRAID_ANALYSIS_MODEL: ${{ vars.BRAID_ANALYSIS_MODEL }}
BRAID_ANALYSIS_PROVIDER: ${{ vars.BRAID_ANALYSIS_PROVIDER }}
BRAID_ANALYSIS_RUNNER: ${{ vars.BRAID_ANALYSIS_RUNNER }}
BRAID_SUPERVISOR_ID: ${{ vars.BRAID_SUPERVISOR_ID }}
BRAID_SUPERVISOR_MESSAGE: ${{ vars.BRAID_SUPERVISOR_MESSAGE }}
BRAID_SUPERVISOR_ROOT: ${{ vars.BRAID_SUPERVISOR_ROOT }}
BRAID_SUPERVISOR_WORKER: ${{ vars.BRAID_SUPERVISOR_WORKER }}
BRAID_RELEASE_CHECKOUT: ${{ github.workspace }}
run: >-
pnpm release:collect --
--artifact-root "$BRAID_RELEASE_ARTIFACT_ROOT"
--tarball "candidate/${{ steps.metadata.outputs.archive }}"

- name: Preserve resumable release state
if: always()
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: ${{ runner.temp }}/braid-release
key: braid-release-${{ inputs.commit }}-${{ github.run_id }}-${{ github.run_attempt }}

- name: Upload partial release state
if: always()
- name: Upload the exact candidate
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: braid-release-partial-${{ inputs.commit }}-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/braid-release
if-no-files-found: warn
retention-days: 90

- name: Qualify the pre-publication candidate
env:
BRAID_RELEASE_CHECKOUT: ${{ github.workspace }}
BRAID_RELEASE_ISOLATED_CHECKOUT: '1'
run: pnpm verify:candidate

- name: Upload the qualified candidate evidence
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: braid-release-qualified-${{ inputs.commit }}
name: braid-release-candidate-${{ inputs.commit }}
path: ${{ runner.temp }}/braid-release
if-no-files-found: error
overwrite: true
retention-days: 90

endorse-candidate:
Expand All @@ -163,7 +90,7 @@ jobs:
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: braid-release-qualified-${{ inputs.commit }}
name: braid-release-candidate-${{ inputs.commit }}
path: ${{ runner.temp }}/braid-release

- name: Endorse the fixed candidate archive
Expand Down Expand Up @@ -244,7 +171,7 @@ jobs:
name: braid-release-${{ inputs.commit }}
path: ${{ runner.temp }}/braid-release

- name: Install and exercise the exact candidate
- name: Install and use the exact candidate
env:
BRAID_EXPECT_ARCHITECTURE: ${{ matrix.architecture }}
BRAID_EXPECT_PLATFORM: ${{ matrix.platform }}
Expand Down Expand Up @@ -311,15 +238,10 @@ jobs:
- name: Publish the approved tarball
env:
ARCHIVE: ${{ needs.candidate.outputs.archive }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
VERSION: ${{ needs.candidate.outputs.version }}
run: |
npm install --global npm@11.18.0
test "$(npm --version)" = 11.18.0
if [ -n "$NODE_AUTH_TOKEN" ]; then
printf '//registry.npmjs.org/:_authToken=%s\n' "$NODE_AUTH_TOKEN" > "$RUNNER_TEMP/braid-npmrc"
export NPM_CONFIG_USERCONFIG="$RUNNER_TEMP/braid-npmrc"
fi
if npm view "@tangle-network/braid@$VERSION" version >/dev/null 2>&1; then
echo "@tangle-network/braid@$VERSION already exists; checking it instead of republishing"
else
Expand Down Expand Up @@ -381,7 +303,7 @@ jobs:
name: braid-release-${{ inputs.commit }}
path: ${{ runner.temp }}/braid-release

- name: Download and exercise the registry package
- name: Download and use the registry package
env:
BRAID_EXPECT_ARCHITECTURE: ${{ matrix.architecture }}
BRAID_EXPECT_PLATFORM: ${{ matrix.platform }}
Expand Down Expand Up @@ -410,15 +332,9 @@ jobs:
with:
ref: ${{ inputs.commit }}

- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9

- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22.19.0
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
Expand All @@ -442,16 +358,12 @@ jobs:
name: braid-npm-provenance-${{ inputs.commit }}
path: ${{ runner.temp }}/braid-release/publication

- name: Assemble the final report
- name: Validate candidate and registry use
env:
BRAID_RELEASE_ARTIFACT_ROOT: ${{ runner.temp }}/braid-release
BRAID_RELEASE_CHECKOUT: ${{ github.workspace }}
BRAID_RELEASE_ISOLATED_CHECKOUT: '1'
run: |
pnpm release:record-publication
pnpm verify:release
run: node scripts/release/record-publication.mjs

- name: Upload the validated final release evidence
- name: Upload the validated release bundle
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: braid-release-final-qualified-${{ inputs.commit }}
Expand Down Expand Up @@ -556,23 +468,23 @@ jobs:
INDEX_SHA256="$(sha256sum "$ENDORSEMENT/files.sha256" | cut -d ' ' -f 1)"
grep -Fx "index_sha256=$INDEX_SHA256" "$ENDORSEMENT/statement.txt"

- name: Tag the proven commit and attach its report
- name: Tag the shipped commit and attach its artifacts
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ needs.candidate.outputs.version }}
run: |
TAG="v$VERSION"
NOTES="Published @tangle-network/braid@$VERSION from commit ${{ inputs.commit }}. The exact package completed candidate and registry install/use checks on Linux x64, macOS arm64, and Windows x64."
if git rev-parse "$TAG" >/dev/null 2>&1; then
test "$(git rev-list -n 1 "$TAG")" = "${{ inputs.commit }}"
else
git tag "$TAG" "${{ inputs.commit }}"
git push origin "$TAG"
fi
REPORT="$RUNNER_TEMP/braid-release/$VERSION/report.md"
if gh release view "$TAG" >/dev/null 2>&1; then
gh release edit "$TAG" --notes-file "$REPORT"
gh release edit "$TAG" --notes "$NOTES"
else
gh release create "$TAG" --verify-tag --title "Braid $VERSION" --notes-file "$REPORT"
gh release create "$TAG" --verify-tag --title "Braid $VERSION" --notes "$NOTES"
fi
EVIDENCE_ARCHIVE="$RUNNER_TEMP/braid-evidence-$VERSION.tar.gz"
tar --directory "$RUNNER_TEMP/braid-release" --create --gzip --file "$EVIDENCE_ARCHIVE" .
Expand Down
42 changes: 28 additions & 14 deletions docs/08-verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -520,23 +520,35 @@ Each row has a forced-process-kill test at every durable boundary.

Each case runs on at least three representative source fixtures and includes a seeded poor output plus a trivial raw-data baseline.

## Release evidence manifest
## Comprehensive audit manifest

The release process requires `BRAID_RELEASE_ARTIFACT_ROOT` to name a real directory outside the clean source checkout.
The comprehensive audit is available when a product decision needs every requirement record in one manifest.

Candidate creation writes one npm tarball, its complete package-file manifest, check streams, terminal captures, resumable collection state, and `release/checks.json` below that external directory.
It is not an npm publication prerequisite.

The audit requires `BRAID_RELEASE_ARTIFACT_ROOT` to name a real directory outside the clean source checkout.

Audit candidate creation writes one npm tarball, its package-file manifest, check streams, terminal captures, resumable state, and `release/checks.json` below that directory.

The collector executes 25 distinct prerequisite commands and materializes 43 exact `UP-*`, `LIVE-*`, `PERF-*`, `EVAL-*`, and `VR-03` records from their matching command outputs, for 65 check records covering all 154 requirement identifiers.

`verify:release` is the final assembler and never appears as one of its own prerequisite checks.
`verify:release` assembles this optional audit and never appears as one of its own prerequisite checks.

Publication uses a smaller direct acceptance path.

The candidate job runs `pnpm check` once, then `pnpm release:prepare` builds and uses one immutable package.

Pre-publication validation checks the candidate before the release key exists in any job that executes package code.
A code-free job endorses that exact package before publication.

After npm publication, the same clean-install, plain-flow, encrypted-storage, digest, architecture, and cleanup smoke runs for the candidate and registry package on Linux x64, macOS arm64, and Windows x64.

The final process validates those six records, adds their immutable JSON artifacts to `VR-10`, then writes `<version>/manifest.json` and `<version>/report.md` below the external artifact directory.
The publication process validates those six records, the package SHA-256, and npm provenance.

The report counts passed, failed, unavailable, uncaptured, and unrecognized check results separately.
It writes `publication/proof.json` below the external artifact directory.

An isolated code-free job endorses the resulting release bundle before tagging the commit.

When the comprehensive audit runs, its report counts passed, failed, unavailable, uncaptured, and unrecognized check results separately.

Each report row includes its exact result.

Expand Down Expand Up @@ -585,7 +597,9 @@ The manifest contains the following top-level data.

Each check records identifier, category, required status, command, working directory, environment identifier, start and end, exit code, attempt count, measured fields, result, stdout and stderr artifact hashes, and failure details.

The complete archive, including every check field and output digest, is covered by the endorsement file index.
An audit archive includes every check field and output digest.

The publication endorsement covers the exact package, package manifest, six platform-use records, and npm provenance.

The publication and tag jobs accept only the public key pinned in `release/endorsement-public-key.pem` and recompute the complete index before accepting its signature.

Expand Down Expand Up @@ -646,7 +660,7 @@ Implementation must provide the following stable scripts.
| `capture` | `pnpm test:capture` | Deterministic baseline real-binary captures |
| `visual` | `pnpm capture:visual` | Deterministic real-binary state captures and manifests |
| `release` | `pnpm check:release` | Release manifest and evidence-set check |
| `verify:release` | `pnpm verify:release` | Validate publication proof and assemble every required result before isolated archive endorsement |
| `verify:release` | `pnpm verify:release` | Assemble the optional comprehensive audit from collected result records |

The deterministic local commands are implemented in this repository.

Expand All @@ -668,15 +682,15 @@ When cancellation is unavailable, the live driver checks the rejected control ag

Tangle, supervisor, and live-analysis commands return a typed unavailable result until protected credentials, deployments, and evidence stores are supplied.

The release workflow uses `pnpm release:prepare`, `pnpm release:collect`, and `pnpm verify:candidate` before publication.
The release workflow runs `pnpm check` once and uses `pnpm release:prepare` before publication.

The candidate job supplies Bridge, eval, Tangle, analysis, and supervisor settings only to the complete release-check step.
It does not require provider, sandbox, supervisor, or analysis credentials.

Inference, sandbox, and analysis use separate GitHub environment secrets.
Those settings remain inputs to explicit live audits.

Endpoints, models, providers, runners, and supervisor identifiers remain environment variables.
After publication, `release:record-publication` validates direct candidate and registry use plus npm provenance.

After publication it uses `pnpm release:record-publication` and `pnpm verify:release`; these workflow commands are not additional check records.
The comprehensive collector and verifier remain separate commands for explicit audits.

## Verification acceptance

Expand Down
Loading