diff --git a/.github/workflows/anchor-liveness.yml b/.github/workflows/anchor-liveness.yml index 9ef7351..a49e4ef 100644 --- a/.github/workflows/anchor-liveness.yml +++ b/.github/workflows/anchor-liveness.yml @@ -4,24 +4,78 @@ on: schedule: - cron: "17 3 * * *" workflow_dispatch: + inputs: + mode: + description: Gate to run + required: true + default: public-monitor + type: choice + options: + - public-monitor + - exact-deployment + +permissions: + contents: read jobs: - prove-liveness: + public-anchor-monitor: + if: ${{ github.event_name == 'schedule' || inputs.mode == 'public-monitor' }} runs-on: ubuntu-latest env: ZAP1_API_BASE: https://api.frontiercompute.cash ZAP1_MAX_ANCHOR_AGE_HOURS: "72" - ZAP1_EXPECTED_DEPLOYMENT_IMAGE_ID: ${{ vars.ZAP1_EXPECTED_DEPLOYMENT_IMAGE_ID }} + # Anchoring is operator-authorized and currently paused. Structural drift + # still fails; age with pending work remains an explicit warning. + ZAP1_REQUIRE_FRESH_ANCHOR: 'false' steps: - name: Check out repo - uses: actions/checkout@v5 + uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5 - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 with: python-version: "3.12" - - name: Verify deployed source, anchor, and current proof + - name: Check public anchor consistency + run: python3 scripts/check_anchor_liveness.py + + public-api-contract: + if: ${{ github.event_name == 'schedule' || inputs.mode == 'public-monitor' }} + runs-on: ubuntu-latest + env: + ZAP1_API_BASE: https://api.frontiercompute.cash + ZAP1_MAX_SYNC_LAG_BLOCKS: '10' + ZAP1_REQUIRE_AUTHENTICATED_ADMIN_CHECKS: 'false' + ZAP1_REQUIRE_SOURCE_PARITY: 'false' + steps: + - name: Check out repo + uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5 + + - name: Set up Python + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 + with: + python-version: '3.12' + + - name: Check public API contract and privacy boundary + run: python3 conformance/check_api.py ${ZAP1_API_BASE} + + exact-deployment-gate: + if: ${{ github.event_name == 'workflow_dispatch' && inputs.mode == 'exact-deployment' }} + runs-on: ubuntu-latest + env: + ZAP1_API_BASE: https://api.frontiercompute.cash + ZAP1_MAX_ANCHOR_AGE_HOURS: '72' + ZAP1_EXPECTED_DEPLOYMENT_IMAGE_ID: ${{ vars.ZAP1_EXPECTED_DEPLOYMENT_IMAGE_ID }} + steps: + - name: Check out repo + uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5 + + - name: Set up Python + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 + with: + python-version: '3.12' + + - name: Verify exact deployed source, anchor, and current proof env: ZAP1_ADMIN_API_KEY: ${{ secrets.ZAP1_ADMIN_API_KEY }} run: bash scripts/check_live.sh diff --git a/STRUCTURAL_BUILDOUT.md b/STRUCTURAL_BUILDOUT.md index 4cb64c0..52b755c 100644 --- a/STRUCTURAL_BUILDOUT.md +++ b/STRUCTURAL_BUILDOUT.md @@ -47,7 +47,7 @@ cargo run --bin zip302_tvlv -- decode ## 3. Anchor liveness check -Nightly GitHub Actions check plus local script. +Nightly GitHub Actions checks plus a local script. Use: - check public anchor surfaces for freshness and internal consistency @@ -66,6 +66,18 @@ Workflow: - `.github/workflows/anchor-liveness.yml` +The scheduled `public-monitor` keeps three claims separate: + +- anchor structure and cross-surface consistency must remain valid; +- a stale anchor with pending work is reported as a warning while transaction + authority is paused; +- the public API contract and preimage-redaction boundary still fail closed. + +The manual `exact-deployment` mode is the only workflow path that reads the +operator-provided expected image ID and admin API secret. It remains strict on +source parity, authenticated admin behavior, anchor freshness, and the current +proof. A green public monitor is not a deployment attestation. + Files: - `src/bin/zap1_audit.rs` diff --git a/scripts/check_operational_controls.py b/scripts/check_operational_controls.py index e30f879..0f9e4ef 100644 --- a/scripts/check_operational_controls.py +++ b/scripts/check_operational_controls.py @@ -20,6 +20,7 @@ def require(condition, message): setup = read("scripts/operator-setup.sh") live = read("scripts/check_live.sh") checker = read("conformance/check_api.py") +anchor_workflow = read(".github/workflows/anchor-liveness.yml") require("Test address (index 0)" not in main_rs, "startup still logs a UFVK-derived address") require("test_addr" not in main_rs, "startup retains a derived-address logging handle") @@ -66,6 +67,34 @@ def require(condition, message): "API checker can silently skip a required admin path", ) +require( + "github.event_name == 'schedule' || inputs.mode == 'public-monitor'" + in anchor_workflow, + "scheduled public monitoring is not isolated from the exact deployment gate", +) +require( + "ZAP1_REQUIRE_FRESH_ANCHOR: 'false'" in anchor_workflow, + "paused anchor authority is not explicit in the public monitor", +) +require( + "python3 conformance/check_api.py ${ZAP1_API_BASE}" in anchor_workflow, + "scheduled monitoring omits the public API privacy contract", +) +require( + "github.event_name == 'workflow_dispatch' && inputs.mode == 'exact-deployment'" + in anchor_workflow, + "the secret-bearing exact deployment gate is not manual-only", +) +require( + anchor_workflow.count("bash scripts/check_live.sh") == 1 + and anchor_workflow.count("ZAP1_ADMIN_API_KEY: ${{ secrets.ZAP1_ADMIN_API_KEY }}") == 1, + "the exact deployment checker or its secret is duplicated across workflow paths", +) +require( + "permissions:\n contents: read" in anchor_workflow, + "anchor workflow token permissions are not explicitly read-only", +) + require(live.startswith("#!/usr/bin/env bash\nset +x\n"), "live checker does not disable xtrace") require( "unset ZAP1_ADMIN_API_KEY" in live @@ -80,4 +109,6 @@ def require(condition, message): "runtime logs can disclose configured RPC endpoints", ) -print("PASS: operational privacy, admin-auth, and anchor-send contracts") +print( + "PASS: operational privacy, admin-auth, anchor-send, and monitoring-separation contracts" +)