From 0a0a49be0ab2adc3b1141389a52d1e8523865945 Mon Sep 17 00:00:00 2001 From: Punk 6529 <108035228+punk6529@users.noreply.github.com> Date: Fri, 12 Jun 2026 21:40:38 +0000 Subject: [PATCH 1/4] Add protocol incident response runbook --- .github/workflows/ci.yml | 10 + CHANGELOG.md | 4 + Makefile | 12 +- SECURITY.md | 6 + docs/audit-package.md | 14 +- docs/dependency-operations.md | 8 +- docs/incident-response.md | 382 ++++++++++++++++++ docs/randomizer-operations.md | 7 + docs/release-readiness.md | 8 + docs/tooling.md | 18 +- ops/AUTONOMOUS_RUN.md | 138 ++++++- ops/ROADMAP.md | 37 +- release-artifacts/README.md | 17 +- release-artifacts/latest/SHA256SUMS | 2 +- .../latest/release-checksums.json | 6 +- .../latest/release-manifest.json | 29 +- scripts/check.ps1 | 2 + scripts/check.sh | 2 + scripts/check_audit_package.py | 3 + scripts/check_incident_response.py | 235 +++++++++++ scripts/check_release_readiness.py | 4 + scripts/generate_release_manifest.py | 1 + scripts/test_incident_response.py | 277 +++++++++++++ scripts/test_release_manifest.py | 1 + scripts/test_release_readiness.py | 4 +- 25 files changed, 1162 insertions(+), 65 deletions(-) create mode 100644 docs/incident-response.md create mode 100644 scripts/check_incident_response.py create mode 100644 scripts/test_incident_response.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ee92bea..8dd8fa0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,6 +79,8 @@ jobs: scripts/test_architecture_threat_model.py \ scripts/check_audit_package.py \ scripts/test_audit_package.py \ + scripts/check_incident_response.py \ + scripts/test_incident_response.py \ scripts/check_release_readiness.py \ scripts/test_release_readiness.py \ scripts/generate_release_manifest.py \ @@ -263,6 +265,14 @@ jobs: python3 scripts/test_audit_package.py 2>&1 | tee ci-logs/audit-package-tests.log python3 scripts/check_audit_package.py 2>&1 | tee ci-logs/audit-package-check.log + - name: Incident response + shell: bash + run: | + set -o pipefail + mkdir -p ci-logs + python3 scripts/test_incident_response.py 2>&1 | tee ci-logs/incident-response-tests.log + python3 scripts/check_incident_response.py 2>&1 | tee ci-logs/incident-response-check.log + - name: Release readiness shell: bash run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 83721f02..91b9dc27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -108,6 +108,10 @@ the release policy in `docs/release-policy.md`. retained placeholder artifact, checker, and local/CI gate so future reviewed non-local evidence can be machine-validated before release manifest and checksum generation. +- Added a protocol incident-response runbook and local/CI checker covering + stuck auctions, failed or stale randomness, bad Merkle roots, bad metadata or + dependency configuration, signer compromise, and release artifact/evidence + mistakes before release manifest and checksum generation. - Added a production dependency operations runbook covering dependency version proposal, review, source packaging, registry registration, unfrozen collection repinning, deprecation, rollback by corrective version, frozen diff --git a/Makefile b/Makefile index e5e67473..e3de71c0 100644 --- a/Makefile +++ b/Makefile @@ -22,9 +22,9 @@ RM_RF := rm -rf out cache broadcast endif PATH := $(FOUNDRY_BIN)$(PATH_SEPARATOR)$(REPO_ROOT)/$(VENV_BIN)$(PATH_SEPARATOR)$(PATH) -.PHONY: check build test gas-snapshot gas-snapshot-check size deploy-rehearsal metadata-fixtures-check release-artifacts release-artifacts-check source-verification-inputs source-verification-inputs-check abi-compatibility abi-compatibility-check broadcast-manifest-inputs broadcast-manifest-inputs-check deployment-manifests deployment-manifest-check address-books address-books-check dependency-artifacts dependency-artifacts-check ceremony-evidence-check randomizer-operations-check release-signatures-check non-local-release-evidence-check public-beta-evidence-check architecture-threat-model-check audit-package-check release-readiness-check release-manifest release-manifest-check release-checksums release-checksums-check changelog-check fmt-check slither clean +.PHONY: check build test gas-snapshot gas-snapshot-check size deploy-rehearsal metadata-fixtures-check release-artifacts release-artifacts-check source-verification-inputs source-verification-inputs-check abi-compatibility abi-compatibility-check broadcast-manifest-inputs broadcast-manifest-inputs-check deployment-manifests deployment-manifest-check address-books address-books-check dependency-artifacts dependency-artifacts-check ceremony-evidence-check randomizer-operations-check release-signatures-check non-local-release-evidence-check public-beta-evidence-check architecture-threat-model-check audit-package-check incident-response-check release-readiness-check release-manifest release-manifest-check release-checksums release-checksums-check changelog-check fmt-check slither clean -check: build test gas-snapshot-check size metadata-fixtures-check release-artifacts-check source-verification-inputs-check abi-compatibility-check non-local-release-evidence-check public-beta-evidence-check architecture-threat-model-check audit-package-check release-readiness-check release-checksums-check changelog-check deploy-rehearsal +check: build test gas-snapshot-check size metadata-fixtures-check release-artifacts-check source-verification-inputs-check abi-compatibility-check non-local-release-evidence-check public-beta-evidence-check architecture-threat-model-check audit-package-check incident-response-check release-readiness-check release-checksums-check changelog-check deploy-rehearsal build: forge build @@ -133,14 +133,18 @@ audit-package-check: $(PYTHON) scripts/test_audit_package.py $(PYTHON) scripts/check_audit_package.py +incident-response-check: + $(PYTHON) scripts/test_incident_response.py + $(PYTHON) scripts/check_incident_response.py + release-readiness-check: $(PYTHON) scripts/test_release_readiness.py $(PYTHON) scripts/check_release_readiness.py -release-manifest: address-books source-verification-inputs dependency-artifacts ceremony-evidence-check randomizer-operations-check release-signatures-check non-local-release-evidence-check public-beta-evidence-check architecture-threat-model-check audit-package-check release-readiness-check +release-manifest: address-books source-verification-inputs dependency-artifacts ceremony-evidence-check randomizer-operations-check release-signatures-check non-local-release-evidence-check public-beta-evidence-check architecture-threat-model-check audit-package-check incident-response-check release-readiness-check $(PYTHON) scripts/generate_release_manifest.py -release-manifest-check: address-books-check source-verification-inputs-check dependency-artifacts-check ceremony-evidence-check randomizer-operations-check release-signatures-check non-local-release-evidence-check public-beta-evidence-check architecture-threat-model-check audit-package-check release-readiness-check +release-manifest-check: address-books-check source-verification-inputs-check dependency-artifacts-check ceremony-evidence-check randomizer-operations-check release-signatures-check non-local-release-evidence-check public-beta-evidence-check architecture-threat-model-check audit-package-check incident-response-check release-readiness-check $(PYTHON) scripts/test_release_manifest.py $(PYTHON) scripts/generate_release_manifest.py --check diff --git a/SECURITY.md b/SECURITY.md index 90766587..35f2e865 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -5,6 +5,10 @@ protocol draft with known P0 blockers tracked in [ops/ROADMAP.md](ops/ROADMAP.md Do not use these contracts for production drops, custody of valuable assets, or public security claims until the launch gates are complete. +Operational incidents that do not contain public exploit details should follow +the no-secret procedure in [docs/incident-response.md](docs/incident-response.md) +after private vulnerability triage starts. + ## Reporting Vulnerabilities Please do not open public GitHub issues for exploitable vulnerabilities. @@ -89,5 +93,7 @@ The current roadmap calls out these high-risk areas: - Randomizer request and callback validation. - Static-analysis high/medium findings. - Deployment rehearsal, verification, and release artifacts. +- Incident response, emergency pause, signer revocation, retry/recovery, + withdrawal availability, and evidence retention. Security reports should reference the relevant roadmap issue ID when possible. diff --git a/docs/audit-package.md b/docs/audit-package.md index 553ef758..eccabb11 100644 --- a/docs/audit-package.md +++ b/docs/audit-package.md @@ -45,6 +45,7 @@ Explicitly out of scope for this package: | Current maturity and evidence | [`docs/status.md`](status.md) | | Known unresolved blockers | [`docs/known-blockers.md`](known-blockers.md) | | Release-readiness dashboard | [`docs/release-readiness.md`](release-readiness.md) | +| Incident response runbook | [`docs/incident-response.md`](incident-response.md) | | Public-beta evidence status | [`docs/public-beta-evidence.md`](public-beta-evidence.md) | | Gated execution roadmap | [`ops/ROADMAP.md`](../ops/ROADMAP.md) | | Autonomous execution state | [`ops/AUTONOMOUS_RUN.md`](../ops/AUTONOMOUS_RUN.md) | @@ -77,6 +78,7 @@ Protocol-specific docs that are useful during review: - [`docs/randomizer-operations.md`](randomizer-operations.md) - [`docs/deployment.md`](deployment.md) - [`docs/release-policy.md`](release-policy.md) +- [`docs/incident-response.md`](incident-response.md) - [`docs/release-signatures.md`](release-signatures.md) - [`docs/public-beta-evidence.md`](public-beta-evidence.md) - [`docs/release-readiness.md`](release-readiness.md) @@ -140,11 +142,13 @@ Local deployment and release evidence: defines the retained status format. The release manifest includes this audit package as a governance document. The -release manifest also includes the architecture map and threat model as -governance documents, and it summarizes the public-beta evidence status. The +release manifest also includes the architecture map, threat model, and incident +response runbook as governance documents, and it summarizes the public-beta +evidence status. The checksum bundle covers the release manifest, so changes to the audit package, -architecture map, threat model, or public-beta evidence status must refresh -release evidence before a release-oriented PR can pass. +architecture map, threat model, incident-response runbook, or public-beta +evidence status must refresh release evidence before a release-oriented PR can +pass. ## Known Blockers And Accepted Risks @@ -193,6 +197,8 @@ python scripts/test_audit_package.py python scripts/check_audit_package.py python scripts/test_architecture_threat_model.py python scripts/check_architecture_threat_model.py +python scripts/test_incident_response.py +python scripts/check_incident_response.py python scripts/test_release_readiness.py python scripts/check_release_readiness.py python scripts/test_public_beta_evidence.py diff --git a/docs/dependency-operations.md b/docs/dependency-operations.md index 41de5ace..6181d95e 100644 --- a/docs/dependency-operations.md +++ b/docs/dependency-operations.md @@ -29,6 +29,10 @@ This runbook applies when an operator or maintainer: It does not authorize mutation of frozen collection output. Frozen collections must remain tied to their pinned dependency key, version, content hash, registry address, freeze manifest, and event history. +If a dependency release, repin, deprecation, source-retention artifact, or +metadata output is wrong, follow +[`docs/incident-response.md`](incident-response.md) before changing public +readiness status. ## Source Of Truth @@ -254,8 +258,8 @@ Allowed operations for frozen collections: if a security incident requires a new path If a frozen collection depends on a version later found to be unsafe, the -incident response must document the risk, the immutable proof, and any new -deployment or collection path. It must not imply that the frozen output was +incident response runbook must document the risk, the immutable proof, and any +new deployment or collection path. It must not imply that the frozen output was mutated. ## Deprecation diff --git a/docs/incident-response.md b/docs/incident-response.md new file mode 100644 index 00000000..f6c26134 --- /dev/null +++ b/docs/incident-response.md @@ -0,0 +1,382 @@ +# Protocol Incident Response + +This runbook is the operator-facing incident response guide for 6529Stream. +The repository is pre-audit and not production-ready. It is not a security claim. +It gives maintainers a no-secret procedure for triage, containment, +recovery, evidence retention, and reopening when protocol or release operations +behave unexpectedly. + +Use this runbook with the private reporting process in +[`SECURITY.md`](../SECURITY.md), the release-readiness dashboard in +[`docs/release-readiness.md`](release-readiness.md), the non-local evidence +intake runbook in +[`docs/non-local-release-evidence.md`](non-local-release-evidence.md), and the +current roadmap in [`ops/ROADMAP.md`](../ops/ROADMAP.md). +Release-status changes should also follow +[`docs/public-beta-evidence.md`](public-beta-evidence.md) and +[`docs/release-policy.md`](release-policy.md). + +## Maturity And Scope + +This runbook applies to incidents involving: + +- stuck auctions, settlement, cancellation, or NFT custody uncertainty; +- failed randomness, stale randomness, failed post-processing, provider + migration, or retry/recovery decisions; +- bad Merkle roots, bad curator reward claims, duplicate leaves, or suspected + proof generation mistakes; +- bad metadata, dependency configuration, dependency source retention, + renderer policy, or unfrozen collection repinning mistakes; +- signer compromise, stale signer epochs, leaked authorization payloads, or + drop execution pause decisions; +- release artifact, checksum, manifest, address-book, evidence, or verification + mistakes. + +This runbook does not replace contract tests, a completed external audit, +production Safe procedures, fork/testnet/live rehearsal evidence, or private +security-advisory handling. If a report contains exploitable details, keep the +technical exploit path private until maintainers agree on disclosure timing. + +## Roles And Severity + +Every incident record should identify public role labels, not private keys or +secret operational details. + +| Role | Responsibility | +| --- | --- | +| Incident lead | Owns triage, severity, decision log, and reopening criteria | +| Protocol maintainer | Maps observed behavior to contracts, ADRs, tests, and rollback or redeployment options | +| Operations maintainer | Executes pause, signer, release, evidence, or deployment actions through the approved ceremony | +| Communications owner | Publishes public status updates that avoid exploit details and unreleased drop payloads | +| Reviewer | Checks evidence, recovery commands, and post-incident notes before reopening | + +Severity should be assigned conservatively: + +| Severity | Examples | Minimum response | +| --- | --- | --- | +| SEV-1 | Live fund loss, active exploit, wrong NFT custody, signer compromise, or public bad metadata for a live drop | Private security channel, emergency pause assessment, public holding statement, retained evidence, post-incident review | +| SEV-2 | Failed settlement, failed randomness, bad Merkle root, bad dependency version, or release artifact drift before public impact | Domain-specific pause assessment, affected-surface freeze, reviewed recovery plan, retained evidence | +| SEV-3 | Local rehearsal failure, checker drift, missing evidence, stale manifest, or documentation mismatch | Fix-forward PR, evidence update, no public readiness claim | + +## Universal Triage + +1. Identify the affected surface: auction, randomizer, curator rewards, + metadata, dependency, signer, admin, deployment, release artifact, or + evidence. +2. Preserve current state before taking action: transaction hashes, block + numbers, chain ID, contract addresses, release commit, manifest hash, + relevant event logs, public screenshots, and no-secret command output. +3. Decide whether an emergency pause is needed for mint, bid, settlement, + signer, metadata, dependency, randomizer, or release operations. +4. Confirm withdrawal availability. Do not pause withdrawals unless the + accepted pause policy says the withdrawal path itself is unsafe. +5. Stop new public claims of public beta or production readiness until the + incident lead records the current status in the issue, advisory, release + notes, or roadmap. +6. Assign the incident lead, protocol maintainer, operations maintainer, + communications owner, and reviewer. +7. Capture a decision log with UTC timestamps, transaction hashes, commands, + evidence paths, and reviewer names. + +## Evidence Retention And Communications + +Incident evidence must be public-safe before it is committed. + +Retain: + +- issue, advisory, PR, release, deployment, and chain references; +- sanitized event logs, traces, screenshots, command output, and manifest + diffs; +- affected contract addresses, chain ID, block range, and confirmation depth; +- current pause, signer epoch, randomizer epoch, Merkle root, dependency + version, release manifest, and checksum state; +- reviewer identity, redaction statement, retained path, and SHA-256 digest for + every non-local artifact. + +Do not retain: + +- private keys, mnemonics, Safe signing secrets, session cookies, API keys, or + bearer tokens; +- private RPC URLs or provider dashboard URLs with embedded credentials; +- unreleased drop authorizations, unreleased Merkle proofs, unreleased token + data, or non-public artist assets; +- exploitable proof-of-concept details in a public issue before disclosure is + approved. + +For fork, testnet, live, audit, explorer, gas, invariant, signature, or +post-incident evidence that changes release readiness, follow +[`docs/non-local-release-evidence.md`](non-local-release-evidence.md) and update +[`release-artifacts/latest/public-beta-evidence.json`](../release-artifacts/latest/public-beta-evidence.json) +only after review. + +## Runbook: Stuck Auctions Or Settlement + +Use this runbook when auction creation, bidding, cancellation, settlement, +custody, or proceeds withdrawal is stuck or inconsistent. + +Immediate checks: + +- Read auction state and compare it with the state model in + [`docs/auction-custody.md`](auction-custody.md). +- Confirm token custody through owner views and relevant transfer events. +- Confirm highest bidder, highest bid, poster credit, curator credit, protocol + credit, bidder credit, and total owed views. +- Confirm whether settlement is already terminal and idempotent. +- Confirm withdrawal availability for unaffected credits. + +Containment: + +- Consider bid pause or settlement pause if new bids or settlements can worsen + the incident. +- Do not move an escrowed token or owed funds through emergency withdrawal. +- Do not erase bidder, poster, curator, or protocol credits during recovery. + +Recovery: + +- Prefer idempotent settlement, cancellation, or withdrawal paths that preserve + credits and event history. +- If custody cannot be corrected safely, follow the emergency redeployment path + in [`docs/deployment.md`](deployment.md) and + [ADR 0007](adr/0007-upgrade-redeployment.md). +- Record the final token owner, final auction state, final owed balances, and + zero-surplus or remaining-surplus explanation. + +Reopen only after tests or retained evidence show token custody is known, +credits are covered by contract balance, and failed withdrawals do not erase +credit. + +## Runbook: Failed Or Stale Randomness + +Use this runbook when a request is pending too long, fails post-processing, +receives an unexpected callback, or is affected by provider migration. + +Immediate checks: + +- Read request ID, token ID, collection ID, provider epoch, randomizer adapter, + raw output hash, pending count, stale state, and failed post-processing + state. +- Compare the live state with + [`docs/randomizer-operations.md`](randomizer-operations.md) and + [ADR 0005](adr/0005-randomness.md). +- Confirm provider funding, billing, and request-health evidence. +- Confirm whether a retry/recovery path uses the stored seed and does not + request new randomness. + +Containment: + +- Consider mint pause or randomizer migration pause if new requests could be + affected. +- Do not replace a provider while pending requests exist unless the accepted + migration policy explicitly permits stale marking. +- Keep withdrawal availability separate from randomizer request handling. + +Recovery: + +- For stale requests, record the stale-marking evidence and the provider epoch. +- For failed post-processing, use the bounded retry/recovery path and retain + command output. +- For invalid callbacks, retain the callback transaction, request ID, token, + collection, provider, and epoch evidence. +- Update randomizer operations evidence after fork, testnet, or live recovery. + +Reopen only after request lifecycle views, metadata state, provider epoch, and +retry or stale status are coherent. + +## Runbook: Bad Merkle Roots Or Curator Claims + +Use this runbook when a curator reward root, proof generator, claim amount, +claimant address, or delegation assumption is wrong. + +Immediate checks: + +- Identify the root, root epoch, collection ID, claimant, amount, proof, and + transaction hash. +- Verify leaf encoding uses `abi.encode` with unambiguous domain fields. +- Check duplicate leaves, double-claim attempts, delegation state, curator + credit, and total owed views. +- Confirm whether any claim has already created withdrawable credit. + +Containment: + +- Pause or withhold new root publication if future claims could be wrong. +- Do not delete existing curator credits. Failed withdrawal must not erase + credit. +- Communicate whether the issue affects only future claims or already-created + credits. + +Recovery: + +- Publish a corrected root only after proof generation is reviewed. +- Retain old and new roots, generator inputs, code commit, review notes, and + no-secret sample proofs. +- If an incorrect claim already executed, document the immutable on-chain state + and any follow-up compensation or redeployment path outside the contract. + +Reopen only after duplicate leaves and double claims are tested or otherwise +reviewed, and the affected root epoch is documented. + +## Runbook: Bad Metadata Or Dependency Configuration + +Use this runbook when metadata JSON, token image URI, attributes, animation +HTML, dependency source, dependency provenance, or collection dependency pinning +is wrong. + +Immediate checks: + +- Determine whether the affected collection is frozen. +- Read dependency key, version, content hash, registry address, freeze manifest, + metadata state, and current `tokenURI` output. +- Compare the incident with [`docs/metadata.md`](metadata.md), + [`docs/dependency-operations.md`](dependency-operations.md), and + [ADR 0006](adr/0006-metadata-freeze.md). +- Run metadata fixture/browser checks for the affected output where possible. + +Containment: + +- For unfrozen collections, pause metadata/dependency update operations if the + accepted admin model supports it. +- For frozen collections, do not imply that frozen output can be mutated. +- Stop publishing marketplaces or indexer guidance that depends on the bad + output until the corrected state is reviewed. + +Recovery: + +- For unfrozen collections, register a corrected dependency version or metadata + update and retain source, descriptor, manifest, checksum, and review evidence. +- For frozen collections, document the immutable proof and decide whether ADR + 0007 redeployment or a new collection path is required. +- Regenerate release manifest and checksum evidence after changing dependency + source, metadata docs, or governance docs. + +Reopen only after the corrected metadata/dependency state is retained, checked, +and communicated to affected integrators. + +## Runbook: Signer Compromise Or Drop Authorization + +Use this runbook when a drop signer, signer manager, signed payload, domain, +deadline, signer epoch, or drop ID is suspected to be compromised. + +Immediate checks: + +- Identify the signer, signer manager, signer epoch, drop ID, deadline, domain, + verifying contract, chain ID, consumed state, cancelled state, and affected + authorization payloads. +- Confirm whether any payload has already been executed. +- Preserve EIP-712 domain and signature validation evidence without committing + unreleased payloads. + +Containment: + +- Use emergency pause for drop execution if new payloads could execute. +- Revoke or rotate the signer through the accepted signer lifecycle controls. +- Increment signer epoch or cancel affected drop IDs where available. +- Keep withdrawal availability separate from drop execution pause unless a + payment path is independently unsafe. + +Recovery: + +- Reissue only reviewed payloads under the new signer epoch and domain. +- Retain redacted signer-rotation evidence, event logs, affected drop IDs, + cancellation transactions, and reviewer notes. +- Update public communications with the new active signer state and any + affected drop status. + +Reopen only after stale signer payloads, wrong domains, expired payloads, +replays, and cancelled drop IDs fail in tests or retained evidence. + +## Runbook: Release Artifact Or Evidence Mistake + +Use this runbook when release manifests, checksum bundles, address books, +source verification inputs, evidence metadata, public-beta status, or release +notes drift from committed reality. + +Immediate checks: + +- Identify the release commit, PR, CI run, manifest path, checksum file, + affected evidence file, and public-beta requirement ID. +- Run the relevant checkers from [`docs/tooling.md`](tooling.md). +- Confirm whether a public release claim, signature, signed tag, address book, + or explorer verification link depended on the bad artifact. + +Containment: + +- Stop distribution of the affected artifact or release claim. +- Do not sign a checksum bundle that is known to be stale. +- Do not mark public-beta evidence complete based on unreviewed or stale + evidence. + +Recovery: + +- Regenerate release manifest and checksum outputs from committed inputs. +- Replace or correct the retained evidence metadata only after redaction and + reviewer checks. +- Update release notes, changelog, public-beta evidence status, and + release-readiness docs if the public claim changed. + +Reopen only after check-mode commands pass and the retained evidence hash +matches the corrected artifact. + +## Reopening And Post-Incident Review + +An incident can be reopened only when: + +- containment actions are either removed or intentionally left in place with an + owner and expiry; +- emergency pause, withdrawal availability, signer revocation, retry/recovery, + and evidence retention decisions are recorded; +- every changed external behavior has a test, retained evidence item, or + explicit non-code acceptance decision; +- release manifest and checksum outputs are current if any covered artifact or + governance document changed; +- public communications and release-readiness docs no longer overstate + maturity. + +The post-incident review should capture root cause, affected users or drops, +timeline, detection gap, recovery actions, tests added, docs changed, remaining +risk, and follow-up issue links. + +## Local Verification Commands + +Run the incident-response checker directly: + +```sh +python scripts/test_incident_response.py +python scripts/check_incident_response.py +``` + +Run the release evidence checks after changing this runbook or linked +governance docs: + +```sh +python scripts/generate_release_manifest.py --check +python scripts/generate_release_checksums.py --check +``` + +Run the full local gate before merge: + +```sh +make check +powershell -ExecutionPolicy Bypass -File scripts\check.ps1 +``` + +## Maintenance + +Update this runbook whenever pause semantics, signer lifecycle, auction +custody, randomizer recovery, Merkle reward handling, metadata/dependency +operations, release evidence, security reporting, or public-beta readiness +changes. + +After editing this file, run: + +```sh +python scripts/test_incident_response.py +python scripts/check_incident_response.py +python scripts/test_release_readiness.py +python scripts/check_release_readiness.py +python scripts/test_audit_package.py +python scripts/check_audit_package.py +python scripts/generate_release_manifest.py +python scripts/generate_release_checksums.py +python scripts/generate_release_manifest.py --check +python scripts/generate_release_checksums.py --check +``` diff --git a/docs/randomizer-operations.md b/docs/randomizer-operations.md index a8327c8f..9a250c79 100644 --- a/docs/randomizer-operations.md +++ b/docs/randomizer-operations.md @@ -19,6 +19,11 @@ provider configuration and operational checks used for a deployment version: - redaction policy proving no private keys, mnemonics, RPC URLs, API keys, or unreleased drop payloads are committed. +If a live request becomes stale, fails post-processing, receives an invalid +callback, or is affected by provider migration, follow the randomizer section +of [`docs/incident-response.md`](incident-response.md) before changing public +readiness status. + Validate the committed local evidence with: ```sh @@ -59,6 +64,8 @@ paste credentials, endpoint URLs, private transaction payloads, or unreleased drop authorization payloads into the JSON. If live provider dashboards or explorers are used, retain sanitized screenshots, text exports, or transaction references as public artifacts and record their hashes. +Incident recovery evidence should use the same retained-artifact and redaction +rules. ## Release Integration diff --git a/docs/release-readiness.md b/docs/release-readiness.md index 1fe45470..0d3fe349 100644 --- a/docs/release-readiness.md +++ b/docs/release-readiness.md @@ -11,6 +11,9 @@ production release? Use [`docs/non-local-release-evidence.md`](non-local-release-evidence.md) as the intake runbook for any fork, testnet, live, audit, gas, invariant, verification, or signing evidence that updates the public-beta evidence status. +Use [`docs/incident-response.md`](incident-response.md) for no-secret triage, +containment, recovery, evidence retention, and reopening procedures when an +operational incident affects release readiness. ## Maturity And Scope @@ -49,6 +52,8 @@ The current local baseline includes: - auditor-facing architecture, threat model, and audit package docs under [`docs/architecture.md`](architecture.md), [`docs/threat-model.md`](threat-model.md), and [`docs/audit-package.md`](audit-package.md); +- incident response procedures in + [`docs/incident-response.md`](incident-response.md); - release manifest and checksum bundle outputs under [`release-artifacts/latest/release-manifest.json`](../release-artifacts/latest/release-manifest.json), [`release-artifacts/latest/SHA256SUMS`](../release-artifacts/latest/SHA256SUMS), @@ -130,6 +135,7 @@ Core project and governance: Audit and protocol evidence: - [docs/audit-package.md](audit-package.md) +- [docs/incident-response.md](incident-response.md) - [docs/architecture.md](architecture.md) - [docs/threat-model.md](threat-model.md) - [docs/deployment.md](deployment.md) @@ -169,6 +175,8 @@ Run the dashboard checker directly: ```sh python scripts/test_release_readiness.py python scripts/check_release_readiness.py +python scripts/test_incident_response.py +python scripts/check_incident_response.py python scripts/test_public_beta_evidence.py python scripts/check_public_beta_evidence.py python scripts/test_non_local_release_evidence.py diff --git a/docs/tooling.md b/docs/tooling.md index ae504a01..f8fd916d 100644 --- a/docs/tooling.md +++ b/docs/tooling.md @@ -53,6 +53,8 @@ python scripts/test_architecture_threat_model.py python scripts/check_architecture_threat_model.py python scripts/test_audit_package.py python scripts/check_audit_package.py +python scripts/test_incident_response.py +python scripts/check_incident_response.py python scripts/test_release_readiness.py python scripts/check_release_readiness.py python scripts/test_release_manifest.py @@ -132,9 +134,9 @@ The release-manifest step builds `release-artifacts/latest/release-manifest.json`, a deterministic top-level index over the committed release artifact catalog, ABI compatibility baseline, deployment manifests, address books, deployment schemas, ceremony evidence, -changelog, governance docs, the audit package, and unavailable release-ceremony -artifacts. It is regenerated with `python scripts/generate_release_manifest.py` -after any covered input changes. +changelog, governance docs, incident-response runbook, the audit package, and +unavailable release-ceremony artifacts. It is regenerated with +`python scripts/generate_release_manifest.py` after any covered input changes. The architecture/threat-model step validates [`architecture.md`](architecture.md) and [`threat-model.md`](threat-model.md), the auditor-facing map of system @@ -146,6 +148,12 @@ single auditor-facing index over maturity, scope, ADRs, tests, static analysis, deployment/release evidence, known blockers, accepted local-baseline dispositions, and security reporting. +The incident-response step validates +[`incident-response.md`](incident-response.md), the no-secret operator runbook +for stuck auctions, failed randomness, bad Merkle roots, bad metadata or +dependency configuration, signer compromise, and release artifact/evidence +mistakes. + The release-readiness step validates [`release-readiness.md`](release-readiness.md), the Gate G dashboard that separates passing local evidence from missing fork/testnet/live evidence, @@ -238,6 +246,8 @@ python scripts/check_non_local_release_evidence.py python scripts/check_public_beta_evidence.py python scripts/check_architecture_threat_model.py python scripts/check_audit_package.py +python scripts/test_incident_response.py +python scripts/check_incident_response.py python scripts/check_release_readiness.py python scripts/generate_release_manifest.py python scripts/generate_release_checksums.py @@ -264,6 +274,8 @@ python scripts/check_non_local_release_evidence.py python scripts/check_public_beta_evidence.py python scripts/check_architecture_threat_model.py python scripts/check_audit_package.py +python scripts/test_incident_response.py +python scripts/check_incident_response.py python scripts/check_release_readiness.py python scripts/generate_release_manifest.py --check python scripts/generate_release_checksums.py --check diff --git a/ops/AUTONOMOUS_RUN.md b/ops/AUTONOMOUS_RUN.md index db088e7c..0fcfdbf1 100644 --- a/ops/AUTONOMOUS_RUN.md +++ b/ops/AUTONOMOUS_RUN.md @@ -32,13 +32,13 @@ tests, security hardening, deployment discipline, and release/audit readiness. | Field | Value | | --- | --- | | Remote | `https://github.com/6529-Collections/6529Stream.git` | -| Active PR branch | `codex/reconcile-nonlocal-evidence-schema` | -| Last merged PR | `https://github.com/6529-Collections/6529Stream/pull/171` | -| Active issue | `https://github.com/6529-Collections/6529Stream/issues/172` | -| Active PR | `https://github.com/6529-Collections/6529Stream/pull/174` | +| Active PR branch | `codex/protocol-incident-response-runbooks` | +| Last merged PR | `https://github.com/6529-Collections/6529Stream/pull/174` | +| Active issue | `https://github.com/6529-Collections/6529Stream/issues/173` | +| Active PR | `TBD` | | Roadmap file | `ops/ROADMAP.md` | | State file | `ops/AUTONOMOUS_RUN.md` | -| Last updated | `2026-06-12 21:03 UTC` | +| Last updated | `2026-06-12 21:38 UTC` | ## Packaging Notes @@ -144,20 +144,130 @@ The queue will evolve as PRs merge and bot feedback arrives. | 86 | Reconcile Gate G roadmap after public beta evidence merge | Gate G support | Implement issue #166 by marking PR #165 merged, refreshing stale roadmap verification metadata, removing #164 from active Gate G blockers, and adding the next non-local evidence queue target | Merged in PR #167 | | 87 | Add non-local release evidence intake runbook | Gate E/Gate G support | Document the operator workflow for retaining fork/testnet/live deployment, metadata-browser, ceremony, randomizer, verification, address-book, gas, invariant, audit, and signed-release evidence without secrets, then wire the docs into readiness/public-beta evidence maintenance | Merged in PR #169 | | 88 | Add non-local release evidence metadata schema and checker | Gate E/Gate G support | Add a no-secret schema, template/example, checker, and tests for reviewed non-local evidence metadata so future operators can produce machine-checkable artifacts without claiming external readiness | Merged in PR #171 | -| 89 | Reconcile Gate G roadmap after non-local evidence schema merge | Gate G support | Implement issue #172 by marking PR #171 merged, refreshing stale roadmap verification metadata, recording CI and CodeRabbit evidence, and preserving the next queue target | PR #174 open; waiting for CI and CodeRabbit | -| 90 | Add protocol incident response runbooks | Gate E/Gate G support | Implement issue #173 by adding no-secret operator runbooks for stuck auctions, failed or stale randomness, bad Merkle roots, bad metadata/dependency configuration, signer compromise, and release artifact/evidence mistakes | Planned next after Queue Item 89 | +| 89 | Reconcile Gate G roadmap after non-local evidence schema merge | Gate G support | Implement issue #172 by marking PR #171 merged, refreshing stale roadmap verification metadata, recording CI and CodeRabbit evidence, and preserving the next queue target | Merged in PR #174 | +| 90 | Add protocol incident response runbooks | Gate E/Gate G support | Implement issue #173 by adding no-secret operator runbooks for stuck auctions, failed or stale randomness, bad Merkle roots, bad metadata/dependency configuration, signer compromise, and release artifact/evidence mistakes | Implemented locally; PR not opened yet | ## Current PR Worklog +### PR candidate: Add protocol incident response runbooks (Queue Item 90) + +Status: implemented locally; PR not opened yet. +Issue: `https://github.com/6529-Collections/6529Stream/issues/173`. +PR: `TBD`. +Branch: `codex/protocol-incident-response-runbooks`. +Branch started from PR #174 squash merge commit +`074ac3eb510ccafa593812677e6c26cbed4171b1`. + +Goal: + +- Add a no-secret protocol incident-response runbook for stuck auctions, + failed or stale randomness, bad Merkle roots, bad metadata/dependency + configuration, signer compromise, and release artifact/evidence mistakes. +- Tie incident procedures to existing pause, signer, randomizer, auction, + dependency, release-readiness, and evidence-retention docs. +- Add a lightweight docs checker/test only if it matches the repository's + existing documentation gate pattern. +- Link the runbook from security, release-readiness, tooling, randomizer, + dependency, roadmap, and any release/governance surfaces that need it. +- Keep the change documentation/tooling-only with no Solidity behavior changes. + +Initial candidate files: + +- `docs/incident-response.md` +- `scripts/check_incident_response.py` +- `scripts/test_incident_response.py` +- `Makefile` +- `.github/workflows/ci.yml` +- `scripts/check.sh` +- `scripts/check.ps1` +- `scripts/generate_release_manifest.py` +- `scripts/test_release_manifest.py` +- `scripts/generate_release_checksums.py` +- `scripts/test_release_checksums.py` +- `docs/release-readiness.md` +- `docs/tooling.md` +- `docs/randomizer-operations.md` +- `docs/dependency-operations.md` +- `SECURITY.md` +- `release-artifacts/README.md` +- `release-artifacts/latest/release-manifest.json` +- `release-artifacts/latest/SHA256SUMS` +- `release-artifacts/latest/release-checksums.json` +- `CHANGELOG.md` +- `ops/ROADMAP.md` +- `ops/AUTONOMOUS_RUN.md` + +Validation target: + +- `python scripts/test_incident_response.py` +- `python scripts/check_incident_response.py` +- `python scripts/test_release_readiness.py` +- `python scripts/check_release_readiness.py` +- `python scripts/test_release_manifest.py` +- `python scripts/generate_release_manifest.py --check` +- `python scripts/test_release_checksums.py` +- `python scripts/generate_release_checksums.py --check` +- `python scripts/check_changelog.py` +- `bash -n scripts/check.sh` +- PowerShell parse check for `scripts/check.ps1` +- `python -m py_compile` for touched scripts/tests +- `rg -n "^#|^##|^###" docs\incident-response.md docs\release-readiness.md docs\tooling.md docs\randomizer-operations.md docs\dependency-operations.md SECURITY.md ops\ROADMAP.md ops\AUTONOMOUS_RUN.md` +- `git diff --check` +- `make check` + +Implementation notes: + +- Added `docs/incident-response.md` as a no-secret operator runbook for + severity classification, universal triage, evidence retention, reopening, + and post-incident review. +- Covered stuck auctions or settlement, failed/stale randomness, bad Merkle + roots or curator claims, bad metadata/dependency configuration, signer + compromise/drop authorization, and release artifact/evidence mistakes. +- Added `scripts/check_incident_response.py` and + `scripts/test_incident_response.py` to enforce required headings, maturity + language, no-secret/private-reporting guidance, local commands, and links. +- Wired the new gate into `Makefile`, CI, `scripts/check.sh`, and + `scripts/check.ps1`. +- Linked the runbook from security, release readiness, tooling, randomizer + operations, dependency operations, audit package, roadmap, and release + artifact documentation. +- Added the runbook to release-manifest governance docs, regenerated the + latest release manifest/checksum artifacts, and updated the changelog. + +Validation completed locally: + +- `python scripts/test_incident_response.py` +- `python scripts/check_incident_response.py` +- `python scripts/test_release_readiness.py` +- `python scripts/check_release_readiness.py` +- `python scripts/test_audit_package.py` +- `python scripts/check_audit_package.py` +- `python scripts/test_release_manifest.py` +- `python scripts/generate_release_manifest.py --check` +- `python scripts/test_release_checksums.py` +- `python scripts/generate_release_checksums.py --check` +- `python scripts/check_changelog.py` +- `bash -n scripts/check.sh` +- PowerShell parser check for `scripts/check.ps1` +- `python -m py_compile` for touched scripts/tests +- `rg -n "^#|^##|^###" docs\incident-response.md docs\release-readiness.md docs\tooling.md docs\randomizer-operations.md docs\dependency-operations.md SECURITY.md ops\ROADMAP.md ops\AUTONOMOUS_RUN.md` +- `git diff --check` +- `make check` + ### PR candidate: Reconcile Gate G roadmap after non-local evidence schema merge (Queue Item 89) -Status: PR #174 open; waiting for CI and CodeRabbit. +Status: merged in PR #174 as +`074ac3eb510ccafa593812677e6c26cbed4171b1`; issue #172 closed completed. Issue: `https://github.com/6529-Collections/6529Stream/issues/172`. PR: `https://github.com/6529-Collections/6529Stream/pull/174`. CodeRabbit request: issue comment `4695446245`. Branch: `codex/reconcile-nonlocal-evidence-schema`. Branch started from PR #171 squash merge commit `6a5a2f96b8196c2387eda3ed3187cbde2616f9cb`. +Final head: `55b7dc716c5bfdd9e003d5b068f24ba7dfb5eddd`. +Squash merge commit: `074ac3eb510ccafa593812677e6c26cbed4171b1`. +CI run: `27442981046`. +CodeRabbit status: success; no actionable comments. Goal: @@ -186,6 +296,15 @@ Validation target: - `python scripts/check_changelog.py` - `git diff --check` +Remote validation: + +- GitHub Actions CI run `27442981046` passed on final head + `55b7dc716c5bfdd9e003d5b068f24ba7dfb5eddd`. +- CodeRabbit status was success, the bot reported no actionable comments, and + all five pre-merge checks passed. +- PR #174 squash-merged as + `074ac3eb510ccafa593812677e6c26cbed4171b1`; issue #172 closed completed. + Implementation notes so far: - Recorded PR #171 merge commit @@ -8162,6 +8281,9 @@ Outcome: | Time UTC | Decision | Rationale | | --- | --- | --- | +| 2026-06-12 21:38 | Prepare Queue Item 90 for PR | Incident-response runbook, docs checker, CI/wrapper wiring, release manifest/checksum refresh, docs links, roadmap/changelog updates, focused checks, `git diff --check`, and `make check` all pass locally | +| 2026-06-12 21:13 | Start Queue Item 90 | PR #174 merged, issue #172 closed completed, and issue #173 is the next active no-secret Gate E/G docs and operations slice | +| 2026-06-12 21:12 | Merge PR #174 | Non-local evidence schema merge reconciliation merged as `074ac3eb510ccafa593812677e6c26cbed4171b1`; final head `55b7dc716c5bfdd9e003d5b068f24ba7dfb5eddd` passed CI run `27442981046`, CodeRabbit status was success with no actionable comments, and issue #172 closed completed | | 2026-06-12 21:03 | Open PR #174 and request CodeRabbit | State-only reconciliation PR opened against `main`, linked `Closes #172`, requested CodeRabbit in comment `4695446245`, and intentionally skipped Claude per current user instruction | | 2026-06-12 20:59 | Create issue #173 and select Queue Item 90 | After PR #171 merged and the state-only reconciliation issue #172 was opened, the next no-secret Gate E/G gap is a protocol incident-response runbook covering stuck auctions, failed randomness, bad Merkle roots, bad metadata/dependency configuration, signer compromise, and release artifact/evidence mistakes | | 2026-06-12 20:57 | Create issue #172 and select Queue Item 89 | PR #171 merged with CI run `27442075849` and CodeRabbit success on head `7050e0ea474c507126c4d2e11744e8b61fd3ab52`; the durable run state and roadmap verification metadata needed a state-only reconciliation before the next implementation PR | diff --git a/ops/ROADMAP.md b/ops/ROADMAP.md index b0fddf5e..a5d9d023 100644 --- a/ops/ROADMAP.md +++ b/ops/ROADMAP.md @@ -19,8 +19,8 @@ order. generated metadata in the browser sandbox, enforces the production size gate, checks deterministic release artifacts, checks the release-readiness dashboard and public-beta evidence status, documents and checks non-local - release evidence metadata intake, and runs the local deployment and auction ceremony - rehearsals. It + release evidence metadata intake, checks the protocol incident-response + runbook, and runs the local deployment and auction ceremony rehearsals. It still does not prove full protocol correctness. - Known remaining production-readiness blockers include fork-level invariant baselines, live fork/testnet rehearsals, production broadcast retention, live @@ -93,8 +93,8 @@ order. | Formatting | Fails broadly | `forge fmt --check smart-contracts` | Passing, or vendored exclusions documented | | Static analysis | Runs with a tracked high/medium baseline: 717 total findings, including 4 High, 19 Medium, and 93 Low; current high/medium rows are fixed, accepted, or documented false positives | `slither . --config-file slither.config.json --foundry-compile-all`, `ops/SLITHER_BASELINE.md`, and `docs/vendored-libraries.md` | High/medium findings fixed, accepted, or documented | | Deployment | Partial local baseline: deploy-and-wire rehearsal script, local auction ceremony rehearsal, local emergency redeployment rehearsal, deployment-rehearsal generated metadata browser sandbox proof, manifest schema, address-book schema, ceremony evidence schema, randomizer operations evidence schema, generated Anvil manifest config/example, sanitized Foundry broadcast fixture ingestion, generated Anvil and broadcast-derived address books, local Anvil ceremony evidence bundle, local Anvil randomizer operations evidence bundle, manifest parsing test, and generated ABI/bytecode checksum inputs exist; live fork/testnet rehearsal, production broadcast retention, fork/testnet/live ceremony evidence contents, and fork/testnet/live randomizer operations evidence contents remain missing | `forge script script/RehearseDeployment.s.sol:RehearseDeployment --sig "run()" --via-ir`, `forge script script/RehearseAuctionCeremony.s.sol:RehearseAuctionCeremony --sig "run()" --via-ir`, `forge script script/RehearseEmergencyRedeployment.s.sol:RehearseEmergencyRedeployment --sig "run()" --via-ir`, `scripts/check_rehearsal_metadata_browser_sandbox.py`, `test/StreamDeploymentManifest.t.sol`, `scripts/generate_broadcast_manifest_input.py --check`, `scripts/generate_deployment_manifest.py --check`, `scripts/generate_deployment_manifest.py --config deployments/config/anvil-6529stream-v0.1.0-001-broadcast.json --check`, `scripts/generate_address_books.py --check`, `scripts/test_ceremony_evidence.py`, `scripts/check_ceremony_evidence.py`, `scripts/test_randomizer_operations.py`, `scripts/check_randomizer_operations.py`, `deployments/broadcasts/`, `deployments/schema/deployment-manifest.schema.json`, `deployments/schema/address-book.schema.json`, `deployments/schema/ceremony-evidence.schema.json`, `deployments/schema/randomizer-operations-evidence.schema.json`, `deployments/address-books/`, `deployments/ceremony-evidence/`, `deployments/randomizer-operations/`, and `release-artifacts/latest/abi-checksums.json` | Anvil deployment, local metadata browser rehearsal, local auction ceremony rehearsal, local emergency redeployment rehearsal, local ceremony evidence, local randomizer operations evidence, and fork rehearsal pass | -| Docs | Architecture map, threat model, audit package, release-readiness dashboard, public-beta evidence status, non-local release evidence intake and metadata schema, status, known blockers, ADRs, security, deployment, release, metadata, dependency, randomizer, auction-custody, tooling, and release-policy docs exist for the local baseline; external audit report and actual fork/testnet/live retained evidence remain missing | `python scripts/check_architecture_threat_model.py`, `python scripts/check_audit_package.py`, `python scripts/check_release_readiness.py`, `python scripts/check_public_beta_evidence.py`, `python scripts/check_non_local_release_evidence.py`, `docs/architecture.md`, `docs/threat-model.md`, `docs/audit-package.md`, `docs/release-readiness.md`, `docs/public-beta-evidence.md`, `docs/non-local-release-evidence.md`, `docs/status.md`, `docs/known-blockers.md`, and `SECURITY.md` | Architecture, security, deployment, protocol, operations, public-beta evidence, non-local release evidence, release-readiness, and audit docs merged and externally reviewed | -| Release artifacts | Partial deterministic baseline: ABI checksums, bytecode checksums, interface IDs, event topic catalog, source verification inputs, ABI compatibility baseline, focused local gas snapshot baseline, sanitized broadcast fixture ingestion, local and broadcast-derived deployment manifest checksums, local and broadcast-derived address books, local ceremony evidence schema/bundle/checker, local randomizer operations schema/bundle/checker, machine-readable release manifest, signable release checksum bundle, public-beta evidence status/checker, non-local release evidence runbook/schema/template/checker, release-readiness dashboard/checker, release change approval policy, architecture/threat-model checks, audit-package check, and changelog gate are generated or documented from committed inputs; detached signatures, signed release tags, production address books, live explorer verification, verified live addresses, fork/testnet/live ceremony evidence contents, and fork/testnet/live randomizer operations evidence contents remain missing | `python scripts/generate_release_artifacts.py --check`, `forge snapshot --match-path test/StreamGasSnapshot.t.sol --check release-artifacts/baselines/v0.1.0/gas-snapshot.snap`, `python scripts/generate_source_verification_inputs.py --check`, `python scripts/check_abi_compatibility.py --check`, `python scripts/generate_broadcast_manifest_input.py --check`, `python scripts/generate_deployment_manifest.py --check`, `python scripts/generate_deployment_manifest.py --config deployments/config/anvil-6529stream-v0.1.0-001-broadcast.json --check`, `python scripts/generate_address_books.py --check`, `python scripts/test_ceremony_evidence.py`, `python scripts/check_ceremony_evidence.py`, `python scripts/test_randomizer_operations.py`, `python scripts/check_randomizer_operations.py`, `python scripts/test_non_local_release_evidence.py`, `python scripts/check_non_local_release_evidence.py`, `python scripts/test_public_beta_evidence.py`, `python scripts/check_public_beta_evidence.py`, `python scripts/check_architecture_threat_model.py`, `python scripts/check_audit_package.py`, `python scripts/test_release_readiness.py`, `python scripts/check_release_readiness.py`, `python scripts/generate_release_manifest.py --check`, `python scripts/generate_release_checksums.py --check`, `python scripts/check_changelog.py`, `release-artifacts/latest/`, `release-artifacts/latest/public-beta-evidence.json`, `release-artifacts/latest/source-verification-inputs.json`, `release-artifacts/latest/release-manifest.json`, `release-artifacts/schema/public-beta-evidence.schema.json`, `release-artifacts/schema/non-local-release-evidence.schema.json`, `release-artifacts/evidence/non-local-release-evidence-template.json`, `release-artifacts/evidence/non-local-template-retained-artifact.txt`, `release-artifacts/baselines/v0.1.0/abi-surface.json`, `release-artifacts/baselines/v0.1.0/gas-snapshot.snap`, `deployments/broadcasts/`, `deployments/schema/address-book.schema.json`, `deployments/schema/ceremony-evidence.schema.json`, `deployments/schema/randomizer-operations-evidence.schema.json`, `deployments/examples/anvil-6529stream-v0.1.0-001.json`, `deployments/examples/anvil-6529stream-v0.1.0-001-broadcast.json`, `deployments/address-books/anvil-6529stream-v0.1.0-001.json`, `deployments/address-books/anvil-6529stream-v0.1.0-001-broadcast.json`, `deployments/ceremony-evidence/anvil-6529stream-v0.1.0-001-local.json`, `deployments/randomizer-operations/anvil-6529stream-v0.1.0-001-local.json`, `CHANGELOG.md`, `docs/architecture.md`, `docs/threat-model.md`, `docs/audit-package.md`, `docs/public-beta-evidence.md`, `docs/non-local-release-evidence.md`, `docs/release-readiness.md`, `docs/release-policy.md`, and `docs/randomizer-operations.md` | ABIs, manifests, ceremony evidence, randomizer operations evidence, source verification inputs, checksums, public-beta evidence status, non-local release evidence, release-readiness status, gas baseline, and verified addresses published | +| Docs | Architecture map, threat model, audit package, incident-response runbook, release-readiness dashboard, public-beta evidence status, non-local release evidence intake and metadata schema, status, known blockers, ADRs, security, deployment, release, metadata, dependency, randomizer, auction-custody, tooling, and release-policy docs exist for the local baseline; external audit report and actual fork/testnet/live retained evidence remain missing | `python scripts/check_architecture_threat_model.py`, `python scripts/check_audit_package.py`, `python scripts/check_incident_response.py`, `python scripts/check_release_readiness.py`, `python scripts/check_public_beta_evidence.py`, `python scripts/check_non_local_release_evidence.py`, `docs/architecture.md`, `docs/threat-model.md`, `docs/audit-package.md`, `docs/incident-response.md`, `docs/release-readiness.md`, `docs/public-beta-evidence.md`, `docs/non-local-release-evidence.md`, `docs/status.md`, `docs/known-blockers.md`, and `SECURITY.md` | Architecture, security, deployment, protocol, operations, incident response, public-beta evidence, non-local release evidence, release-readiness, and audit docs merged and externally reviewed | +| Release artifacts | Partial deterministic baseline: ABI checksums, bytecode checksums, interface IDs, event topic catalog, source verification inputs, ABI compatibility baseline, focused local gas snapshot baseline, sanitized broadcast fixture ingestion, local and broadcast-derived deployment manifest checksums, local and broadcast-derived address books, local ceremony evidence schema/bundle/checker, local randomizer operations schema/bundle/checker, machine-readable release manifest, signable release checksum bundle, public-beta evidence status/checker, non-local release evidence runbook/schema/template/checker, incident-response runbook/checker, release-readiness dashboard/checker, release change approval policy, architecture/threat-model checks, audit-package check, and changelog gate are generated or documented from committed inputs; detached signatures, signed release tags, production address books, live explorer verification, verified live addresses, fork/testnet/live ceremony evidence contents, and fork/testnet/live randomizer operations evidence contents remain missing | `python scripts/generate_release_artifacts.py --check`, `forge snapshot --match-path test/StreamGasSnapshot.t.sol --check release-artifacts/baselines/v0.1.0/gas-snapshot.snap`, `python scripts/generate_source_verification_inputs.py --check`, `python scripts/check_abi_compatibility.py --check`, `python scripts/generate_broadcast_manifest_input.py --check`, `python scripts/generate_deployment_manifest.py --check`, `python scripts/generate_deployment_manifest.py --config deployments/config/anvil-6529stream-v0.1.0-001-broadcast.json --check`, `python scripts/generate_address_books.py --check`, `python scripts/test_ceremony_evidence.py`, `python scripts/check_ceremony_evidence.py`, `python scripts/test_randomizer_operations.py`, `python scripts/check_randomizer_operations.py`, `python scripts/test_non_local_release_evidence.py`, `python scripts/check_non_local_release_evidence.py`, `python scripts/test_public_beta_evidence.py`, `python scripts/check_public_beta_evidence.py`, `python scripts/check_architecture_threat_model.py`, `python scripts/check_audit_package.py`, `python scripts/test_incident_response.py`, `python scripts/check_incident_response.py`, `python scripts/test_release_readiness.py`, `python scripts/check_release_readiness.py`, `python scripts/generate_release_manifest.py --check`, `python scripts/generate_release_checksums.py --check`, `python scripts/check_changelog.py`, `release-artifacts/latest/`, `release-artifacts/latest/public-beta-evidence.json`, `release-artifacts/latest/source-verification-inputs.json`, `release-artifacts/latest/release-manifest.json`, `release-artifacts/schema/public-beta-evidence.schema.json`, `release-artifacts/schema/non-local-release-evidence.schema.json`, `release-artifacts/evidence/non-local-release-evidence-template.json`, `release-artifacts/evidence/non-local-template-retained-artifact.txt`, `release-artifacts/baselines/v0.1.0/abi-surface.json`, `release-artifacts/baselines/v0.1.0/gas-snapshot.snap`, `deployments/broadcasts/`, `deployments/schema/address-book.schema.json`, `deployments/schema/ceremony-evidence.schema.json`, `deployments/schema/randomizer-operations-evidence.schema.json`, `deployments/examples/anvil-6529stream-v0.1.0-001.json`, `deployments/examples/anvil-6529stream-v0.1.0-001-broadcast.json`, `deployments/address-books/anvil-6529stream-v0.1.0-001.json`, `deployments/address-books/anvil-6529stream-v0.1.0-001-broadcast.json`, `deployments/ceremony-evidence/anvil-6529stream-v0.1.0-001-local.json`, `deployments/randomizer-operations/anvil-6529stream-v0.1.0-001-local.json`, `CHANGELOG.md`, `docs/architecture.md`, `docs/threat-model.md`, `docs/audit-package.md`, `docs/incident-response.md`, `docs/public-beta-evidence.md`, `docs/non-local-release-evidence.md`, `docs/release-readiness.md`, `docs/release-policy.md`, and `docs/randomizer-operations.md` | ABIs, manifests, ceremony evidence, randomizer operations evidence, source verification inputs, checksums, incident response, public-beta evidence status, non-local release evidence, release-readiness status, gas baseline, and verified addresses published | | Windows setup | Foundry installed under `~/.foundry/bin`, but current shell may not resolve `forge` from `PATH` | direct binary invocation | Bootstrap works in current and future shells, or limitation documented | ### Issue Tracker Reconciliation @@ -597,9 +597,10 @@ later sections. ### Operational Maturity - Add deployment scripts and post-deployment checklists. -- Add runbooks for stuck auctions, failed randomness, bad Merkle roots, bad - metadata, failed payouts, and signer compromise. Tracked by - [`#173`](https://github.com/6529-Collections/6529Stream/issues/173). +- Maintain the no-secret incident-response runbook for stuck auctions, failed + randomness, bad Merkle roots, bad metadata/dependency configuration, failed + payouts, signer compromise, and release artifact/evidence mistakes. Tracked + by [`#173`](https://github.com/6529-Collections/6529Stream/issues/173). - Monitor admin changes, pending randomness, pending auctions, curator pool balances, and failed claims. - Document the trust model and accepted risks publicly. @@ -2020,16 +2021,16 @@ No P0 contract PR may merge without: ### Operational Runbooks -- Stuck auction. -- Failed payout. -- Stuck randomness. -- Incorrect Merkle root. -- Incorrect dependency script. -- Bad metadata before freeze. -- Bad metadata after freeze. -- Compromised signer. -- Compromised admin. -- Marketplace metadata not refreshing. +- Maintain [`docs/incident-response.md`](../docs/incident-response.md) as the + no-secret runbook for stuck auction, failed payout, stuck randomness, + incorrect Merkle root, incorrect dependency script, bad metadata before + freeze, bad metadata after freeze, compromised signer, compromised admin, + marketplace metadata refresh, and release artifact/evidence mistakes. +- Keep `scripts/check_incident_response.py` and + `scripts/test_incident_response.py` in the default local/CI gate. +- Update the runbook whenever pause, signer, withdrawal, randomizer, + metadata/dependency, release-artifact, or evidence-retention behavior + changes. ### Monitoring, Indexing, And Reorgs @@ -2415,7 +2416,7 @@ Status values: `Missing`, `Planned`, `In Progress`, `Passing`, `Blocked`. | ERC-4906 metadata signaling | `supportsInterface(0x49064906)` succeeds and `MetadataUpdate` / `BatchMetadataUpdate` emit from metadata write paths that can change token JSON | `test/StreamMetadataEvents.t.sol` | Passing for current `StreamCore` behavior: ERC-4906 interface support succeeds, randomness fulfillment and token metadata input writes emit `MetadataUpdate`, collection-level metadata mode/base URI/display/script/dependency-reference writes emit `BatchMetadataUpdate` over the minted-ever range, empty collections do not emit empty batch events, and mint-only plus burn paths do not emit ERC-4906. Dependency registry version creation does not emit ERC-4906 for pinned collections because their output does not change; explicit repinning goes through `updateCollectionInfo` and emits the existing collection-range update. | [`P1-META-004`](https://github.com/6529-Collections/6529Stream/issues/49), [`P1-META-003`](https://github.com/6529-Collections/6529Stream/issues/48) | Gate D | TBD | | Dependency script packed encoding | Dependency script retrieval uses safe typed concatenation/hash encoding and cannot collide across script segments | `test/StreamMetadataEncoding.t.sol` | Passing: typed chunk/content hashes include dependency key, chunk count, chunk index, chunk byte length, and chunk content hash; ambiguous chunk splits that render the same JavaScript produce distinct content hashes while preserving rendered-script compatibility; zero-chunk dependency hashes are deterministic | [`P0-META-001`](https://github.com/6529-Collections/6529Stream/issues/9), [`P1-META-003`](https://github.com/6529-Collections/6529Stream/issues/48) | Gate C/Gate D | TBD | | Deployment redeployment rehearsal | Deployment manifests, broadcast-derived manifest inputs, address books, ABI hashes, source verification inputs, dependency artifact manifests, dependency operation runbooks, admin ceremony, signer setup, deprecation checks, generated metadata browser proof, local auction ceremony proof, emergency redeployment rehearsal, ceremony evidence bundle, and non-local release evidence intake/schema/checker follow ADR 0007 | `test/StreamDeploymentManifest.t.sol`, `script/RehearseDeployment.s.sol`, `script/RehearseMetadataBrowser.s.sol`, `script/RehearseAuctionCeremony.s.sol`, `script/RehearseEmergencyRedeployment.s.sol`, `scripts/test_rehearsal_metadata_browser_sandbox.py`, `scripts/check_rehearsal_metadata_browser_sandbox.py`, `scripts/generate_release_artifacts.py`, `scripts/test_release_artifacts.py`, `scripts/generate_source_verification_inputs.py`, `scripts/test_source_verification_inputs.py`, `scripts/generate_dependency_artifact_manifest.py`, `scripts/test_dependency_artifact_manifest.py`, `scripts/check_abi_compatibility.py`, `scripts/test_abi_compatibility.py`, `scripts/generate_broadcast_manifest_input.py`, `scripts/test_broadcast_manifest_input.py`, `scripts/generate_deployment_manifest.py`, `scripts/test_deployment_manifest.py`, `scripts/generate_address_books.py`, `scripts/test_address_books.py`, `scripts/test_ceremony_evidence.py`, `scripts/check_ceremony_evidence.py`, `scripts/test_release_signatures.py`, `scripts/check_release_signatures.py`, `scripts/test_non_local_release_evidence.py`, `scripts/check_non_local_release_evidence.py`, `docs/non-local-release-evidence.md`, `release-artifacts/schema/non-local-release-evidence.schema.json`, `release-artifacts/evidence/non-local-release-evidence-template.json`, `scripts/generate_release_manifest.py`, `scripts/test_release_manifest.py`, `scripts/generate_release_checksums.py`, and `scripts/test_release_checksums.py` | In Progress: local deploy-and-wire rehearsal, local deployment-rehearsal generated metadata browser proof, local auction ceremony from signed auction drop through bid, settlement, proceeds withdrawal, and zero owed funds, local emergency redeployment rehearsal with distinct old/replacement deployment versions, manifests, drop domains, core/drops/auction addresses, Safe-rooted ceremony state, deployer-admin removal, and replacement fixed-price mint smoke, Safe-placeholder ownership transfer, temporary admin revocation, manifest schema/example parsing, generated Anvil manifest config/example, sanitized Foundry broadcast fixture ingestion, generated broadcast-derived manifest config/example, generated local and broadcast-derived address books, local ceremony evidence schema/bundle/checker, local release signature evidence schema/bundle/checker, non-local release evidence schema/template/checker, deterministic source verification input bundle, deterministic dependency artifact manifest baseline for the local rehearsal dependency, production dependency operations runbook, deterministic top-level release manifest, deterministic manifest checksum, generated ABI/bytecode checksum baseline, generated interface ID catalog, event topic catalog, ABI compatibility baseline, signable checksum bundle, non-local evidence intake runbook, and default check-script gate added; live fork rehearsal, production broadcast retention, production address books, live explorer verification, retained fork/testnet/live ceremony evidence contents, retained fork/testnet/live emergency redeployment evidence contents, actual production checksum signatures and signed tags, and fork/testnet/live production metadata browser evidence remain open | [`P2-UPGRADE-ADR`](https://github.com/6529-Collections/6529Stream/issues/53), [`P1-DEPLOY-002`](https://github.com/6529-Collections/6529Stream/issues/91), [`P1-RELEASE-001`](https://github.com/6529-Collections/6529Stream/issues/93), [`P1-RELEASE-002`](https://github.com/6529-Collections/6529Stream/issues/97), [`P1-DEPLOY-003`](https://github.com/6529-Collections/6529Stream/issues/95), [`P1-RELEASE-003`](https://github.com/6529-Collections/6529Stream/issues/99), [`P1-RELEASE-004`](https://github.com/6529-Collections/6529Stream/issues/101), [`P1-RELEASE-006`](https://github.com/6529-Collections/6529Stream/issues/105), [`P1-RELEASE-007`](https://github.com/6529-Collections/6529Stream/issues/107), [`P1-DEPLOY-004`](https://github.com/6529-Collections/6529Stream/issues/109), [`P1-META-003 dependency artifact packaging`](https://github.com/6529-Collections/6529Stream/issues/117), [`Dependency migration runbooks`](https://github.com/6529-Collections/6529Stream/issues/136), [`Live/fork metadata browser execution`](https://github.com/6529-Collections/6529Stream/issues/135), [`Dry-run auction ceremony rehearsal`](https://github.com/6529-Collections/6529Stream/issues/140), [`Local emergency redeployment rehearsal`](https://github.com/6529-Collections/6529Stream/issues/142), [`Deployment ceremony evidence bundle schema`](https://github.com/6529-Collections/6529Stream/issues/144), [`#156`](https://github.com/6529-Collections/6529Stream/issues/156), [`#168`](https://github.com/6529-Collections/6529Stream/issues/168), [`#170`](https://github.com/6529-Collections/6529Stream/issues/170) | Gate E/Gate G | TBD | -| Release artifact catalog | ABI checksums, bytecode checksums, standard/custom interface IDs, event topics, source verification inputs, dependency artifact manifests, broadcast-derived manifest inputs, ABI compatibility, gas snapshot baseline, address books, ceremony evidence, release signature evidence, non-local release evidence metadata, machine-readable release manifest, signable checksum files, release-readiness dashboard, release-impact changelog policy, and the maintained non-local release evidence runbook/schema/template/checker are generated, checked, or hashed deterministically from current committed inputs and Foundry/deployment artifacts | `scripts/generate_release_artifacts.py`, `scripts/test_release_artifacts.py`, `forge snapshot --match-path test/StreamGasSnapshot.t.sol --check release-artifacts/baselines/v0.1.0/gas-snapshot.snap`, `scripts/generate_source_verification_inputs.py`, `scripts/test_source_verification_inputs.py`, `scripts/generate_dependency_artifact_manifest.py`, `scripts/test_dependency_artifact_manifest.py`, `scripts/check_abi_compatibility.py`, `scripts/test_abi_compatibility.py`, `scripts/generate_broadcast_manifest_input.py`, `scripts/test_broadcast_manifest_input.py`, `scripts/generate_deployment_manifest.py`, `scripts/test_deployment_manifest.py`, `scripts/generate_address_books.py`, `scripts/test_address_books.py`, `scripts/test_ceremony_evidence.py`, `scripts/check_ceremony_evidence.py`, `scripts/test_release_signatures.py`, `scripts/check_release_signatures.py`, `scripts/test_non_local_release_evidence.py`, `scripts/check_non_local_release_evidence.py`, `scripts/test_release_readiness.py`, `scripts/check_release_readiness.py`, `scripts/generate_release_manifest.py`, `scripts/test_release_manifest.py`, `scripts/generate_release_checksums.py`, `scripts/test_release_checksums.py`, `scripts/check_changelog.py`, `scripts/test_changelog_check.py`, `release-artifacts/latest/`, `release-artifacts/latest/source-verification-inputs.json`, `release-artifacts/latest/dependency-artifact-manifest.json`, `release-artifacts/latest/release-manifest.json`, `release-artifacts/signatures/`, `release-artifacts/schema/release-signature-evidence.schema.json`, `release-artifacts/schema/non-local-release-evidence.schema.json`, `release-artifacts/evidence/non-local-release-evidence-template.json`, `release-artifacts/evidence/non-local-template-retained-artifact.txt`, `release-artifacts/dependencies/`, `release-artifacts/baselines/v0.1.0/abi-surface.json`, `release-artifacts/baselines/v0.1.0/gas-snapshot.snap`, `deployments/broadcasts/`, `deployments/address-books/`, `deployments/ceremony-evidence/`, `deployments/schema/ceremony-evidence.schema.json`, `CHANGELOG.md`, `docs/public-beta-evidence.md`, `docs/non-local-release-evidence.md`, `docs/release-readiness.md`, and `docs/release-policy.md` | In Progress locally for the deterministic Gate G baseline: generator self-tests cover ABI hashing, bytecode hashing, event topic generation, configured standard interface IDs, computed selector XOR traceability, and drift detection; local gas snapshot check covers fixed-price mint, auction bid, auction settlement, curator reward claim, final on-chain `tokenURI`, and dependency/script reads; source-verification self-tests cover deterministic generation, check-mode drift, missing source/artifact errors, linked-bytecode reporting, constructor ABI retention, verification command templates, and ABI checksum mismatches; dependency-artifact self-tests cover deterministic generation, check-mode drift, missing artifact files, malformed dependency keys, duplicate dependency identity, and descriptor path-boundary validation; ABI compatibility self-tests cover compatible, additive, removed, changed, missing-contract, and check-mode drift cases; broadcast-ingestion self-tests cover deterministic generation, check-mode drift, wrong-chain broadcasts, missing/unexpected deployments, failed receipts, boolean receipt status rejection, receipt address mismatch, duplicate deployment names, and secret-like key rejection; address-book self-tests cover deterministic generation, drift detection, missing output directories, duplicate/invalid addresses, `source_dirty`, chain ID, lifecycle state, git commit, verification status, hash-format validation, missing metadata, missing release contracts, and unknown contracts; ceremony-evidence self-tests cover deterministic local evidence validation, required sections, stale hashes, missing referenced files, non-local retained artifacts, testnet verification status, and secret-like key rejection; release-signature self-tests cover local placeholder evidence, non-local placeholder rejection, signed-output verification requirements, production signed-output requirements, stale retained hashes, malformed confirmation depth, and secret-like value rejection; non-local release evidence self-tests cover the committed template, reviewed evidence, template reviewer placeholders, invalid reviewers, unknown requirement IDs, unsupported environments, retained path/hash drift, path escape, and secret-like key/value rejection; release-readiness self-tests cover required maturity language, local/public-beta/production blocker separation, required evidence links, required commands, missing linked files, and path-boundary rejection including the non-local release evidence runbook; release-manifest self-tests cover deterministic generation, check-mode drift, missing required artifacts, required JSON schema versions, governance-doc hashing, release/deployment/broadcast metadata, source-verification/dependency-artifact/ceremony-evidence/gas-snapshot/signature-evidence/non-local-evidence coverage, and explicit checksum-bundle self-reference policy; checksum-bundle self-tests cover deterministic generation, sorted SHA256SUMS output, dependency source coverage, manifest coverage, ceremony evidence coverage, gas snapshot coverage, release signature evidence coverage, non-local evidence coverage, self-reference exclusion, drift detection, deleted covered files, missing generated outputs, and missing covered roots; changelog self-tests cover release-impacting path detection, missing changelog edits, missing `Unreleased`, placeholder entries, and valid release notes. Actual production detached checksum signatures, signed tags, production address books, live explorer verification, verified live deployment hashes, fork/testnet/live ceremony evidence contents, fork/testnet/live randomizer operations evidence contents, reviewed non-local evidence contents, and external audit completion remain future Gate G work | [`P1-RELEASE-001`](https://github.com/6529-Collections/6529Stream/issues/93), [`P1-RELEASE-002`](https://github.com/6529-Collections/6529Stream/issues/97), [`P1-RELEASE-003`](https://github.com/6529-Collections/6529Stream/issues/99), [`P1-RELEASE-004`](https://github.com/6529-Collections/6529Stream/issues/101), [`P1-RELEASE-005`](https://github.com/6529-Collections/6529Stream/issues/103), [`P1-RELEASE-006`](https://github.com/6529-Collections/6529Stream/issues/105), [`P1-RELEASE-007`](https://github.com/6529-Collections/6529Stream/issues/107), [`P1-DEPLOY-004`](https://github.com/6529-Collections/6529Stream/issues/109), [`P1-META-003 dependency artifact packaging`](https://github.com/6529-Collections/6529Stream/issues/117), [`Deployment ceremony evidence bundle schema`](https://github.com/6529-Collections/6529Stream/issues/144), [`Local gas snapshot baseline`](https://github.com/6529-Collections/6529Stream/issues/146), [`#156`](https://github.com/6529-Collections/6529Stream/issues/156), [`#162`](https://github.com/6529-Collections/6529Stream/issues/162), [`#168`](https://github.com/6529-Collections/6529Stream/issues/168), [`#170`](https://github.com/6529-Collections/6529Stream/issues/170) | Gate G | TBD | +| Release artifact catalog | ABI checksums, bytecode checksums, standard/custom interface IDs, event topics, source verification inputs, dependency artifact manifests, broadcast-derived manifest inputs, ABI compatibility, gas snapshot baseline, address books, ceremony evidence, release signature evidence, non-local release evidence metadata, machine-readable release manifest, signable checksum files, incident-response runbook, release-readiness dashboard, release-impact changelog policy, and the maintained non-local release evidence runbook/schema/template/checker are generated, checked, or hashed deterministically from current committed inputs and Foundry/deployment artifacts | `scripts/generate_release_artifacts.py`, `scripts/test_release_artifacts.py`, `forge snapshot --match-path test/StreamGasSnapshot.t.sol --check release-artifacts/baselines/v0.1.0/gas-snapshot.snap`, `scripts/generate_source_verification_inputs.py`, `scripts/test_source_verification_inputs.py`, `scripts/generate_dependency_artifact_manifest.py`, `scripts/test_dependency_artifact_manifest.py`, `scripts/check_abi_compatibility.py`, `scripts/test_abi_compatibility.py`, `scripts/generate_broadcast_manifest_input.py`, `scripts/test_broadcast_manifest_input.py`, `scripts/generate_deployment_manifest.py`, `scripts/test_deployment_manifest.py`, `scripts/generate_address_books.py`, `scripts/test_address_books.py`, `scripts/test_ceremony_evidence.py`, `scripts/check_ceremony_evidence.py`, `scripts/test_release_signatures.py`, `scripts/check_release_signatures.py`, `scripts/test_non_local_release_evidence.py`, `scripts/check_non_local_release_evidence.py`, `scripts/test_incident_response.py`, `scripts/check_incident_response.py`, `scripts/test_release_readiness.py`, `scripts/check_release_readiness.py`, `scripts/generate_release_manifest.py`, `scripts/test_release_manifest.py`, `scripts/generate_release_checksums.py`, `scripts/test_release_checksums.py`, `scripts/check_changelog.py`, `scripts/test_changelog_check.py`, `release-artifacts/latest/`, `release-artifacts/latest/source-verification-inputs.json`, `release-artifacts/latest/dependency-artifact-manifest.json`, `release-artifacts/latest/release-manifest.json`, `release-artifacts/signatures/`, `release-artifacts/schema/release-signature-evidence.schema.json`, `release-artifacts/schema/non-local-release-evidence.schema.json`, `release-artifacts/evidence/non-local-release-evidence-template.json`, `release-artifacts/evidence/non-local-template-retained-artifact.txt`, `release-artifacts/dependencies/`, `release-artifacts/baselines/v0.1.0/abi-surface.json`, `release-artifacts/baselines/v0.1.0/gas-snapshot.snap`, `deployments/broadcasts/`, `deployments/address-books/`, `deployments/ceremony-evidence/`, `deployments/schema/ceremony-evidence.schema.json`, `CHANGELOG.md`, `docs/incident-response.md`, `docs/public-beta-evidence.md`, `docs/non-local-release-evidence.md`, `docs/release-readiness.md`, and `docs/release-policy.md` | In Progress locally for the deterministic Gate G baseline: generator self-tests cover ABI hashing, bytecode hashing, event topic generation, configured standard interface IDs, computed selector XOR traceability, and drift detection; local gas snapshot check covers fixed-price mint, auction bid, auction settlement, curator reward claim, final on-chain `tokenURI`, and dependency/script reads; source-verification self-tests cover deterministic generation, check-mode drift, missing source/artifact errors, linked-bytecode reporting, constructor ABI retention, verification command templates, and ABI checksum mismatches; dependency-artifact self-tests cover deterministic generation, check-mode drift, missing artifact files, malformed dependency keys, duplicate dependency identity, and descriptor path-boundary validation; ABI compatibility self-tests cover compatible, additive, removed, changed, missing-contract, and check-mode drift cases; broadcast-ingestion self-tests cover deterministic generation, check-mode drift, wrong-chain broadcasts, missing/unexpected deployments, failed receipts, boolean receipt status rejection, receipt address mismatch, duplicate deployment names, and secret-like key rejection; address-book self-tests cover deterministic generation, drift detection, missing output directories, duplicate/invalid addresses, `source_dirty`, chain ID, lifecycle state, git commit, verification status, hash-format validation, missing metadata, missing release contracts, and unknown contracts; ceremony-evidence self-tests cover deterministic local evidence validation, required sections, stale hashes, missing referenced files, non-local retained artifacts, testnet verification status, and secret-like key rejection; release-signature self-tests cover local placeholder evidence, non-local placeholder rejection, signed-output verification requirements, production signed-output requirements, stale retained hashes, malformed confirmation depth, and secret-like value rejection; non-local release evidence self-tests cover the committed template, reviewed evidence, template reviewer placeholders, invalid reviewers, unknown requirement IDs, unsupported environments, retained path/hash drift, path escape, and secret-like key/value rejection; incident-response self-tests cover required maturity language, required runbook sections, emergency pause, withdrawal availability, signer revocation, retry/recovery, evidence retention, required links, required commands, missing linked files, and path-boundary rejection; release-readiness self-tests cover required maturity language, local/public-beta/production blocker separation, required evidence links, required commands, missing linked files, and path-boundary rejection including the non-local release evidence runbook; release-manifest self-tests cover deterministic generation, check-mode drift, missing required artifacts, required JSON schema versions, governance-doc hashing, release/deployment/broadcast metadata, source-verification/dependency-artifact/ceremony-evidence/gas-snapshot/signature-evidence/non-local-evidence coverage, and explicit checksum-bundle self-reference policy; checksum-bundle self-tests cover deterministic generation, sorted SHA256SUMS output, dependency source coverage, manifest coverage, ceremony evidence coverage, gas snapshot coverage, release signature evidence coverage, non-local evidence coverage, self-reference exclusion, drift detection, deleted covered files, missing generated outputs, and missing covered roots; changelog self-tests cover release-impacting path detection, missing changelog edits, missing `Unreleased`, placeholder entries, and valid release notes. Actual production detached checksum signatures, signed tags, production address books, live explorer verification, verified live deployment hashes, fork/testnet/live ceremony evidence contents, fork/testnet/live randomizer operations evidence contents, reviewed non-local evidence contents, and external audit completion remain future Gate G work | [`P1-RELEASE-001`](https://github.com/6529-Collections/6529Stream/issues/93), [`P1-RELEASE-002`](https://github.com/6529-Collections/6529Stream/issues/97), [`P1-RELEASE-003`](https://github.com/6529-Collections/6529Stream/issues/99), [`P1-RELEASE-004`](https://github.com/6529-Collections/6529Stream/issues/101), [`P1-RELEASE-005`](https://github.com/6529-Collections/6529Stream/issues/103), [`P1-RELEASE-006`](https://github.com/6529-Collections/6529Stream/issues/105), [`P1-RELEASE-007`](https://github.com/6529-Collections/6529Stream/issues/107), [`P1-DEPLOY-004`](https://github.com/6529-Collections/6529Stream/issues/109), [`P1-META-003 dependency artifact packaging`](https://github.com/6529-Collections/6529Stream/issues/117), [`Deployment ceremony evidence bundle schema`](https://github.com/6529-Collections/6529Stream/issues/144), [`Local gas snapshot baseline`](https://github.com/6529-Collections/6529Stream/issues/146), [`#156`](https://github.com/6529-Collections/6529Stream/issues/156), [`#162`](https://github.com/6529-Collections/6529Stream/issues/162), [`#168`](https://github.com/6529-Collections/6529Stream/issues/168), [`#170`](https://github.com/6529-Collections/6529Stream/issues/170), [`#173`](https://github.com/6529-Collections/6529Stream/issues/173) | Gate G | TBD | | Mint-accounting state | Dead counters are removed or retained counters initialize and update according to the accepted drop/mint accounting design | `test/StreamMintAccounting.t.sol` | Passing: removed never-written public/allowlist mint-count mappings and retrieval APIs; retained airdrop counter starts at zero, increments on authorized minter calls, and remains unchanged on unauthorized mint attempts | [`P0-CORE-001`](https://github.com/6529-Collections/6529Stream/issues/13) | Gate C | TBD | | Uninitialized local findings | First-party default-local behavior is explicit, removed, or covered by targeted regressions | `test/StreamInitialization.t.sol` | Passing: Bytes32 character counts, missing/matching delegation lookups, subdelegation register/revoke gates, empty-script generative rendering, and multi-recipient minter return indexes cover the remaining first-party production rows; Slither now reports only one accepted test-only `uninitialized-local` row | [`P0-INIT-001`](https://github.com/6529-Collections/6529Stream/issues/15) | Gate C | TBD | | Vendored library Slither findings | Retained OpenZeppelin utility files have provenance, local delta notes, and regressions for flagged math/encoding behavior | `test/StreamVendoredLibraries.t.sol` | Passing: Base64 golden/padding vectors, `Math.mulDiv` full-precision boundaries, rounding-up behavior, overflow, and zero-denominator reverts cover the current vendored false-positive rows | [`P0-LIB-001`](https://github.com/6529-Collections/6529Stream/issues/11) | Gate F | TBD | diff --git a/release-artifacts/README.md b/release-artifacts/README.md index 2abbf552..c8fd4bb9 100644 --- a/release-artifacts/README.md +++ b/release-artifacts/README.md @@ -91,8 +91,8 @@ records release metadata, release artifact hashes, ABI compatibility baseline hashes, deployment manifest/address-book hashes, ceremony evidence hashes, public-beta evidence status, schema hashes, governance doc hashes including `docs/architecture.md`, `docs/threat-model.md`, `docs/audit-package.md`, -`docs/public-beta-evidence.md`, `docs/non-local-release-evidence.md`, and -`docs/release-readiness.md`, and the +`docs/incident-response.md`, `docs/public-beta-evidence.md`, +`docs/non-local-release-evidence.md`, and `docs/release-readiness.md`, and the release-ceremony items that are not yet available for this pre-audit local baseline. @@ -109,12 +109,13 @@ points at `evidence/non-local-template-retained-artifact.txt` to prove retained artifact hash validation without claiming public-beta or production readiness. `docs/architecture.md`, `docs/threat-model.md`, `docs/audit-package.md`, -`docs/public-beta-evidence.md`, `docs/non-local-release-evidence.md`, and -`docs/release-readiness.md` are the auditor-facing architecture, -trust-boundary, package, evidence-status, non-local evidence intake, and Gate G -readiness indexes for the current local baseline. They are validated before -release manifest generation, and the release manifest records their hashes as -governance documents. +`docs/incident-response.md`, `docs/public-beta-evidence.md`, +`docs/non-local-release-evidence.md`, and `docs/release-readiness.md` are the +auditor-facing architecture, trust-boundary, package, incident-response, +evidence-status, non-local evidence intake, and Gate G readiness indexes for +the current local baseline. They are validated before release manifest +generation, and the release manifest records their hashes as governance +documents. `latest/SHA256SUMS` and `latest/release-checksums.json` are also generated outputs. They cover the committed release artifact config, generated release diff --git a/release-artifacts/latest/SHA256SUMS b/release-artifacts/latest/SHA256SUMS index efc1a4ca..823aeda0 100644 --- a/release-artifacts/latest/SHA256SUMS +++ b/release-artifacts/latest/SHA256SUMS @@ -25,7 +25,7 @@ e072f786d66e7052ff1d63f1c5e6a3fc9ef46c986bd2f0a1843626a5dc21cccc release-artifa 8a8a41d89757d3d16f0718638d1175290dcd00cfea86204665cd525f608d7c1a release-artifacts/latest/interface-ids.json 3b75cde36f75662061011af793d4c9240a1fec6447294ce0e3ee1d331d729b4d release-artifacts/latest/public-beta-evidence.json 9ff4283a72a51f7b770b8a217b9f7f8b5dde793af14532c80572186bc7acf6d5 release-artifacts/latest/release-artifact-manifest.json -92c39444ad39badde397deb0fb54e332cbfd6ec7284b208293b11a675c75edac release-artifacts/latest/release-manifest.json +b22e55b1f9c8f2a609c2e452125e63ba0d3e7ebc50b483fd23580b4cd41e62bb release-artifacts/latest/release-manifest.json 374fce4bc46746d61cbae487cf6cb549cb40b5db5a95951568ed4455669af33a release-artifacts/latest/source-verification-inputs.json 5a5aede0ab1b7bee194e495bdac98e0f37b814879be62a72ce819be07da82c1a release-artifacts/schema/non-local-release-evidence.schema.json cec024757bfe8967a27106ee8c4739c6f51ff8b0ec6a2fb564939441fd9ac9bb release-artifacts/schema/public-beta-evidence.schema.json diff --git a/release-artifacts/latest/release-checksums.json b/release-artifacts/latest/release-checksums.json index 49fac049..588328ca 100644 --- a/release-artifacts/latest/release-checksums.json +++ b/release-artifacts/latest/release-checksums.json @@ -24,7 +24,7 @@ "text_checksum_file": { "path": "release-artifacts/latest/SHA256SUMS", "format": "sha256sum", - "sha256": "sha256:0baf73807d3fba71b7b0c2f5bda1ef793811e4a37b7e82d4714df235dace3d27" + "sha256": "sha256:cff62764130f41a0a461da8bb17f4665d7070fe0daae851358bc502bc8f1d4b9" }, "manifest_file": { "path": "release-artifacts/latest/release-checksums.json", @@ -168,8 +168,8 @@ }, { "path": "release-artifacts/latest/release-manifest.json", - "sha256": "sha256:92c39444ad39badde397deb0fb54e332cbfd6ec7284b208293b11a675c75edac", - "size_bytes": 23500 + "sha256": "sha256:b22e55b1f9c8f2a609c2e452125e63ba0d3e7ebc50b483fd23580b4cd41e62bb", + "size_bytes": 23683 }, { "path": "release-artifacts/latest/source-verification-inputs.json", diff --git a/release-artifacts/latest/release-manifest.json b/release-artifacts/latest/release-manifest.json index 1374e777..7fea04dd 100644 --- a/release-artifacts/latest/release-manifest.json +++ b/release-artifacts/latest/release-manifest.json @@ -467,8 +467,8 @@ "release_notes_and_policy": { "changelog": { "path": "CHANGELOG.md", - "sha256": "sha256:604e4173f38e12234d9c4703ba2ad7c9cef5bee15376eea7d52ec9c752bea227", - "size_bytes": 13181 + "sha256": "sha256:dcf184b61eb06d35ccbd99c91acf0cc9d51c10be0e04e34e25a18c1bfe630f03", + "size_bytes": 13473 }, "governance_docs": [ { @@ -483,13 +483,13 @@ }, { "path": "docs/dependency-operations.md", - "sha256": "sha256:49bfef2fa4b4699d1cda94f1cf8caf3486ce9d0a2cb455f8296bbe91adaf1888", - "size_bytes": 13645 + "sha256": "sha256:989a66a27e77db4226e979814297e049d2164698ddd6c45014681324b6ea8e20", + "size_bytes": 13854 }, { "path": "docs/randomizer-operations.md", - "sha256": "sha256:94940d1cbe2efc58c056fcbf7b4de7ed3aa2c126b529cf03adc53acf2d6d5095", - "size_bytes": 3488 + "sha256": "sha256:c683ffb29f93a541e7e1650a29d078adc4fb6ff3db4df2e297e36e019f248018", + "size_bytes": 3825 }, { "path": "docs/release-signatures.md", @@ -518,18 +518,23 @@ }, { "path": "docs/audit-package.md", - "sha256": "sha256:b895cabdf61b7c066b145012fa0596247f6158906c55cafde1413d8eed8cd0c3", - "size_bytes": 11744 + "sha256": "sha256:1a5ce4f55438cb231c050b57cf4a0fcf577cd79341a0aaa1da910bdae5b8f1f5", + "size_bytes": 12020 + }, + { + "path": "docs/incident-response.md", + "sha256": "sha256:8c80a6e3d3fa8ef89917880d74963fce1d29178d373213a513bc85bcb41f80e4", + "size_bytes": 16129 }, { "path": "docs/release-readiness.md", - "sha256": "sha256:a8909810e050d1cd4c2800a1c388b1f60b1bb0be48137f698e427fb8b683a8c4", - "size_bytes": 12258 + "sha256": "sha256:42f28629c10b1fe849d6dcf9e09c8883da0724f31e1eef9b9705e3675e767bd2", + "size_bytes": 12684 }, { "path": "docs/tooling.md", - "sha256": "sha256:9d4615d7f9d8a43aa4470c0792abb8fb07eace6e402b9e46c84bc23e829def1b", - "size_bytes": 15858 + "sha256": "sha256:5a3288ca43b5dc0f2e22a2c10634922b5f714283d9592c996a6947d25c76b9e4", + "size_bytes": 16409 }, { "path": "docs/status.md", diff --git a/scripts/check.ps1 b/scripts/check.ps1 index 24f8463a..e5733eab 100644 --- a/scripts/check.ps1 +++ b/scripts/check.ps1 @@ -78,6 +78,8 @@ forge build --sizes --via-ir --skip test --skip script --force & $pythonPath @pythonArgs "scripts\check_architecture_threat_model.py" & $pythonPath @pythonArgs "scripts\test_audit_package.py" & $pythonPath @pythonArgs "scripts\check_audit_package.py" +& $pythonPath @pythonArgs "scripts\test_incident_response.py" +& $pythonPath @pythonArgs "scripts\check_incident_response.py" & $pythonPath @pythonArgs "scripts\test_release_readiness.py" & $pythonPath @pythonArgs "scripts\check_release_readiness.py" & $pythonPath @pythonArgs "scripts\test_release_manifest.py" diff --git a/scripts/check.sh b/scripts/check.sh index 142e20a9..966d9933 100644 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -63,6 +63,8 @@ forge build --sizes --via-ir --skip test --skip script --force "$python_bin" scripts/check_architecture_threat_model.py "$python_bin" scripts/test_audit_package.py "$python_bin" scripts/check_audit_package.py +"$python_bin" scripts/test_incident_response.py +"$python_bin" scripts/check_incident_response.py "$python_bin" scripts/test_release_readiness.py "$python_bin" scripts/check_release_readiness.py "$python_bin" scripts/test_release_manifest.py diff --git a/scripts/check_audit_package.py b/scripts/check_audit_package.py index 07ec53e0..9f16360f 100644 --- a/scripts/check_audit_package.py +++ b/scripts/check_audit_package.py @@ -35,6 +35,8 @@ REQUIRED_COMMANDS = [ "python scripts/test_audit_package.py", "python scripts/check_audit_package.py", + "python scripts/test_incident_response.py", + "python scripts/check_incident_response.py", "python scripts/test_release_readiness.py", "python scripts/check_release_readiness.py", "python scripts/test_public_beta_evidence.py", @@ -53,6 +55,7 @@ "ops/SLITHER_BASELINE.md", "docs/architecture.md", "docs/threat-model.md", + "docs/incident-response.md", "docs/release-readiness.md", "docs/status.md", "docs/known-blockers.md", diff --git a/scripts/check_incident_response.py b/scripts/check_incident_response.py new file mode 100644 index 00000000..7fbbe11c --- /dev/null +++ b/scripts/check_incident_response.py @@ -0,0 +1,235 @@ +#!/usr/bin/env python3 +"""Validate the protocol incident-response runbook.""" + +from __future__ import annotations + +import argparse +import re +import sys +from pathlib import Path + + +DEFAULT_INCIDENT_RESPONSE = Path("docs/incident-response.md") + +REQUIRED_HEADINGS = [ + (1, "Protocol Incident Response"), + (2, "Maturity And Scope"), + (2, "Roles And Severity"), + (2, "Universal Triage"), + (2, "Evidence Retention And Communications"), + (2, "Runbook: Stuck Auctions Or Settlement"), + (2, "Runbook: Failed Or Stale Randomness"), + (2, "Runbook: Bad Merkle Roots Or Curator Claims"), + (2, "Runbook: Bad Metadata Or Dependency Configuration"), + (2, "Runbook: Signer Compromise Or Drop Authorization"), + (2, "Runbook: Release Artifact Or Evidence Mistake"), + (2, "Reopening And Post-Incident Review"), + (2, "Local Verification Commands"), + (2, "Maintenance"), +] + +REQUIRED_MATURITY_PHRASES = [ + "pre-audit", + "not production-ready", + "not a security claim", + "no-secret", + "private reporting", +] + +REQUIRED_INCIDENT_PHRASES = [ + "stuck auctions", + "failed randomness", + "stale randomness", + "bad Merkle roots", + "bad metadata", + "dependency configuration", + "signer compromise", + "release artifact", + "emergency pause", + "withdrawal availability", + "signer revocation", + "retry/recovery", + "evidence retention", + "post-incident review", +] + +REQUIRED_COMMANDS = [ + "python scripts/test_incident_response.py", + "python scripts/check_incident_response.py", + "python scripts/generate_release_manifest.py --check", + "python scripts/generate_release_checksums.py --check", + "make check", + "powershell -ExecutionPolicy Bypass -File scripts\\check.ps1", +] + +REQUIRED_LINK_TARGETS = [ + "SECURITY.md", + "ops/ROADMAP.md", + "docs/release-readiness.md", + "docs/non-local-release-evidence.md", + "docs/public-beta-evidence.md", + "docs/tooling.md", + "docs/randomizer-operations.md", + "docs/dependency-operations.md", + "docs/auction-custody.md", + "docs/metadata.md", + "docs/deployment.md", + "docs/release-policy.md", + "docs/adr/0005-randomness.md", + "docs/adr/0006-metadata-freeze.md", + "docs/adr/0007-upgrade-redeployment.md", + "release-artifacts/latest/public-beta-evidence.json", +] + +HEADING_RE = re.compile(r"^(#{1,6})\s+(.+?)\s*$", re.MULTILINE) +LINK_RE = re.compile(r"\[[^\]]+\]\(([^)\s]+)(?:\s+\"[^\"]*\")?\)") + + +class IncidentResponseError(ValueError): + """Raised when the incident-response runbook is incomplete.""" + + +def normalize_repo_path(path: Path, repo_root: Path) -> str: + """Return a repository-relative POSIX path or reject path escapes.""" + try: + return path.resolve().relative_to(repo_root.resolve()).as_posix() + except ValueError as exc: + raise IncidentResponseError(f"linked path escapes repository: {path}") from exc + + +def markdown_headings(text: str) -> set[tuple[int, str]]: + """Extract Markdown headings as level/title pairs.""" + headings = set() + for match in HEADING_RE.finditer(text): + level = len(match.group(1)) + title = match.group(2).strip().rstrip("#").strip() + headings.add((level, title)) + return headings + + +def normalized_link_target(raw_target: str) -> str | None: + """Return a local Markdown link path without anchors or query strings.""" + target = raw_target.strip() + if not target or target.startswith("#"): + return None + if "://" in target or target.startswith("mailto:"): + return None + + path_part = target.split("#", 1)[0].split("?", 1)[0] + if not path_part: + return None + return path_part + + +def linked_repo_paths(repo_root: Path, document_path: Path, text: str) -> set[str]: + """Collect existing repository-relative file links from Markdown text.""" + links = set() + missing = [] + for match in LINK_RE.finditer(text): + target = normalized_link_target(match.group(1)) + if target is None: + continue + + target_path = Path(target) + if not target_path.is_absolute(): + target_path = document_path.parent / target_path + + resolved = target_path.resolve() + relative = normalize_repo_path(resolved, repo_root) + if not resolved.exists(): + missing.append(relative) + continue + links.add(relative) + + if missing: + raise IncidentResponseError( + "linked targets are missing: " + ", ".join(sorted(set(missing))) + ) + return links + + +def missing_phrases(text: str, phrases: list[str]) -> list[str]: + """Return required phrases that are absent from text, case-insensitively.""" + normalized_text = text.lower() + return [phrase for phrase in phrases if phrase.lower() not in normalized_text] + + +def validate_incident_response(repo_root: Path, document_path: Path) -> None: + """Validate the incident-response runbook against required content.""" + if not document_path.is_file(): + relative = normalize_repo_path(document_path, repo_root) + raise IncidentResponseError(f"missing document: {relative}") + + text = document_path.read_text(encoding="utf-8") + + headings = markdown_headings(text) + missing_headings = [ + f"{'#' * level} {title}" + for level, title in REQUIRED_HEADINGS + if (level, title) not in headings + ] + if missing_headings: + relative = normalize_repo_path(document_path, repo_root) + raise IncidentResponseError( + f"{relative} is missing required headings: " + + ", ".join(missing_headings) + ) + + missing_maturity = missing_phrases(text, REQUIRED_MATURITY_PHRASES) + if missing_maturity: + raise IncidentResponseError( + "incident-response runbook is missing required maturity language: " + + ", ".join(missing_maturity) + ) + + missing_incidents = missing_phrases(text, REQUIRED_INCIDENT_PHRASES) + if missing_incidents: + raise IncidentResponseError( + "incident-response runbook is missing required incident content: " + + ", ".join(missing_incidents) + ) + + missing_commands = [command for command in REQUIRED_COMMANDS if command not in text] + if missing_commands: + raise IncidentResponseError( + "incident-response runbook is missing required commands: " + + ", ".join(missing_commands) + ) + + links = linked_repo_paths(repo_root, document_path, text) + missing_targets = [target for target in REQUIRED_LINK_TARGETS if target not in links] + if missing_targets: + raise IncidentResponseError( + "incident-response runbook is missing required links: " + + ", ".join(missing_targets) + ) + + +def parse_args(argv: list[str]) -> argparse.Namespace: + """Parse incident-response checker command-line options.""" + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--repo-root", type=Path, default=Path.cwd()) + parser.add_argument("--incident-response", type=Path, default=DEFAULT_INCIDENT_RESPONSE) + return parser.parse_args(argv) + + +def main(argv: list[str] | None = None) -> int: + """Run the incident-response checker CLI.""" + args = parse_args(argv or []) + repo_root = args.repo_root.resolve() + incident_response_path = args.incident_response + if not incident_response_path.is_absolute(): + incident_response_path = repo_root / incident_response_path + + try: + validate_incident_response(repo_root, incident_response_path.resolve()) + except IncidentResponseError as exc: + print(f"incident-response check failed: {exc}", file=sys.stderr) + return 1 + + print("incident-response runbook is current") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv[1:])) diff --git a/scripts/check_release_readiness.py b/scripts/check_release_readiness.py index f14e111f..80fb5644 100644 --- a/scripts/check_release_readiness.py +++ b/scripts/check_release_readiness.py @@ -48,6 +48,7 @@ "release-signature evidence", "public-beta evidence status", "non-local release evidence", + "incident response", "Slither baseline", "test matrix", "ADR index", @@ -56,6 +57,8 @@ REQUIRED_COMMANDS = [ "python scripts/test_release_readiness.py", "python scripts/check_release_readiness.py", + "python scripts/test_incident_response.py", + "python scripts/check_incident_response.py", "python scripts/test_public_beta_evidence.py", "python scripts/check_public_beta_evidence.py", "python scripts/generate_release_manifest.py --check", @@ -76,6 +79,7 @@ "docs/status.md", "docs/known-blockers.md", "docs/audit-package.md", + "docs/incident-response.md", "docs/architecture.md", "docs/threat-model.md", "docs/deployment.md", diff --git a/scripts/generate_release_manifest.py b/scripts/generate_release_manifest.py index 573dae42..84aac7a4 100644 --- a/scripts/generate_release_manifest.py +++ b/scripts/generate_release_manifest.py @@ -48,6 +48,7 @@ Path("docs/architecture.md"), Path("docs/threat-model.md"), Path("docs/audit-package.md"), + Path("docs/incident-response.md"), Path("docs/release-readiness.md"), Path("docs/tooling.md"), Path("docs/status.md"), diff --git a/scripts/test_incident_response.py b/scripts/test_incident_response.py new file mode 100644 index 00000000..25d296e1 --- /dev/null +++ b/scripts/test_incident_response.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python3 +"""Focused tests for the incident-response checker.""" + +from __future__ import annotations + +import importlib.util +import tempfile +import unittest +from contextlib import redirect_stderr, redirect_stdout +from io import StringIO +from pathlib import Path + + +SCRIPT_PATH = Path(__file__).with_name("check_incident_response.py") +SPEC = importlib.util.spec_from_file_location("check_incident_response", SCRIPT_PATH) +assert SPEC is not None and SPEC.loader is not None +checker = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(checker) + + +def write_text(path: Path, value: str) -> None: + """Write UTF-8 text while creating parent directories.""" + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(value, encoding="utf-8", newline="\n") + + +def seed_required_targets(root: Path) -> None: + """Create placeholder files for every required runbook link target.""" + for relative in checker.REQUIRED_LINK_TARGETS: + write_text(root / relative, f"seed for {relative}\n") + + +def target_links(prefix: str = "../") -> str: + """Render Markdown list items for all required link targets.""" + return "\n".join( + f"- [{target}]({prefix}{target})" for target in checker.REQUIRED_LINK_TARGETS + ) + + +def minimal_incident_response_doc(link_prefix: str = "../") -> str: + """Build the smallest incident-response runbook accepted by the checker.""" + commands = "\n".join(checker.REQUIRED_COMMANDS) + links = target_links(link_prefix) + return f"""# Protocol Incident Response + +This pre-audit runbook is not production-ready, not a security claim, and uses +no-secret evidence retention plus private reporting. + +## Maturity And Scope + +It covers stuck auctions, failed randomness, stale randomness, bad Merkle roots, +bad metadata, dependency configuration, signer compromise, and release artifact +or evidence mistakes. + +## Roles And Severity + +Incident lead, protocol maintainer, operations maintainer, communications owner, +and reviewer roles are assigned. + +## Universal Triage + +Universal triage considers emergency pause, withdrawal availability, signer revocation, +retry/recovery, evidence retention, and post-incident review. + +## Evidence Retention And Communications + +Public-safe artifacts are retained without secrets. + +## Runbook: Stuck Auctions Or Settlement + +Auction custody, settlement, credits, and withdrawals are checked. + +## Runbook: Failed Or Stale Randomness + +Randomizer request, callback, provider epoch, stale, failed, and retry states +are checked. + +## Runbook: Bad Merkle Roots Or Curator Claims + +Merkle root, root epoch, leaf encoding, and double claims are checked. + +## Runbook: Bad Metadata Or Dependency Configuration + +Metadata, dependency source, freeze, and repinning mistakes are checked. + +## Runbook: Signer Compromise Or Drop Authorization + +Signer epoch, payload, domain, cancellation, and rotation mistakes are checked. + +## Runbook: Release Artifact Or Evidence Mistake + +Manifest, checksum, address-book, evidence, and release notes mistakes are +checked. + +## Reopening And Post-Incident Review + +Reopening requires a reviewed recovery decision. + +## Local Verification Commands + +```sh +{commands} +``` + +## Maintenance + +{links} +""" + + +class IncidentResponseTests(unittest.TestCase): + def test_accepts_committed_doc(self) -> None: + """The committed incident-response runbook satisfies the checker.""" + repo_root = Path(__file__).resolve().parents[1] + + with redirect_stdout(StringIO()), redirect_stderr(StringIO()): + result = checker.main(["--repo-root", str(repo_root)]) + + self.assertEqual(result, 0) + + def test_accepts_minimal_valid_doc(self) -> None: + """A minimal complete incident-response runbook passes validation.""" + with tempfile.TemporaryDirectory() as temp_dir: + root = Path(temp_dir) + seed_required_targets(root) + write_text( + root / checker.DEFAULT_INCIDENT_RESPONSE, + minimal_incident_response_doc(), + ) + + with redirect_stdout(StringIO()), redirect_stderr(StringIO()): + result = checker.main(["--repo-root", str(root)]) + + self.assertEqual(result, 0) + + def test_accepts_custom_incident_response_path(self) -> None: + """The CLI accepts a non-default runbook path.""" + with tempfile.TemporaryDirectory() as temp_dir: + root = Path(temp_dir) + seed_required_targets(root) + custom_path = Path("incident-response.md") + write_text(root / custom_path, minimal_incident_response_doc("")) + + with redirect_stdout(StringIO()), redirect_stderr(StringIO()): + result = checker.main( + [ + "--repo-root", + str(root), + "--incident-response", + str(custom_path), + ] + ) + + self.assertEqual(result, 0) + + def test_rejects_missing_heading(self) -> None: + """Missing required headings are rejected.""" + with tempfile.TemporaryDirectory() as temp_dir: + root = Path(temp_dir) + seed_required_targets(root) + text = minimal_incident_response_doc().replace( + "## Runbook: Failed Or Stale Randomness\n", "" + ) + write_text(root / checker.DEFAULT_INCIDENT_RESPONSE, text) + + with self.assertRaisesRegex( + checker.IncidentResponseError, "missing required headings" + ): + checker.validate_incident_response( + root, root / checker.DEFAULT_INCIDENT_RESPONSE + ) + + def test_rejects_missing_maturity_language(self) -> None: + """Missing maturity warnings are rejected.""" + with tempfile.TemporaryDirectory() as temp_dir: + root = Path(temp_dir) + seed_required_targets(root) + text = minimal_incident_response_doc().replace( + "not production-ready", "ready" + ) + write_text(root / checker.DEFAULT_INCIDENT_RESPONSE, text) + + with self.assertRaisesRegex( + checker.IncidentResponseError, "missing required maturity language" + ): + checker.validate_incident_response( + root, root / checker.DEFAULT_INCIDENT_RESPONSE + ) + + def test_rejects_missing_incident_phrase(self) -> None: + """Missing required incident themes are rejected.""" + with tempfile.TemporaryDirectory() as temp_dir: + root = Path(temp_dir) + seed_required_targets(root) + text = minimal_incident_response_doc().replace( + "retry/recovery", "retry path" + ) + write_text(root / checker.DEFAULT_INCIDENT_RESPONSE, text) + + with self.assertRaisesRegex( + checker.IncidentResponseError, "missing required incident content" + ): + checker.validate_incident_response( + root, root / checker.DEFAULT_INCIDENT_RESPONSE + ) + + def test_rejects_missing_command(self) -> None: + """Missing required commands are rejected.""" + with tempfile.TemporaryDirectory() as temp_dir: + root = Path(temp_dir) + seed_required_targets(root) + text = minimal_incident_response_doc().replace( + "python scripts/check_incident_response.py\n", "" + ) + write_text(root / checker.DEFAULT_INCIDENT_RESPONSE, text) + + with self.assertRaisesRegex( + checker.IncidentResponseError, "missing required commands" + ): + checker.validate_incident_response( + root, root / checker.DEFAULT_INCIDENT_RESPONSE + ) + + def test_rejects_missing_required_link(self) -> None: + """Missing required links are rejected.""" + with tempfile.TemporaryDirectory() as temp_dir: + root = Path(temp_dir) + seed_required_targets(root) + text = minimal_incident_response_doc().replace( + "- [SECURITY.md](../SECURITY.md)\n", "" + ) + write_text(root / checker.DEFAULT_INCIDENT_RESPONSE, text) + + with self.assertRaisesRegex( + checker.IncidentResponseError, "missing required links" + ): + checker.validate_incident_response( + root, root / checker.DEFAULT_INCIDENT_RESPONSE + ) + + def test_rejects_missing_linked_file(self) -> None: + """Links to missing files are rejected.""" + with tempfile.TemporaryDirectory() as temp_dir: + root = Path(temp_dir) + seed_required_targets(root) + (root / "SECURITY.md").unlink() + write_text( + root / checker.DEFAULT_INCIDENT_RESPONSE, + minimal_incident_response_doc(), + ) + + with self.assertRaisesRegex( + checker.IncidentResponseError, "linked targets are missing" + ): + checker.validate_incident_response( + root, root / checker.DEFAULT_INCIDENT_RESPONSE + ) + + def test_rejects_link_escape(self) -> None: + """Links that escape the repository are rejected.""" + with tempfile.TemporaryDirectory() as temp_dir: + root = Path(temp_dir) + seed_required_targets(root) + text = minimal_incident_response_doc() + text += "\n- [escape](../../outside.md)\n" + write_text(root / checker.DEFAULT_INCIDENT_RESPONSE, text) + + with self.assertRaisesRegex( + checker.IncidentResponseError, "linked path escapes repository" + ): + checker.validate_incident_response( + root, root / checker.DEFAULT_INCIDENT_RESPONSE + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/scripts/test_release_manifest.py b/scripts/test_release_manifest.py index 5eddf81b..9a28fd8b 100644 --- a/scripts/test_release_manifest.py +++ b/scripts/test_release_manifest.py @@ -71,6 +71,7 @@ def seed_release_tree(root: Path) -> dict[str, Path]: root / "docs" / "architecture.md", root / "docs" / "threat-model.md", root / "docs" / "audit-package.md", + root / "docs" / "incident-response.md", root / "docs" / "release-readiness.md", ] diff --git a/scripts/test_release_readiness.py b/scripts/test_release_readiness.py index 05d413d3..863a5060 100644 --- a/scripts/test_release_readiness.py +++ b/scripts/test_release_readiness.py @@ -54,8 +54,8 @@ def minimal_release_readiness_doc() -> str: Release manifest, checksum bundle, source verification inputs, ceremony evidence, randomizer operations evidence, release-signature evidence, Slither baseline, -public-beta evidence status, non-local release evidence, test matrix, and ADR index -evidence are summarized. +public-beta evidence status, non-local release evidence, incident response, +test matrix, and ADR index evidence are summarized. ## Local Evidence Already Passing From 08466151647bed25277feb454191f88d00609da7 Mon Sep 17 00:00:00 2001 From: Punk 6529 <108035228+punk6529@users.noreply.github.com> Date: Fri, 12 Jun 2026 21:42:20 +0000 Subject: [PATCH 2/4] Record incident response PR state --- ops/AUTONOMOUS_RUN.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/ops/AUTONOMOUS_RUN.md b/ops/AUTONOMOUS_RUN.md index 0fcfdbf1..e2a54a35 100644 --- a/ops/AUTONOMOUS_RUN.md +++ b/ops/AUTONOMOUS_RUN.md @@ -35,10 +35,10 @@ tests, security hardening, deployment discipline, and release/audit readiness. | Active PR branch | `codex/protocol-incident-response-runbooks` | | Last merged PR | `https://github.com/6529-Collections/6529Stream/pull/174` | | Active issue | `https://github.com/6529-Collections/6529Stream/issues/173` | -| Active PR | `TBD` | +| Active PR | `https://github.com/6529-Collections/6529Stream/pull/175` | | Roadmap file | `ops/ROADMAP.md` | | State file | `ops/AUTONOMOUS_RUN.md` | -| Last updated | `2026-06-12 21:38 UTC` | +| Last updated | `2026-06-12 21:41 UTC` | ## Packaging Notes @@ -145,18 +145,20 @@ The queue will evolve as PRs merge and bot feedback arrives. | 87 | Add non-local release evidence intake runbook | Gate E/Gate G support | Document the operator workflow for retaining fork/testnet/live deployment, metadata-browser, ceremony, randomizer, verification, address-book, gas, invariant, audit, and signed-release evidence without secrets, then wire the docs into readiness/public-beta evidence maintenance | Merged in PR #169 | | 88 | Add non-local release evidence metadata schema and checker | Gate E/Gate G support | Add a no-secret schema, template/example, checker, and tests for reviewed non-local evidence metadata so future operators can produce machine-checkable artifacts without claiming external readiness | Merged in PR #171 | | 89 | Reconcile Gate G roadmap after non-local evidence schema merge | Gate G support | Implement issue #172 by marking PR #171 merged, refreshing stale roadmap verification metadata, recording CI and CodeRabbit evidence, and preserving the next queue target | Merged in PR #174 | -| 90 | Add protocol incident response runbooks | Gate E/Gate G support | Implement issue #173 by adding no-secret operator runbooks for stuck auctions, failed or stale randomness, bad Merkle roots, bad metadata/dependency configuration, signer compromise, and release artifact/evidence mistakes | Implemented locally; PR not opened yet | +| 90 | Add protocol incident response runbooks | Gate E/Gate G support | Implement issue #173 by adding no-secret operator runbooks for stuck auctions, failed or stale randomness, bad Merkle roots, bad metadata/dependency configuration, signer compromise, and release artifact/evidence mistakes | PR #175 open; waiting for CI and CodeRabbit | ## Current PR Worklog ### PR candidate: Add protocol incident response runbooks (Queue Item 90) -Status: implemented locally; PR not opened yet. +Status: PR #175 open; waiting for CI and CodeRabbit. Issue: `https://github.com/6529-Collections/6529Stream/issues/173`. -PR: `TBD`. +PR: `https://github.com/6529-Collections/6529Stream/pull/175`. +CodeRabbit request: issue comment `4695671204`. Branch: `codex/protocol-incident-response-runbooks`. Branch started from PR #174 squash merge commit `074ac3eb510ccafa593812677e6c26cbed4171b1`. +Current head: `0a0a49be0ab2adc3b1141389a52d1e8523865945`. Goal: @@ -215,6 +217,11 @@ Validation target: - `git diff --check` - `make check` +Remote validation: + +- GitHub Actions CI pending on PR #175. +- CodeRabbit review requested in issue comment `4695671204`; review pending. + Implementation notes: - Added `docs/incident-response.md` as a no-secret operator runbook for @@ -8281,6 +8288,7 @@ Outcome: | Time UTC | Decision | Rationale | | --- | --- | --- | +| 2026-06-12 21:41 | Open PR #175 and request CodeRabbit | Incident-response runbook PR opened against `main`, linked `Closes #173`, pushed head `0a0a49be0ab2adc3b1141389a52d1e8523865945`, requested CodeRabbit in comment `4695671204`, and intentionally skipped Claude per current user instruction | | 2026-06-12 21:38 | Prepare Queue Item 90 for PR | Incident-response runbook, docs checker, CI/wrapper wiring, release manifest/checksum refresh, docs links, roadmap/changelog updates, focused checks, `git diff --check`, and `make check` all pass locally | | 2026-06-12 21:13 | Start Queue Item 90 | PR #174 merged, issue #172 closed completed, and issue #173 is the next active no-secret Gate E/G docs and operations slice | | 2026-06-12 21:12 | Merge PR #174 | Non-local evidence schema merge reconciliation merged as `074ac3eb510ccafa593812677e6c26cbed4171b1`; final head `55b7dc716c5bfdd9e003d5b068f24ba7dfb5eddd` passed CI run `27442981046`, CodeRabbit status was success with no actionable comments, and issue #172 closed completed | From db49a0e73f6a840cdea8b59876cf27b8af34a2ad Mon Sep 17 00:00:00 2001 From: Punk 6529 <108035228+punk6529@users.noreply.github.com> Date: Fri, 12 Jun 2026 21:52:26 +0000 Subject: [PATCH 3/4] Address incident response review alignment --- docs/release-readiness.md | 2 +- ops/AUTONOMOUS_RUN.md | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/release-readiness.md b/docs/release-readiness.md index 0d3fe349..09721af5 100644 --- a/docs/release-readiness.md +++ b/docs/release-readiness.md @@ -35,7 +35,7 @@ local tests prove protocol correctness. | Area | Current state | Blocks public beta | Blocks production release | | --- | --- | --- | --- | -| CI and local gates | Passing local/CI baseline exists for build, tests, size, local deployment rehearsals, release artifacts, architecture/threat model, audit package, release manifest, checksums, and changelog | No | No, but release commit CI must be green | +| CI and local gates | Passing local/CI baseline exists for build, tests, size, local deployment rehearsals, incident response, release artifacts, architecture/threat model, audit package, release manifest, checksums, and changelog | No | No, but release commit CI must be green | | Protocol maturity | Pre-audit, not production-ready, local baseline only | Yes | Yes | | External audit | Audit package exists; completed external audit report and post-audit remediation do not exist | Yes | Yes | | Deployment evidence | Local Anvil deployment, auction, metadata-browser, and emergency redeployment rehearsals exist | Fork/testnet/live evidence missing | Production broadcast retention, verified deployed addresses, and explorer verification missing | diff --git a/ops/AUTONOMOUS_RUN.md b/ops/AUTONOMOUS_RUN.md index e2a54a35..74b5ccb6 100644 --- a/ops/AUTONOMOUS_RUN.md +++ b/ops/AUTONOMOUS_RUN.md @@ -158,13 +158,15 @@ CodeRabbit request: issue comment `4695671204`. Branch: `codex/protocol-incident-response-runbooks`. Branch started from PR #174 squash merge commit `074ac3eb510ccafa593812677e6c26cbed4171b1`. -Current head: `0a0a49be0ab2adc3b1141389a52d1e8523865945`. +Head before CodeRabbit follow-up: +`08466151647bed25277feb454191f88d00609da7`. Goal: - Add a no-secret protocol incident-response runbook for stuck auctions, - failed or stale randomness, bad Merkle roots, bad metadata/dependency - configuration, signer compromise, and release artifact/evidence mistakes. + failed or stale randomness, bad Merkle roots, curator claims, bad + metadata/dependency configuration, signer compromise, drop-pause decisions, + and release artifact/evidence mistakes from issue #173. - Tie incident procedures to existing pause, signer, randomizer, auction, dependency, release-readiness, and evidence-retention docs. - Add a lightweight docs checker/test only if it matches the repository's From 574804b6421c5658001839d483dd5a24dcbb2ad8 Mon Sep 17 00:00:00 2001 From: Punk 6529 <108035228+punk6529@users.noreply.github.com> Date: Fri, 12 Jun 2026 21:58:14 +0000 Subject: [PATCH 4/4] Refresh incident response release artifacts --- release-artifacts/latest/SHA256SUMS | 2 +- release-artifacts/latest/release-checksums.json | 4 ++-- release-artifacts/latest/release-manifest.json | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/release-artifacts/latest/SHA256SUMS b/release-artifacts/latest/SHA256SUMS index 823aeda0..c33f4529 100644 --- a/release-artifacts/latest/SHA256SUMS +++ b/release-artifacts/latest/SHA256SUMS @@ -25,7 +25,7 @@ e072f786d66e7052ff1d63f1c5e6a3fc9ef46c986bd2f0a1843626a5dc21cccc release-artifa 8a8a41d89757d3d16f0718638d1175290dcd00cfea86204665cd525f608d7c1a release-artifacts/latest/interface-ids.json 3b75cde36f75662061011af793d4c9240a1fec6447294ce0e3ee1d331d729b4d release-artifacts/latest/public-beta-evidence.json 9ff4283a72a51f7b770b8a217b9f7f8b5dde793af14532c80572186bc7acf6d5 release-artifacts/latest/release-artifact-manifest.json -b22e55b1f9c8f2a609c2e452125e63ba0d3e7ebc50b483fd23580b4cd41e62bb release-artifacts/latest/release-manifest.json +3737d89628abbd266410fbe28ed4bce49bfb43846fb3307394cc796c9c212c93 release-artifacts/latest/release-manifest.json 374fce4bc46746d61cbae487cf6cb549cb40b5db5a95951568ed4455669af33a release-artifacts/latest/source-verification-inputs.json 5a5aede0ab1b7bee194e495bdac98e0f37b814879be62a72ce819be07da82c1a release-artifacts/schema/non-local-release-evidence.schema.json cec024757bfe8967a27106ee8c4739c6f51ff8b0ec6a2fb564939441fd9ac9bb release-artifacts/schema/public-beta-evidence.schema.json diff --git a/release-artifacts/latest/release-checksums.json b/release-artifacts/latest/release-checksums.json index 588328ca..f49b54ce 100644 --- a/release-artifacts/latest/release-checksums.json +++ b/release-artifacts/latest/release-checksums.json @@ -24,7 +24,7 @@ "text_checksum_file": { "path": "release-artifacts/latest/SHA256SUMS", "format": "sha256sum", - "sha256": "sha256:cff62764130f41a0a461da8bb17f4665d7070fe0daae851358bc502bc8f1d4b9" + "sha256": "sha256:dbf366a453e139be6904f59435c7d7869f4c3d8e7c83044afa24437315c5c855" }, "manifest_file": { "path": "release-artifacts/latest/release-checksums.json", @@ -168,7 +168,7 @@ }, { "path": "release-artifacts/latest/release-manifest.json", - "sha256": "sha256:b22e55b1f9c8f2a609c2e452125e63ba0d3e7ebc50b483fd23580b4cd41e62bb", + "sha256": "sha256:3737d89628abbd266410fbe28ed4bce49bfb43846fb3307394cc796c9c212c93", "size_bytes": 23683 }, { diff --git a/release-artifacts/latest/release-manifest.json b/release-artifacts/latest/release-manifest.json index 7fea04dd..69c10bd7 100644 --- a/release-artifacts/latest/release-manifest.json +++ b/release-artifacts/latest/release-manifest.json @@ -528,8 +528,8 @@ }, { "path": "docs/release-readiness.md", - "sha256": "sha256:42f28629c10b1fe849d6dcf9e09c8883da0724f31e1eef9b9705e3675e767bd2", - "size_bytes": 12684 + "sha256": "sha256:f9d9b126a912e8674b43c422f51c958f0d8884f42a41d45b6e45ddedb9fb0137", + "size_bytes": 12703 }, { "path": "docs/tooling.md",