diff --git a/.bitcode/v34-deployment-host-capability-catalog.json b/.bitcode/v34-deployment-host-capability-catalog.json new file mode 100644 index 000000000..175bc152a --- /dev/null +++ b/.bitcode/v34-deployment-host-capability-catalog.json @@ -0,0 +1,429 @@ +{ + "artifactId": "v34-deployment-host-capability-catalog", + "catalogRoot": "v34-deployment-host-capability-catalog:bc3f604570a21cab6e5dd3c5", + "closureCommand": "pnpm run check:v34-gate2", + "coverage": { + "apiRepresented": true, + "credentialsSerialized": false, + "databaseProjectionRepresented": true, + "hostCount": 12, + "ledgerProjectionRepresented": true, + "missingHostIds": [], + "objectStorageRepresented": true, + "observedHostIds": [ + "website", + "api", + "mcp_api", + "chatgpt_app", + "pipeline_workers", + "runtime_observers", + "ledger_broadcasters", + "proof_services", + "repair_jobs", + "object_storage", + "database_projection", + "ledger_projection" + ], + "protectedSourceVisible": false, + "valueBearingMainnetHidden": false, + "websiteRepresented": true + }, + "currentTarget": "V33", + "generatedAt": "2026-05-22T00:00:00.000Z", + "passed": true, + "requiredHostIds": [ + "website", + "api", + "mcp_api", + "chatgpt_app", + "pipeline_workers", + "runtime_observers", + "ledger_broadcasters", + "proof_services", + "repair_jobs", + "object_storage", + "database_projection", + "ledger_projection" + ], + "rows": [ + { + "admissionStatus": "admitted_non_value_lanes", + "hostId": "website", + "outboundNetworkPosture": "provider_bound", + "ownerPackage": "uapi", + "proofOutputPaths": [ + ".bitcode/v34-deployment-host-capability-catalog.json" + ], + "rowRoot": "v34-deployment-host-capability-row:1db748d077a454764d255952", + "runtimeCarrier": "vercel-nextjs-website", + "runtimeSurface": "website", + "supportedLaneIds": [ + "local", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run" + ], + "telemetryProofHookId": "deployment.telemetry.website", + "validationCommand": "pnpm --dir uapi exec jest --runTestsByPath tests/terminalInterfaceIntegrationRegression.test.ts --runInBand" + }, + { + "admissionStatus": "admitted_non_value_lanes", + "hostId": "api", + "outboundNetworkPosture": "provider_bound", + "ownerPackage": "packages/api", + "proofOutputPaths": [ + ".bitcode/v34-environment-lane-contracts.json" + ], + "rowRoot": "v34-deployment-host-capability-row:9d2d45dfbbd9c02a3d5dac38", + "runtimeCarrier": "vercel-node-api", + "runtimeSurface": "api", + "supportedLaneIds": [ + "local", + "regtest", + "signet", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run" + ], + "telemetryProofHookId": "deployment.telemetry.api", + "validationCommand": "pnpm --filter @bitcode/api exec jest --config jest.config.cjs --runTestsByPath src/routes/__tests__/btd-crypto.test.ts --runInBand" + }, + { + "admissionStatus": "admitted_non_value_lanes", + "hostId": "mcp_api", + "outboundNetworkPosture": "outbound_restricted", + "ownerPackage": "packages/executions-mcp/src/mcp-server", + "proofOutputPaths": [ + ".bitcode/v33-mcp-api-tool-contracts.json" + ], + "rowRoot": "v34-deployment-host-capability-row:4e3aaa870ecc44d10b99cf5c", + "runtimeCarrier": "mcp-server-process", + "runtimeSurface": "mcp_api", + "supportedLaneIds": [ + "local", + "staging-testnet", + "public-testnet" + ], + "telemetryProofHookId": "deployment.telemetry.mcp-api", + "validationCommand": "pnpm --dir packages/executions-mcp/src/mcp-server run test:mcp -- --runTestsByPath src/__tests__/unit/pipeline-ingress-contract.test.ts --runInBand" + }, + { + "admissionStatus": "admitted_non_value_lanes", + "hostId": "chatgpt_app", + "outboundNetworkPosture": "outbound_restricted", + "ownerPackage": "packages/chatgptapp", + "proofOutputPaths": [ + ".bitcode/v33-chatgpt-app-action-contracts.json" + ], + "rowRoot": "v34-deployment-host-capability-row:5c473c71724fe715f27332bd", + "runtimeCarrier": "chatgpt-action-service", + "runtimeSurface": "chatgpt_app", + "supportedLaneIds": [ + "local", + "staging-testnet", + "public-testnet" + ], + "telemetryProofHookId": "deployment.telemetry.chatgpt-app", + "validationCommand": "pnpm --dir packages/chatgptapp exec jest --runTestsByPath src/__tests__/tools.test.ts --runInBand" + }, + { + "admissionStatus": "admitted_non_value_lanes", + "hostId": "pipeline_workers", + "outboundNetworkPosture": "provider_bound", + "ownerPackage": "packages/pipeline-hosts", + "proofOutputPaths": [ + ".bitcode/v34-distributed-execution-runtime-receipts.json" + ], + "rowRoot": "v34-deployment-host-capability-row:66cfb1d9d03e6a9d96f18a45", + "runtimeCarrier": "vercel-sandbox-worker", + "runtimeSurface": "worker", + "supportedLaneIds": [ + "local", + "regtest", + "signet", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run" + ], + "telemetryProofHookId": "deployment.telemetry.pipeline-worker", + "validationCommand": "pnpm --filter @bitcode/pipeline-hosts typecheck" + }, + { + "admissionStatus": "admitted_non_value_lanes", + "hostId": "runtime_observers", + "outboundNetworkPosture": "provider_bound", + "ownerPackage": "packages/btd", + "proofOutputPaths": [ + ".bitcode/v34-runtime-observers-broadcasters-repair-jobs.json" + ], + "rowRoot": "v34-deployment-host-capability-row:c532072cd51cd6bdef2065a8", + "runtimeCarrier": "scheduled-observer-job", + "runtimeSurface": "observer", + "supportedLaneIds": [ + "regtest", + "signet", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run" + ], + "telemetryProofHookId": "deployment.telemetry.runtime-observer", + "validationCommand": "pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/v32-testnet-mainnet-readiness-rehearsal.test.ts" + }, + { + "admissionStatus": "admitted_non_value_lanes", + "hostId": "ledger_broadcasters", + "outboundNetworkPosture": "provider_bound", + "ownerPackage": "packages/btd", + "proofOutputPaths": [ + ".bitcode/v34-runtime-observers-broadcasters-repair-jobs.json" + ], + "rowRoot": "v34-deployment-host-capability-row:61411ba66e535a92aa35f067", + "runtimeCarrier": "ledger-broadcaster-job", + "runtimeSurface": "broadcaster", + "supportedLaneIds": [ + "regtest", + "signet", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run" + ], + "telemetryProofHookId": "deployment.telemetry.ledger-broadcaster", + "validationCommand": "pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/btc-fee-operation.test.ts" + }, + { + "admissionStatus": "admitted_non_value_lanes", + "hostId": "proof_services", + "outboundNetworkPosture": "none", + "ownerPackage": "packages/protocol", + "proofOutputPaths": [ + ".bitcode/v34-promotion-readiness-report.json" + ], + "rowRoot": "v34-deployment-host-capability-row:b50fdc82ab49c004369b97db", + "runtimeCarrier": "proof-generation-job", + "runtimeSurface": "proof_service", + "supportedLaneIds": [ + "local", + "regtest", + "signet", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run" + ], + "telemetryProofHookId": "deployment.telemetry.proof-service", + "validationCommand": "pnpm --filter @bitcode/protocol test" + }, + { + "admissionStatus": "admitted_non_value_lanes", + "hostId": "repair_jobs", + "outboundNetworkPosture": "provider_bound", + "ownerPackage": "packages/btd", + "proofOutputPaths": [ + ".bitcode/v34-rollback-upgrade-data-repair-playbooks.json" + ], + "rowRoot": "v34-deployment-host-capability-row:9a9499c1a6720c5eb51a8c04", + "runtimeCarrier": "operator-repair-command", + "runtimeSurface": "repair_job", + "supportedLaneIds": [ + "local", + "regtest", + "signet", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run" + ], + "telemetryProofHookId": "deployment.telemetry.repair-job", + "validationCommand": "pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/reconciliation.test.ts" + }, + { + "admissionStatus": "admitted_projection_carrier", + "hostId": "object_storage", + "outboundNetworkPosture": "egress_locked", + "ownerPackage": "packages/pipeline-hosts", + "proofOutputPaths": [ + ".bitcode/v34-deployment-storage-posture.json" + ], + "rowRoot": "v34-deployment-host-capability-row:c8bf4a2dfd0049b19cd718a1", + "runtimeCarrier": "durable-object-storage", + "runtimeSurface": "object_storage", + "supportedLaneIds": [ + "local", + "regtest", + "signet", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run" + ], + "telemetryProofHookId": "deployment.telemetry.object-storage", + "validationCommand": "pnpm --filter @bitcode/pipeline-hosts typecheck" + }, + { + "admissionStatus": "admitted_projection_carrier", + "hostId": "database_projection", + "outboundNetworkPosture": "provider_bound", + "ownerPackage": "packages/supabase", + "proofOutputPaths": [ + ".bitcode/v34-deployment-storage-posture.json" + ], + "rowRoot": "v34-deployment-host-capability-row:a181d5634100336931b99ca4", + "runtimeCarrier": "supabase-postgres-projection", + "runtimeSurface": "database_projection", + "supportedLaneIds": [ + "local", + "regtest", + "signet", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run" + ], + "telemetryProofHookId": "deployment.telemetry.database-projection", + "validationCommand": "pnpm --filter @bitcode/btd typecheck" + }, + { + "admissionStatus": "admitted_projection_carrier", + "hostId": "ledger_projection", + "outboundNetworkPosture": "provider_bound", + "ownerPackage": "packages/btd", + "proofOutputPaths": [ + ".bitcode/v34-deployment-storage-posture.json" + ], + "rowRoot": "v34-deployment-host-capability-row:55997b32709c616360c422bd", + "runtimeCarrier": "ledger-projection-store", + "runtimeSurface": "ledger_projection", + "supportedLaneIds": [ + "regtest", + "signet", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run" + ], + "telemetryProofHookId": "deployment.telemetry.ledger-projection", + "validationCommand": "pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/reconciliation.test.ts" + } + ], + "schemaId": "bitcode.v34.deploymentHostCapabilityCatalog.v1", + "sharedFixtureFiles": [ + "packages/btd/src/deployment-host-capability-catalog.ts", + "packages/btd/src/index.ts", + "BITCODE_SPEC_V34.md", + "BITCODE_SPEC_V34_DELTA.md", + "BITCODE_SPEC_V34_PARITY_MATRIX.md", + "packages/btd/__tests__/deployment-host-capability-catalog.test.ts", + "scripts/check-v34-gate2-host-capability-environment-lanes.mjs" + ], + "sourceEvidence": [ + { + "digest": "sha256:b4e7d2ef3b7ee2ce09922527aa88a5796ae4093c1bd77ff926f822bf13038a5a", + "relativePath": "packages/btd/src/deployment-host-capability-catalog.ts", + "requiredTokens": [ + { + "present": true, + "token": "DeploymentHostCapabilityCatalog" + }, + { + "present": true, + "token": "EnvironmentLaneContract" + }, + { + "present": true, + "token": "value-bearing-mainnet" + }, + { + "present": true, + "token": "blocked_future_canon_required" + }, + { + "present": true, + "token": "pipeline_workers" + }, + { + "present": true, + "token": "object_storage" + }, + { + "present": true, + "token": "database_projection" + }, + { + "present": true, + "token": "ledger_projection" + } + ] + }, + { + "digest": "sha256:6090906d79ae35e86d0548ca67e0f3c75300dd24d21574bb5b407174ba1ed0c2", + "relativePath": "packages/btd/src/index.ts", + "requiredTokens": [ + { + "present": true, + "token": "deployment-host-capability-catalog" + } + ] + }, + { + "digest": "sha256:fa2d5f781b1c7ac6d5466972471406ebedc998b25384a9edfcc68954e8cf3125", + "relativePath": "BITCODE_SPEC_V34.md", + "requiredTokens": [ + { + "present": true, + "token": ".bitcode/v34-deployment-host-capability-catalog.json" + }, + { + "present": true, + "token": ".bitcode/v34-environment-lane-contracts.json" + }, + { + "present": true, + "token": "DeploymentHostCapabilityCatalog" + }, + { + "present": true, + "token": "EnvironmentLaneContract" + } + ] + } + ], + "sourceSafetyVerdict": "source-safe-deployment-host-capability-metadata", + "testEvidence": [ + { + "digest": "sha256:3f620773822bfd97dde2617fd166318991d1d454bf624c17baff96b0d2bc3532", + "relativePath": "packages/btd/__tests__/deployment-host-capability-catalog.test.ts", + "requiredTokens": [ + { + "present": true, + "token": "catalogs website, API, MCP API, ChatGPT App, workers, observers, broadcasters, proof services, repair jobs, and storage projections" + }, + { + "present": true, + "token": "keeps value-bearing mainnet visible as blocked and without admitted runtime hosts" + }, + { + "present": true, + "token": "fails closed when a required deployment host row is missing" + }, + { + "present": true, + "token": "fails closed on secret-shaped or non-disclosable source catalog text" + } + ] + }, + { + "digest": "sha256:da4dbd254c1804bb44eb3bd6e32d37f51715bf77bdacfe57c7af4ec8d1bcd8ff", + "relativePath": "scripts/check-v34-gate2-host-capability-environment-lanes.mjs", + "requiredTokens": [ + { + "present": true, + "token": "check:v34-host-capability-environment-lanes" + }, + { + "present": true, + "token": "deployment-host-capability-catalog.test.ts" + }, + { + "present": true, + "token": "Host Capability And Environment Lane Catalog" + } + ] + } + ], + "version": "V34" +} diff --git a/.bitcode/v34-environment-lane-contracts.json b/.bitcode/v34-environment-lane-contracts.json new file mode 100644 index 000000000..f5d2d983a --- /dev/null +++ b/.bitcode/v34-environment-lane-contracts.json @@ -0,0 +1,275 @@ +{ + "artifactId": "v34-environment-lane-contracts", + "closureCommand": "pnpm run check:v34-gate2", + "coverage": { + "credentialsSerialized": false, + "laneCount": 7, + "mainnetReadyDryRunAdmission": "dry_run_only", + "missingLaneIds": [], + "observedLaneIds": [ + "local", + "regtest", + "signet", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run", + "value-bearing-mainnet" + ], + "protectedSourceVisible": false, + "valueBearingMainnetAdmission": "blocked_future_canon_required", + "valueBearingMainnetAdmittedHostCount": 0, + "valueBearingMainnetHidden": false + }, + "currentTarget": "V33", + "generatedAt": "2026-05-22T00:00:00.000Z", + "laneContractRoot": "v34-environment-lane-contracts:4c4874984efa54e0eac959a7", + "lanes": [ + { + "admittedHostIds": [ + "website", + "api", + "mcp_api", + "chatgpt_app", + "pipeline_workers", + "proof_services", + "repair_jobs", + "object_storage", + "database_projection" + ], + "bitcoinNetworkPosture": "none", + "laneId": "local", + "laneRoot": "v34-environment-lane-contract:74296544dca9b29e341ea7c8", + "supabaseProjectPosture": "local_process", + "telemetryProofHookId": "deployment.telemetry.lane.local", + "valueBearingAdmission": "not_value_bearing", + "vercelProjectPosture": "local_process", + "walletPolicy": "no_wallet" + }, + { + "admittedHostIds": [ + "api", + "pipeline_workers", + "runtime_observers", + "ledger_broadcasters", + "proof_services", + "repair_jobs", + "object_storage", + "database_projection", + "ledger_projection" + ], + "bitcoinNetworkPosture": "regtest", + "laneId": "regtest", + "laneRoot": "v34-environment-lane-contract:7d91c2c69a9939b4e6aca64c", + "supabaseProjectPosture": "local_project", + "telemetryProofHookId": "deployment.telemetry.lane.regtest", + "valueBearingAdmission": "not_value_bearing", + "vercelProjectPosture": "local_project", + "walletPolicy": "regtest_wallet" + }, + { + "admittedHostIds": [ + "api", + "pipeline_workers", + "runtime_observers", + "ledger_broadcasters", + "proof_services", + "repair_jobs", + "object_storage", + "database_projection", + "ledger_projection" + ], + "bitcoinNetworkPosture": "signet", + "laneId": "signet", + "laneRoot": "v34-environment-lane-contract:e438d47a5e32d600ef07e9cb", + "supabaseProjectPosture": "staging_testnet_project", + "telemetryProofHookId": "deployment.telemetry.lane.signet", + "valueBearingAdmission": "not_value_bearing", + "vercelProjectPosture": "staging_testnet_project", + "walletPolicy": "signet_wallet" + }, + { + "admittedHostIds": [ + "website", + "api", + "mcp_api", + "chatgpt_app", + "pipeline_workers", + "runtime_observers", + "ledger_broadcasters", + "proof_services", + "repair_jobs", + "object_storage", + "database_projection", + "ledger_projection" + ], + "bitcoinNetworkPosture": "signet", + "laneId": "staging-testnet", + "laneRoot": "v34-environment-lane-contract:6653d398e7f72c5c09b73c25", + "supabaseProjectPosture": "staging_testnet_project", + "telemetryProofHookId": "deployment.telemetry.lane.staging-testnet", + "valueBearingAdmission": "not_value_bearing", + "vercelProjectPosture": "staging_testnet_project", + "walletPolicy": "signet_wallet" + }, + { + "admittedHostIds": [ + "website", + "api", + "mcp_api", + "chatgpt_app", + "pipeline_workers", + "runtime_observers", + "ledger_broadcasters", + "proof_services", + "repair_jobs", + "object_storage", + "database_projection", + "ledger_projection" + ], + "bitcoinNetworkPosture": "testnet", + "laneId": "public-testnet", + "laneRoot": "v34-environment-lane-contract:25ca918cccc58f2b320c2235", + "supabaseProjectPosture": "public_testnet_project", + "telemetryProofHookId": "deployment.telemetry.lane.public-testnet", + "valueBearingAdmission": "not_value_bearing", + "vercelProjectPosture": "public_testnet_project", + "walletPolicy": "testnet_wallet" + }, + { + "admittedHostIds": [ + "website", + "api", + "mcp_api", + "chatgpt_app", + "pipeline_workers", + "runtime_observers", + "proof_services", + "repair_jobs", + "object_storage", + "database_projection", + "ledger_projection" + ], + "bitcoinNetworkPosture": "mainnet", + "laneId": "mainnet-ready-dry-run", + "laneRoot": "v34-environment-lane-contract:097dee47502a17c5611d5c9c", + "supabaseProjectPosture": "production_project_dry_run", + "telemetryProofHookId": "deployment.telemetry.lane.mainnet-ready-dry-run", + "valueBearingAdmission": "dry_run_only", + "vercelProjectPosture": "production_project_dry_run", + "walletPolicy": "mainnet_watch_only" + }, + { + "admittedHostIds": [], + "bitcoinNetworkPosture": "mainnet", + "laneId": "value-bearing-mainnet", + "laneRoot": "v34-environment-lane-contract:c08b11a2a29dbe2e4c20a035", + "supabaseProjectPosture": "production_project_blocked", + "telemetryProofHookId": "deployment.telemetry.lane.value-bearing-mainnet", + "valueBearingAdmission": "blocked_future_canon_required", + "vercelProjectPosture": "production_project_blocked", + "walletPolicy": "mainnet_value_blocked" + } + ], + "passed": true, + "requiredLaneIds": [ + "local", + "regtest", + "signet", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run", + "value-bearing-mainnet" + ], + "schemaId": "bitcode.v34.environmentLaneContracts.v1", + "sharedFixtureFiles": [ + "packages/btd/src/deployment-host-capability-catalog.ts", + "packages/btd/src/index.ts", + "BITCODE_SPEC_V34.md", + "BITCODE_SPEC_V34_DELTA.md", + "BITCODE_SPEC_V34_PARITY_MATRIX.md", + "packages/btd/__tests__/deployment-host-capability-catalog.test.ts", + "scripts/check-v34-gate2-host-capability-environment-lanes.mjs" + ], + "sourceEvidence": [ + { + "digest": "sha256:b4e7d2ef3b7ee2ce09922527aa88a5796ae4093c1bd77ff926f822bf13038a5a", + "relativePath": "packages/btd/src/deployment-host-capability-catalog.ts", + "requiredTokens": [ + { + "present": true, + "token": "ENVIRONMENT_LANE_CONTRACT_IDS" + }, + { + "present": true, + "token": "mainnet-ready-dry-run" + }, + { + "present": true, + "token": "value-bearing-mainnet" + }, + { + "present": true, + "token": "blocked_future_canon_required" + }, + { + "present": true, + "token": "buildEnvironmentLaneContracts" + } + ] + }, + { + "digest": "sha256:ddbf849e72492008f814fde47e1c4ebf0565a445a579d01942cd543e18ff6b74", + "relativePath": "BITCODE_SPEC_V34_DELTA.md", + "requiredTokens": [ + { + "present": true, + "token": "local, regtest, signet, staging-testnet, public testnet, mainnet-ready dry run, and value-bearing mainnet lanes are represented" + }, + { + "present": true, + "token": ".bitcode/v34-environment-lane-contracts.json" + } + ] + } + ], + "sourceSafetyVerdict": "source-safe-environment-lane-contract-metadata", + "testEvidence": [ + { + "digest": "sha256:3f620773822bfd97dde2617fd166318991d1d454bf624c17baff96b0d2bc3532", + "relativePath": "packages/btd/__tests__/deployment-host-capability-catalog.test.ts", + "requiredTokens": [ + { + "present": true, + "token": "catalogs local, regtest, signet, staging-testnet, public testnet, mainnet dry run, and blocked value-bearing mainnet lanes" + }, + { + "present": true, + "token": "fails closed when value-bearing mainnet admits hosts or stops being blocked" + }, + { + "present": true, + "token": "fails closed when mainnet-ready dry run is made value-bearing" + } + ] + }, + { + "digest": "sha256:da4dbd254c1804bb44eb3bd6e32d37f51715bf77bdacfe57c7af4ec8d1bcd8ff", + "relativePath": "scripts/check-v34-gate2-host-capability-environment-lanes.mjs", + "requiredTokens": [ + { + "present": true, + "token": "value-bearing-mainnet" + }, + { + "present": true, + "token": "blocked_future_canon_required" + }, + { + "present": true, + "token": "environment-lane-contracts" + } + ] + } + ], + "version": "V34" +} diff --git a/.github/workflows/bitcode-gate-quality.yml b/.github/workflows/bitcode-gate-quality.yml index e876d3d58..0491fe04e 100644 --- a/.github/workflows/bitcode-gate-quality.yml +++ b/.github/workflows/bitcode-gate-quality.yml @@ -148,6 +148,7 @@ jobs: fi node scripts/check-v33-gate10-promotion-readiness.mjs --promotion-mode --skip-branch-check node scripts/check-v34-gate1-deployment-roadmap-opening.mjs --skip-branch-check + node scripts/check-v34-gate2-host-capability-environment-lanes.mjs --skip-branch-check else echo "Unexpected BITCODE_SPEC.txt pointer: $POINTER" >&2 exit 1 @@ -183,6 +184,7 @@ jobs: pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/api-schema-compatibility-matrix.test.ts pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/interface-telemetry-proof-hook.test.ts pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/interface-consumer-ux-regression-proof.test.ts + pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/deployment-host-capability-catalog.test.ts pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/v32-testnet-mainnet-readiness-rehearsal.test.ts pnpm --dir packages/protocol exec node --test --test-force-exit test/v32-promotion-proof-generation.test.js pnpm --filter @bitcode/api exec jest --config jest.config.cjs --runTestsByPath src/routes/__tests__/btd-crypto.test.ts --runInBand diff --git a/BITCODE_SPEC_V34.md b/BITCODE_SPEC_V34.md index 1527a946c..513276d2d 100644 --- a/BITCODE_SPEC_V34.md +++ b/BITCODE_SPEC_V34.md @@ -3,12 +3,12 @@ ## Status - Version: `V34` -- V34 state: Gate 1 deployment-roadmap opening is active over promoted V33 canon +- V34 state: Gate 2 host capability and environment lane catalog is closed over promoted V33 canon - Current canonical/latest target: `V33` - Prior canonical anchor: `BITCODE_SPEC_V33.md` - Prior generated proof appendix: `BITCODE_SPEC_V33_PROVEN.md` -- Generated structured artifact inventory: draft V34 specifying artifacts `.bitcode/v34-spec-family-report.json`, `.bitcode/v34-canonical-input-report.json`, and later deployment-depth artifacts as gates close -- Source parity state: Gate 1 opens V34 deployment-depth parity; host capability, environment lane, distributed execution, storage, approval, rollback, repair, and rehearsal source parity is not closed until the relevant gates close +- Generated structured artifact inventory: draft V34 specifying artifacts `.bitcode/v34-spec-family-report.json`, `.bitcode/v34-canonical-input-report.json`, Gate 2 artifacts `.bitcode/v34-deployment-host-capability-catalog.json` and `.bitcode/v34-environment-lane-contracts.json`, and later deployment-depth artifacts as gates close +- Source parity state: Gate 2 closes V34 host capability and environment lane parity; distributed execution, storage, approval, rollback, repair, and rehearsal source parity is not closed until the relevant gates close - Active canonical pointer during draft opening: `BITCODE_SPEC.txt` -> `V33` - Notes companion: `BITCODE_SPEC_V34_NOTES.md` - Delta companion: `BITCODE_SPEC_V34_DELTA.md` @@ -194,7 +194,7 @@ Inherited V33 objects remain active: `Deposit`, `ReadRequest`, `ReadNeed`, `Find V34 closes through ten gates: 1. **Gate 1: V34 Deployment Roadmap And Spec Opening** opens the V34 family over V33 canon, updates `SPECIFICATIONS_ROADMAP.md`, documents V33 active / V34 draft posture, and wires `check:v34-gate1`. -2. **Gate 2: Host Capability And Environment Lane Catalog** inventories runtime hosts, services, queues, observers, broadcasters, storage carriers, and lanes through `DeploymentHostCapabilityCatalog` and `EnvironmentLaneContract`. +2. **Gate 2: Host Capability And Environment Lane Catalog** inventories runtime hosts, services, queues, observers, broadcasters, storage carriers, and lanes through `DeploymentHostCapabilityCatalog` and `EnvironmentLaneContract`. It is closed by `packages/btd/src/deployment-host-capability-catalog.ts`, `.bitcode/v34-deployment-host-capability-catalog.json`, `.bitcode/v34-environment-lane-contracts.json`, `packages/btd/__tests__/deployment-host-capability-catalog.test.ts`, and `pnpm run check:v34-gate2`. 3. **Gate 3: Distributed Execution Runtime Contracts** defines `DistributedExecutionRuntimeReceipt` for long-running pipeline, ledger, wallet, proof, object-storage, and repair work. 4. **Gate 4: Ledger Database Object Storage Deployment Posture** hardens ledger-derived state, database projection, object storage, generated proof artifacts, audit logs, backup, retention, and rollback material. 5. **Gate 5: Secret Rotation And Credential Boundary Operations** defines secret families, storage owners, rotation commands, leak-response posture, CI masking, and runtime availability checks. @@ -504,8 +504,11 @@ Primary V34 types: `DeploymentHostCapabilityCatalog`, `EnvironmentLaneContract`, Primary surfaces: website, API, MCP API, ChatGPT App, workers, observers, broadcasters, repair jobs, ledger projection, database projection, object-storage projection, and proof replay. Gate 2 catalog rows are package-owned source-safe metadata. -Required row ids include `website_app`, `public_api`, `mcp_api`, `chatgpt_app`, `pipeline_worker`, `ledger_observer`, `settlement_broadcaster`, `proof_service`, and `repair_job`. -Each row names owner package, runtime surface, host capability, lane contract, storage carrier, required secret family, validation command, compatibility status, failure mode, repair posture, telemetry proof hook id, and deterministic proof root. +Required host row ids are `website`, `api`, `mcp_api`, `chatgpt_app`, `pipeline_workers`, `runtime_observers`, `ledger_broadcasters`, `proof_services`, `repair_jobs`, `object_storage`, `database_projection`, and `ledger_projection`. +Required lane row ids are `local`, `regtest`, `signet`, `staging-testnet`, `public-testnet`, `mainnet-ready-dry-run`, and `value-bearing-mainnet`. +The `value-bearing-mainnet` lane is visible as `blocked_future_canon_required`; it admits no runtime hosts and carries `mainnet_value_blocked` wallet policy. +Each host row names owner package, runtime surface, runtime carrier, packages, network posture, secret family names without values, storage carrier, observer/broadcaster/repair capability, validation path, failure mode, repair posture, telemetry proof hook id, and deterministic proof root. +Each lane row names Bitcoin network posture, Supabase and Vercel project posture, value-bearing admission, retention, wallet policy, secret scope, proof requirements, admitted hosts, failure mode, repair posture, telemetry proof hook id, and deterministic proof root. ## Appendix B. Proof family closure catalog @@ -541,8 +544,8 @@ V34 preserves operator-quality proof output expectations and extends them to dep | --- | --- | --- | --- | | `.bitcode/v34-spec-family-report.json` | protocol | source-safe | `node scripts/check-bitcode-spec-family.mjs --version V34 --mode draft --current-target V33` | | `.bitcode/v34-canonical-input-report.json` | protocol | source-safe | `node scripts/check-bitcode-canonical-inputs.mjs --current-target V33` | -| `.bitcode/v34-deployment-host-capability-catalog.json` | protocol | source-safe | later V34 gate | -| `.bitcode/v34-environment-lane-contracts.json` | protocol | source-safe | later V34 gate | +| `.bitcode/v34-deployment-host-capability-catalog.json` | btd | source-safe-deployment-host-capability-metadata | `pnpm run check:v34-host-capability-environment-lanes` | +| `.bitcode/v34-environment-lane-contracts.json` | btd | source-safe-environment-lane-contract-metadata | `pnpm run check:v34-host-capability-environment-lanes` | | `.bitcode/v34-distributed-execution-runtime-receipts.json` | protocol | source-safe | later V34 gate | | `.bitcode/v34-deployment-storage-posture.json` | protocol | source-safe | later V34 gate | | `.bitcode/v34-secret-rotation-boundary-operations.json` | protocol | source-safe | later V34 gate | @@ -555,7 +558,8 @@ V34 preserves operator-quality proof output expectations and extends them to dep ### V34 specifying generated artifacts Gate 1 requires `.bitcode/v34-spec-family-report.json` and `.bitcode/v34-canonical-input-report.json` to be declared. -Later V34 gates introduce the deployment artifacts listed above. +Gate 2 requires `.bitcode/v34-deployment-host-capability-catalog.json` and `.bitcode/v34-environment-lane-contracts.json` to be generated, source-safe, deterministic, and checked by `pnpm run check:v34-gate2`. +Later V34 gates introduce the remaining deployment artifacts listed above. ### Shared generated-artifact fields @@ -581,7 +585,8 @@ Canonical regeneration fails closed when generated inputs drift, source safety f ## Appendix D. Validation and checking gate catalog Gate 1 validation is `pnpm run check:v34-gate1`. -The gate also runs spec-family, canonical-input, canon-posture drift, and diff hygiene checks. +Gate 2 validation is `pnpm run check:v34-gate2`, with artifact freshness checked by `pnpm run check:v34-host-capability-environment-lanes` and focused package coverage in `packages/btd/__tests__/deployment-host-capability-catalog.test.ts`. +The gate-quality workflow also runs spec-family, canonical-input, canon-posture drift, and diff hygiene checks. Later gates add generated artifact checks close to their deployment contract surfaces. ## Appendix E. Current canonical source map diff --git a/BITCODE_SPEC_V34_DELTA.md b/BITCODE_SPEC_V34_DELTA.md index f616c38a2..83317e293 100644 --- a/BITCODE_SPEC_V34_DELTA.md +++ b/BITCODE_SPEC_V34_DELTA.md @@ -3,12 +3,12 @@ ## Status - Version: `V34` -- V34 state: Gate 1 deployment-roadmap opening is active over promoted V33 canon +- V34 state: Gate 2 host capability and environment lane catalog is closed over promoted V33 canon - Current canonical/latest target: `V33` - Prior canonical anchor: `BITCODE_SPEC_V33.md` - Prior generated proof appendix: `BITCODE_SPEC_V33_PROVEN.md` -- Generated structured artifact inventory: draft V34 specifying artifacts `.bitcode/v34-spec-family-report.json`, `.bitcode/v34-canonical-input-report.json`, and later deployment-depth artifacts as gates close -- Source parity state: Gate 1 opens V34 deployment-depth parity; source-side deployment contracts are drafted but not closed until their gates close +- Generated structured artifact inventory: draft V34 specifying artifacts `.bitcode/v34-spec-family-report.json`, `.bitcode/v34-canonical-input-report.json`, Gate 2 artifacts `.bitcode/v34-deployment-host-capability-catalog.json` and `.bitcode/v34-environment-lane-contracts.json`, and later deployment-depth artifacts as gates close +- Source parity state: Gate 2 closes host capability and environment lane contracts; source-side distributed execution, storage, credential, approval, repair, rehearsal, and promotion contracts remain drafted until their gates close - Spec companion: `BITCODE_SPEC_V34.md` - Notes companion: `BITCODE_SPEC_V34_NOTES.md` - Parity companion: `BITCODE_SPEC_V34_PARITY_MATRIX.md` @@ -80,9 +80,16 @@ Closure acceptance: - website, API, MCP API, ChatGPT App, pipeline workers, observers, broadcasters, proof services, repair jobs, object storage, database projection, and ledger projection are enumerated; - local, regtest, signet, staging-testnet, public testnet, mainnet-ready dry run, and value-bearing mainnet lanes are represented; -- value-bearing mainnet is visible as blocked, not hidden confidence; +- `value-bearing-mainnet` is visible as `blocked_future_canon_required`, not hidden confidence; - `.bitcode/v34-deployment-host-capability-catalog.json` and `.bitcode/v34-environment-lane-contracts.json` are source-safe generated artifacts. +Closure evidence: + +- `packages/btd/src/deployment-host-capability-catalog.ts` owns `DeploymentHostCapabilityCatalog` and `EnvironmentLaneContract` builders. +- `packages/btd/__tests__/deployment-host-capability-catalog.test.ts` proves required hosts, lanes, value-bearing mainnet blocking, duplicate/missing failures, and source-safety rejection. +- `scripts/generate-v34-host-capability-environment-lanes.mjs` emits deterministic `.bitcode/v34-deployment-host-capability-catalog.json` and `.bitcode/v34-environment-lane-contracts.json`. +- `scripts/check-v34-gate2-host-capability-environment-lanes.mjs` and `pnpm run check:v34-gate2` fail closed on stale artifacts, hidden value-bearing mainnet, missing rows, docs drift, package-script drift, and workflow drift. + ### Gate 3: Distributed Execution Runtime Contracts Gate 3 defines `DistributedExecutionRuntimeReceipt`. @@ -163,4 +170,4 @@ Closure acceptance: ## Completion condition -This delta is complete when Gate 1 is merged into `version/v34`, the roadmap and docs are truthful for V33 active / V34 draft posture, and the gate checker prevents V34 from drifting back into V33 interface-depth wording or hidden deployment assumptions. +This delta is complete for Gate 2 when `version/v34` contains the Gate 2 host capability and lane contracts, source-safe generated artifacts, focused tests, workflow wiring, and `pnpm run check:v34-gate2` closure. Remaining delta closure advances through Gates 3 through 10. diff --git a/BITCODE_SPEC_V34_NOTES.md b/BITCODE_SPEC_V34_NOTES.md index 6be0e2463..b3e2748b5 100644 --- a/BITCODE_SPEC_V34_NOTES.md +++ b/BITCODE_SPEC_V34_NOTES.md @@ -3,12 +3,12 @@ ## Status - Version: `V34` -- V34 state: Gate 1 deployment-roadmap opening is active over promoted V33 canon +- V34 state: Gate 2 host capability and environment lane catalog is closed over promoted V33 canon - Current canonical/latest target: `V33` - Prior canonical anchor: `BITCODE_SPEC_V33.md` - Prior generated proof appendix: `BITCODE_SPEC_V33_PROVEN.md` -- Generated structured artifact inventory: draft V34 specifying artifacts `.bitcode/v34-spec-family-report.json`, `.bitcode/v34-canonical-input-report.json`, and later deployment-depth artifacts as gates close -- Source parity state: V34 deployment-depth source parity begins at Gate 1 and remains drafted until each gate closes +- Generated structured artifact inventory: draft V34 specifying artifacts `.bitcode/v34-spec-family-report.json`, `.bitcode/v34-canonical-input-report.json`, Gate 2 artifacts `.bitcode/v34-deployment-host-capability-catalog.json` and `.bitcode/v34-environment-lane-contracts.json`, and later deployment-depth artifacts as gates close +- Source parity state: Gate 2 closes host capability and environment lane source parity; later deployment-depth source parity remains drafted until each gate closes - Scope: active draft notes for deployment depth after V33 commercial interface canon This NOTES file does not promote V34. @@ -52,8 +52,9 @@ Read the system as: ## Deployment-depth notes -- `DeploymentHostCapabilityCatalog` must make hosts explicit: website, API, MCP API, ChatGPT App, pipeline workers, observers, broadcasters, proof services, repair jobs, and storage carriers. -- `EnvironmentLaneContract` must distinguish local, regtest, signet, staging-testnet, public testnet, mainnet-ready dry run, and value-bearing mainnet. +- `DeploymentHostCapabilityCatalog` now makes hosts explicit: `website`, `api`, `mcp_api`, `chatgpt_app`, `pipeline_workers`, `runtime_observers`, `ledger_broadcasters`, `proof_services`, `repair_jobs`, `object_storage`, `database_projection`, and `ledger_projection`. +- `EnvironmentLaneContract` now distinguishes `local`, `regtest`, `signet`, `staging-testnet`, `public-testnet`, `mainnet-ready-dry-run`, and `value-bearing-mainnet`, with `value-bearing-mainnet` visible as `blocked_future_canon_required`. +- Gate 2 source truth is `packages/btd/src/deployment-host-capability-catalog.ts`; generated truth is `.bitcode/v34-deployment-host-capability-catalog.json` and `.bitcode/v34-environment-lane-contracts.json`; validation is `pnpm run check:v34-gate2`. - `DistributedExecutionRuntimeReceipt` must represent long-running Reading, settlement, wallet, proof, object-storage, and repair work without relying on route-handler duration. - `DeploymentStoragePosture` must cover ledger-derived state, database projection, object storage, proof artifacts, audit logs, rollback material, retention, encryption, and repair commands. - `SecretRotationPlan` must never place secret values in tracked files or generated artifacts. diff --git a/BITCODE_SPEC_V34_PARITY_MATRIX.md b/BITCODE_SPEC_V34_PARITY_MATRIX.md index bc4df1915..87d285733 100644 --- a/BITCODE_SPEC_V34_PARITY_MATRIX.md +++ b/BITCODE_SPEC_V34_PARITY_MATRIX.md @@ -3,12 +3,12 @@ ## Status - Version: `V34` -- V34 state: Gate 1 deployment-roadmap opening is active over promoted V33 canon +- V34 state: Gate 2 host capability and environment lane catalog is closed over promoted V33 canon - Current canonical/latest target: `V33` - Prior canonical anchor: `BITCODE_SPEC_V33.md` - Prior generated proof appendix: `BITCODE_SPEC_V33_PROVEN.md` -- Generated structured artifact inventory: draft V34 specifying artifacts `.bitcode/v34-spec-family-report.json`, `.bitcode/v34-canonical-input-report.json`, and later deployment-depth artifacts as gates close -- Source parity state: Gate 1 closes roadmap/checker parity; Gates 2 through 10 remain draft-required deployment-depth parity rows +- Generated structured artifact inventory: draft V34 specifying artifacts `.bitcode/v34-spec-family-report.json`, `.bitcode/v34-canonical-input-report.json`, Gate 2 artifacts `.bitcode/v34-deployment-host-capability-catalog.json` and `.bitcode/v34-environment-lane-contracts.json`, and later deployment-depth artifacts as gates close +- Source parity state: Gate 2 closes host capability and environment lane parity; Gates 3 through 10 remain draft-required deployment-depth parity rows - Spec companion: `BITCODE_SPEC_V34.md` - Notes companion: `BITCODE_SPEC_V34_NOTES.md` - Delta companion: `BITCODE_SPEC_V34_DELTA.md` @@ -54,7 +54,7 @@ No `_legacy/` source is active source truth. | --- | --- | --- | --- | --- | | Draft family and branch posture | Gate 1 | `BITCODE_SPEC_V34.md`, DELTA, NOTES, PARITY, `BITCODE_SPEC.txt`, branch `v34/gate-1-deployment-roadmap-opening` | closed | V34 family validates in draft mode over active V33 and `check:v34-gate1` passes. | | Roadmap truth | Gate 1 | `SPECIFICATIONS_ROADMAP.md`, README, PR template, workflow posture | closed | Roadmap states V33 active, V34 draft, and coherent V35-V37 responsibilities. | -| Host capability and environment lane catalog | Gate 2 | planned package source, generated artifacts, and `check:v34-gate2` | draft-required | Hosts, services, lanes, storage carriers, and value-bearing blockers have package-owned rows. | +| Host capability and environment lane catalog | Gate 2 | `packages/btd/src/deployment-host-capability-catalog.ts`, `.bitcode/v34-deployment-host-capability-catalog.json`, `.bitcode/v34-environment-lane-contracts.json`, `packages/btd/__tests__/deployment-host-capability-catalog.test.ts`, and `check:v34-gate2` | closed | Hosts, services, lanes, storage carriers, and value-bearing blockers have package-owned rows. | | Distributed execution runtime contracts | Gate 3 | planned runtime receipt source, tests, generated artifact, and `check:v34-gate3` | draft-required | Pipeline, tool, ledger, wallet, proof, object-storage, and repair work emits typed receipts. | | Ledger/database/object-storage posture | Gate 4 | planned storage posture source, tests, generated artifact, and `check:v34-gate4` | draft-required | Ledger-derived state, database projection, object storage, proof artifacts, audit logs, backups, and rollback material are durable and repairable. | | Secret rotation and credential boundaries | Gate 5 | planned secret-family source, tests, generated artifact, and `check:v34-gate5` | draft-required | Secret values stay out of tracked files and logs while rotation, leak response, and runtime availability are provable. | @@ -76,6 +76,9 @@ No `_legacy/` source is active source truth. | Canon-quality workflow | Canon workflow validates promoted V33 canon, V34 draft family when present, and V33/V34 posture | closed | | Package docs | README, protocol package README, demonstration README, and PR template state V33 active / V34 draft workflow | closed | | Deployment vocabulary | V34 spec family names `DeploymentHostCapabilityCatalog`, `EnvironmentLaneContract`, `DistributedExecutionRuntimeReceipt`, `DeploymentStoragePosture`, `MigrationApprovalGate`, `SecretRotationPlan`, `RuntimeObserverRepairJob`, `RollbackUpgradeRepairPlaybook`, and `DeploymentReadinessRehearsal` | closed | +| Host/lane catalog package source | `packages/btd/src/deployment-host-capability-catalog.ts` owns `DeploymentHostCapabilityCatalog` and `EnvironmentLaneContract` builders | closed | +| Gate 2 generated artifacts | `.bitcode/v34-deployment-host-capability-catalog.json` and `.bitcode/v34-environment-lane-contracts.json` are deterministic and source-safe | closed | +| Value-bearing mainnet blocker | `value-bearing-mainnet` remains `blocked_future_canon_required` and admits no hosts | closed | ## Gate 1 Parity @@ -95,10 +98,10 @@ No `_legacy/` source is active source truth. | Requirement | Source evidence | Current V34 judgment | | --- | --- | --- | -| Package-owned host capability catalog exists | planned source package export | draft-required | -| Environment lanes are explicit | planned `EnvironmentLaneContract` registry | draft-required | -| Value-bearing mainnet is blocked | planned lane fixture and checker | draft-required | -| Generated artifacts are source-safe and deterministic | planned `.bitcode/v34-deployment-host-capability-catalog.json` and `.bitcode/v34-environment-lane-contracts.json` | draft-required | +| Package-owned host capability catalog exists | `packages/btd/src/deployment-host-capability-catalog.ts`, `packages/btd/src/index.ts`, `packages/btd/package.json` | closed | +| Environment lanes are explicit | `EnvironmentLaneContract`, `ENVIRONMENT_LANE_CONTRACT_IDS`, and `.bitcode/v34-environment-lane-contracts.json` | closed | +| Value-bearing mainnet is blocked | `value-bearing-mainnet` lane fixture, `blocked_future_canon_required`, focused tests, and checker assertions | closed | +| Generated artifacts are source-safe and deterministic | `.bitcode/v34-deployment-host-capability-catalog.json`, `.bitcode/v34-environment-lane-contracts.json`, and `pnpm run check:v34-host-capability-environment-lanes` | closed | ## Gate 3 Parity diff --git a/SPECIFICATIONS_ROADMAP.md b/SPECIFICATIONS_ROADMAP.md index 429489c3f..5dece8f1b 100644 --- a/SPECIFICATIONS_ROADMAP.md +++ b/SPECIFICATIONS_ROADMAP.md @@ -5,10 +5,10 @@ - Current active canonical pointer: `BITCODE_SPEC.txt` -> `V33` - Current active canon: `BITCODE_SPEC_V33.md` - Current draft target: `BITCODE_SPEC_V34.md` -- Current working gate: V34 Gate 1 Deployment Roadmap And Spec Opening, which opens deployment-depth canon over promoted V33, refreshes roadmap/docs/workflows, and wires `check:v34-gate1`. +- Current working gate: V34 Gate 3 Distributed Execution Runtime Contracts, following closed Gate 2 host capability and environment lane catalog work. - Latest closed version: V33 Commercial Interface Depth, which promoted MCP API, ChatGPT App, public API, package-owned schemas, interface authorization, Read license and AssetPack rights contracts, compatibility matrices, telemetry/proof hooks, consumer UX proof, and V33 promotion readiness. - Recent V33 closure anchor: V33 Gate 10 Promotion Readiness generated V33 proof support, promoted `BITCODE_SPEC.txt` to `V33`, and prepared V33 active / V34 draft runtime posture. -- V34 opening anchor: deployment-depth now owns host capabilities, environment lanes, distributed execution runtime receipts, storage posture, secret rotation, migration approvals, observers, broadcasters, repair jobs, rollback/upgrade/data repair playbooks, and local/staging-testnet rehearsal. +- V34 Gate 2 closure anchor: deployment-depth now owns package-backed `DeploymentHostCapabilityCatalog` and `EnvironmentLaneContract` source, deterministic `.bitcode/v34-deployment-host-capability-catalog.json` and `.bitcode/v34-environment-lane-contracts.json`, and visible `value-bearing-mainnet` blocking through `blocked_future_canon_required`. - Purpose: concise running index of Bitcode/ENGI specification history, current work, and planned work. This roadmap is not an active system specification. diff --git a/package.json b/package.json index 3cf187912..823f3d837 100644 --- a/package.json +++ b/package.json @@ -129,6 +129,9 @@ "check:v33-promotion-readiness": "node scripts/generate-v33-promotion-readiness-report.mjs --check", "check:v33-gate10": "node scripts/check-v33-gate10-promotion-readiness.mjs", "check:v34-gate1": "node scripts/check-v34-gate1-deployment-roadmap-opening.mjs", + "generate:v34-host-capability-environment-lanes": "node scripts/generate-v34-host-capability-environment-lanes.mjs", + "check:v34-host-capability-environment-lanes": "node scripts/generate-v34-host-capability-environment-lanes.mjs --check", + "check:v34-gate2": "node scripts/check-v34-gate2-host-capability-environment-lanes.mjs", "check:spec-quality": "node scripts/run-bitcode-spec-quality.mjs --mode basic", "check:spec-quality:title": "node scripts/run-bitcode-spec-quality.mjs --mode strict-from-title", "check:spec-quality:v24": "node scripts/run-bitcode-spec-quality.mjs --mode strict-version --version V24", diff --git a/packages/btd/__tests__/deployment-host-capability-catalog.test.ts b/packages/btd/__tests__/deployment-host-capability-catalog.test.ts new file mode 100644 index 000000000..65473c5ae --- /dev/null +++ b/packages/btd/__tests__/deployment-host-capability-catalog.test.ts @@ -0,0 +1,155 @@ +import { + DEPLOYMENT_HOST_CAPABILITY_IDS, + DEPLOYMENT_HOST_CAPABILITY_REQUIRED_ROW_FIELDS, + ENVIRONMENT_LANE_CONTRACT_IDS, + ENVIRONMENT_LANE_CONTRACT_REQUIRED_ROW_FIELDS, + buildDeploymentHostCapabilityCatalog, + buildDeploymentHostCapabilityRow, + buildDeploymentHostCapabilityRows, + buildEnvironmentLaneContract, + buildEnvironmentLaneContractRows, + buildEnvironmentLaneContracts, +} from '../src/deployment-host-capability-catalog'; + +describe('deployment host capability and environment lane catalog', () => { + it('catalogs website, API, MCP API, ChatGPT App, workers, observers, broadcasters, proof services, repair jobs, and storage projections', () => { + const catalog = buildDeploymentHostCapabilityCatalog(); + + expect(catalog.kind).toBe('bitcode.deployment_host_capability_catalog'); + expect(catalog.schemaId).toBe('bitcode.deploymentHostCapabilityCatalog.v1'); + expect(catalog.rowCount).toBe(12); + expect(catalog.missingHostIds).toEqual([]); + expect(catalog.requiredHostIds).toEqual([...DEPLOYMENT_HOST_CAPABILITY_IDS]); + expect(catalog.rows.map((row) => row.hostId)).toEqual([ + 'website', + 'api', + 'mcp_api', + 'chatgpt_app', + 'pipeline_workers', + 'runtime_observers', + 'ledger_broadcasters', + 'proof_services', + 'repair_jobs', + 'object_storage', + 'database_projection', + 'ledger_projection', + ]); + expect(catalog.rows.every((row) => row.sourceSafety.sourceSafe)).toBe(true); + expect(catalog.rows.every((row) => !row.sourceSafety.containsSecret)).toBe(true); + expect(catalog.rows.every((row) => !row.sourceSafety.containsProtectedSource)).toBe(true); + }); + + it('requires owner, runtime, package, secret-family, storage, proof, failure, repair, and telemetry fields for each host row', () => { + const catalog = buildDeploymentHostCapabilityCatalog(); + + for (const row of catalog.rows) { + for (const field of DEPLOYMENT_HOST_CAPABILITY_REQUIRED_ROW_FIELDS) { + expect(row[field]).toBeTruthy(); + } + expect(row.rowRoot).toMatch(/^deployment-host-capability-row:[a-f0-9]{24}$/); + expect(row.telemetryProofHookId).toMatch(/^deployment\.telemetry\./); + } + expect(catalog.catalogRoot).toMatch(/^deployment-host-capability-catalog:[a-f0-9]{24}$/); + }); + + it('catalogs local, regtest, signet, staging-testnet, public testnet, mainnet dry run, and blocked value-bearing mainnet lanes', () => { + const lanes = buildEnvironmentLaneContracts(); + + expect(lanes.kind).toBe('bitcode.environment_lane_contracts'); + expect(lanes.schemaId).toBe('bitcode.environmentLaneContracts.v1'); + expect(lanes.laneCount).toBe(7); + expect(lanes.missingLaneIds).toEqual([]); + expect(lanes.requiredLaneIds).toEqual([...ENVIRONMENT_LANE_CONTRACT_IDS]); + expect(lanes.lanes.map((lane) => lane.laneId)).toEqual([ + 'local', + 'regtest', + 'signet', + 'staging-testnet', + 'public-testnet', + 'mainnet-ready-dry-run', + 'value-bearing-mainnet', + ]); + expect(lanes.valueBearingMainnetBlocked).toBe(true); + expect(lanes.laneContractRoot).toMatch(/^environment-lane-contracts:[a-f0-9]{24}$/); + }); + + it('keeps value-bearing mainnet visible as blocked and without admitted runtime hosts', () => { + const lanes = buildEnvironmentLaneContracts(); + const mainnet = lanes.lanes.find((lane) => lane.laneId === 'value-bearing-mainnet'); + + expect(mainnet).toBeTruthy(); + expect(mainnet?.bitcoinNetworkPosture).toBe('mainnet'); + expect(mainnet?.valueBearingAdmission).toBe('blocked_future_canon_required'); + expect(mainnet?.walletPolicy).toBe('mainnet_value_blocked'); + expect(mainnet?.admittedHostIds).toEqual([]); + expect(mainnet?.failureMode).toBe('value-bearing-mainnet-requested-before-canonical-admission'); + }); + + it('requires Bitcoin, Supabase, Vercel, value-bearing, retention, wallet, proof, failure, repair, and telemetry fields for each lane', () => { + const lanes = buildEnvironmentLaneContracts(); + + for (const lane of lanes.lanes) { + for (const field of ENVIRONMENT_LANE_CONTRACT_REQUIRED_ROW_FIELDS) { + expect(lane[field]).toBeTruthy(); + } + expect(lane.laneRoot).toMatch(/^environment-lane-contract:[a-f0-9]{24}$/); + expect(lane.telemetryProofHookId).toMatch(/^deployment\.telemetry\.lane\./); + } + }); + + it('fails closed when a required deployment host row is missing', () => { + const rows = buildDeploymentHostCapabilityRows().filter((row) => row.hostId !== 'api'); + + expect(() => buildDeploymentHostCapabilityCatalog({ rows })).toThrow(/missing host ids: api/); + }); + + it('fails closed on duplicate deployment host ids', () => { + const rows = buildDeploymentHostCapabilityRows(); + + expect(() => buildDeploymentHostCapabilityCatalog({ rows: [...rows, rows[0]] })).toThrow( + /duplicate host ids: website/, + ); + }); + + it('fails closed when value-bearing mainnet admits hosts or stops being blocked', () => { + const lanes = buildEnvironmentLaneContractRows(); + const changed = lanes.map((lane) => + lane.laneId === 'value-bearing-mainnet' + ? { + ...lane, + valueBearingAdmission: 'dry_run_only' as const, + admittedHostIds: ['api'] as const, + } + : lane, + ); + + expect(() => buildEnvironmentLaneContracts({ lanes: changed })).toThrow( + /value-bearing-mainnet must remain blocked/, + ); + }); + + it('fails closed when mainnet-ready dry run is made value-bearing', () => { + const lane = buildEnvironmentLaneContractRows().find( + (row) => row.laneId === 'mainnet-ready-dry-run', + ); + + expect(lane).toBeTruthy(); + expect(() => + buildEnvironmentLaneContract({ + ...lane!, + valueBearingAdmission: 'not_value_bearing', + }), + ).toThrow(/mainnet-ready-dry-run must be dry-run only/); + }); + + it('fails closed on secret-shaped or non-disclosable source catalog text', () => { + const [firstRow] = buildDeploymentHostCapabilityRows(); + + expect(() => + buildDeploymentHostCapabilityRow({ + ...firstRow, + failureMode: 'sk-proj-abcdefghijklmnop1234567890', + }), + ).toThrow(/must not contain secrets or non-disclosable source/); + }); +}); diff --git a/packages/btd/package.json b/packages/btd/package.json index ec1024972..b7a6576db 100644 --- a/packages/btd/package.json +++ b/packages/btd/package.json @@ -8,6 +8,7 @@ ".": "./src/index.ts", "./api-schema-compatibility-matrix": "./src/api-schema-compatibility-matrix.ts", "./chatgpt-app-action-contract": "./src/chatgpt-app-action-contract.ts", + "./deployment-host-capability-catalog": "./src/deployment-host-capability-catalog.ts", "./interface-authorization-policy": "./src/interface-authorization-policy.ts", "./interface-consumer-ux-regression-proof": "./src/interface-consumer-ux-regression-proof.ts", "./interface-integration-contract": "./src/interface-integration-contract.ts", diff --git a/packages/btd/src/deployment-host-capability-catalog.ts b/packages/btd/src/deployment-host-capability-catalog.ts new file mode 100644 index 000000000..d6c1ecb42 --- /dev/null +++ b/packages/btd/src/deployment-host-capability-catalog.ts @@ -0,0 +1,1008 @@ +import { createHash } from 'crypto'; +import { assertNonEmptyString } from './constants'; +import type { BtdProtocolTelemetrySourceSafety } from './telemetry'; + +export const DEPLOYMENT_HOST_CAPABILITY_IDS = [ + 'website', + 'api', + 'mcp_api', + 'chatgpt_app', + 'pipeline_workers', + 'runtime_observers', + 'ledger_broadcasters', + 'proof_services', + 'repair_jobs', + 'object_storage', + 'database_projection', + 'ledger_projection', +] as const; + +export type DeploymentHostCapabilityId = (typeof DEPLOYMENT_HOST_CAPABILITY_IDS)[number]; + +export const ENVIRONMENT_LANE_CONTRACT_IDS = [ + 'local', + 'regtest', + 'signet', + 'staging-testnet', + 'public-testnet', + 'mainnet-ready-dry-run', + 'value-bearing-mainnet', +] as const; + +export type EnvironmentLaneContractId = (typeof ENVIRONMENT_LANE_CONTRACT_IDS)[number]; + +export type DeploymentHostRuntimeSurface = + | 'website' + | 'api' + | 'mcp_api' + | 'chatgpt_app' + | 'worker' + | 'observer' + | 'broadcaster' + | 'proof_service' + | 'repair_job' + | 'object_storage' + | 'database_projection' + | 'ledger_projection'; + +export type DeploymentHostNetworkPosture = + | 'none' + | 'inbound_http' + | 'outbound_restricted' + | 'provider_bound' + | 'egress_locked'; + +export type DeploymentCapabilityPosture = + | 'required' + | 'supported' + | 'not_applicable'; + +export type DeploymentHostAdmissionStatus = + | 'admitted_non_value_lanes' + | 'admitted_projection_carrier' + | 'blocked_until_lane_contract'; + +export interface DeploymentHostCapabilityRowInput { + hostId: DeploymentHostCapabilityId; + runtimeSurface: DeploymentHostRuntimeSurface; + ownerPackage: string; + runtimeCarrier: string; + requiredPackages: readonly string[]; + outboundNetworkPosture: DeploymentHostNetworkPosture; + requiredSecretFamilies: readonly string[]; + storageCarriers: readonly string[]; + observerCapability: DeploymentCapabilityPosture; + broadcasterCapability: DeploymentCapabilityPosture; + repairCapability: DeploymentCapabilityPosture; + proofOutputPaths: readonly string[]; + validationCommand: string; + supportedLaneIds: readonly EnvironmentLaneContractId[]; + admissionStatus: DeploymentHostAdmissionStatus; + failureMode: string; + repairPosture: string; + telemetryProofHookId: string; + proofRootBasis: readonly string[]; +} + +export interface DeploymentHostCapabilityRow extends DeploymentHostCapabilityRowInput { + kind: 'bitcode.deployment_host_capability_catalog.row'; + requiredPackages: string[]; + requiredSecretFamilies: string[]; + storageCarriers: string[]; + proofOutputPaths: string[]; + supportedLaneIds: EnvironmentLaneContractId[]; + proofRootBasis: string[]; + rowRoot: string; + sourceSafety: BtdProtocolTelemetrySourceSafety; +} + +export interface DeploymentHostCapabilityCatalogInput { + rows?: readonly DeploymentHostCapabilityRowInput[]; + requiredHostIds?: readonly DeploymentHostCapabilityId[]; +} + +export interface DeploymentHostCapabilityCatalog { + kind: 'bitcode.deployment_host_capability_catalog'; + schemaId: 'bitcode.deploymentHostCapabilityCatalog.v1'; + catalogRoot: string; + rowCount: number; + requiredHostIds: DeploymentHostCapabilityId[]; + observedHostIds: DeploymentHostCapabilityId[]; + missingHostIds: DeploymentHostCapabilityId[]; + rows: DeploymentHostCapabilityRow[]; + sourceSafety: BtdProtocolTelemetrySourceSafety; +} + +export type EnvironmentLaneBitcoinNetworkPosture = + | 'none' + | 'regtest' + | 'signet' + | 'testnet' + | 'mainnet'; + +export type EnvironmentLaneProjectPosture = + | 'local_process' + | 'local_project' + | 'staging_testnet_project' + | 'public_testnet_project' + | 'production_project_dry_run' + | 'production_project_blocked'; + +export type EnvironmentLaneValueBearingAdmission = + | 'not_value_bearing' + | 'dry_run_only' + | 'blocked_future_canon_required'; + +export type EnvironmentLaneWalletPolicy = + | 'no_wallet' + | 'regtest_wallet' + | 'signet_wallet' + | 'testnet_wallet' + | 'mainnet_watch_only' + | 'mainnet_value_blocked'; + +export interface EnvironmentLaneContractInput { + laneId: EnvironmentLaneContractId; + bitcoinNetworkPosture: EnvironmentLaneBitcoinNetworkPosture; + supabaseProjectPosture: EnvironmentLaneProjectPosture; + vercelProjectPosture: EnvironmentLaneProjectPosture; + valueBearingAdmission: EnvironmentLaneValueBearingAdmission; + dataRetentionPolicy: string; + walletPolicy: EnvironmentLaneWalletPolicy; + secretScope: string; + proofRequirements: readonly string[]; + admittedHostIds: readonly DeploymentHostCapabilityId[]; + failureMode: string; + repairPosture: string; + telemetryProofHookId: string; + proofRootBasis: readonly string[]; +} + +export interface EnvironmentLaneContract extends EnvironmentLaneContractInput { + kind: 'bitcode.environment_lane_contract'; + proofRequirements: string[]; + admittedHostIds: DeploymentHostCapabilityId[]; + proofRootBasis: string[]; + laneRoot: string; + sourceSafety: BtdProtocolTelemetrySourceSafety; +} + +export interface EnvironmentLaneContractsInput { + lanes?: readonly EnvironmentLaneContractInput[]; + requiredLaneIds?: readonly EnvironmentLaneContractId[]; +} + +export interface EnvironmentLaneContracts { + kind: 'bitcode.environment_lane_contracts'; + schemaId: 'bitcode.environmentLaneContracts.v1'; + laneContractRoot: string; + laneCount: number; + requiredLaneIds: EnvironmentLaneContractId[]; + observedLaneIds: EnvironmentLaneContractId[]; + missingLaneIds: EnvironmentLaneContractId[]; + lanes: EnvironmentLaneContract[]; + valueBearingMainnetBlocked: true; + sourceSafety: BtdProtocolTelemetrySourceSafety; +} + +const SOURCE_SAFETY: BtdProtocolTelemetrySourceSafety = { + sourceSafe: true, + protectedSourceVisible: false, + containsProtectedSource: false, + containsSecret: false, +}; + +const SECRET_OR_SOURCE_PATTERNS = [ + new RegExp(`${['sb', 'secret'].join('_')}__`, 'iu'), + /\bsk-(?:proj|live|test)?[-_A-Za-z0-9]{16,}\b/u, + /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/u, + /-----BEGIN [A-Z ]*PRIVATE KEY-----/u, + /\bprivate\s+key\b/iu, + /\bwallet\s+seed\b/iu, + /\bmnemonic\b/iu, + /\braw\s+source\b/iu, +]; + +export const DEPLOYMENT_HOST_CAPABILITY_REQUIRED_ROW_FIELDS = [ + 'ownerPackage', + 'runtimeCarrier', + 'requiredPackages', + 'outboundNetworkPosture', + 'requiredSecretFamilies', + 'storageCarriers', + 'proofOutputPaths', + 'validationCommand', + 'supportedLaneIds', + 'admissionStatus', + 'failureMode', + 'repairPosture', + 'telemetryProofHookId', +] as const; + +export const ENVIRONMENT_LANE_CONTRACT_REQUIRED_ROW_FIELDS = [ + 'bitcoinNetworkPosture', + 'supabaseProjectPosture', + 'vercelProjectPosture', + 'valueBearingAdmission', + 'dataRetentionPolicy', + 'walletPolicy', + 'secretScope', + 'proofRequirements', + 'admittedHostIds', + 'failureMode', + 'repairPosture', + 'telemetryProofHookId', +] as const; + +export function buildDeploymentHostCapabilityRows(): DeploymentHostCapabilityRowInput[] { + return [ + { + hostId: 'website', + runtimeSurface: 'website', + ownerPackage: 'uapi', + runtimeCarrier: 'vercel-nextjs-website', + requiredPackages: ['@bitcode/api', '@bitcode/pipeline-asset-pack', '@bitcode/btd'], + outboundNetworkPosture: 'provider_bound', + requiredSecretFamilies: ['vercel_project_identity', 'supabase_project_credentials', 'github_app_installation'], + storageCarriers: ['database_projection', 'object_storage'], + observerCapability: 'supported', + broadcasterCapability: 'not_applicable', + repairCapability: 'supported', + proofOutputPaths: ['.bitcode/v34-deployment-host-capability-catalog.json'], + validationCommand: + 'pnpm --dir uapi exec jest --runTestsByPath tests/terminalInterfaceIntegrationRegression.test.ts --runInBand', + supportedLaneIds: ['local', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + admissionStatus: 'admitted_non_value_lanes', + failureMode: 'website-host-without-lane-contract-or-disclosure-lock', + repairPosture: 'deny-read-unlock-and-run-deployment-host-capability-check', + telemetryProofHookId: 'deployment.telemetry.website', + proofRootBasis: ['Terminal interface contracts', 'DeploymentHostCapabilityCatalog'], + }, + { + hostId: 'api', + runtimeSurface: 'api', + ownerPackage: 'packages/api', + runtimeCarrier: 'vercel-node-api', + requiredPackages: ['@bitcode/api', '@bitcode/btd'], + outboundNetworkPosture: 'provider_bound', + requiredSecretFamilies: ['supabase_project_credentials', 'github_app_installation'], + storageCarriers: ['database_projection', 'ledger_projection'], + observerCapability: 'supported', + broadcasterCapability: 'supported', + repairCapability: 'supported', + proofOutputPaths: ['.bitcode/v34-environment-lane-contracts.json'], + validationCommand: + 'pnpm --filter @bitcode/api exec jest --config jest.config.cjs --runTestsByPath src/routes/__tests__/btd-crypto.test.ts --runInBand', + supportedLaneIds: ['local', 'regtest', 'signet', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + admissionStatus: 'admitted_non_value_lanes', + failureMode: 'api-host-without-source-safe-contracts-or-lane-policy', + repairPosture: 'deny-route-admission-and-run-api-contract-checks', + telemetryProofHookId: 'deployment.telemetry.api', + proofRootBasis: ['Public API contract catalog', 'EnvironmentLaneContract'], + }, + { + hostId: 'mcp_api', + runtimeSurface: 'mcp_api', + ownerPackage: 'packages/executions-mcp/src/mcp-server', + runtimeCarrier: 'mcp-server-process', + requiredPackages: ['@bitcode/btd', '@bitcode/pipeline-asset-pack'], + outboundNetworkPosture: 'outbound_restricted', + requiredSecretFamilies: ['mcp_session_authority', 'supabase_project_credentials'], + storageCarriers: ['database_projection', 'proof_artifacts'], + observerCapability: 'supported', + broadcasterCapability: 'not_applicable', + repairCapability: 'supported', + proofOutputPaths: ['.bitcode/v33-mcp-api-tool-contracts.json'], + validationCommand: + 'pnpm --dir packages/executions-mcp/src/mcp-server run test:mcp -- --runTestsByPath src/__tests__/unit/pipeline-ingress-contract.test.ts --runInBand', + supportedLaneIds: ['local', 'staging-testnet', 'public-testnet'], + admissionStatus: 'admitted_non_value_lanes', + failureMode: 'mcp-api-host-without-tool-contract-or-permission-proof', + repairPosture: 'deny-tool-write-and-refresh-mcp-contract-proof', + telemetryProofHookId: 'deployment.telemetry.mcp-api', + proofRootBasis: ['MCP API tool contracts', 'DeploymentHostCapabilityCatalog'], + }, + { + hostId: 'chatgpt_app', + runtimeSurface: 'chatgpt_app', + ownerPackage: 'packages/chatgptapp', + runtimeCarrier: 'chatgpt-action-service', + requiredPackages: ['@bitcode/btd'], + outboundNetworkPosture: 'outbound_restricted', + requiredSecretFamilies: ['chatgpt_action_authority', 'supabase_project_credentials'], + storageCarriers: ['database_projection'], + observerCapability: 'supported', + broadcasterCapability: 'not_applicable', + repairCapability: 'supported', + proofOutputPaths: ['.bitcode/v33-chatgpt-app-action-contracts.json'], + validationCommand: + 'pnpm --dir packages/chatgptapp exec jest --runTestsByPath src/__tests__/tools.test.ts --runInBand', + supportedLaneIds: ['local', 'staging-testnet', 'public-testnet'], + admissionStatus: 'admitted_non_value_lanes', + failureMode: 'chatgpt-app-host-without-confirmed-action-contract', + repairPosture: 'deny-action-write-and-refresh-chatgpt-contract-proof', + telemetryProofHookId: 'deployment.telemetry.chatgpt-app', + proofRootBasis: ['ChatGPT App action contracts', 'EnvironmentLaneContract'], + }, + { + hostId: 'pipeline_workers', + runtimeSurface: 'worker', + ownerPackage: 'packages/pipeline-hosts', + runtimeCarrier: 'vercel-sandbox-worker', + requiredPackages: ['@bitcode/pipeline-hosts', '@bitcode/pipeline-asset-pack'], + outboundNetworkPosture: 'provider_bound', + requiredSecretFamilies: ['vercel_project_identity', 'openai_inference_access', 'supabase_project_credentials'], + storageCarriers: ['object_storage', 'database_projection', 'proof_artifacts'], + observerCapability: 'supported', + broadcasterCapability: 'not_applicable', + repairCapability: 'supported', + proofOutputPaths: ['.bitcode/v34-distributed-execution-runtime-receipts.json'], + validationCommand: 'pnpm --filter @bitcode/pipeline-hosts typecheck', + supportedLaneIds: ['local', 'regtest', 'signet', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + admissionStatus: 'admitted_non_value_lanes', + failureMode: 'pipeline-worker-without-runtime-receipt-or-storage-root', + repairPosture: 'retry-through-distributed-execution-runtime-receipt', + telemetryProofHookId: 'deployment.telemetry.pipeline-worker', + proofRootBasis: ['ReadFitsFindingSynthesis', 'DistributedExecutionRuntimeReceipt'], + }, + { + hostId: 'runtime_observers', + runtimeSurface: 'observer', + ownerPackage: 'packages/btd', + runtimeCarrier: 'scheduled-observer-job', + requiredPackages: ['@bitcode/btd'], + outboundNetworkPosture: 'provider_bound', + requiredSecretFamilies: ['ledger_read_access', 'supabase_project_credentials'], + storageCarriers: ['ledger_projection', 'database_projection', 'proof_artifacts'], + observerCapability: 'required', + broadcasterCapability: 'not_applicable', + repairCapability: 'supported', + proofOutputPaths: ['.bitcode/v34-runtime-observers-broadcasters-repair-jobs.json'], + validationCommand: + 'pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/v32-testnet-mainnet-readiness-rehearsal.test.ts', + supportedLaneIds: ['regtest', 'signet', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + admissionStatus: 'admitted_non_value_lanes', + failureMode: 'observer-host-lag-without-repairable-proof-root', + repairPosture: 'block-unlock-and-run-observer-repair-job', + telemetryProofHookId: 'deployment.telemetry.runtime-observer', + proofRootBasis: ['settlement observer receipts', 'ledger projection roots'], + }, + { + hostId: 'ledger_broadcasters', + runtimeSurface: 'broadcaster', + ownerPackage: 'packages/btd', + runtimeCarrier: 'ledger-broadcaster-job', + requiredPackages: ['@bitcode/btd'], + outboundNetworkPosture: 'provider_bound', + requiredSecretFamilies: ['wallet_signing_authority', 'ledger_broadcast_access'], + storageCarriers: ['ledger_projection', 'proof_artifacts'], + observerCapability: 'supported', + broadcasterCapability: 'required', + repairCapability: 'supported', + proofOutputPaths: ['.bitcode/v34-runtime-observers-broadcasters-repair-jobs.json'], + validationCommand: + 'pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/btc-fee-operation.test.ts', + supportedLaneIds: ['regtest', 'signet', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + admissionStatus: 'admitted_non_value_lanes', + failureMode: 'ledger-broadcaster-without-lane-wallet-policy', + repairPosture: 'deny-broadcast-and-refresh-lane-wallet-policy', + telemetryProofHookId: 'deployment.telemetry.ledger-broadcaster', + proofRootBasis: ['BtcFeeOperation', 'BtdRightsTransferReceipt'], + }, + { + hostId: 'proof_services', + runtimeSurface: 'proof_service', + ownerPackage: 'packages/protocol', + runtimeCarrier: 'proof-generation-job', + requiredPackages: ['@bitcode/protocol', '@bitcode/btd'], + outboundNetworkPosture: 'none', + requiredSecretFamilies: [], + storageCarriers: ['proof_artifacts', 'object_storage'], + observerCapability: 'not_applicable', + broadcasterCapability: 'not_applicable', + repairCapability: 'supported', + proofOutputPaths: ['.bitcode/v34-promotion-readiness-report.json'], + validationCommand: 'pnpm --filter @bitcode/protocol test', + supportedLaneIds: ['local', 'regtest', 'signet', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + admissionStatus: 'admitted_non_value_lanes', + failureMode: 'proof-service-without-deterministic-artifact-inputs', + repairPosture: 'regenerate-artifacts-and-replay-proof-checks', + telemetryProofHookId: 'deployment.telemetry.proof-service', + proofRootBasis: ['canonical input report', 'spec family report'], + }, + { + hostId: 'repair_jobs', + runtimeSurface: 'repair_job', + ownerPackage: 'packages/btd', + runtimeCarrier: 'operator-repair-command', + requiredPackages: ['@bitcode/btd', '@bitcode/protocol'], + outboundNetworkPosture: 'provider_bound', + requiredSecretFamilies: ['operator_repair_authority', 'supabase_project_credentials'], + storageCarriers: ['database_projection', 'ledger_projection', 'object_storage', 'proof_artifacts'], + observerCapability: 'supported', + broadcasterCapability: 'supported', + repairCapability: 'required', + proofOutputPaths: ['.bitcode/v34-rollback-upgrade-data-repair-playbooks.json'], + validationCommand: + 'pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/reconciliation.test.ts', + supportedLaneIds: ['local', 'regtest', 'signet', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + admissionStatus: 'admitted_non_value_lanes', + failureMode: 'repair-job-without-operator-approval-or-proof-root', + repairPosture: 'require-approval-and-emit-repair-playbook-receipt', + telemetryProofHookId: 'deployment.telemetry.repair-job', + proofRootBasis: ['RollbackUpgradeRepairPlaybook', 'RuntimeObserverRepairJob'], + }, + { + hostId: 'object_storage', + runtimeSurface: 'object_storage', + ownerPackage: 'packages/pipeline-hosts', + runtimeCarrier: 'durable-object-storage', + requiredPackages: ['@bitcode/pipeline-hosts'], + outboundNetworkPosture: 'egress_locked', + requiredSecretFamilies: ['object_storage_write_access'], + storageCarriers: ['object_storage', 'proof_artifacts'], + observerCapability: 'supported', + broadcasterCapability: 'not_applicable', + repairCapability: 'supported', + proofOutputPaths: ['.bitcode/v34-deployment-storage-posture.json'], + validationCommand: 'pnpm --filter @bitcode/pipeline-hosts typecheck', + supportedLaneIds: ['local', 'regtest', 'signet', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + admissionStatus: 'admitted_projection_carrier', + failureMode: 'object-storage-carrier-without-retention-or-disclosure-policy', + repairPosture: 'lock-delivery-and-run-storage-posture-repair', + telemetryProofHookId: 'deployment.telemetry.object-storage', + proofRootBasis: ['AssetPackPreview', 'DeploymentStoragePosture'], + }, + { + hostId: 'database_projection', + runtimeSurface: 'database_projection', + ownerPackage: 'packages/supabase', + runtimeCarrier: 'supabase-postgres-projection', + requiredPackages: ['@bitcode/supabase', '@bitcode/btd'], + outboundNetworkPosture: 'provider_bound', + requiredSecretFamilies: ['supabase_project_credentials'], + storageCarriers: ['database_projection'], + observerCapability: 'supported', + broadcasterCapability: 'not_applicable', + repairCapability: 'supported', + proofOutputPaths: ['.bitcode/v34-deployment-storage-posture.json'], + validationCommand: 'pnpm --filter @bitcode/btd typecheck', + supportedLaneIds: ['local', 'regtest', 'signet', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + admissionStatus: 'admitted_projection_carrier', + failureMode: 'database-projection-drift-without-repair-command', + repairPosture: 'block-derived-read-state-and-run-projection-repair', + telemetryProofHookId: 'deployment.telemetry.database-projection', + proofRootBasis: ['ledger database reconciliation', 'DeploymentStoragePosture'], + }, + { + hostId: 'ledger_projection', + runtimeSurface: 'ledger_projection', + ownerPackage: 'packages/btd', + runtimeCarrier: 'ledger-projection-store', + requiredPackages: ['@bitcode/btd'], + outboundNetworkPosture: 'provider_bound', + requiredSecretFamilies: ['ledger_read_access'], + storageCarriers: ['ledger_projection', 'proof_artifacts'], + observerCapability: 'required', + broadcasterCapability: 'supported', + repairCapability: 'supported', + proofOutputPaths: ['.bitcode/v34-deployment-storage-posture.json'], + validationCommand: + 'pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/reconciliation.test.ts', + supportedLaneIds: ['regtest', 'signet', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + admissionStatus: 'admitted_projection_carrier', + failureMode: 'ledger-projection-drift-without-finality-repair', + repairPosture: 'hold-rights-unlock-and-run-ledger-projection-repair', + telemetryProofHookId: 'deployment.telemetry.ledger-projection', + proofRootBasis: ['ledger finality state', 'BtdRightsTransferReceipt'], + }, + ]; +} + +export function buildEnvironmentLaneContractRows(): EnvironmentLaneContractInput[] { + const allNonValueHosts = DEPLOYMENT_HOST_CAPABILITY_IDS.filter( + (hostId) => hostId !== 'ledger_broadcasters', + ); + + return [ + { + laneId: 'local', + bitcoinNetworkPosture: 'none', + supabaseProjectPosture: 'local_process', + vercelProjectPosture: 'local_process', + valueBearingAdmission: 'not_value_bearing', + dataRetentionPolicy: 'ephemeral-local-development', + walletPolicy: 'no_wallet', + secretScope: 'developer-local-env-file', + proofRequirements: ['spec-family-check', 'gate-check'], + admittedHostIds: ['website', 'api', 'mcp_api', 'chatgpt_app', 'pipeline_workers', 'proof_services', 'repair_jobs', 'object_storage', 'database_projection'], + failureMode: 'local-lane-without-deterministic-checks', + repairPosture: 'rerun-local-gate-checks-before-pr', + telemetryProofHookId: 'deployment.telemetry.lane.local', + proofRootBasis: ['local checks', 'source-safe fixtures'], + }, + { + laneId: 'regtest', + bitcoinNetworkPosture: 'regtest', + supabaseProjectPosture: 'local_project', + vercelProjectPosture: 'local_project', + valueBearingAdmission: 'not_value_bearing', + dataRetentionPolicy: 'ephemeral-regtest-development', + walletPolicy: 'regtest_wallet', + secretScope: 'local-regtest-only', + proofRequirements: ['regtest-wallet-proof', 'ledger-projection-proof'], + admittedHostIds: ['api', 'pipeline_workers', 'runtime_observers', 'ledger_broadcasters', 'proof_services', 'repair_jobs', 'object_storage', 'database_projection', 'ledger_projection'], + failureMode: 'regtest-lane-without-wallet-or-ledger-proof', + repairPosture: 'reset-regtest-ledger-and-replay-settlement-fixture', + telemetryProofHookId: 'deployment.telemetry.lane.regtest', + proofRootBasis: ['regtest settlement fixture', 'ledger projection repair'], + }, + { + laneId: 'signet', + bitcoinNetworkPosture: 'signet', + supabaseProjectPosture: 'staging_testnet_project', + vercelProjectPosture: 'staging_testnet_project', + valueBearingAdmission: 'not_value_bearing', + dataRetentionPolicy: 'bounded-testnet-retention', + walletPolicy: 'signet_wallet', + secretScope: 'staging-testnet-secret-set', + proofRequirements: ['signet-finality-proof', 'source-safe-runtime-receipts'], + admittedHostIds: ['api', 'pipeline_workers', 'runtime_observers', 'ledger_broadcasters', 'proof_services', 'repair_jobs', 'object_storage', 'database_projection', 'ledger_projection'], + failureMode: 'signet-lane-without-finality-or-repair-proof', + repairPosture: 'pause-settlement-unlock-and-run-finality-repair', + telemetryProofHookId: 'deployment.telemetry.lane.signet', + proofRootBasis: ['signet finality state', 'runtime observer receipt'], + }, + { + laneId: 'staging-testnet', + bitcoinNetworkPosture: 'signet', + supabaseProjectPosture: 'staging_testnet_project', + vercelProjectPosture: 'staging_testnet_project', + valueBearingAdmission: 'not_value_bearing', + dataRetentionPolicy: 'bounded-staging-testnet-retention', + walletPolicy: 'signet_wallet', + secretScope: 'staging-testnet-secret-set', + proofRequirements: ['terminal-rehearsal-proof', 'pipeline-runtime-receipts', 'source-safe-log-proof'], + admittedHostIds: [...DEPLOYMENT_HOST_CAPABILITY_IDS], + failureMode: 'staging-testnet-lane-without-complete-rehearsal-proof', + repairPosture: 'block-promotion-and-repeat-staging-testnet-rehearsal', + telemetryProofHookId: 'deployment.telemetry.lane.staging-testnet', + proofRootBasis: ['DeploymentReadinessRehearsal', 'Terminal transaction proof'], + }, + { + laneId: 'public-testnet', + bitcoinNetworkPosture: 'testnet', + supabaseProjectPosture: 'public_testnet_project', + vercelProjectPosture: 'public_testnet_project', + valueBearingAdmission: 'not_value_bearing', + dataRetentionPolicy: 'bounded-public-testnet-retention', + walletPolicy: 'testnet_wallet', + secretScope: 'public-testnet-secret-set', + proofRequirements: ['public-testnet-finality-proof', 'operator-approval-proof'], + admittedHostIds: [...DEPLOYMENT_HOST_CAPABILITY_IDS], + failureMode: 'public-testnet-lane-without-operator-approval', + repairPosture: 'remove-public-testnet-admission-and-replay-approval-gate', + telemetryProofHookId: 'deployment.telemetry.lane.public-testnet', + proofRootBasis: ['MigrationApprovalGate', 'DeploymentReadinessRehearsal'], + }, + { + laneId: 'mainnet-ready-dry-run', + bitcoinNetworkPosture: 'mainnet', + supabaseProjectPosture: 'production_project_dry_run', + vercelProjectPosture: 'production_project_dry_run', + valueBearingAdmission: 'dry_run_only', + dataRetentionPolicy: 'production-shaped-dry-run-retention', + walletPolicy: 'mainnet_watch_only', + secretScope: 'mainnet-dry-run-secret-set', + proofRequirements: ['mainnet-watch-only-proof', 'dry-run-settlement-proof', 'operator-approval-proof'], + admittedHostIds: allNonValueHosts, + failureMode: 'mainnet-ready-dry-run-attempts-value-bearing-broadcast', + repairPosture: 'deny-broadcast-and-demote-to-public-testnet-lane', + telemetryProofHookId: 'deployment.telemetry.lane.mainnet-ready-dry-run', + proofRootBasis: ['mainnet watch-only receipt', 'deployment approval gate'], + }, + { + laneId: 'value-bearing-mainnet', + bitcoinNetworkPosture: 'mainnet', + supabaseProjectPosture: 'production_project_blocked', + vercelProjectPosture: 'production_project_blocked', + valueBearingAdmission: 'blocked_future_canon_required', + dataRetentionPolicy: 'blocked-until-future-canon', + walletPolicy: 'mainnet_value_blocked', + secretScope: 'blocked-no-runtime-secret-scope', + proofRequirements: ['future-canon-authorization', 'operator-approval-proof', 'mainnet-broadcast-proof'], + admittedHostIds: [], + failureMode: 'value-bearing-mainnet-requested-before-canonical-admission', + repairPosture: 'fail-closed-and-run-mainnet-blocker-report', + telemetryProofHookId: 'deployment.telemetry.lane.value-bearing-mainnet', + proofRootBasis: ['future canon blocker', 'value-bearing mainnet blocked'], + }, + ]; +} + +export function buildDeploymentHostCapabilityRow( + input: DeploymentHostCapabilityRowInput, +): DeploymentHostCapabilityRow { + const hostId = assertDeploymentHostCapabilityId(input.hostId); + const supportedLaneIds = assertEnvironmentLaneIds(input.supportedLaneIds); + const row = { + kind: 'bitcode.deployment_host_capability_catalog.row' as const, + hostId, + runtimeSurface: assertRuntimeSurface(input.runtimeSurface), + ownerPackage: assertSourceSafeString(input.ownerPackage, 'ownerPackage'), + runtimeCarrier: assertSourceSafeString(input.runtimeCarrier, 'runtimeCarrier'), + requiredPackages: assertSourceSafeStrings(input.requiredPackages, 'requiredPackages'), + outboundNetworkPosture: assertNetworkPosture(input.outboundNetworkPosture), + requiredSecretFamilies: assertSourceSafeStrings(input.requiredSecretFamilies, 'requiredSecretFamilies'), + storageCarriers: assertSourceSafeStrings(input.storageCarriers, 'storageCarriers'), + observerCapability: assertCapabilityPosture(input.observerCapability), + broadcasterCapability: assertCapabilityPosture(input.broadcasterCapability), + repairCapability: assertCapabilityPosture(input.repairCapability), + proofOutputPaths: assertSourceSafeStrings(input.proofOutputPaths, 'proofOutputPaths'), + validationCommand: assertSourceSafeString(input.validationCommand, 'validationCommand'), + supportedLaneIds, + admissionStatus: assertHostAdmissionStatus(input.admissionStatus), + failureMode: assertSourceSafeString(input.failureMode, 'failureMode'), + repairPosture: assertSourceSafeString(input.repairPosture, 'repairPosture'), + telemetryProofHookId: assertSourceSafeString(input.telemetryProofHookId, 'telemetryProofHookId'), + proofRootBasis: assertSourceSafeStrings(input.proofRootBasis, 'proofRootBasis').sort(), + sourceSafety: { ...SOURCE_SAFETY }, + }; + + if (row.admissionStatus === 'blocked_until_lane_contract' && row.supportedLaneIds.length > 0) { + throw new Error(`${hostId} cannot support lanes while blocked until lane contract.`); + } + if (row.hostId === 'ledger_broadcasters' && row.broadcasterCapability !== 'required') { + throw new Error('ledger_broadcasters must require broadcaster capability.'); + } + if (row.hostId === 'runtime_observers' && row.observerCapability !== 'required') { + throw new Error('runtime_observers must require observer capability.'); + } + if (row.hostId === 'repair_jobs' && row.repairCapability !== 'required') { + throw new Error('repair_jobs must require repair capability.'); + } + + return { + ...row, + rowRoot: stableRoot('deployment-host-capability-row', [ + row.hostId, + row.runtimeSurface, + row.ownerPackage, + row.runtimeCarrier, + row.requiredPackages.join(','), + row.outboundNetworkPosture, + row.requiredSecretFamilies.join(','), + row.storageCarriers.join(','), + row.observerCapability, + row.broadcasterCapability, + row.repairCapability, + row.proofOutputPaths.join(','), + row.validationCommand, + row.supportedLaneIds.join(','), + row.admissionStatus, + row.failureMode, + row.repairPosture, + row.telemetryProofHookId, + row.proofRootBasis.join(','), + ]), + }; +} + +export function buildDeploymentHostCapabilityCatalog( + input: DeploymentHostCapabilityCatalogInput = {}, +): DeploymentHostCapabilityCatalog { + const rows = (input.rows ?? buildDeploymentHostCapabilityRows()).map( + buildDeploymentHostCapabilityRow, + ); + const requiredHostIds = [...(input.requiredHostIds ?? DEPLOYMENT_HOST_CAPABILITY_IDS)]; + const observedHostIds = Array.from(new Set(rows.map((row) => row.hostId))).sort(); + const missingHostIds = requiredHostIds.filter((hostId) => !observedHostIds.includes(hostId)); + const duplicateHostIds = findDuplicates(rows.map((row) => row.hostId)); + + if (missingHostIds.length) { + throw new Error(`Deployment host capability catalog missing host ids: ${missingHostIds.join(', ')}.`); + } + if (duplicateHostIds.length) { + throw new Error(`Deployment host capability catalog contains duplicate host ids: ${duplicateHostIds.join(', ')}.`); + } + + return { + kind: 'bitcode.deployment_host_capability_catalog', + schemaId: 'bitcode.deploymentHostCapabilityCatalog.v1', + catalogRoot: stableRoot('deployment-host-capability-catalog', [ + ...rows.map((row) => row.rowRoot), + requiredHostIds.join(','), + ]), + rowCount: rows.length, + requiredHostIds, + observedHostIds, + missingHostIds, + rows, + sourceSafety: { ...SOURCE_SAFETY }, + }; +} + +export function buildEnvironmentLaneContract( + input: EnvironmentLaneContractInput, +): EnvironmentLaneContract { + const laneId = assertEnvironmentLaneContractId(input.laneId); + const admittedHostIds = assertDeploymentHostCapabilityIds(input.admittedHostIds); + const row = { + kind: 'bitcode.environment_lane_contract' as const, + laneId, + bitcoinNetworkPosture: assertBitcoinNetworkPosture(input.bitcoinNetworkPosture), + supabaseProjectPosture: assertProjectPosture(input.supabaseProjectPosture), + vercelProjectPosture: assertProjectPosture(input.vercelProjectPosture), + valueBearingAdmission: assertValueBearingAdmission(input.valueBearingAdmission), + dataRetentionPolicy: assertSourceSafeString(input.dataRetentionPolicy, 'dataRetentionPolicy'), + walletPolicy: assertWalletPolicy(input.walletPolicy), + secretScope: assertSourceSafeString(input.secretScope, 'secretScope'), + proofRequirements: assertSourceSafeStrings(input.proofRequirements, 'proofRequirements'), + admittedHostIds, + failureMode: assertSourceSafeString(input.failureMode, 'failureMode'), + repairPosture: assertSourceSafeString(input.repairPosture, 'repairPosture'), + telemetryProofHookId: assertSourceSafeString(input.telemetryProofHookId, 'telemetryProofHookId'), + proofRootBasis: assertSourceSafeStrings(input.proofRootBasis, 'proofRootBasis').sort(), + sourceSafety: { ...SOURCE_SAFETY }, + }; + + if (row.laneId === 'value-bearing-mainnet') { + if (row.valueBearingAdmission !== 'blocked_future_canon_required') { + throw new Error('value-bearing-mainnet must remain blocked until future canon admits it.'); + } + if (row.admittedHostIds.length !== 0) { + throw new Error('value-bearing-mainnet must not admit runtime hosts.'); + } + if (row.walletPolicy !== 'mainnet_value_blocked') { + throw new Error('value-bearing-mainnet must use the blocked mainnet wallet policy.'); + } + } + if (row.laneId === 'mainnet-ready-dry-run' && row.valueBearingAdmission !== 'dry_run_only') { + throw new Error('mainnet-ready-dry-run must be dry-run only.'); + } + + return { + ...row, + laneRoot: stableRoot('environment-lane-contract', [ + row.laneId, + row.bitcoinNetworkPosture, + row.supabaseProjectPosture, + row.vercelProjectPosture, + row.valueBearingAdmission, + row.dataRetentionPolicy, + row.walletPolicy, + row.secretScope, + row.proofRequirements.join(','), + row.admittedHostIds.join(','), + row.failureMode, + row.repairPosture, + row.telemetryProofHookId, + row.proofRootBasis.join(','), + ]), + }; +} + +export function buildEnvironmentLaneContracts( + input: EnvironmentLaneContractsInput = {}, +): EnvironmentLaneContracts { + const lanes = (input.lanes ?? buildEnvironmentLaneContractRows()).map( + buildEnvironmentLaneContract, + ); + const requiredLaneIds = [...(input.requiredLaneIds ?? ENVIRONMENT_LANE_CONTRACT_IDS)]; + const observedLaneIds = Array.from(new Set(lanes.map((lane) => lane.laneId))).sort(); + const missingLaneIds = requiredLaneIds.filter((laneId) => !observedLaneIds.includes(laneId)); + const duplicateLaneIds = findDuplicates(lanes.map((lane) => lane.laneId)); + const valueBearingMainnet = lanes.find((lane) => lane.laneId === 'value-bearing-mainnet'); + + if (missingLaneIds.length) { + throw new Error(`Environment lane contracts missing lane ids: ${missingLaneIds.join(', ')}.`); + } + if (duplicateLaneIds.length) { + throw new Error(`Environment lane contracts contain duplicate lane ids: ${duplicateLaneIds.join(', ')}.`); + } + if (valueBearingMainnet?.valueBearingAdmission !== 'blocked_future_canon_required') { + throw new Error('Environment lane contracts must keep value-bearing-mainnet blocked.'); + } + + return { + kind: 'bitcode.environment_lane_contracts', + schemaId: 'bitcode.environmentLaneContracts.v1', + laneContractRoot: stableRoot('environment-lane-contracts', [ + ...lanes.map((lane) => lane.laneRoot), + requiredLaneIds.join(','), + ]), + laneCount: lanes.length, + requiredLaneIds, + observedLaneIds, + missingLaneIds, + lanes, + valueBearingMainnetBlocked: true, + sourceSafety: { ...SOURCE_SAFETY }, + }; +} + +function assertDeploymentHostCapabilityId(hostId: string): DeploymentHostCapabilityId { + if (!DEPLOYMENT_HOST_CAPABILITY_IDS.includes(hostId as DeploymentHostCapabilityId)) { + throw new Error(`Unsupported deployment host capability id: ${hostId}.`); + } + + return hostId as DeploymentHostCapabilityId; +} + +function assertDeploymentHostCapabilityIds( + hostIds: readonly DeploymentHostCapabilityId[], +): DeploymentHostCapabilityId[] { + return Array.from(new Set(hostIds.map(assertDeploymentHostCapabilityId))).sort(); +} + +function assertEnvironmentLaneContractId(laneId: string): EnvironmentLaneContractId { + if (!ENVIRONMENT_LANE_CONTRACT_IDS.includes(laneId as EnvironmentLaneContractId)) { + throw new Error(`Unsupported environment lane contract id: ${laneId}.`); + } + + return laneId as EnvironmentLaneContractId; +} + +function assertEnvironmentLaneIds( + laneIds: readonly EnvironmentLaneContractId[], +): EnvironmentLaneContractId[] { + return Array.from(new Set(laneIds.map(assertEnvironmentLaneContractId))).sort(); +} + +function assertRuntimeSurface(surface: string): DeploymentHostRuntimeSurface { + const allowed: readonly DeploymentHostRuntimeSurface[] = [ + 'website', + 'api', + 'mcp_api', + 'chatgpt_app', + 'worker', + 'observer', + 'broadcaster', + 'proof_service', + 'repair_job', + 'object_storage', + 'database_projection', + 'ledger_projection', + ]; + if (!allowed.includes(surface as DeploymentHostRuntimeSurface)) { + throw new Error(`Unsupported deployment host runtime surface: ${surface}.`); + } + + return surface as DeploymentHostRuntimeSurface; +} + +function assertNetworkPosture(posture: string): DeploymentHostNetworkPosture { + const allowed: readonly DeploymentHostNetworkPosture[] = [ + 'none', + 'inbound_http', + 'outbound_restricted', + 'provider_bound', + 'egress_locked', + ]; + if (!allowed.includes(posture as DeploymentHostNetworkPosture)) { + throw new Error(`Unsupported deployment host network posture: ${posture}.`); + } + + return posture as DeploymentHostNetworkPosture; +} + +function assertCapabilityPosture(posture: string): DeploymentCapabilityPosture { + const allowed: readonly DeploymentCapabilityPosture[] = [ + 'required', + 'supported', + 'not_applicable', + ]; + if (!allowed.includes(posture as DeploymentCapabilityPosture)) { + throw new Error(`Unsupported deployment capability posture: ${posture}.`); + } + + return posture as DeploymentCapabilityPosture; +} + +function assertHostAdmissionStatus(status: string): DeploymentHostAdmissionStatus { + const allowed: readonly DeploymentHostAdmissionStatus[] = [ + 'admitted_non_value_lanes', + 'admitted_projection_carrier', + 'blocked_until_lane_contract', + ]; + if (!allowed.includes(status as DeploymentHostAdmissionStatus)) { + throw new Error(`Unsupported deployment host admission status: ${status}.`); + } + + return status as DeploymentHostAdmissionStatus; +} + +function assertBitcoinNetworkPosture(posture: string): EnvironmentLaneBitcoinNetworkPosture { + const allowed: readonly EnvironmentLaneBitcoinNetworkPosture[] = [ + 'none', + 'regtest', + 'signet', + 'testnet', + 'mainnet', + ]; + if (!allowed.includes(posture as EnvironmentLaneBitcoinNetworkPosture)) { + throw new Error(`Unsupported environment lane Bitcoin network posture: ${posture}.`); + } + + return posture as EnvironmentLaneBitcoinNetworkPosture; +} + +function assertProjectPosture(posture: string): EnvironmentLaneProjectPosture { + const allowed: readonly EnvironmentLaneProjectPosture[] = [ + 'local_process', + 'local_project', + 'staging_testnet_project', + 'public_testnet_project', + 'production_project_dry_run', + 'production_project_blocked', + ]; + if (!allowed.includes(posture as EnvironmentLaneProjectPosture)) { + throw new Error(`Unsupported environment lane project posture: ${posture}.`); + } + + return posture as EnvironmentLaneProjectPosture; +} + +function assertValueBearingAdmission( + admission: string, +): EnvironmentLaneValueBearingAdmission { + const allowed: readonly EnvironmentLaneValueBearingAdmission[] = [ + 'not_value_bearing', + 'dry_run_only', + 'blocked_future_canon_required', + ]; + if (!allowed.includes(admission as EnvironmentLaneValueBearingAdmission)) { + throw new Error(`Unsupported environment lane value-bearing admission: ${admission}.`); + } + + return admission as EnvironmentLaneValueBearingAdmission; +} + +function assertWalletPolicy(policy: string): EnvironmentLaneWalletPolicy { + const allowed: readonly EnvironmentLaneWalletPolicy[] = [ + 'no_wallet', + 'regtest_wallet', + 'signet_wallet', + 'testnet_wallet', + 'mainnet_watch_only', + 'mainnet_value_blocked', + ]; + if (!allowed.includes(policy as EnvironmentLaneWalletPolicy)) { + throw new Error(`Unsupported environment lane wallet policy: ${policy}.`); + } + + return policy as EnvironmentLaneWalletPolicy; +} + +function assertSourceSafeStrings(values: readonly string[], label: string): string[] { + if (!Array.isArray(values)) { + throw new Error(`${label} must be an array.`); + } + + return Array.from(new Set(values.map((value) => assertSourceSafeString(value, label)))).sort(); +} + +function assertSourceSafeString(value: unknown, label: string): string { + const text = assertNonEmptyString(value, label); + if (SECRET_OR_SOURCE_PATTERNS.some((pattern) => pattern.test(text))) { + throw new Error(`${label} must not contain secrets or non-disclosable source.`); + } + + return text; +} + +function findDuplicates(values: readonly string[]): string[] { + const seen = new Set(); + const duplicate = new Set(); + for (const value of values) { + if (seen.has(value)) duplicate.add(value); + seen.add(value); + } + + return [...duplicate].sort(); +} + +function stableRoot(prefix: string, parts: string[]): string { + const hash = createHash('sha256').update(parts.join('\u001f')).digest('hex').slice(0, 24); + return `${prefix}:${hash}`; +} diff --git a/packages/btd/src/index.ts b/packages/btd/src/index.ts index 0c0d1d62d..c1f7d7cfd 100644 --- a/packages/btd/src/index.ts +++ b/packages/btd/src/index.ts @@ -168,6 +168,7 @@ export * from './authority'; export * from './auxillaries-support'; export * from './constants'; export * from './deployment-lanes'; +export * from './deployment-host-capability-catalog'; export * from './exchange'; export * from './interface-contract-catalog'; export * from './interface-contract-regression'; diff --git a/packages/protocol/src/canonical/v21-specifying.js b/packages/protocol/src/canonical/v21-specifying.js index 119776f6f..728b2e8f9 100644 --- a/packages/protocol/src/canonical/v21-specifying.js +++ b/packages/protocol/src/canonical/v21-specifying.js @@ -372,6 +372,12 @@ function buildV21LikeProfile(version) { '.bitcode/v33-promotion-readiness-report.json' ] : []), + ...(version === 'V34' + ? [ + '.bitcode/v34-deployment-host-capability-catalog.json', + '.bitcode/v34-environment-lane-contracts.json' + ] + : []), ...(version === 'V26' ? [ '.bitcode/terminal-composition-proof.json', diff --git a/scripts/check-v34-gate2-host-capability-environment-lanes.mjs b/scripts/check-v34-gate2-host-capability-environment-lanes.mjs new file mode 100644 index 000000000..ddd3812e4 --- /dev/null +++ b/scripts/check-v34-gate2-host-capability-environment-lanes.mjs @@ -0,0 +1,330 @@ +#!/usr/bin/env node + +import { execFileSync } from 'node:child_process'; +import { existsSync, readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const defaultRepoRoot = path.resolve(__dirname, '..'); +const HOST_ARTIFACT = '.bitcode/v34-deployment-host-capability-catalog.json'; +const LANE_ARTIFACT = '.bitcode/v34-environment-lane-contracts.json'; + +const REQUIRED_HOST_IDS = [ + 'website', + 'api', + 'mcp_api', + 'chatgpt_app', + 'pipeline_workers', + 'runtime_observers', + 'ledger_broadcasters', + 'proof_services', + 'repair_jobs', + 'object_storage', + 'database_projection', + 'ledger_projection', +]; + +const REQUIRED_LANE_IDS = [ + 'local', + 'regtest', + 'signet', + 'staging-testnet', + 'public-testnet', + 'mainnet-ready-dry-run', + 'value-bearing-mainnet', +]; + +const SECRET_MARKERS = [ + `${['sk', 'proj'].join('-')}-`, + `${['sb', 'secret'].join('_')}__`, + ['service', 'role'].join('_'), + ['eyJhbGciOiJI', 'UzI1NiIsInR5cCI6IkpXVCJ9'].join(''), + ['OPENAI', 'API', 'KEY'].join('_'), + ['VERCEL', 'TOKEN'].join('_'), + ['VERCEL', 'OIDC', 'TOKEN'].join('_'), +]; + +function read(root, relativePath) { + return readFileSync(path.join(root, relativePath), 'utf8'); +} + +function fileExists(root, relativePath) { + return existsSync(path.join(root, relativePath)); +} + +function git(root, args) { + return execFileSync('git', args, { cwd: root, encoding: 'utf8' }).trim(); +} + +function run(root, command, args) { + return execFileSync(command, args, { + cwd: root, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + }).trim(); +} + +function assertCheck(failures, condition, message) { + if (!condition) failures.push(message); +} + +function includesAll(values, requiredValues) { + return requiredValues.every((value) => values.includes(value)); +} + +function parseArgs(argv) { + const args = { + skipBranchCheck: false, + repoRoot: defaultRepoRoot, + }; + + for (let index = 0; index < argv.length; index += 1) { + const arg = argv[index]; + if (arg === '--skip-branch-check') args.skipBranchCheck = true; + else if (arg === '--repo-root') args.repoRoot = path.resolve(argv[++index]); + else if (arg === '--help' || arg === '-h') args.help = true; + else throw new Error(`Unknown argument ${arg}`); + } + + return args; +} + +function printHelp() { + process.stdout.write( + [ + 'Usage: node scripts/check-v34-gate2-host-capability-environment-lanes.mjs [--skip-branch-check] [--repo-root ]', + '', + 'Checks V34 Gate 2 Host Capability And Environment Lane Catalog source, generated artifacts, tests, docs, package scripts, and workflow wiring.', + ].join('\n'), + ); + process.stdout.write('\n'); +} + +function main() { + const args = parseArgs(process.argv.slice(2)); + if (args.help) { + printHelp(); + return; + } + + const root = args.repoRoot; + const failures = []; + const pointer = read(root, 'BITCODE_SPEC.txt').trim(); + + assertCheck( + failures, + pointer === 'V33', + `BITCODE_SPEC.txt must remain V33 during V34 gate work. Observed ${pointer || 'empty'}.`, + ); + + if (!args.skipBranchCheck) { + const branch = git(root, ['branch', '--show-current']); + assertCheck( + failures, + branch === 'version/v34' || /^v34\/gate-(?:[2-9]|10)-[a-z0-9][a-z0-9-]*$/u.test(branch), + `V34 Gate 2+ work must occur on version/v34 or v34/gate-2..10-* branches. Observed ${branch || 'detached HEAD'}.`, + ); + } + + const requiredFiles = [ + HOST_ARTIFACT, + LANE_ARTIFACT, + 'packages/btd/src/deployment-host-capability-catalog.ts', + 'packages/btd/src/index.ts', + 'packages/btd/__tests__/deployment-host-capability-catalog.test.ts', + 'scripts/generate-v34-host-capability-environment-lanes.mjs', + 'scripts/check-v34-gate2-host-capability-environment-lanes.mjs', + 'BITCODE_SPEC_V34.md', + 'BITCODE_SPEC_V34_DELTA.md', + 'BITCODE_SPEC_V34_NOTES.md', + 'BITCODE_SPEC_V34_PARITY_MATRIX.md', + 'SPECIFICATIONS_ROADMAP.md', + 'package.json', + '.github/workflows/bitcode-gate-quality.yml', + 'packages/protocol/src/canonical/v21-specifying.js', + ]; + + for (const relativePath of requiredFiles) { + assertCheck(failures, fileExists(root, relativePath), `Missing V34 Gate 2 file: ${relativePath}`); + } + + if (failures.length === 0) { + try { + run(root, 'pnpm', ['run', 'check:v34-host-capability-environment-lanes']); + } catch (error) { + failures.push(`V34 Gate 2 artifact check failed: ${error.stderr || error.message}`); + } + } + + const serializedHostArtifact = fileExists(root, HOST_ARTIFACT) ? read(root, HOST_ARTIFACT) : ''; + const serializedLaneArtifact = fileExists(root, LANE_ARTIFACT) ? read(root, LANE_ARTIFACT) : ''; + for (const marker of SECRET_MARKERS) { + assertCheck(failures, !serializedHostArtifact.includes(marker), `V34 host artifact must not contain secret marker ${marker}.`); + assertCheck(failures, !serializedLaneArtifact.includes(marker), `V34 lane artifact must not contain secret marker ${marker}.`); + } + + const hostArtifact = serializedHostArtifact ? JSON.parse(serializedHostArtifact) : null; + const laneArtifact = serializedLaneArtifact ? JSON.parse(serializedLaneArtifact) : null; + + if (hostArtifact) { + assertCheck(failures, hostArtifact.artifactId === 'v34-deployment-host-capability-catalog', 'Host artifactId must match.'); + assertCheck(failures, hostArtifact.schemaId === 'bitcode.v34.deploymentHostCapabilityCatalog.v1', 'Host schemaId must match.'); + assertCheck(failures, hostArtifact.version === 'V34' && hostArtifact.currentTarget === 'V33', 'Host artifact must bind V34 over active V33.'); + assertCheck(failures, hostArtifact.passed === true, 'Host artifact must pass.'); + assertCheck( + failures, + hostArtifact.sourceSafetyVerdict === 'source-safe-deployment-host-capability-metadata', + 'Host artifact must be source-safe deployment host metadata.', + ); + assertCheck(failures, includesAll(hostArtifact.requiredHostIds, REQUIRED_HOST_IDS), 'Host artifact must enumerate every required host.'); + assertCheck(failures, includesAll(hostArtifact.coverage.observedHostIds, REQUIRED_HOST_IDS), 'Host coverage must observe every host.'); + assertCheck(failures, hostArtifact.coverage.hostCount === 12, 'Host artifact must prove twelve host rows.'); + assertCheck(failures, hostArtifact.coverage.objectStorageRepresented === true, 'Host artifact must represent object storage.'); + assertCheck(failures, hostArtifact.coverage.databaseProjectionRepresented === true, 'Host artifact must represent database projection.'); + assertCheck(failures, hostArtifact.coverage.ledgerProjectionRepresented === true, 'Host artifact must represent ledger projection.'); + assertCheck(failures, hostArtifact.coverage.credentialsSerialized === false, 'Host artifact must not serialize credentials.'); + assertCheck(failures, hostArtifact.coverage.protectedSourceVisible === false, 'Host artifact must not expose source-bearing payloads.'); + assertCheck( + failures, + hostArtifact.rows.every((row) => /^v34-deployment-host-capability-row:[a-f0-9]{24}$/u.test(row.rowRoot)), + 'Host rows must have deterministic row roots.', + ); + assertCheck( + failures, + hostArtifact.sourceEvidence.every((entry) => entry.requiredTokens.every((token) => token.present === true)), + 'Host source evidence tokens must all be present.', + ); + assertCheck( + failures, + hostArtifact.testEvidence.every((entry) => entry.requiredTokens.every((token) => token.present === true)), + 'Host test evidence tokens must all be present.', + ); + } + + if (laneArtifact) { + assertCheck(failures, laneArtifact.artifactId === 'v34-environment-lane-contracts', 'Lane artifactId must match.'); + assertCheck(failures, laneArtifact.schemaId === 'bitcode.v34.environmentLaneContracts.v1', 'Lane schemaId must match.'); + assertCheck(failures, laneArtifact.version === 'V34' && laneArtifact.currentTarget === 'V33', 'Lane artifact must bind V34 over active V33.'); + assertCheck(failures, laneArtifact.passed === true, 'Lane artifact must pass.'); + assertCheck( + failures, + laneArtifact.sourceSafetyVerdict === 'source-safe-environment-lane-contract-metadata', + 'Lane artifact must be source-safe environment lane metadata.', + ); + assertCheck(failures, includesAll(laneArtifact.requiredLaneIds, REQUIRED_LANE_IDS), 'Lane artifact must enumerate every required lane.'); + assertCheck(failures, includesAll(laneArtifact.coverage.observedLaneIds, REQUIRED_LANE_IDS), 'Lane coverage must observe every lane.'); + assertCheck(failures, laneArtifact.coverage.laneCount === 7, 'Lane artifact must prove seven lane rows.'); + assertCheck( + failures, + laneArtifact.coverage.valueBearingMainnetAdmission === 'blocked_future_canon_required', + 'Lane artifact must keep value-bearing mainnet blocked.', + ); + assertCheck( + failures, + laneArtifact.coverage.valueBearingMainnetAdmittedHostCount === 0, + 'Lane artifact must not admit hosts in value-bearing mainnet.', + ); + assertCheck( + failures, + laneArtifact.coverage.mainnetReadyDryRunAdmission === 'dry_run_only', + 'Lane artifact must keep mainnet-ready dry run non-value-bearing.', + ); + assertCheck(failures, laneArtifact.coverage.credentialsSerialized === false, 'Lane artifact must not serialize credentials.'); + assertCheck(failures, laneArtifact.coverage.protectedSourceVisible === false, 'Lane artifact must not expose source-bearing payloads.'); + assertCheck( + failures, + laneArtifact.lanes.every((lane) => /^v34-environment-lane-contract:[a-f0-9]{24}$/u.test(lane.laneRoot)), + 'Lane rows must have deterministic lane roots.', + ); + assertCheck( + failures, + laneArtifact.sourceEvidence.every((entry) => entry.requiredTokens.every((token) => token.present === true)), + 'Lane source evidence tokens must all be present.', + ); + assertCheck( + failures, + laneArtifact.testEvidence.every((entry) => entry.requiredTokens.every((token) => token.present === true)), + 'Lane test evidence tokens must all be present.', + ); + } + + const spec = read(root, 'BITCODE_SPEC_V34.md'); + const delta = read(root, 'BITCODE_SPEC_V34_DELTA.md'); + const notes = read(root, 'BITCODE_SPEC_V34_NOTES.md'); + const parity = read(root, 'BITCODE_SPEC_V34_PARITY_MATRIX.md'); + const roadmap = read(root, 'SPECIFICATIONS_ROADMAP.md'); + const packageJson = read(root, 'package.json'); + const workflow = read(root, '.github/workflows/bitcode-gate-quality.yml'); + const source = read(root, 'packages/btd/src/deployment-host-capability-catalog.ts'); + const test = read(root, 'packages/btd/__tests__/deployment-host-capability-catalog.test.ts'); + const specifying = read(root, 'packages/protocol/src/canonical/v21-specifying.js'); + + for (const doc of [spec, delta, notes, parity]) { + assertCheck(failures, doc.includes(HOST_ARTIFACT), `V34 docs must mention ${HOST_ARTIFACT}.`); + assertCheck(failures, doc.includes(LANE_ARTIFACT), `V34 docs must mention ${LANE_ARTIFACT}.`); + assertCheck(failures, doc.includes('DeploymentHostCapabilityCatalog'), 'V34 docs must name DeploymentHostCapabilityCatalog.'); + assertCheck(failures, doc.includes('EnvironmentLaneContract'), 'V34 docs must name EnvironmentLaneContract.'); + assertCheck(failures, doc.includes('value-bearing-mainnet'), 'V34 docs must name value-bearing-mainnet.'); + assertCheck(failures, doc.includes('blocked_future_canon_required'), 'V34 docs must name blocked_future_canon_required.'); + } + + assertCheck( + failures, + /Current working gate: V34 Gate (?:[3-9]|10)\b/u.test(roadmap), + 'Roadmap must advance past V34 Gate 2 after this gate closes.', + ); + assertCheck(failures, packageJson.includes('"generate:v34-host-capability-environment-lanes"'), 'package.json must expose the Gate 2 generator.'); + assertCheck(failures, packageJson.includes('"check:v34-host-capability-environment-lanes"'), 'package.json must expose the Gate 2 artifact check.'); + assertCheck(failures, packageJson.includes('"check:v34-gate2"'), 'package.json must expose check:v34-gate2.'); + assertCheck(failures, workflow.includes('check-v34-gate2-host-capability-environment-lanes.mjs'), 'Gate workflow must run the V34 Gate 2 checker.'); + assertCheck(failures, workflow.includes('deployment-host-capability-catalog.test.ts'), 'Gate workflow must run the focused deployment host/lane test.'); + assertCheck(failures, specifying.includes(HOST_ARTIFACT), 'Spec-family profile must include the host artifact path.'); + assertCheck(failures, specifying.includes(LANE_ARTIFACT), 'Spec-family profile must include the lane artifact path.'); + + for (const phrase of [ + 'buildDeploymentHostCapabilityCatalog', + 'buildEnvironmentLaneContracts', + 'DEPLOYMENT_HOST_CAPABILITY_IDS', + 'ENVIRONMENT_LANE_CONTRACT_IDS', + 'pipeline_workers', + 'runtime_observers', + 'ledger_broadcasters', + 'object_storage', + 'database_projection', + 'ledger_projection', + 'blocked_future_canon_required', + ]) { + assertCheck(failures, source.includes(phrase), `Gate 2 source must include ${phrase}.`); + } + + for (const phrase of [ + 'catalogs website, API, MCP API, ChatGPT App, workers, observers, broadcasters, proof services, repair jobs, and storage projections', + 'catalogs local, regtest, signet, staging-testnet, public testnet, mainnet dry run, and blocked value-bearing mainnet lanes', + 'keeps value-bearing mainnet visible as blocked and without admitted runtime hosts', + 'fails closed when a required deployment host row is missing', + 'fails closed when value-bearing mainnet admits hosts or stops being blocked', + 'fails closed on secret-shaped or non-disclosable source catalog text', + ]) { + assertCheck(failures, test.includes(phrase), `Gate 2 test must assert: ${phrase}.`); + } + + if (failures.length) { + process.stderr.write('V34 Gate 2 Host Capability And Environment Lane Catalog check failed:\n'); + for (const failure of failures) { + process.stderr.write(`- ${failure}\n`); + } + process.exit(1); + } + + process.stdout.write(`V34 Gate 2 Host Capability And Environment Lane Catalog ok ${HOST_ARTIFACT} ${LANE_ARTIFACT}\n`); +} + +try { + main(); +} catch (error) { + const detail = error instanceof Error ? error.message : String(error); + process.stderr.write(`${detail}\n`); + process.exitCode = 1; +} diff --git a/scripts/generate-v34-host-capability-environment-lanes.mjs b/scripts/generate-v34-host-capability-environment-lanes.mjs new file mode 100644 index 000000000..6d0aa55ec --- /dev/null +++ b/scripts/generate-v34-host-capability-environment-lanes.mjs @@ -0,0 +1,569 @@ +#!/usr/bin/env node + +import { createHash } from 'node:crypto'; +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const repoRoot = path.resolve(__dirname, '..'); +const HOST_ARTIFACT_PATH = '.bitcode/v34-deployment-host-capability-catalog.json'; +const LANE_ARTIFACT_PATH = '.bitcode/v34-environment-lane-contracts.json'; +const GENERATED_AT = '2026-05-22T00:00:00.000Z'; + +const SECRET_MARKERS = Object.freeze([ + `${['sk', 'proj'].join('-')}-`, + `${['sb', 'secret'].join('_')}__`, + ['service', 'role'].join('_'), + ['eyJhbGciOiJI', 'UzI1NiIsInR5cCI6IkpXVCJ9'].join(''), + ['SUPABASE', 'SERVICE', 'ROLE'].join('_'), + ['OPENAI', 'API', 'KEY'].join('_'), + ['VERCEL', 'TOKEN'].join('_'), + ['VERCEL', 'OIDC', 'TOKEN'].join('_'), +]); +const SECRET_PATTERN = new RegExp(SECRET_MARKERS.map(escapeRegex).join('|'), 'u'); + +const requiredHostIds = Object.freeze([ + 'website', + 'api', + 'mcp_api', + 'chatgpt_app', + 'pipeline_workers', + 'runtime_observers', + 'ledger_broadcasters', + 'proof_services', + 'repair_jobs', + 'object_storage', + 'database_projection', + 'ledger_projection', +]); + +const requiredLaneIds = Object.freeze([ + 'local', + 'regtest', + 'signet', + 'staging-testnet', + 'public-testnet', + 'mainnet-ready-dry-run', + 'value-bearing-mainnet', +]); + +const hostRows = Object.freeze([ + { + hostId: 'website', + runtimeSurface: 'website', + ownerPackage: 'uapi', + runtimeCarrier: 'vercel-nextjs-website', + outboundNetworkPosture: 'provider_bound', + admissionStatus: 'admitted_non_value_lanes', + proofOutputPaths: [HOST_ARTIFACT_PATH], + validationCommand: + 'pnpm --dir uapi exec jest --runTestsByPath tests/terminalInterfaceIntegrationRegression.test.ts --runInBand', + supportedLaneIds: ['local', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + telemetryProofHookId: 'deployment.telemetry.website', + }, + { + hostId: 'api', + runtimeSurface: 'api', + ownerPackage: 'packages/api', + runtimeCarrier: 'vercel-node-api', + outboundNetworkPosture: 'provider_bound', + admissionStatus: 'admitted_non_value_lanes', + proofOutputPaths: [LANE_ARTIFACT_PATH], + validationCommand: + 'pnpm --filter @bitcode/api exec jest --config jest.config.cjs --runTestsByPath src/routes/__tests__/btd-crypto.test.ts --runInBand', + supportedLaneIds: ['local', 'regtest', 'signet', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + telemetryProofHookId: 'deployment.telemetry.api', + }, + { + hostId: 'mcp_api', + runtimeSurface: 'mcp_api', + ownerPackage: 'packages/executions-mcp/src/mcp-server', + runtimeCarrier: 'mcp-server-process', + outboundNetworkPosture: 'outbound_restricted', + admissionStatus: 'admitted_non_value_lanes', + proofOutputPaths: ['.bitcode/v33-mcp-api-tool-contracts.json'], + validationCommand: + 'pnpm --dir packages/executions-mcp/src/mcp-server run test:mcp -- --runTestsByPath src/__tests__/unit/pipeline-ingress-contract.test.ts --runInBand', + supportedLaneIds: ['local', 'staging-testnet', 'public-testnet'], + telemetryProofHookId: 'deployment.telemetry.mcp-api', + }, + { + hostId: 'chatgpt_app', + runtimeSurface: 'chatgpt_app', + ownerPackage: 'packages/chatgptapp', + runtimeCarrier: 'chatgpt-action-service', + outboundNetworkPosture: 'outbound_restricted', + admissionStatus: 'admitted_non_value_lanes', + proofOutputPaths: ['.bitcode/v33-chatgpt-app-action-contracts.json'], + validationCommand: + 'pnpm --dir packages/chatgptapp exec jest --runTestsByPath src/__tests__/tools.test.ts --runInBand', + supportedLaneIds: ['local', 'staging-testnet', 'public-testnet'], + telemetryProofHookId: 'deployment.telemetry.chatgpt-app', + }, + { + hostId: 'pipeline_workers', + runtimeSurface: 'worker', + ownerPackage: 'packages/pipeline-hosts', + runtimeCarrier: 'vercel-sandbox-worker', + outboundNetworkPosture: 'provider_bound', + admissionStatus: 'admitted_non_value_lanes', + proofOutputPaths: ['.bitcode/v34-distributed-execution-runtime-receipts.json'], + validationCommand: 'pnpm --filter @bitcode/pipeline-hosts typecheck', + supportedLaneIds: ['local', 'regtest', 'signet', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + telemetryProofHookId: 'deployment.telemetry.pipeline-worker', + }, + { + hostId: 'runtime_observers', + runtimeSurface: 'observer', + ownerPackage: 'packages/btd', + runtimeCarrier: 'scheduled-observer-job', + outboundNetworkPosture: 'provider_bound', + admissionStatus: 'admitted_non_value_lanes', + proofOutputPaths: ['.bitcode/v34-runtime-observers-broadcasters-repair-jobs.json'], + validationCommand: + 'pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/v32-testnet-mainnet-readiness-rehearsal.test.ts', + supportedLaneIds: ['regtest', 'signet', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + telemetryProofHookId: 'deployment.telemetry.runtime-observer', + }, + { + hostId: 'ledger_broadcasters', + runtimeSurface: 'broadcaster', + ownerPackage: 'packages/btd', + runtimeCarrier: 'ledger-broadcaster-job', + outboundNetworkPosture: 'provider_bound', + admissionStatus: 'admitted_non_value_lanes', + proofOutputPaths: ['.bitcode/v34-runtime-observers-broadcasters-repair-jobs.json'], + validationCommand: + 'pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/btc-fee-operation.test.ts', + supportedLaneIds: ['regtest', 'signet', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + telemetryProofHookId: 'deployment.telemetry.ledger-broadcaster', + }, + { + hostId: 'proof_services', + runtimeSurface: 'proof_service', + ownerPackage: 'packages/protocol', + runtimeCarrier: 'proof-generation-job', + outboundNetworkPosture: 'none', + admissionStatus: 'admitted_non_value_lanes', + proofOutputPaths: ['.bitcode/v34-promotion-readiness-report.json'], + validationCommand: 'pnpm --filter @bitcode/protocol test', + supportedLaneIds: ['local', 'regtest', 'signet', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + telemetryProofHookId: 'deployment.telemetry.proof-service', + }, + { + hostId: 'repair_jobs', + runtimeSurface: 'repair_job', + ownerPackage: 'packages/btd', + runtimeCarrier: 'operator-repair-command', + outboundNetworkPosture: 'provider_bound', + admissionStatus: 'admitted_non_value_lanes', + proofOutputPaths: ['.bitcode/v34-rollback-upgrade-data-repair-playbooks.json'], + validationCommand: + 'pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/reconciliation.test.ts', + supportedLaneIds: ['local', 'regtest', 'signet', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + telemetryProofHookId: 'deployment.telemetry.repair-job', + }, + { + hostId: 'object_storage', + runtimeSurface: 'object_storage', + ownerPackage: 'packages/pipeline-hosts', + runtimeCarrier: 'durable-object-storage', + outboundNetworkPosture: 'egress_locked', + admissionStatus: 'admitted_projection_carrier', + proofOutputPaths: ['.bitcode/v34-deployment-storage-posture.json'], + validationCommand: 'pnpm --filter @bitcode/pipeline-hosts typecheck', + supportedLaneIds: ['local', 'regtest', 'signet', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + telemetryProofHookId: 'deployment.telemetry.object-storage', + }, + { + hostId: 'database_projection', + runtimeSurface: 'database_projection', + ownerPackage: 'packages/supabase', + runtimeCarrier: 'supabase-postgres-projection', + outboundNetworkPosture: 'provider_bound', + admissionStatus: 'admitted_projection_carrier', + proofOutputPaths: ['.bitcode/v34-deployment-storage-posture.json'], + validationCommand: 'pnpm --filter @bitcode/btd typecheck', + supportedLaneIds: ['local', 'regtest', 'signet', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + telemetryProofHookId: 'deployment.telemetry.database-projection', + }, + { + hostId: 'ledger_projection', + runtimeSurface: 'ledger_projection', + ownerPackage: 'packages/btd', + runtimeCarrier: 'ledger-projection-store', + outboundNetworkPosture: 'provider_bound', + admissionStatus: 'admitted_projection_carrier', + proofOutputPaths: ['.bitcode/v34-deployment-storage-posture.json'], + validationCommand: + 'pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/reconciliation.test.ts', + supportedLaneIds: ['regtest', 'signet', 'staging-testnet', 'public-testnet', 'mainnet-ready-dry-run'], + telemetryProofHookId: 'deployment.telemetry.ledger-projection', + }, +]); + +const laneRows = Object.freeze([ + { + laneId: 'local', + bitcoinNetworkPosture: 'none', + supabaseProjectPosture: 'local_process', + vercelProjectPosture: 'local_process', + valueBearingAdmission: 'not_value_bearing', + walletPolicy: 'no_wallet', + admittedHostIds: ['website', 'api', 'mcp_api', 'chatgpt_app', 'pipeline_workers', 'proof_services', 'repair_jobs', 'object_storage', 'database_projection'], + telemetryProofHookId: 'deployment.telemetry.lane.local', + }, + { + laneId: 'regtest', + bitcoinNetworkPosture: 'regtest', + supabaseProjectPosture: 'local_project', + vercelProjectPosture: 'local_project', + valueBearingAdmission: 'not_value_bearing', + walletPolicy: 'regtest_wallet', + admittedHostIds: ['api', 'pipeline_workers', 'runtime_observers', 'ledger_broadcasters', 'proof_services', 'repair_jobs', 'object_storage', 'database_projection', 'ledger_projection'], + telemetryProofHookId: 'deployment.telemetry.lane.regtest', + }, + { + laneId: 'signet', + bitcoinNetworkPosture: 'signet', + supabaseProjectPosture: 'staging_testnet_project', + vercelProjectPosture: 'staging_testnet_project', + valueBearingAdmission: 'not_value_bearing', + walletPolicy: 'signet_wallet', + admittedHostIds: ['api', 'pipeline_workers', 'runtime_observers', 'ledger_broadcasters', 'proof_services', 'repair_jobs', 'object_storage', 'database_projection', 'ledger_projection'], + telemetryProofHookId: 'deployment.telemetry.lane.signet', + }, + { + laneId: 'staging-testnet', + bitcoinNetworkPosture: 'signet', + supabaseProjectPosture: 'staging_testnet_project', + vercelProjectPosture: 'staging_testnet_project', + valueBearingAdmission: 'not_value_bearing', + walletPolicy: 'signet_wallet', + admittedHostIds: [...requiredHostIds], + telemetryProofHookId: 'deployment.telemetry.lane.staging-testnet', + }, + { + laneId: 'public-testnet', + bitcoinNetworkPosture: 'testnet', + supabaseProjectPosture: 'public_testnet_project', + vercelProjectPosture: 'public_testnet_project', + valueBearingAdmission: 'not_value_bearing', + walletPolicy: 'testnet_wallet', + admittedHostIds: [...requiredHostIds], + telemetryProofHookId: 'deployment.telemetry.lane.public-testnet', + }, + { + laneId: 'mainnet-ready-dry-run', + bitcoinNetworkPosture: 'mainnet', + supabaseProjectPosture: 'production_project_dry_run', + vercelProjectPosture: 'production_project_dry_run', + valueBearingAdmission: 'dry_run_only', + walletPolicy: 'mainnet_watch_only', + admittedHostIds: requiredHostIds.filter((hostId) => hostId !== 'ledger_broadcasters'), + telemetryProofHookId: 'deployment.telemetry.lane.mainnet-ready-dry-run', + }, + { + laneId: 'value-bearing-mainnet', + bitcoinNetworkPosture: 'mainnet', + supabaseProjectPosture: 'production_project_blocked', + vercelProjectPosture: 'production_project_blocked', + valueBearingAdmission: 'blocked_future_canon_required', + walletPolicy: 'mainnet_value_blocked', + admittedHostIds: [], + telemetryProofHookId: 'deployment.telemetry.lane.value-bearing-mainnet', + }, +]); + +const sourceFiles = Object.freeze([ + 'packages/btd/src/deployment-host-capability-catalog.ts', + 'packages/btd/src/index.ts', + 'BITCODE_SPEC_V34.md', + 'BITCODE_SPEC_V34_DELTA.md', + 'BITCODE_SPEC_V34_PARITY_MATRIX.md', +]); + +const testFiles = Object.freeze([ + 'packages/btd/__tests__/deployment-host-capability-catalog.test.ts', + 'scripts/check-v34-gate2-host-capability-environment-lanes.mjs', +]); + +function escapeRegex(value) { + return value.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&'); +} + +function read(relativePath) { + return readFileSync(path.join(repoRoot, relativePath), 'utf8'); +} + +function sha256(value) { + return `sha256:${createHash('sha256').update(value).digest('hex')}`; +} + +function stableRoot(prefix, parts) { + const hash = createHash('sha256').update(parts.join('\u001f')).digest('hex').slice(0, 24); + return `${prefix}:${hash}`; +} + +function sortJson(value) { + if (Array.isArray(value)) return value.map(sortJson); + if (!value || typeof value !== 'object') return value; + return Object.fromEntries( + Object.entries(value) + .sort(([left], [right]) => left.localeCompare(right)) + .map(([key, entry]) => [key, sortJson(entry)]), + ); +} + +function stableStringify(value) { + return `${JSON.stringify(sortJson(value), null, 2)}\n`; +} + +function scanTokens(relativePath, tokens) { + const text = read(relativePath); + return { + relativePath, + digest: sha256(text), + requiredTokens: tokens.map((token) => ({ + token, + present: text.includes(token), + })), + }; +} + +function allTokensPresent(scan) { + return scan.requiredTokens.every((entry) => entry.present); +} + +function withHostRowRoots(rows) { + return rows.map((row) => ({ + ...row, + rowRoot: stableRoot('v34-deployment-host-capability-row', [ + row.hostId, + row.runtimeSurface, + row.ownerPackage, + row.runtimeCarrier, + row.outboundNetworkPosture, + row.admissionStatus, + row.proofOutputPaths.join(','), + row.validationCommand, + row.supportedLaneIds.join(','), + row.telemetryProofHookId, + ]), + })); +} + +function withLaneRoots(rows) { + return rows.map((row) => ({ + ...row, + laneRoot: stableRoot('v34-environment-lane-contract', [ + row.laneId, + row.bitcoinNetworkPosture, + row.supabaseProjectPosture, + row.vercelProjectPosture, + row.valueBearingAdmission, + row.walletPolicy, + row.admittedHostIds.join(','), + row.telemetryProofHookId, + ]), + })); +} + +export function buildV34DeploymentHostCapabilityCatalogArtifact() { + const rows = withHostRowRoots(hostRows); + const observedHostIds = rows.map((row) => row.hostId); + const missingHostIds = requiredHostIds.filter((hostId) => !observedHostIds.includes(hostId)); + const sourceEvidence = [ + scanTokens('packages/btd/src/deployment-host-capability-catalog.ts', [ + 'DeploymentHostCapabilityCatalog', + 'EnvironmentLaneContract', + 'value-bearing-mainnet', + 'blocked_future_canon_required', + 'pipeline_workers', + 'object_storage', + 'database_projection', + 'ledger_projection', + ]), + scanTokens('packages/btd/src/index.ts', ['deployment-host-capability-catalog']), + scanTokens('BITCODE_SPEC_V34.md', [ + HOST_ARTIFACT_PATH, + LANE_ARTIFACT_PATH, + 'DeploymentHostCapabilityCatalog', + 'EnvironmentLaneContract', + ]), + ]; + const testEvidence = [ + scanTokens('packages/btd/__tests__/deployment-host-capability-catalog.test.ts', [ + 'catalogs website, API, MCP API, ChatGPT App, workers, observers, broadcasters, proof services, repair jobs, and storage projections', + 'keeps value-bearing mainnet visible as blocked and without admitted runtime hosts', + 'fails closed when a required deployment host row is missing', + 'fails closed on secret-shaped or non-disclosable source catalog text', + ]), + scanTokens('scripts/check-v34-gate2-host-capability-environment-lanes.mjs', [ + 'check:v34-host-capability-environment-lanes', + 'deployment-host-capability-catalog.test.ts', + 'Host Capability And Environment Lane Catalog', + ]), + ]; + const sourceEvidenceComplete = sourceEvidence.every(allTokensPresent); + const testEvidenceComplete = testEvidence.every(allTokensPresent); + const passed = + missingHostIds.length === 0 && + rows.length === 12 && + sourceEvidenceComplete && + testEvidenceComplete; + + return { + artifactId: 'v34-deployment-host-capability-catalog', + schemaId: 'bitcode.v34.deploymentHostCapabilityCatalog.v1', + version: 'V34', + currentTarget: 'V33', + generatedAt: GENERATED_AT, + sourceSafetyVerdict: 'source-safe-deployment-host-capability-metadata', + requiredHostIds, + catalogRoot: stableRoot('v34-deployment-host-capability-catalog', rows.map((row) => row.rowRoot)), + rows, + coverage: { + observedHostIds, + missingHostIds, + hostCount: rows.length, + websiteRepresented: observedHostIds.includes('website'), + apiRepresented: observedHostIds.includes('api'), + objectStorageRepresented: observedHostIds.includes('object_storage'), + databaseProjectionRepresented: observedHostIds.includes('database_projection'), + ledgerProjectionRepresented: observedHostIds.includes('ledger_projection'), + valueBearingMainnetHidden: false, + protectedSourceVisible: false, + credentialsSerialized: false, + }, + sharedFixtureFiles: [...sourceFiles, ...testFiles], + sourceEvidence, + testEvidence, + passed, + closureCommand: 'pnpm run check:v34-gate2', + }; +} + +export function buildV34EnvironmentLaneContractsArtifact() { + const lanes = withLaneRoots(laneRows); + const observedLaneIds = lanes.map((lane) => lane.laneId); + const missingLaneIds = requiredLaneIds.filter((laneId) => !observedLaneIds.includes(laneId)); + const valueBearingMainnet = lanes.find((lane) => lane.laneId === 'value-bearing-mainnet'); + const sourceEvidence = [ + scanTokens('packages/btd/src/deployment-host-capability-catalog.ts', [ + 'ENVIRONMENT_LANE_CONTRACT_IDS', + 'mainnet-ready-dry-run', + 'value-bearing-mainnet', + 'blocked_future_canon_required', + 'buildEnvironmentLaneContracts', + ]), + scanTokens('BITCODE_SPEC_V34_DELTA.md', [ + 'local, regtest, signet, staging-testnet, public testnet, mainnet-ready dry run, and value-bearing mainnet lanes are represented', + LANE_ARTIFACT_PATH, + ]), + ]; + const testEvidence = [ + scanTokens('packages/btd/__tests__/deployment-host-capability-catalog.test.ts', [ + 'catalogs local, regtest, signet, staging-testnet, public testnet, mainnet dry run, and blocked value-bearing mainnet lanes', + 'fails closed when value-bearing mainnet admits hosts or stops being blocked', + 'fails closed when mainnet-ready dry run is made value-bearing', + ]), + scanTokens('scripts/check-v34-gate2-host-capability-environment-lanes.mjs', [ + 'value-bearing-mainnet', + 'blocked_future_canon_required', + 'environment-lane-contracts', + ]), + ]; + const sourceEvidenceComplete = sourceEvidence.every(allTokensPresent); + const testEvidenceComplete = testEvidence.every(allTokensPresent); + const passed = + missingLaneIds.length === 0 && + lanes.length === 7 && + valueBearingMainnet?.valueBearingAdmission === 'blocked_future_canon_required' && + valueBearingMainnet.admittedHostIds.length === 0 && + sourceEvidenceComplete && + testEvidenceComplete; + + return { + artifactId: 'v34-environment-lane-contracts', + schemaId: 'bitcode.v34.environmentLaneContracts.v1', + version: 'V34', + currentTarget: 'V33', + generatedAt: GENERATED_AT, + sourceSafetyVerdict: 'source-safe-environment-lane-contract-metadata', + requiredLaneIds, + laneContractRoot: stableRoot('v34-environment-lane-contracts', lanes.map((lane) => lane.laneRoot)), + lanes, + coverage: { + observedLaneIds, + missingLaneIds, + laneCount: lanes.length, + valueBearingMainnetAdmission: valueBearingMainnet?.valueBearingAdmission ?? null, + valueBearingMainnetAdmittedHostCount: valueBearingMainnet?.admittedHostIds.length ?? null, + mainnetReadyDryRunAdmission: + lanes.find((lane) => lane.laneId === 'mainnet-ready-dry-run')?.valueBearingAdmission ?? null, + valueBearingMainnetHidden: false, + protectedSourceVisible: false, + credentialsSerialized: false, + }, + sharedFixtureFiles: [...sourceFiles, ...testFiles], + sourceEvidence, + testEvidence, + passed, + closureCommand: 'pnpm run check:v34-gate2', + }; +} + +function assertSafeArtifact(artifact, artifactPath) { + const serialized = stableStringify(artifact); + if (SECRET_PATTERN.test(serialized)) { + throw new Error(`${artifactPath} contains a secret-shaped marker.`); + } + if (!artifact.passed) { + throw new Error(`${artifactPath} source or test evidence is incomplete.`); + } + + return serialized; +} + +function writeArtifact(artifact, artifactPath) { + const serialized = assertSafeArtifact(artifact, artifactPath); + mkdirSync(path.dirname(path.join(repoRoot, artifactPath)), { recursive: true }); + writeFileSync(path.join(repoRoot, artifactPath), serialized); + return serialized; +} + +function checkArtifact(artifact, artifactPath) { + const next = assertSafeArtifact(artifact, artifactPath); + const artifactFile = path.join(repoRoot, artifactPath); + if (!existsSync(artifactFile)) { + throw new Error(`${artifactPath} is missing. Run pnpm run generate:v34-host-capability-environment-lanes.`); + } + const current = readFileSync(artifactFile, 'utf8'); + if (current !== next) { + throw new Error(`${artifactPath} is stale. Run pnpm run generate:v34-host-capability-environment-lanes.`); + } +} + +function main() { + const mode = process.argv.includes('--check') ? 'check' : 'write'; + const hostArtifact = buildV34DeploymentHostCapabilityCatalogArtifact(); + const laneArtifact = buildV34EnvironmentLaneContractsArtifact(); + + if (mode === 'check') { + checkArtifact(hostArtifact, HOST_ARTIFACT_PATH); + checkArtifact(laneArtifact, LANE_ARTIFACT_PATH); + process.stdout.write(`V34 host capability and environment lane artifacts ok ${HOST_ARTIFACT_PATH} ${LANE_ARTIFACT_PATH}\n`); + return; + } + + writeArtifact(hostArtifact, HOST_ARTIFACT_PATH); + writeArtifact(laneArtifact, LANE_ARTIFACT_PATH); + process.stdout.write(`Wrote ${HOST_ARTIFACT_PATH}\nWrote ${LANE_ARTIFACT_PATH}\n`); +} + +if (import.meta.url === `file://${process.argv[1]}`) { + main(); +}