diff --git a/.bitcode/v34-deployment-host-capability-catalog.json b/.bitcode/v34-deployment-host-capability-catalog.json index 798f534da..042823256 100644 --- a/.bitcode/v34-deployment-host-capability-catalog.json +++ b/.bitcode/v34-deployment-host-capability-catalog.json @@ -350,7 +350,7 @@ ] }, { - "digest": "sha256:6090906d79ae35e86d0548ca67e0f3c75300dd24d21574bb5b407174ba1ed0c2", + "digest": "sha256:70f7d98470ffa6ec95d96a079bfa50f4893070256d158c247bcf54b12b5651f9", "relativePath": "packages/btd/src/index.ts", "requiredTokens": [ { @@ -360,7 +360,7 @@ ] }, { - "digest": "sha256:82edb3ca798a13834379748430690382cfb38bb9f7489e5a180986431abfdff4", + "digest": "sha256:91837c65a866ed769915f0440cf05e799197f2b1634a2d11b7fc58915e923ce6", "relativePath": "BITCODE_SPEC_V34.md", "requiredTokens": [ { diff --git a/.bitcode/v34-deployment-storage-posture.json b/.bitcode/v34-deployment-storage-posture.json new file mode 100644 index 000000000..bddb8aaf1 --- /dev/null +++ b/.bitcode/v34-deployment-storage-posture.json @@ -0,0 +1,540 @@ +{ + "artifactId": "v34-deployment-storage-posture", + "carriers": [ + { + "backupPosture": "replay from ledger roots, database projection roots, and proof artifacts", + "carrierId": "ledger_derived_state", + "carrierRoot": "v34-deployment-storage-carrier:4a5cb4645541c3d3950251fb", + "disclosurePolicy": "ledger_commitment_only", + "driftDetection": "compare-ledger-root-to-database-projection-root-before-unlock", + "durabilityPosture": "append_only_replayable", + "encryptionPosture": "hash-chained source-safe roots with provider encryption at rest", + "ownerHostId": "ledger_projection", + "postSettlementVisibility": "source_safe_roots_only", + "preSettlementVisibility": "source_safe_roots_only", + "proofRootBasis": [ + "BtdAssetPackMintReceipt", + "BtdReadReceipt", + "BtdRightsTransferReceipt" + ], + "repairCommand": "pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/reconciliation.test.ts", + "repairPosture": "hold AssetPack unlock and replay ledger projection from committed ledger root", + "requiredRoots": [ + "ledger", + "proof", + "audit_log" + ], + "retentionClass": "testnet-and-mainnet-dry-run-audit-retained", + "rollbackMaterialPosture": "rollback uses prior ledger projection root and replay command", + "storageClass": "ledger_derived_state", + "storageOwnerPackage": "packages/btd", + "storesProtectedSourcePayload": false, + "supportedLaneIds": [ + "local", + "regtest", + "signet", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run" + ], + "validationCommand": "pnpm run check:v34-gate4" + }, + { + "backupPosture": "Supabase point-in-time restore plus deterministic projection replay", + "carrierId": "canonical_database_projection", + "carrierRoot": "v34-deployment-storage-carrier:5aff28b395029c6b4b30b534", + "disclosurePolicy": "database_projection_only", + "driftDetection": "compare-database-projection-root-to-ledger-root-and-object-storage-root", + "durabilityPosture": "durable_projection", + "encryptionPosture": "provider encrypted at rest; service access only through server-side policy", + "ownerHostId": "database_projection", + "postSettlementVisibility": "source_safe_roots_only", + "preSettlementVisibility": "source_safe_roots_only", + "proofRootBasis": [ + "ledger database reconciliation", + "DeploymentStoragePosture" + ], + "repairCommand": "pnpm run db:data-health:ci && pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/reconciliation.test.ts", + "repairPosture": "block paid unlock until projection repair writes a new database projection root", + "requiredRoots": [ + "database_projection", + "ledger", + "proof", + "audit_log" + ], + "retentionClass": "lane-retained-postgres-projection-with-point-in-time-restore", + "rollbackMaterialPosture": "migration rollback requires prior schema root and projection repair root", + "storageClass": "canonical_database_projection", + "storageOwnerPackage": "packages/supabase", + "storesProtectedSourcePayload": false, + "supportedLaneIds": [ + "local", + "regtest", + "signet", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run" + ], + "validationCommand": "pnpm run check:v34-gate4" + }, + { + "backupPosture": "encrypted protected-object backup with proof-rooted restore command", + "carrierId": "protected_assetpack_object_storage", + "carrierRoot": "v34-deployment-storage-carrier:d5712f8905013e26b4b02af7", + "disclosurePolicy": "protected_source_locked_until_settlement", + "driftDetection": "deny source visibility unless paid settlement root and object root both verify", + "durabilityPosture": "encrypted_durable_object", + "encryptionPosture": "encrypted at rest with lane-scoped object key policy and no tracked key material", + "ownerHostId": "object_storage", + "postSettlementVisibility": "reader_unlocked_after_settlement", + "preSettlementVisibility": "blocked_before_settlement", + "proofRootBasis": [ + "AssetPackPreview", + "SettlementUnlock", + "object-storage receipt root" + ], + "repairCommand": "pnpm run check:v34-gate4", + "repairPosture": "lock delivery and rewrite from authorized encrypted artifact root after operator approval", + "requiredRoots": [ + "object_storage", + "proof", + "audit_log", + "rollback" + ], + "retentionClass": "rights-retained-until-read-license-expiry-or-operator-deletion", + "rollbackMaterialPosture": "rollback material may reference encrypted object root but never exposes payload", + "storageClass": "object_storage", + "storageOwnerPackage": "packages/pipeline-hosts", + "storesProtectedSourcePayload": true, + "supportedLaneIds": [ + "local", + "regtest", + "signet", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run" + ], + "validationCommand": "pnpm run check:v34-gate4" + }, + { + "backupPosture": "preview object can be regenerated from runtime receipt roots", + "carrierId": "source_safe_assetpack_preview_storage", + "carrierRoot": "v34-deployment-storage-carrier:33387fd5adf4880bf3701b07", + "disclosurePolicy": "source_safe_preview_only", + "driftDetection": "compare-preview-object-root-to-database-preview-root", + "durabilityPosture": "source_safe_durable_object", + "encryptionPosture": "provider encrypted at rest; payload limited to measurements and roots", + "ownerHostId": "object_storage", + "postSettlementVisibility": "source_safe_roots_only", + "preSettlementVisibility": "source_safe_roots_only", + "proofRootBasis": [ + "AssetPackPreview", + "ReadFitsFindingSynthesis", + "InterfaceTelemetryProofHook" + ], + "repairCommand": "pnpm --filter @bitcode/pipeline-asset-pack exec jest --config jest.config.cjs --runTestsByPath src/__tests__/asset-pack-disclosure.test.ts --runInBand", + "repairPosture": "withhold preview update until source-safe projection is regenerated", + "requiredRoots": [ + "object_storage", + "database_projection", + "proof" + ], + "retentionClass": "read-preview-retained-for-transaction-history", + "rollbackMaterialPosture": "preview rollback rewrites source-safe measurements from receipt output root", + "storageClass": "object_storage", + "storageOwnerPackage": "packages/pipeline-hosts", + "storesProtectedSourcePayload": false, + "supportedLaneIds": [ + "local", + "regtest", + "signet", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run" + ], + "validationCommand": "pnpm run check:v34-gate4" + }, + { + "backupPosture": "recreate from canonical inputs and deterministic generator scripts", + "carrierId": "generated_proof_artifacts", + "carrierRoot": "v34-deployment-storage-carrier:b090cebdb206396fabe6ec44", + "disclosurePolicy": "source_safe_proof_only", + "driftDetection": "generated-artifact-check-compares-current-output-to-tracked-artifact", + "durabilityPosture": "generated_replayable_artifact", + "encryptionPosture": "source-safe generated JSON with secret scanning before commit", + "ownerHostId": "proof_services", + "postSettlementVisibility": "source_safe_roots_only", + "preSettlementVisibility": "source_safe_roots_only", + "proofRootBasis": [ + "BITCODE_SPEC_V34.md", + "BITCODE_SPEC_V34_PARITY_MATRIX.md" + ], + "repairCommand": "pnpm run check:spec-quality && pnpm run check:v34-gate4", + "repairPosture": "regenerate proof artifact from canonical source and re-run promotion checks", + "requiredRoots": [ + "proof", + "audit_log" + ], + "retentionClass": "repository-retained-generated-proof-artifact", + "rollbackMaterialPosture": "rollback restores prior generated artifact root before promotion", + "storageClass": "proof_artifact", + "storageOwnerPackage": "packages/protocol", + "storesProtectedSourcePayload": false, + "supportedLaneIds": [ + "local", + "regtest", + "signet", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run" + ], + "validationCommand": "pnpm run check:v34-gate4" + }, + { + "backupPosture": "audit log export stores roots and redacted event envelopes", + "carrierId": "audit_log_stream", + "carrierRoot": "v34-deployment-storage-carrier:accc20b99c7ad5791e4cc382", + "disclosurePolicy": "operator_audit_only", + "driftDetection": "runtime receipt log root must match audit event stream root", + "durabilityPosture": "append_only_log", + "encryptionPosture": "provider encrypted at rest with redacted structured events", + "ownerHostId": "runtime_observers", + "postSettlementVisibility": "operator_source_safe_only", + "preSettlementVisibility": "operator_source_safe_only", + "proofRootBasis": [ + "DistributedExecutionRuntimeReceipt", + "InterfaceTelemetryProofHook" + ], + "repairCommand": "pnpm run check:v34-gate3 && pnpm run check:v34-gate4", + "repairPosture": "mark execution blocked and append repair event before replay", + "requiredRoots": [ + "audit_log", + "proof" + ], + "retentionClass": "lane-audit-log-retention-with-operator-export", + "rollbackMaterialPosture": "audit rollback is append-only correction event, not deletion", + "storageClass": "audit_log", + "storageOwnerPackage": "packages/observability", + "storesProtectedSourcePayload": false, + "supportedLaneIds": [ + "local", + "regtest", + "signet", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run" + ], + "validationCommand": "pnpm run check:v34-gate4" + }, + { + "backupPosture": "rollback bundle copied to encrypted backup carrier with proof root", + "carrierId": "rollback_material", + "carrierRoot": "v34-deployment-storage-carrier:20bbbf2b14b3f5e0768fd790", + "disclosurePolicy": "operator_rollback_only", + "driftDetection": "rollback gap blocks deployment promotion and paid unlock", + "durabilityPosture": "operator_controlled_rollback", + "encryptionPosture": "encrypted at rest; rollback bundle references roots instead of payload text", + "ownerHostId": "repair_jobs", + "postSettlementVisibility": "internal_recovery_only", + "preSettlementVisibility": "blocked_before_settlement", + "proofRootBasis": [ + "RollbackUpgradeRepairPlaybook", + "DeploymentStoragePosture" + ], + "repairCommand": "pnpm run check:v34-gate4", + "repairPosture": "block traffic promotion until rollback bundle root and verification command pass", + "requiredRoots": [ + "rollback", + "object_storage", + "database_projection", + "ledger", + "proof" + ], + "retentionClass": "operator-retained-until-deployment-successor-proof", + "rollbackMaterialPosture": "required for migration, projection, and object-storage repair", + "storageClass": "rollback_material", + "storageOwnerPackage": "packages/btd", + "storesProtectedSourcePayload": true, + "supportedLaneIds": [ + "local", + "regtest", + "signet", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run" + ], + "validationCommand": "pnpm run check:v34-gate4" + }, + { + "backupPosture": "required backup root for protected objects, projections, proofs, and rollback bundles", + "carrierId": "encrypted_backups", + "carrierRoot": "v34-deployment-storage-carrier:ee276b370b8b45bd035ff7fe", + "disclosurePolicy": "encrypted_recovery_only", + "driftDetection": "backup root must match carrier roots before promotion readiness", + "durabilityPosture": "encrypted_backup", + "encryptionPosture": "encrypted backup carrier with no tracked key values", + "ownerHostId": "object_storage", + "postSettlementVisibility": "internal_recovery_only", + "preSettlementVisibility": "blocked_before_settlement", + "proofRootBasis": [ + "DeploymentReadinessRehearsal", + "DeploymentStoragePosture" + ], + "repairCommand": "pnpm run check:v34-gate4", + "repairPosture": "deny restore visibility until backup proof root and repair job receipt verify", + "requiredRoots": [ + "object_storage", + "database_projection", + "ledger", + "proof", + "audit_log", + "rollback" + ], + "retentionClass": "lane-scoped-backup-retention-with-manual-deletion-proof", + "rollbackMaterialPosture": "backup restore is admissible only through repair job receipt", + "storageClass": "backup", + "storageOwnerPackage": "packages/pipeline-hosts", + "storesProtectedSourcePayload": true, + "supportedLaneIds": [ + "local", + "regtest", + "signet", + "staging-testnet", + "public-testnet", + "mainnet-ready-dry-run" + ], + "validationCommand": "pnpm run check:v34-gate4" + } + ], + "closureCommand": "pnpm run check:v34-gate4", + "coverage": { + "auditLogsCovered": true, + "backupsCovered": true, + "carrierCount": 8, + "credentialsSerialized": false, + "databaseProjectionCovered": true, + "encryptionCovered": true, + "ledgerDatabaseProjectionDriftRepairable": true, + "ledgerDerivedStateCovered": true, + "missingCarrierIds": [], + "objectStorageCovered": true, + "objectStorageProjectionDriftRepairable": true, + "observedCarrierIds": [ + "ledger_derived_state", + "canonical_database_projection", + "protected_assetpack_object_storage", + "source_safe_assetpack_preview_storage", + "generated_proof_artifacts", + "audit_log_stream", + "rollback_material", + "encrypted_backups" + ], + "proofArtifactsCovered": true, + "protectedSourcePayloadCarrierCount": 3, + "protectedSourceVisible": false, + "repairCommandsCovered": true, + "retentionCovered": true, + "rollbackMaterialCovered": true, + "sourceBearingAssetPackLockedBeforeSettlement": true, + "valueBearingMainnetAdmitted": false + }, + "currentTarget": "V33", + "driftRepairFixtures": [ + { + "affectedCarrierIds": [ + "ledger_derived_state", + "canonical_database_projection" + ], + "blocksSourceVisibility": true, + "blocksUnlock": true, + "detectionRoot": "sha256:ledger-database-projection-drift-root", + "driftKind": "ledger_database_projection_drift", + "expectedFinalPosture": "ledger and database projection roots agree before unlock resumes", + "fixtureId": "ledger-database-projection-drift", + "fixtureRoot": "v34-deployment-storage-drift-repair-fixture:b5be350a0d692d8f996ff6e0", + "proofRootBasis": [ + "ledgerProjectionRoot", + "databaseProjectionRoot", + "repairJobReceiptRoot" + ], + "repairCommand": "pnpm run db:data-health:ci && pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/reconciliation.test.ts", + "repairPosture": "repair database projection from ledger root before AssetPack unlock", + "validationCommand": "pnpm run check:v34-gate4" + }, + { + "affectedCarrierIds": [ + "canonical_database_projection", + "protected_assetpack_object_storage", + "source_safe_assetpack_preview_storage" + ], + "blocksSourceVisibility": true, + "blocksUnlock": true, + "detectionRoot": "sha256:database-object-storage-projection-drift-root", + "driftKind": "database_object_storage_projection_drift", + "expectedFinalPosture": "database projection and object storage roots agree before delivery resumes", + "fixtureId": "database-object-storage-projection-drift", + "fixtureRoot": "v34-deployment-storage-drift-repair-fixture:29c07cf2b069b0d455342099", + "proofRootBasis": [ + "databaseProjectionRoot", + "objectStorageRoot", + "repairJobReceiptRoot" + ], + "repairCommand": "pnpm run check:v34-gate4", + "repairPosture": "repair object root projection or rewrite authorized object before source visibility", + "validationCommand": "pnpm run check:v34-gate4" + }, + { + "affectedCarrierIds": [ + "protected_assetpack_object_storage", + "rollback_material", + "encrypted_backups" + ], + "blocksSourceVisibility": true, + "blocksUnlock": true, + "detectionRoot": "sha256:unpaid-protected-assetpack-access-attempt-root", + "driftKind": "unpaid_source_visibility_attempt", + "expectedFinalPosture": "protected AssetPack payload remains hidden before settlement", + "fixtureId": "unpaid-protected-assetpack-access-attempt", + "fixtureRoot": "v34-deployment-storage-drift-repair-fixture:b4d6b1c41b870b59022fdf3b", + "proofRootBasis": [ + "SettlementUnlock", + "InterfaceAuthorizationPolicy", + "DeploymentStoragePosture" + ], + "repairCommand": "pnpm run check:v34-gate4", + "repairPosture": "deny response, append audit event, and require paid settlement root before retry", + "validationCommand": "pnpm run check:v34-gate4" + } + ], + "generatedAt": "2026-05-22T00:00:00.000Z", + "passed": true, + "postureRoot": "v34-deployment-storage-posture:b75c4bdb6bfbbbde86ae142c", + "requiredCarrierIds": [ + "ledger_derived_state", + "canonical_database_projection", + "protected_assetpack_object_storage", + "source_safe_assetpack_preview_storage", + "generated_proof_artifacts", + "audit_log_stream", + "rollback_material", + "encrypted_backups" + ], + "schemaId": "bitcode.v34.deploymentStoragePosture.v1", + "sharedFixtureFiles": [ + "packages/btd/src/deployment-storage-posture.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-storage-posture.test.ts", + "scripts/check-v34-gate4-deployment-storage-posture.mjs" + ], + "sourceEvidence": [ + { + "digest": "sha256:2a80634772bc48cfa6f68183ca8e38698107a511e986beb171062a9444e16109", + "relativePath": "packages/btd/src/deployment-storage-posture.ts", + "requiredTokens": [ + { + "present": true, + "token": "DeploymentStoragePosture" + }, + { + "present": true, + "token": "DEPLOYMENT_STORAGE_CARRIER_IDS" + }, + { + "present": true, + "token": "protected_source_locked_until_settlement" + }, + { + "present": true, + "token": "ledger_database_projection_drift" + }, + { + "present": true, + "token": "database_object_storage_projection_drift" + }, + { + "present": true, + "token": "value-bearing-mainnet" + } + ] + }, + { + "digest": "sha256:70f7d98470ffa6ec95d96a079bfa50f4893070256d158c247bcf54b12b5651f9", + "relativePath": "packages/btd/src/index.ts", + "requiredTokens": [ + { + "present": true, + "token": "deployment-storage-posture" + } + ] + }, + { + "digest": "sha256:91837c65a866ed769915f0440cf05e799197f2b1634a2d11b7fc58915e923ce6", + "relativePath": "BITCODE_SPEC_V34.md", + "requiredTokens": [ + { + "present": true, + "token": ".bitcode/v34-deployment-storage-posture.json" + }, + { + "present": true, + "token": "DeploymentStoragePosture" + }, + { + "present": true, + "token": "protected source is never an unpaid interface payload" + } + ] + } + ], + "sourceSafetyVerdict": "source-safe-deployment-storage-posture-metadata", + "testEvidence": [ + { + "digest": "sha256:c95759e223c555305cabc8905a20dcd3eebd5ee41d77e56c68b211487cc9e99a", + "relativePath": "packages/btd/__tests__/deployment-storage-posture.test.ts", + "requiredTokens": [ + { + "present": true, + "token": "catalogs ledger-derived state, database projections, object storage, proofs, audit logs, rollback material, and backups" + }, + { + "present": true, + "token": "keeps source-bearing AssetPack storage locked before settlement" + }, + { + "present": true, + "token": "proves ledger/database and object-storage projection drift repair fixtures" + }, + { + "present": true, + "token": "fails closed when source-bearing AssetPack storage becomes visible before settlement" + }, + { + "present": true, + "token": "fails closed on secret-shaped or non-disclosable source storage text" + } + ] + }, + { + "digest": "sha256:e9b17d403f2994087efd8c9e317012489b064f814d7e9e08ebdc6d61f1d249bd", + "relativePath": "scripts/check-v34-gate4-deployment-storage-posture.mjs", + "requiredTokens": [ + { + "present": true, + "token": "check:v34-deployment-storage-posture" + }, + { + "present": true, + "token": "deployment-storage-posture.test.ts" + }, + { + "present": true, + "token": "Ledger Database Object Storage Deployment Posture" + } + ] + } + ], + "version": "V34" +} diff --git a/.bitcode/v34-distributed-execution-runtime-receipts.json b/.bitcode/v34-distributed-execution-runtime-receipts.json index b8ffaa756..8b7d32f01 100644 --- a/.bitcode/v34-distributed-execution-runtime-receipts.json +++ b/.bitcode/v34-distributed-execution-runtime-receipts.json @@ -379,7 +379,7 @@ ] }, { - "digest": "sha256:82edb3ca798a13834379748430690382cfb38bb9f7489e5a180986431abfdff4", + "digest": "sha256:91837c65a866ed769915f0440cf05e799197f2b1634a2d11b7fc58915e923ce6", "relativePath": "BITCODE_SPEC_V34.md", "requiredTokens": [ { diff --git a/.bitcode/v34-environment-lane-contracts.json b/.bitcode/v34-environment-lane-contracts.json index 68d7a8dbb..714c49e5f 100644 --- a/.bitcode/v34-environment-lane-contracts.json +++ b/.bitcode/v34-environment-lane-contracts.json @@ -218,7 +218,7 @@ ] }, { - "digest": "sha256:81211d9c1ac9db3b0c01ce07629c3c6170fbb3711a3ffefe0a52292c937196b8", + "digest": "sha256:11a994733d2754edaa7776fb30d7299f2087455775cd87b965d229e831154940", "relativePath": "BITCODE_SPEC_V34_DELTA.md", "requiredTokens": [ { diff --git a/.github/workflows/bitcode-gate-quality.yml b/.github/workflows/bitcode-gate-quality.yml index c47e15e72..0b3bd8fc3 100644 --- a/.github/workflows/bitcode-gate-quality.yml +++ b/.github/workflows/bitcode-gate-quality.yml @@ -150,6 +150,7 @@ jobs: 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 node scripts/check-v34-gate3-distributed-execution-runtime-contracts.mjs --skip-branch-check + node scripts/check-v34-gate4-deployment-storage-posture.mjs --skip-branch-check else echo "Unexpected BITCODE_SPEC.txt pointer: $POINTER" >&2 exit 1 @@ -186,6 +187,7 @@ jobs: 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__/deployment-storage-posture.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/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4f7090d43..1d46e878a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -254,7 +254,9 @@ jobs: - uses: actions/checkout@v4 - uses: reviewdog/action-actionlint@v1 with: - github_token: ${{ secrets.GITHUB_TOKEN }} + reporter: local + filter_mode: nofilter + fail_level: error # -------------------------------------------------------------------------- # 5. CodeQL static security analysis (JavaScript/TypeScript) diff --git a/BITCODE_SPEC_V34.md b/BITCODE_SPEC_V34.md index 6467fad6e..e02d29cff 100644 --- a/BITCODE_SPEC_V34.md +++ b/BITCODE_SPEC_V34.md @@ -3,12 +3,12 @@ ## Status - Version: `V34` -- V34 state: Gate 3 distributed execution runtime contracts are closed over promoted V33 canon +- V34 state: Gate 4 ledger/database/object-storage deployment posture 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`, Gate 2 artifacts `.bitcode/v34-deployment-host-capability-catalog.json` and `.bitcode/v34-environment-lane-contracts.json`, Gate 3 artifact `.bitcode/v34-distributed-execution-runtime-receipts.json`, and later deployment-depth artifacts as gates close -- Source parity state: Gate 3 closes V34 host capability, environment lane, and distributed execution runtime receipt parity; storage, approval, rollback, repair-job registry, rehearsal, and promotion 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`, Gate 3 artifact `.bitcode/v34-distributed-execution-runtime-receipts.json`, Gate 4 artifact `.bitcode/v34-deployment-storage-posture.json`, and later deployment-depth artifacts as gates close +- Source parity state: Gate 4 closes V34 host capability, environment lane, distributed execution runtime receipt, and storage posture parity; approval, rollback, repair-job registry, rehearsal, and promotion 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` @@ -196,7 +196,7 @@ 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`. 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, PTRR agent, ThricifiedGeneration, tool, ledger, wallet, proof, object-storage, and repair work. It is closed by `packages/pipeline-hosts/src/distributed-execution-runtime-receipt.ts`, `.bitcode/v34-distributed-execution-runtime-receipts.json`, `packages/pipeline-hosts/src/__tests__/distributed-execution-runtime-receipt.test.ts`, and `pnpm run check:v34-gate3`. -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. +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. It is closed by `packages/btd/src/deployment-storage-posture.ts`, `.bitcode/v34-deployment-storage-posture.json`, `packages/btd/__tests__/deployment-storage-posture.test.ts`, and `pnpm run check:v34-gate4`; source-bearing AssetPack storage remains locked before settlement. 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. 6. **Gate 6: Migration CI/CD Deployment Approval Gates** hardens schema migration approvals, generated type refresh, route scans, promotion commits, Vercel/Supabase lane checks, and deployment blockers. 7. **Gate 7: Runtime Observers Broadcasters Repair Jobs** makes settlement observers, ledger broadcasters, projection repair jobs, proof generators, and queue consumers explicit deployment jobs with receipts. diff --git a/BITCODE_SPEC_V34_DELTA.md b/BITCODE_SPEC_V34_DELTA.md index 18ac95ea2..1d2c54fe7 100644 --- a/BITCODE_SPEC_V34_DELTA.md +++ b/BITCODE_SPEC_V34_DELTA.md @@ -3,12 +3,12 @@ ## Status - Version: `V34` -- V34 state: Gate 3 distributed execution runtime contracts are closed over promoted V33 canon +- V34 state: Gate 4 ledger/database/object-storage deployment posture 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`, Gate 2 artifacts `.bitcode/v34-deployment-host-capability-catalog.json` and `.bitcode/v34-environment-lane-contracts.json`, Gate 3 artifact `.bitcode/v34-distributed-execution-runtime-receipts.json`, and later deployment-depth artifacts as gates close -- Source parity state: Gate 3 closes host capability, environment lane, and distributed execution runtime receipt contracts; source-side storage, credential, approval, repair-job registry, rehearsal, and promotion contracts remain drafted 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`, Gate 3 artifact `.bitcode/v34-distributed-execution-runtime-receipts.json`, Gate 4 artifact `.bitcode/v34-deployment-storage-posture.json`, and later deployment-depth artifacts as gates close +- Source parity state: Gate 4 closes host capability, environment lane, distributed execution runtime receipt, and storage posture contracts; source-side credential, approval, repair-job registry, 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` @@ -113,9 +113,10 @@ Gate 4 defines durable storage posture. Closure acceptance: -- ledger-derived state, canonical database projections, object storage, proof artifacts, audit logs, rollback material, backups, retention, encryption posture, and repair commands are specified and tested; +- ledger-derived state, canonical database projection, object storage, proof artifacts, audit logs, rollback material, backups, retention, encryption posture, and repair commands are specified and tested through `DeploymentStoragePosture`; - source-bearing AssetPack storage remains locked before settlement; -- database and ledger projection drift has a repair posture. +- database and ledger projection drift has a repair posture; +- generated storage posture proof is source-safe in `.bitcode/v34-deployment-storage-posture.json`. ### Gate 5: Secret Rotation And Credential Boundary Operations diff --git a/BITCODE_SPEC_V34_NOTES.md b/BITCODE_SPEC_V34_NOTES.md index 6ec6cf0aa..9d188b726 100644 --- a/BITCODE_SPEC_V34_NOTES.md +++ b/BITCODE_SPEC_V34_NOTES.md @@ -3,12 +3,12 @@ ## Status - Version: `V34` -- V34 state: Gate 3 distributed execution runtime contracts are closed over promoted V33 canon +- V34 state: Gate 4 ledger/database/object-storage deployment posture 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`, Gate 2 artifacts `.bitcode/v34-deployment-host-capability-catalog.json` and `.bitcode/v34-environment-lane-contracts.json`, Gate 3 artifact `.bitcode/v34-distributed-execution-runtime-receipts.json`, and later deployment-depth artifacts as gates close -- Source parity state: Gate 3 closes host capability, environment lane, and distributed execution runtime receipt source parity; later deployment-depth source parity 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`, Gate 3 artifact `.bitcode/v34-distributed-execution-runtime-receipts.json`, Gate 4 artifact `.bitcode/v34-deployment-storage-posture.json`, and later deployment-depth artifacts as gates close +- Source parity state: Gate 4 closes host capability, environment lane, distributed execution runtime receipt, and storage posture 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. @@ -57,7 +57,7 @@ Read the system as: - 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` now represents long-running Reading, settlement, wallet, proof, object-storage, and repair work without relying on route-handler duration. Gate 3 source truth is `packages/pipeline-hosts/src/distributed-execution-runtime-receipt.ts`; generated truth is `.bitcode/v34-distributed-execution-runtime-receipts.json`; validation is `pnpm run check:v34-gate3`. - Receipt work kinds are `pipeline_run`, `ptrr_agent`, `thricified_generation`, `tool_call`, `ledger_operation`, `wallet_operation`, `proof_generation`, `object_storage_write`, and `repair_job`; long-running work uses `request_response_not_required` and source-safe roots instead of serialized source, prompt payloads, credentials, or wallet private material. -- `DeploymentStoragePosture` must cover ledger-derived state, database projection, object storage, proof artifacts, audit logs, rollback material, retention, encryption, and repair commands. +- `DeploymentStoragePosture` now covers ledger-derived state, database projection, object storage, proof artifacts, audit logs, rollback material, backups, retention, encryption, and repair commands. Gate 4 source truth is `packages/btd/src/deployment-storage-posture.ts`; generated truth is `.bitcode/v34-deployment-storage-posture.json`; validation is `pnpm run check:v34-gate4`; source-bearing AssetPack storage remains locked before settlement. - `SecretRotationPlan` must never place secret values in tracked files or generated artifacts. - `MigrationApprovalGate` must connect schema diffs, generated types, dry-runs, reviewer approvals, and rollback plans. - `DeploymentReadinessRehearsal` must prove local and staging-testnet readiness without value-bearing mainnet admission. diff --git a/BITCODE_SPEC_V34_PARITY_MATRIX.md b/BITCODE_SPEC_V34_PARITY_MATRIX.md index 7d61068e0..ea91caa4a 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 3 distributed execution runtime contracts are closed over promoted V33 canon +- V34 state: Gate 4 ledger/database/object-storage deployment posture 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`, Gate 2 artifacts `.bitcode/v34-deployment-host-capability-catalog.json` and `.bitcode/v34-environment-lane-contracts.json`, Gate 3 artifact `.bitcode/v34-distributed-execution-runtime-receipts.json`, and later deployment-depth artifacts as gates close -- Source parity state: Gate 3 closes host capability, environment lane, and distributed execution runtime receipt parity; Gates 4 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`, Gate 3 artifact `.bitcode/v34-distributed-execution-runtime-receipts.json`, Gate 4 artifact `.bitcode/v34-deployment-storage-posture.json`, and later deployment-depth artifacts as gates close +- Source parity state: Gate 4 closes host capability, environment lane, distributed execution runtime receipt, and storage posture parity; Gates 5 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` @@ -56,7 +56,7 @@ No `_legacy/` source is active source truth. | 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 | `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 | `packages/pipeline-hosts/src/distributed-execution-runtime-receipt.ts`, `.bitcode/v34-distributed-execution-runtime-receipts.json`, `packages/pipeline-hosts/src/__tests__/distributed-execution-runtime-receipt.test.ts`, and `check:v34-gate3` | closed | Pipeline, PTRR agent, ThricifiedGeneration, 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. | +| Ledger/database/object-storage posture | Gate 4 | `packages/btd/src/deployment-storage-posture.ts`, `.bitcode/v34-deployment-storage-posture.json`, `packages/btd/__tests__/deployment-storage-posture.test.ts`, and `check:v34-gate4` | closed | ledger-derived state, database projection, object storage, proof artifacts, audit logs, backups, and rollback material are durable and repairable; source-bearing AssetPack storage remains locked before settlement. | | 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. | | Migration CI/CD deployment approval gates | Gate 6 | planned approval gate source, workflows, generated artifact, and `check:v34-gate6` | draft-required | Schema migration, generated types, route scans, builds, deployment approvals, and promotion commits fail closed. | | Runtime observers, broadcasters, and repair jobs | Gate 7 | planned runtime job source, tests, generated artifact, and `check:v34-gate7` | draft-required | Settlement observers, broadcasters, finality watchers, projection repair, object-storage repair, and proof jobs have receipts. | @@ -81,6 +81,8 @@ No `_legacy/` source is active source truth. | Value-bearing mainnet blocker | `value-bearing-mainnet` remains `blocked_future_canon_required` and admits no hosts | closed | | Gate 3 generated artifact | `.bitcode/v34-distributed-execution-runtime-receipts.json` is deterministic and source-safe | closed | | Runtime route boundary | Long-running `DistributedExecutionRuntimeReceipt` rows use `request_response_not_required` instead of request/response completion | closed | +| Gate 4 generated artifact | `.bitcode/v34-deployment-storage-posture.json` is deterministic and source-safe | closed | +| Storage disclosure boundary | `DeploymentStoragePosture` blocks protected AssetPack object storage, rollback material, and encrypted backups before settlement | closed | ## Gate 1 Parity @@ -120,9 +122,10 @@ No `_legacy/` source is active source truth. | Requirement | Source evidence | Current V34 judgment | | --- | --- | --- | -| Storage posture object exists | planned package-owned type and generated artifact | draft-required | -| Ledger/database/object-storage drift is repairable | planned repair fixtures | draft-required | -| Source-bearing AssetPack storage remains locked before settlement | planned storage access tests | draft-required | +| Storage posture object exists | `packages/btd/src/deployment-storage-posture.ts`, `.bitcode/v34-deployment-storage-posture.json`, `packages/btd/__tests__/deployment-storage-posture.test.ts`, `pnpm run check:v34-gate4` | closed | +| Ledger/database/object-storage drift is repairable | `DeploymentStoragePosture` drift fixtures cover ledger/database projection drift and database/object storage projection drift with blocking repair commands | closed | +| Source-bearing AssetPack storage remains locked before settlement | protected AssetPack object storage, rollback material, and encrypted backups use `blocked_before_settlement`; source-bearing AssetPack storage remains locked before settlement | closed | +| Retention, encryption, backups, rollback material, and audit logs are covered | Gate 4 carriers include retention, encryption, backup posture, rollback material, audit log stream, and validation commands | closed | ## Gate 5 Parity diff --git a/SPECIFICATIONS_ROADMAP.md b/SPECIFICATIONS_ROADMAP.md index d94e3dfbd..f7790d789 100644 --- a/SPECIFICATIONS_ROADMAP.md +++ b/SPECIFICATIONS_ROADMAP.md @@ -5,11 +5,12 @@ - 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 4 Ledger Database Object Storage Deployment Posture, following closed Gate 3 distributed execution runtime contract work. +- Current working gate: V34 Gate 5 Secret Rotation And Credential Boundary Operations, following closed Gate 4 ledger/database/object-storage deployment posture 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 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`. - V34 Gate 3 closure anchor: deployment-depth now owns package-backed `DistributedExecutionRuntimeReceipt` source, deterministic `.bitcode/v34-distributed-execution-runtime-receipts.json`, `request_response_not_required` long-running work posture, and source-safe roots for pipeline, PTRR agent, ThricifiedGeneration, tool, ledger, wallet, proof, object-storage, and repair work. +- V34 Gate 4 closure anchor: deployment-depth now owns package-backed `DeploymentStoragePosture` source, deterministic `.bitcode/v34-deployment-storage-posture.json`, ledger/database/object-storage drift repair fixtures, retention/encryption/backup/rollback/audit posture, and source-bearing AssetPack storage remains locked before settlement. - 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 faab1f90b..688a53fcd 100644 --- a/package.json +++ b/package.json @@ -135,6 +135,9 @@ "generate:v34-distributed-execution-runtime-receipts": "node scripts/generate-v34-distributed-execution-runtime-receipts.mjs", "check:v34-distributed-execution-runtime-receipts": "node scripts/generate-v34-distributed-execution-runtime-receipts.mjs --check", "check:v34-gate3": "node scripts/check-v34-gate3-distributed-execution-runtime-contracts.mjs", + "generate:v34-deployment-storage-posture": "node scripts/generate-v34-deployment-storage-posture.mjs", + "check:v34-deployment-storage-posture": "node scripts/generate-v34-deployment-storage-posture.mjs --check", + "check:v34-gate4": "node scripts/check-v34-gate4-deployment-storage-posture.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-storage-posture.test.ts b/packages/btd/__tests__/deployment-storage-posture.test.ts new file mode 100644 index 000000000..4faf6af21 --- /dev/null +++ b/packages/btd/__tests__/deployment-storage-posture.test.ts @@ -0,0 +1,153 @@ +import { + DEPLOYMENT_STORAGE_CARRIER_IDS, + DEPLOYMENT_STORAGE_REQUIRED_CARRIER_FIELDS, + buildDeploymentStorageCarrier, + buildDeploymentStorageCarrierRows, + buildDeploymentStorageDriftRepairFixtures, + buildDeploymentStoragePosture, +} from '../src/deployment-storage-posture'; + +describe('deployment storage posture', () => { + it('catalogs ledger-derived state, database projections, object storage, proofs, audit logs, rollback material, and backups', () => { + const posture = buildDeploymentStoragePosture(); + + expect(posture.kind).toBe('bitcode.deployment_storage_posture'); + expect(posture.schemaId).toBe('bitcode.deploymentStoragePosture.v1'); + expect(posture.carrierCount).toBe(8); + expect(posture.missingCarrierIds).toEqual([]); + expect(posture.requiredCarrierIds).toEqual([...DEPLOYMENT_STORAGE_CARRIER_IDS].sort()); + expect(posture.observedCarrierIds).toEqual([...DEPLOYMENT_STORAGE_CARRIER_IDS]); + expect(posture.postureRoot).toMatch(/^deployment-storage-posture:[a-f0-9]{24}$/); + expect(posture.sourceSafety.sourceSafe).toBe(true); + expect(posture.sourceSafety.containsSecret).toBe(false); + expect(posture.sourceSafety.protectedSourceVisible).toBe(false); + }); + + it('requires durability, disclosure, retention, encryption, backup, rollback, root, repair, validation, and proof fields for each storage carrier', () => { + const posture = buildDeploymentStoragePosture(); + + for (const carrier of posture.carriers) { + for (const field of DEPLOYMENT_STORAGE_REQUIRED_CARRIER_FIELDS) { + expect(carrier[field]).toBeTruthy(); + } + expect(carrier.supportedLaneIds).not.toContain('value-bearing-mainnet'); + expect(carrier.requiredRoots.length).toBeGreaterThan(0); + expect(carrier.proofRootBasis.length).toBeGreaterThan(0); + expect(carrier.carrierRoot).toMatch(/^deployment-storage-carrier:[a-f0-9]{24}$/); + expect(carrier.sourceSafety.sourceSafe).toBe(true); + } + }); + + it('keeps source-bearing AssetPack storage locked before settlement', () => { + const posture = buildDeploymentStoragePosture(); + const protectedCarriers = posture.carriers.filter( + (carrier) => carrier.storesProtectedSourcePayload, + ); + + expect(protectedCarriers.map((carrier) => carrier.carrierId)).toEqual([ + 'protected_assetpack_object_storage', + 'rollback_material', + 'encrypted_backups', + ]); + expect(posture.sourceBearingAssetPackLockedBeforeSettlement).toBe(true); + expect( + protectedCarriers.every( + (carrier) => carrier.preSettlementVisibility === 'blocked_before_settlement', + ), + ).toBe(true); + expect( + protectedCarriers.every((carrier) => carrier.sourceSafety.protectedSourceVisible === false), + ).toBe(true); + }); + + it('proves ledger/database and object-storage projection drift repair fixtures', () => { + const posture = buildDeploymentStoragePosture(); + + expect(posture.ledgerDatabaseProjectionDriftRepairable).toBe(true); + expect(posture.objectStorageProjectionDriftRepairable).toBe(true); + expect(posture.driftRepairFixtures.map((fixture) => fixture.fixtureId)).toEqual([ + 'ledger-database-projection-drift', + 'database-object-storage-projection-drift', + 'unpaid-protected-assetpack-access-attempt', + ]); + + for (const fixture of posture.driftRepairFixtures) { + expect(fixture.blocksUnlock).toBe(true); + expect(fixture.blocksSourceVisibility).toBe(true); + expect(fixture.detectionRoot).toMatch(/^sha256:/); + expect(fixture.fixtureRoot).toMatch(/^deployment-storage-drift-repair-fixture:[a-f0-9]{24}$/); + expect(fixture.proofRootBasis.length).toBeGreaterThan(0); + } + }); + + it('fails closed when a required deployment storage carrier is missing', () => { + const carriers = buildDeploymentStorageCarrierRows().filter( + (carrier) => carrier.carrierId !== 'canonical_database_projection', + ); + + expect(() => buildDeploymentStoragePosture({ carriers })).toThrow( + /missing carrier ids: canonical_database_projection/, + ); + }); + + it('fails closed on duplicate deployment storage carrier ids', () => { + const carriers = buildDeploymentStorageCarrierRows(); + + expect(() => buildDeploymentStoragePosture({ carriers: [...carriers, carriers[0]] })).toThrow( + /duplicate carrier ids: ledger_derived_state/, + ); + }); + + it('fails closed when source-bearing AssetPack storage becomes visible before settlement', () => { + const [protectedCarrier] = buildDeploymentStorageCarrierRows().filter( + (carrier) => carrier.carrierId === 'protected_assetpack_object_storage', + ); + + expect(() => + buildDeploymentStorageCarrier({ + ...protectedCarrier, + preSettlementVisibility: 'source_safe_roots_only', + }), + ).toThrow(/must block source-bearing payload visibility before settlement/); + }); + + it('fails closed when ledger/database drift repair does not block unlock and source visibility', () => { + const fixtures = buildDeploymentStorageDriftRepairFixtures().map((fixture) => + fixture.fixtureId === 'ledger-database-projection-drift' + ? { + ...fixture, + blocksUnlock: false, + } + : fixture, + ); + + expect(() => buildDeploymentStoragePosture({ driftRepairFixtures: fixtures })).toThrow( + /must block unlock and source visibility/, + ); + }); + + it('fails closed when storage posture admits value-bearing mainnet before future canon', () => { + const [previewCarrier] = buildDeploymentStorageCarrierRows().filter( + (carrier) => carrier.carrierId === 'source_safe_assetpack_preview_storage', + ); + + expect(() => + buildDeploymentStorageCarrier({ + ...previewCarrier, + supportedLaneIds: [...previewCarrier.supportedLaneIds, 'value-bearing-mainnet'], + }), + ).toThrow(/must not admit value-bearing-mainnet/); + }); + + it('fails closed on secret-shaped or non-disclosable source storage text', () => { + const [firstCarrier] = buildDeploymentStorageCarrierRows(); + const secretShapedText = `${['sk', 'proj'].join('-')}-${'abcdefghijklmnop1234567890'}`; + + expect(() => + buildDeploymentStorageCarrier({ + ...firstCarrier, + retentionClass: secretShapedText, + }), + ).toThrow(/must not contain secrets or non-disclosable source/); + }); +}); diff --git a/packages/btd/package.json b/packages/btd/package.json index b7a6576db..9564c33cf 100644 --- a/packages/btd/package.json +++ b/packages/btd/package.json @@ -9,6 +9,7 @@ "./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", + "./deployment-storage-posture": "./src/deployment-storage-posture.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-storage-posture.ts b/packages/btd/src/deployment-storage-posture.ts new file mode 100644 index 000000000..219ed588d --- /dev/null +++ b/packages/btd/src/deployment-storage-posture.ts @@ -0,0 +1,761 @@ +import { createHash } from 'crypto'; +import { assertNonEmptyString } from './constants'; +import { + DEPLOYMENT_HOST_CAPABILITY_IDS, + ENVIRONMENT_LANE_CONTRACT_IDS, + type DeploymentHostCapabilityId, + type EnvironmentLaneContractId, +} from './deployment-host-capability-catalog'; +import type { BtdProtocolTelemetrySourceSafety } from './telemetry'; + +export const DEPLOYMENT_STORAGE_CARRIER_IDS = [ + 'ledger_derived_state', + 'canonical_database_projection', + 'protected_assetpack_object_storage', + 'source_safe_assetpack_preview_storage', + 'generated_proof_artifacts', + 'audit_log_stream', + 'rollback_material', + 'encrypted_backups', +] as const; + +export type DeploymentStorageCarrierId = (typeof DEPLOYMENT_STORAGE_CARRIER_IDS)[number]; + +export type DeploymentStorageClass = + | 'ledger_derived_state' + | 'canonical_database_projection' + | 'object_storage' + | 'proof_artifact' + | 'audit_log' + | 'rollback_material' + | 'backup'; + +export type DeploymentStorageDurabilityPosture = + | 'append_only_replayable' + | 'durable_projection' + | 'encrypted_durable_object' + | 'source_safe_durable_object' + | 'generated_replayable_artifact' + | 'append_only_log' + | 'operator_controlled_rollback' + | 'encrypted_backup'; + +export type DeploymentStorageDisclosurePolicy = + | 'ledger_commitment_only' + | 'database_projection_only' + | 'protected_source_locked_until_settlement' + | 'source_safe_preview_only' + | 'source_safe_proof_only' + | 'operator_audit_only' + | 'operator_rollback_only' + | 'encrypted_recovery_only'; + +export type DeploymentStorageVisibility = + | 'source_safe_roots_only' + | 'blocked_before_settlement' + | 'operator_source_safe_only' + | 'reader_unlocked_after_settlement' + | 'internal_recovery_only'; + +export type DeploymentStorageRootKind = + | 'ledger' + | 'database_projection' + | 'object_storage' + | 'proof' + | 'audit_log' + | 'rollback'; + +export type DeploymentStorageDriftKind = + | 'ledger_database_projection_drift' + | 'database_object_storage_projection_drift' + | 'unpaid_source_visibility_attempt'; + +export const DEPLOYMENT_STORAGE_DRIFT_REPAIR_FIXTURE_IDS = [ + 'ledger-database-projection-drift', + 'database-object-storage-projection-drift', + 'unpaid-protected-assetpack-access-attempt', +] as const; + +export type DeploymentStorageDriftRepairFixtureId = + (typeof DEPLOYMENT_STORAGE_DRIFT_REPAIR_FIXTURE_IDS)[number]; + +export interface DeploymentStorageCarrierInput { + carrierId: DeploymentStorageCarrierId; + storageClass: DeploymentStorageClass; + ownerHostId: DeploymentHostCapabilityId; + storageOwnerPackage: string; + supportedLaneIds: readonly EnvironmentLaneContractId[]; + durabilityPosture: DeploymentStorageDurabilityPosture; + disclosurePolicy: DeploymentStorageDisclosurePolicy; + storesProtectedSourcePayload: boolean; + preSettlementVisibility: DeploymentStorageVisibility; + postSettlementVisibility: DeploymentStorageVisibility; + retentionClass: string; + encryptionPosture: string; + backupPosture: string; + rollbackMaterialPosture: string; + requiredRoots: readonly DeploymentStorageRootKind[]; + driftDetection: string; + repairCommand: string; + repairPosture: string; + validationCommand: string; + proofRootBasis: readonly string[]; +} + +export interface DeploymentStorageCarrier extends DeploymentStorageCarrierInput { + kind: 'bitcode.deployment_storage_posture.carrier'; + supportedLaneIds: EnvironmentLaneContractId[]; + requiredRoots: DeploymentStorageRootKind[]; + proofRootBasis: string[]; + carrierRoot: string; + sourceSafety: BtdProtocolTelemetrySourceSafety; +} + +export interface DeploymentStorageDriftRepairFixtureInput { + fixtureId: DeploymentStorageDriftRepairFixtureId; + driftKind: DeploymentStorageDriftKind; + affectedCarrierIds: readonly DeploymentStorageCarrierId[]; + detectionRoot: string; + blocksUnlock: boolean; + blocksSourceVisibility: boolean; + repairCommand: string; + repairPosture: string; + expectedFinalPosture: string; + validationCommand: string; + proofRootBasis: readonly string[]; +} + +export interface DeploymentStorageDriftRepairFixture extends DeploymentStorageDriftRepairFixtureInput { + kind: 'bitcode.deployment_storage_posture.drift_repair_fixture'; + affectedCarrierIds: DeploymentStorageCarrierId[]; + proofRootBasis: string[]; + fixtureRoot: string; + sourceSafety: BtdProtocolTelemetrySourceSafety; +} + +export interface DeploymentStoragePostureInput { + carriers?: readonly DeploymentStorageCarrierInput[]; + driftRepairFixtures?: readonly DeploymentStorageDriftRepairFixtureInput[]; + requiredCarrierIds?: readonly DeploymentStorageCarrierId[]; +} + +export interface DeploymentStoragePosture { + kind: 'bitcode.deployment_storage_posture'; + schemaId: 'bitcode.deploymentStoragePosture.v1'; + postureRoot: string; + carrierCount: number; + requiredCarrierIds: DeploymentStorageCarrierId[]; + observedCarrierIds: DeploymentStorageCarrierId[]; + missingCarrierIds: DeploymentStorageCarrierId[]; + carriers: DeploymentStorageCarrier[]; + driftRepairFixtures: DeploymentStorageDriftRepairFixture[]; + sourceBearingAssetPackLockedBeforeSettlement: true; + ledgerDatabaseProjectionDriftRepairable: true; + objectStorageProjectionDriftRepairable: true; + backupsCovered: true; + retentionCovered: true; + encryptionCovered: true; + rollbackMaterialCovered: true; + auditLogsCovered: true; + sourceSafety: BtdProtocolTelemetrySourceSafety; +} + +export const DEPLOYMENT_STORAGE_REQUIRED_CARRIER_FIELDS = [ + 'storageOwnerPackage', + 'supportedLaneIds', + 'durabilityPosture', + 'disclosurePolicy', + 'preSettlementVisibility', + 'postSettlementVisibility', + 'retentionClass', + 'encryptionPosture', + 'backupPosture', + 'rollbackMaterialPosture', + 'requiredRoots', + 'driftDetection', + 'repairCommand', + 'repairPosture', + 'validationCommand', + 'proofRootBasis', +] as const; + +const SOURCE_SAFETY: BtdProtocolTelemetrySourceSafety = { + sourceSafe: true, + protectedSourceVisible: false, + containsProtectedSource: false, + containsSecret: false, +}; + +const NON_VALUE_LANES: EnvironmentLaneContractId[] = [ + 'local', + 'regtest', + 'signet', + 'staging-testnet', + 'public-testnet', + 'mainnet-ready-dry-run', +]; + +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, + /\bsource\s+contents\b/iu, +]; + +export function buildDeploymentStorageCarrierRows(): DeploymentStorageCarrierInput[] { + return [ + { + carrierId: 'ledger_derived_state', + storageClass: 'ledger_derived_state', + ownerHostId: 'ledger_projection', + storageOwnerPackage: 'packages/btd', + supportedLaneIds: NON_VALUE_LANES, + durabilityPosture: 'append_only_replayable', + disclosurePolicy: 'ledger_commitment_only', + storesProtectedSourcePayload: false, + preSettlementVisibility: 'source_safe_roots_only', + postSettlementVisibility: 'source_safe_roots_only', + retentionClass: 'testnet-and-mainnet-dry-run-audit-retained', + encryptionPosture: 'hash-chained source-safe roots with provider encryption at rest', + backupPosture: 'replay from ledger roots, database projection roots, and proof artifacts', + rollbackMaterialPosture: 'rollback uses prior ledger projection root and replay command', + requiredRoots: ['ledger', 'proof', 'audit_log'], + driftDetection: 'compare-ledger-root-to-database-projection-root-before-unlock', + repairCommand: 'pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/reconciliation.test.ts', + repairPosture: 'hold AssetPack unlock and replay ledger projection from committed ledger root', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['BtdAssetPackMintReceipt', 'BtdReadReceipt', 'BtdRightsTransferReceipt'], + }, + { + carrierId: 'canonical_database_projection', + storageClass: 'canonical_database_projection', + ownerHostId: 'database_projection', + storageOwnerPackage: 'packages/supabase', + supportedLaneIds: NON_VALUE_LANES, + durabilityPosture: 'durable_projection', + disclosurePolicy: 'database_projection_only', + storesProtectedSourcePayload: false, + preSettlementVisibility: 'source_safe_roots_only', + postSettlementVisibility: 'source_safe_roots_only', + retentionClass: 'lane-retained-postgres-projection-with-point-in-time-restore', + encryptionPosture: 'provider encrypted at rest; service access only through server-side policy', + backupPosture: 'Supabase point-in-time restore plus deterministic projection replay', + rollbackMaterialPosture: 'migration rollback requires prior schema root and projection repair root', + requiredRoots: ['database_projection', 'ledger', 'proof', 'audit_log'], + driftDetection: 'compare-database-projection-root-to-ledger-root-and-object-storage-root', + repairCommand: 'pnpm run db:data-health:ci && pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/reconciliation.test.ts', + repairPosture: 'block paid unlock until projection repair writes a new database projection root', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['ledger database reconciliation', 'DeploymentStoragePosture'], + }, + { + carrierId: 'protected_assetpack_object_storage', + storageClass: 'object_storage', + ownerHostId: 'object_storage', + storageOwnerPackage: 'packages/pipeline-hosts', + supportedLaneIds: NON_VALUE_LANES, + durabilityPosture: 'encrypted_durable_object', + disclosurePolicy: 'protected_source_locked_until_settlement', + storesProtectedSourcePayload: true, + preSettlementVisibility: 'blocked_before_settlement', + postSettlementVisibility: 'reader_unlocked_after_settlement', + retentionClass: 'rights-retained-until-read-license-expiry-or-operator-deletion', + encryptionPosture: 'encrypted at rest with lane-scoped object key policy and no tracked key material', + backupPosture: 'encrypted protected-object backup with proof-rooted restore command', + rollbackMaterialPosture: 'rollback material may reference encrypted object root but never exposes payload', + requiredRoots: ['object_storage', 'proof', 'audit_log', 'rollback'], + driftDetection: 'deny source visibility unless paid settlement root and object root both verify', + repairCommand: 'pnpm run check:v34-gate4', + repairPosture: 'lock delivery and rewrite from authorized encrypted artifact root after operator approval', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['AssetPackPreview', 'SettlementUnlock', 'object-storage receipt root'], + }, + { + carrierId: 'source_safe_assetpack_preview_storage', + storageClass: 'object_storage', + ownerHostId: 'object_storage', + storageOwnerPackage: 'packages/pipeline-hosts', + supportedLaneIds: NON_VALUE_LANES, + durabilityPosture: 'source_safe_durable_object', + disclosurePolicy: 'source_safe_preview_only', + storesProtectedSourcePayload: false, + preSettlementVisibility: 'source_safe_roots_only', + postSettlementVisibility: 'source_safe_roots_only', + retentionClass: 'read-preview-retained-for-transaction-history', + encryptionPosture: 'provider encrypted at rest; payload limited to measurements and roots', + backupPosture: 'preview object can be regenerated from runtime receipt roots', + rollbackMaterialPosture: 'preview rollback rewrites source-safe measurements from receipt output root', + requiredRoots: ['object_storage', 'database_projection', 'proof'], + driftDetection: 'compare-preview-object-root-to-database-preview-root', + repairCommand: 'pnpm --filter @bitcode/pipeline-asset-pack exec jest --config jest.config.cjs --runTestsByPath src/__tests__/asset-pack-disclosure.test.ts --runInBand', + repairPosture: 'withhold preview update until source-safe projection is regenerated', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['AssetPackPreview', 'ReadFitsFindingSynthesis', 'InterfaceTelemetryProofHook'], + }, + { + carrierId: 'generated_proof_artifacts', + storageClass: 'proof_artifact', + ownerHostId: 'proof_services', + storageOwnerPackage: 'packages/protocol', + supportedLaneIds: NON_VALUE_LANES, + durabilityPosture: 'generated_replayable_artifact', + disclosurePolicy: 'source_safe_proof_only', + storesProtectedSourcePayload: false, + preSettlementVisibility: 'source_safe_roots_only', + postSettlementVisibility: 'source_safe_roots_only', + retentionClass: 'repository-retained-generated-proof-artifact', + encryptionPosture: 'source-safe generated JSON with secret scanning before commit', + backupPosture: 'recreate from canonical inputs and deterministic generator scripts', + rollbackMaterialPosture: 'rollback restores prior generated artifact root before promotion', + requiredRoots: ['proof', 'audit_log'], + driftDetection: 'generated-artifact-check-compares-current-output-to-tracked-artifact', + repairCommand: 'pnpm run check:spec-quality && pnpm run check:v34-gate4', + repairPosture: 'regenerate proof artifact from canonical source and re-run promotion checks', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['BITCODE_SPEC_V34.md', 'BITCODE_SPEC_V34_PARITY_MATRIX.md'], + }, + { + carrierId: 'audit_log_stream', + storageClass: 'audit_log', + ownerHostId: 'runtime_observers', + storageOwnerPackage: 'packages/observability', + supportedLaneIds: NON_VALUE_LANES, + durabilityPosture: 'append_only_log', + disclosurePolicy: 'operator_audit_only', + storesProtectedSourcePayload: false, + preSettlementVisibility: 'operator_source_safe_only', + postSettlementVisibility: 'operator_source_safe_only', + retentionClass: 'lane-audit-log-retention-with-operator-export', + encryptionPosture: 'provider encrypted at rest with redacted structured events', + backupPosture: 'audit log export stores roots and redacted event envelopes', + rollbackMaterialPosture: 'audit rollback is append-only correction event, not deletion', + requiredRoots: ['audit_log', 'proof'], + driftDetection: 'runtime receipt log root must match audit event stream root', + repairCommand: 'pnpm run check:v34-gate3 && pnpm run check:v34-gate4', + repairPosture: 'mark execution blocked and append repair event before replay', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['DistributedExecutionRuntimeReceipt', 'InterfaceTelemetryProofHook'], + }, + { + carrierId: 'rollback_material', + storageClass: 'rollback_material', + ownerHostId: 'repair_jobs', + storageOwnerPackage: 'packages/btd', + supportedLaneIds: NON_VALUE_LANES, + durabilityPosture: 'operator_controlled_rollback', + disclosurePolicy: 'operator_rollback_only', + storesProtectedSourcePayload: true, + preSettlementVisibility: 'blocked_before_settlement', + postSettlementVisibility: 'internal_recovery_only', + retentionClass: 'operator-retained-until-deployment-successor-proof', + encryptionPosture: 'encrypted at rest; rollback bundle references roots instead of payload text', + backupPosture: 'rollback bundle copied to encrypted backup carrier with proof root', + rollbackMaterialPosture: 'required for migration, projection, and object-storage repair', + requiredRoots: ['rollback', 'object_storage', 'database_projection', 'ledger', 'proof'], + driftDetection: 'rollback gap blocks deployment promotion and paid unlock', + repairCommand: 'pnpm run check:v34-gate4', + repairPosture: 'block traffic promotion until rollback bundle root and verification command pass', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['RollbackUpgradeRepairPlaybook', 'DeploymentStoragePosture'], + }, + { + carrierId: 'encrypted_backups', + storageClass: 'backup', + ownerHostId: 'object_storage', + storageOwnerPackage: 'packages/pipeline-hosts', + supportedLaneIds: NON_VALUE_LANES, + durabilityPosture: 'encrypted_backup', + disclosurePolicy: 'encrypted_recovery_only', + storesProtectedSourcePayload: true, + preSettlementVisibility: 'blocked_before_settlement', + postSettlementVisibility: 'internal_recovery_only', + retentionClass: 'lane-scoped-backup-retention-with-manual-deletion-proof', + encryptionPosture: 'encrypted backup carrier with no tracked key values', + backupPosture: 'required backup root for protected objects, projections, proofs, and rollback bundles', + rollbackMaterialPosture: 'backup restore is admissible only through repair job receipt', + requiredRoots: ['object_storage', 'database_projection', 'ledger', 'proof', 'audit_log', 'rollback'], + driftDetection: 'backup root must match carrier roots before promotion readiness', + repairCommand: 'pnpm run check:v34-gate4', + repairPosture: 'deny restore visibility until backup proof root and repair job receipt verify', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['DeploymentReadinessRehearsal', 'DeploymentStoragePosture'], + }, + ]; +} + +export function buildDeploymentStorageDriftRepairFixtures(): DeploymentStorageDriftRepairFixtureInput[] { + return [ + { + fixtureId: 'ledger-database-projection-drift', + driftKind: 'ledger_database_projection_drift', + affectedCarrierIds: ['ledger_derived_state', 'canonical_database_projection'], + detectionRoot: 'sha256:ledger-database-projection-drift-root', + blocksUnlock: true, + blocksSourceVisibility: true, + repairCommand: 'pnpm run db:data-health:ci && pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/reconciliation.test.ts', + repairPosture: 'repair database projection from ledger root before AssetPack unlock', + expectedFinalPosture: 'ledger and database projection roots agree before unlock resumes', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['ledgerProjectionRoot', 'databaseProjectionRoot', 'repairJobReceiptRoot'], + }, + { + fixtureId: 'database-object-storage-projection-drift', + driftKind: 'database_object_storage_projection_drift', + affectedCarrierIds: [ + 'canonical_database_projection', + 'protected_assetpack_object_storage', + 'source_safe_assetpack_preview_storage', + ], + detectionRoot: 'sha256:database-object-storage-projection-drift-root', + blocksUnlock: true, + blocksSourceVisibility: true, + repairCommand: 'pnpm run check:v34-gate4', + repairPosture: 'repair object root projection or rewrite authorized object before source visibility', + expectedFinalPosture: 'database projection and object storage roots agree before delivery resumes', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['databaseProjectionRoot', 'objectStorageRoot', 'repairJobReceiptRoot'], + }, + { + fixtureId: 'unpaid-protected-assetpack-access-attempt', + driftKind: 'unpaid_source_visibility_attempt', + affectedCarrierIds: ['protected_assetpack_object_storage', 'rollback_material', 'encrypted_backups'], + detectionRoot: 'sha256:unpaid-protected-assetpack-access-attempt-root', + blocksUnlock: true, + blocksSourceVisibility: true, + repairCommand: 'pnpm run check:v34-gate4', + repairPosture: 'deny response, append audit event, and require paid settlement root before retry', + expectedFinalPosture: 'protected AssetPack payload remains hidden before settlement', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['SettlementUnlock', 'InterfaceAuthorizationPolicy', 'DeploymentStoragePosture'], + }, + ]; +} + +export function buildDeploymentStorageCarrier( + input: DeploymentStorageCarrierInput, +): DeploymentStorageCarrier { + const carrier: DeploymentStorageCarrier = { + ...input, + kind: 'bitcode.deployment_storage_posture.carrier', + carrierId: assertDeploymentStorageCarrierId(input.carrierId), + ownerHostId: assertDeploymentHostCapabilityId(input.ownerHostId), + storageOwnerPackage: assertNonEmptyString(input.storageOwnerPackage, 'storageOwnerPackage'), + supportedLaneIds: normalizeLaneIds(input.supportedLaneIds), + retentionClass: assertSafeNonEmptyString(input.retentionClass, 'retentionClass'), + encryptionPosture: assertSafeNonEmptyString(input.encryptionPosture, 'encryptionPosture'), + backupPosture: assertSafeNonEmptyString(input.backupPosture, 'backupPosture'), + rollbackMaterialPosture: assertSafeNonEmptyString( + input.rollbackMaterialPosture, + 'rollbackMaterialPosture', + ), + requiredRoots: normalizeRequiredRoots(input.requiredRoots), + driftDetection: assertSafeNonEmptyString(input.driftDetection, 'driftDetection'), + repairCommand: assertSafeNonEmptyString(input.repairCommand, 'repairCommand'), + repairPosture: assertSafeNonEmptyString(input.repairPosture, 'repairPosture'), + validationCommand: assertSafeNonEmptyString(input.validationCommand, 'validationCommand'), + proofRootBasis: normalizeStringArray(input.proofRootBasis, 'proofRootBasis'), + carrierRoot: '', + sourceSafety: SOURCE_SAFETY, + }; + + assertNoValueBearingMainnet(carrier.supportedLaneIds); + assertProtectedPayloadBoundary(carrier); + assertSafeObject(carrier, `deployment storage carrier ${carrier.carrierId}`); + + return { + ...carrier, + carrierRoot: stableRoot('deployment-storage-carrier', [ + carrier.carrierId, + carrier.storageClass, + carrier.ownerHostId, + carrier.storageOwnerPackage, + carrier.supportedLaneIds.join(','), + carrier.durabilityPosture, + carrier.disclosurePolicy, + String(carrier.storesProtectedSourcePayload), + carrier.preSettlementVisibility, + carrier.postSettlementVisibility, + carrier.retentionClass, + carrier.encryptionPosture, + carrier.backupPosture, + carrier.rollbackMaterialPosture, + carrier.requiredRoots.join(','), + carrier.driftDetection, + carrier.repairCommand, + carrier.repairPosture, + carrier.validationCommand, + carrier.proofRootBasis.join(','), + ]), + }; +} + +export function buildDeploymentStorageDriftRepairFixture( + input: DeploymentStorageDriftRepairFixtureInput, +): DeploymentStorageDriftRepairFixture { + const fixture: DeploymentStorageDriftRepairFixture = { + ...input, + kind: 'bitcode.deployment_storage_posture.drift_repair_fixture', + fixtureId: assertDeploymentStorageDriftRepairFixtureId(input.fixtureId), + affectedCarrierIds: normalizeCarrierIds(input.affectedCarrierIds), + detectionRoot: assertSafeNonEmptyString(input.detectionRoot, 'detectionRoot'), + repairCommand: assertSafeNonEmptyString(input.repairCommand, 'repairCommand'), + repairPosture: assertSafeNonEmptyString(input.repairPosture, 'repairPosture'), + expectedFinalPosture: assertSafeNonEmptyString( + input.expectedFinalPosture, + 'expectedFinalPosture', + ), + validationCommand: assertSafeNonEmptyString(input.validationCommand, 'validationCommand'), + proofRootBasis: normalizeStringArray(input.proofRootBasis, 'proofRootBasis'), + fixtureRoot: '', + sourceSafety: SOURCE_SAFETY, + }; + + if (!fixture.detectionRoot.startsWith('sha256:')) { + throw new Error(`${fixture.fixtureId} detectionRoot must be a sha256 root.`); + } + if (!fixture.blocksUnlock || !fixture.blocksSourceVisibility) { + throw new Error(`${fixture.fixtureId} must block unlock and source visibility while drift is unresolved.`); + } + assertSafeObject(fixture, `deployment storage drift fixture ${fixture.fixtureId}`); + + return { + ...fixture, + fixtureRoot: stableRoot('deployment-storage-drift-repair-fixture', [ + fixture.fixtureId, + fixture.driftKind, + fixture.affectedCarrierIds.join(','), + fixture.detectionRoot, + String(fixture.blocksUnlock), + String(fixture.blocksSourceVisibility), + fixture.repairCommand, + fixture.repairPosture, + fixture.expectedFinalPosture, + fixture.validationCommand, + fixture.proofRootBasis.join(','), + ]), + }; +} + +export function buildDeploymentStoragePosture( + input: DeploymentStoragePostureInput = {}, +): DeploymentStoragePosture { + const requiredCarrierIds = normalizeCarrierIds( + input.requiredCarrierIds ?? DEPLOYMENT_STORAGE_CARRIER_IDS, + ); + const carriers = (input.carriers ?? buildDeploymentStorageCarrierRows()).map( + buildDeploymentStorageCarrier, + ); + const driftRepairFixtures = ( + input.driftRepairFixtures ?? buildDeploymentStorageDriftRepairFixtures() + ).map(buildDeploymentStorageDriftRepairFixture); + const observedCarrierIds = carriers.map((carrier) => carrier.carrierId); + const duplicateCarrierIds = findDuplicates(observedCarrierIds); + if (duplicateCarrierIds.length > 0) { + throw new Error(`DeploymentStoragePosture duplicate carrier ids: ${duplicateCarrierIds.join(', ')}.`); + } + const missingCarrierIds = requiredCarrierIds.filter( + (carrierId) => !observedCarrierIds.includes(carrierId), + ); + if (missingCarrierIds.length > 0) { + throw new Error(`DeploymentStoragePosture missing carrier ids: ${missingCarrierIds.join(', ')}.`); + } + + const sourceBearingAssetPackLockedBeforeSettlement = carriers + .filter((carrier) => carrier.storesProtectedSourcePayload) + .every((carrier) => carrier.preSettlementVisibility === 'blocked_before_settlement'); + if (!sourceBearingAssetPackLockedBeforeSettlement) { + throw new Error('Source-bearing AssetPack storage must remain locked before settlement.'); + } + + const ledgerDatabaseProjectionDriftRepairable = driftRepairFixtures.some( + (fixture) => + fixture.driftKind === 'ledger_database_projection_drift' && + fixture.affectedCarrierIds.includes('ledger_derived_state') && + fixture.affectedCarrierIds.includes('canonical_database_projection') && + fixture.blocksUnlock && + fixture.blocksSourceVisibility, + ); + if (!ledgerDatabaseProjectionDriftRepairable) { + throw new Error('Ledger/database projection drift must have a blocking repair fixture.'); + } + + const objectStorageProjectionDriftRepairable = driftRepairFixtures.some( + (fixture) => + fixture.driftKind === 'database_object_storage_projection_drift' && + fixture.affectedCarrierIds.includes('canonical_database_projection') && + fixture.affectedCarrierIds.includes('protected_assetpack_object_storage') && + fixture.blocksUnlock && + fixture.blocksSourceVisibility, + ); + if (!objectStorageProjectionDriftRepairable) { + throw new Error('Object-storage projection drift must have a blocking repair fixture.'); + } + + const posture: DeploymentStoragePosture = { + kind: 'bitcode.deployment_storage_posture', + schemaId: 'bitcode.deploymentStoragePosture.v1', + postureRoot: stableRoot('deployment-storage-posture', [ + ...carriers.map((carrier) => carrier.carrierRoot), + ...driftRepairFixtures.map((fixture) => fixture.fixtureRoot), + ]), + carrierCount: carriers.length, + requiredCarrierIds, + observedCarrierIds, + missingCarrierIds, + carriers, + driftRepairFixtures, + sourceBearingAssetPackLockedBeforeSettlement: true, + ledgerDatabaseProjectionDriftRepairable: true, + objectStorageProjectionDriftRepairable: true, + backupsCovered: carriers.some((carrier) => carrier.storageClass === 'backup') as true, + retentionCovered: carriers.every((carrier) => Boolean(carrier.retentionClass)) as true, + encryptionCovered: carriers.every((carrier) => Boolean(carrier.encryptionPosture)) as true, + rollbackMaterialCovered: carriers.some((carrier) => carrier.storageClass === 'rollback_material') as true, + auditLogsCovered: carriers.some((carrier) => carrier.storageClass === 'audit_log') as true, + sourceSafety: SOURCE_SAFETY, + }; + + if ( + !posture.backupsCovered || + !posture.retentionCovered || + !posture.encryptionCovered || + !posture.rollbackMaterialCovered || + !posture.auditLogsCovered + ) { + throw new Error('DeploymentStoragePosture must cover backups, retention, encryption, rollback material, and audit logs.'); + } + assertSafeObject(posture, 'deployment storage posture'); + + return posture; +} + +function assertDeploymentStorageCarrierId(carrierId: string): DeploymentStorageCarrierId { + if (!DEPLOYMENT_STORAGE_CARRIER_IDS.includes(carrierId as DeploymentStorageCarrierId)) { + throw new Error(`Unknown deployment storage carrier id: ${carrierId}.`); + } + return carrierId as DeploymentStorageCarrierId; +} + +function assertDeploymentStorageDriftRepairFixtureId( + fixtureId: string, +): DeploymentStorageDriftRepairFixtureId { + if ( + !DEPLOYMENT_STORAGE_DRIFT_REPAIR_FIXTURE_IDS.includes( + fixtureId as DeploymentStorageDriftRepairFixtureId, + ) + ) { + throw new Error(`Unknown deployment storage drift repair fixture id: ${fixtureId}.`); + } + return fixtureId as DeploymentStorageDriftRepairFixtureId; +} + +function assertDeploymentHostCapabilityId(hostId: string): DeploymentHostCapabilityId { + if (!DEPLOYMENT_HOST_CAPABILITY_IDS.includes(hostId as DeploymentHostCapabilityId)) { + throw new Error(`Unknown deployment storage owner host id: ${hostId}.`); + } + return hostId as DeploymentHostCapabilityId; +} + +function normalizeCarrierIds( + carrierIds: readonly string[], +): DeploymentStorageCarrierId[] { + return Array.from(new Set(carrierIds.map(assertDeploymentStorageCarrierId))).sort(); +} + +function normalizeLaneIds( + laneIds: readonly EnvironmentLaneContractId[], +): EnvironmentLaneContractId[] { + const normalized = Array.from( + new Set( + laneIds.map((laneId) => { + if (!ENVIRONMENT_LANE_CONTRACT_IDS.includes(laneId)) { + throw new Error(`Unknown environment lane contract id for storage posture: ${laneId}.`); + } + return laneId; + }), + ), + ).sort(); + if (normalized.length === 0) { + throw new Error('Deployment storage carrier must support at least one environment lane.'); + } + return normalized; +} + +function normalizeRequiredRoots( + requiredRoots: readonly DeploymentStorageRootKind[], +): DeploymentStorageRootKind[] { + const normalized = Array.from(new Set(requiredRoots)).sort(); + if (normalized.length === 0) { + throw new Error('Deployment storage carrier must require at least one root.'); + } + return normalized; +} + +function normalizeStringArray(values: readonly string[], label: string): string[] { + const normalized = Array.from( + new Set(values.map((value) => assertSafeNonEmptyString(value, label))), + ).sort(); + if (normalized.length === 0) { + throw new Error(`${label} must contain at least one value.`); + } + return normalized; +} + +function assertNoValueBearingMainnet(laneIds: readonly EnvironmentLaneContractId[]): void { + if (laneIds.includes('value-bearing-mainnet')) { + throw new Error('DeploymentStoragePosture must not admit value-bearing-mainnet before future canon.'); + } +} + +function assertProtectedPayloadBoundary(carrier: DeploymentStorageCarrier): void { + if (!carrier.storesProtectedSourcePayload) return; + if (carrier.preSettlementVisibility !== 'blocked_before_settlement') { + throw new Error(`${carrier.carrierId} must block source-bearing payload visibility before settlement.`); + } + if ( + carrier.disclosurePolicy !== 'protected_source_locked_until_settlement' && + carrier.disclosurePolicy !== 'operator_rollback_only' && + carrier.disclosurePolicy !== 'encrypted_recovery_only' + ) { + throw new Error(`${carrier.carrierId} must use a locked or encrypted disclosure policy.`); + } +} + +function assertSafeNonEmptyString(value: string, label: string): string { + const normalized = assertNonEmptyString(value, label); + for (const pattern of SECRET_OR_SOURCE_PATTERNS) { + if (pattern.test(normalized)) { + throw new Error(`${label} must not contain secrets or non-disclosable source.`); + } + } + return normalized; +} + +function assertSafeObject(value: unknown, label: string): void { + const serialized = JSON.stringify(value); + for (const pattern of SECRET_OR_SOURCE_PATTERNS) { + if (pattern.test(serialized)) { + throw new Error(`${label} must not contain secrets or non-disclosable source.`); + } + } +} + +function findDuplicates(values: readonly string[]): string[] { + const seen = new Set(); + const duplicates = new Set(); + for (const value of values) { + if (seen.has(value)) duplicates.add(value); + seen.add(value); + } + return Array.from(duplicates).sort(); +} + +function stableRoot(prefix: string, parts: readonly 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 c1f7d7cfd..01ec175d8 100644 --- a/packages/btd/src/index.ts +++ b/packages/btd/src/index.ts @@ -169,6 +169,7 @@ export * from './auxillaries-support'; export * from './constants'; export * from './deployment-lanes'; export * from './deployment-host-capability-catalog'; +export * from './deployment-storage-posture'; 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 386839c0a..c3584b295 100644 --- a/packages/protocol/src/canonical/v21-specifying.js +++ b/packages/protocol/src/canonical/v21-specifying.js @@ -376,7 +376,8 @@ function buildV21LikeProfile(version) { ? [ '.bitcode/v34-deployment-host-capability-catalog.json', '.bitcode/v34-environment-lane-contracts.json', - '.bitcode/v34-distributed-execution-runtime-receipts.json' + '.bitcode/v34-distributed-execution-runtime-receipts.json', + '.bitcode/v34-deployment-storage-posture.json' ] : []), ...(version === 'V26' diff --git a/scripts/check-v34-gate4-deployment-storage-posture.mjs b/scripts/check-v34-gate4-deployment-storage-posture.mjs new file mode 100644 index 000000000..6a29ea659 --- /dev/null +++ b/scripts/check-v34-gate4-deployment-storage-posture.mjs @@ -0,0 +1,294 @@ +#!/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 ARTIFACT = '.bitcode/v34-deployment-storage-posture.json'; + +const REQUIRED_CARRIER_IDS = [ + 'ledger_derived_state', + 'canonical_database_projection', + 'protected_assetpack_object_storage', + 'source_safe_assetpack_preview_storage', + 'generated_proof_artifacts', + 'audit_log_stream', + 'rollback_material', + 'encrypted_backups', +]; + +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('_'), + 'raw source', + 'source contents', +]; + +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-gate4-deployment-storage-posture.mjs [--skip-branch-check] [--repo-root ]', + '', + 'Checks V34 Gate 4 Ledger Database Object Storage Deployment Posture source, generated artifact, 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-(?:[4-9]|10)-[a-z0-9][a-z0-9-]*$/u.test(branch), + `V34 Gate 4+ work must occur on version/v34 or v34/gate-4..10-* branches. Observed ${branch || 'detached HEAD'}.`, + ); + } + + const requiredFiles = [ + ARTIFACT, + 'packages/btd/src/deployment-storage-posture.ts', + 'packages/btd/src/index.ts', + 'packages/btd/package.json', + 'packages/btd/__tests__/deployment-storage-posture.test.ts', + 'scripts/generate-v34-deployment-storage-posture.mjs', + 'scripts/check-v34-gate4-deployment-storage-posture.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 4 file: ${relativePath}`); + } + + if (failures.length === 0) { + try { + run(root, 'pnpm', ['run', 'check:v34-deployment-storage-posture']); + } catch (error) { + failures.push(`V34 Gate 4 artifact check failed: ${error.stderr || error.message}`); + } + } + + const serializedArtifact = fileExists(root, ARTIFACT) ? read(root, ARTIFACT) : ''; + for (const marker of SECRET_MARKERS) { + assertCheck(failures, !serializedArtifact.includes(marker), `V34 storage posture artifact must not contain secret/source marker ${marker}.`); + } + + const artifact = serializedArtifact ? JSON.parse(serializedArtifact) : null; + if (artifact) { + assertCheck(failures, artifact.artifactId === 'v34-deployment-storage-posture', 'Artifact id must match Gate 4 storage posture.'); + assertCheck(failures, artifact.schemaId === 'bitcode.v34.deploymentStoragePosture.v1', 'Artifact schema id must match.'); + assertCheck(failures, artifact.version === 'V34' && artifact.currentTarget === 'V33', 'Artifact must bind V34 over active V33.'); + assertCheck(failures, artifact.passed === true, 'Artifact must pass.'); + assertCheck( + failures, + artifact.sourceSafetyVerdict === 'source-safe-deployment-storage-posture-metadata', + 'Artifact must be source-safe deployment storage metadata.', + ); + assertCheck(failures, includesAll(artifact.requiredCarrierIds, REQUIRED_CARRIER_IDS), 'Artifact must enumerate every required storage carrier.'); + assertCheck(failures, includesAll(artifact.coverage.observedCarrierIds, REQUIRED_CARRIER_IDS), 'Artifact coverage must observe every carrier.'); + assertCheck(failures, artifact.coverage.carrierCount === 8, 'Artifact must prove eight storage carrier rows.'); + assertCheck(failures, artifact.coverage.ledgerDerivedStateCovered === true, 'Ledger-derived state must be covered.'); + assertCheck(failures, artifact.coverage.databaseProjectionCovered === true, 'Database projection must be covered.'); + assertCheck(failures, artifact.coverage.objectStorageCovered === true, 'Object storage must be covered.'); + assertCheck(failures, artifact.coverage.proofArtifactsCovered === true, 'Proof artifacts must be covered.'); + assertCheck(failures, artifact.coverage.auditLogsCovered === true, 'Audit logs must be covered.'); + assertCheck(failures, artifact.coverage.rollbackMaterialCovered === true, 'Rollback material must be covered.'); + assertCheck(failures, artifact.coverage.backupsCovered === true, 'Backups must be covered.'); + assertCheck(failures, artifact.coverage.retentionCovered === true, 'Retention posture must be covered.'); + assertCheck(failures, artifact.coverage.encryptionCovered === true, 'Encryption posture must be covered.'); + assertCheck(failures, artifact.coverage.repairCommandsCovered === true, 'Repair commands must be covered.'); + assertCheck( + failures, + artifact.coverage.sourceBearingAssetPackLockedBeforeSettlement === true, + 'Source-bearing AssetPack storage must remain locked before settlement.', + ); + assertCheck( + failures, + artifact.coverage.ledgerDatabaseProjectionDriftRepairable === true, + 'Ledger/database projection drift must be repairable.', + ); + assertCheck( + failures, + artifact.coverage.objectStorageProjectionDriftRepairable === true, + 'Object-storage projection drift must be repairable.', + ); + assertCheck(failures, artifact.coverage.valueBearingMainnetAdmitted === false, 'Storage posture must not admit value-bearing mainnet.'); + assertCheck(failures, artifact.coverage.credentialsSerialized === false, 'Artifact must not serialize credentials.'); + assertCheck(failures, artifact.coverage.protectedSourceVisible === false, 'Artifact must not expose protected source.'); + assertCheck( + failures, + artifact.carriers.every((carrier) => /^v34-deployment-storage-carrier:[a-f0-9]{24}$/u.test(carrier.carrierRoot)), + 'Storage carrier rows must have deterministic carrier roots.', + ); + assertCheck( + failures, + artifact.driftRepairFixtures.every((fixture) => /^v34-deployment-storage-drift-repair-fixture:[a-f0-9]{24}$/u.test(fixture.fixtureRoot)), + 'Storage drift repair fixtures must have deterministic fixture roots.', + ); + assertCheck( + failures, + artifact.sourceEvidence.every((entry) => entry.requiredTokens.every((token) => token.present === true)), + 'Source evidence tokens must all be present.', + ); + assertCheck( + failures, + artifact.testEvidence.every((entry) => entry.requiredTokens.every((token) => token.present === true)), + '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 btdPackageJson = read(root, 'packages/btd/package.json'); + const workflow = read(root, '.github/workflows/bitcode-gate-quality.yml'); + const source = read(root, 'packages/btd/src/deployment-storage-posture.ts'); + const test = read(root, 'packages/btd/__tests__/deployment-storage-posture.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(ARTIFACT), `V34 docs must mention ${ARTIFACT}.`); + assertCheck(failures, doc.includes('DeploymentStoragePosture'), 'V34 docs must name DeploymentStoragePosture.'); + assertCheck(failures, doc.includes('ledger-derived state'), 'V34 docs must name ledger-derived state.'); + assertCheck(failures, doc.includes('database projection'), 'V34 docs must name database projection.'); + assertCheck(failures, doc.includes('object storage'), 'V34 docs must name object storage.'); + assertCheck(failures, doc.includes('rollback material'), 'V34 docs must name rollback material.'); + assertCheck(failures, doc.includes('source-bearing AssetPack storage remains locked before settlement'), 'V34 docs must name the source lock invariant.'); + } + + assertCheck( + failures, + /Current working gate: V34 Gate (?:[5-9]|10)\b/u.test(roadmap), + 'Roadmap must advance past V34 Gate 4 after this gate closes.', + ); + assertCheck(failures, packageJson.includes('"generate:v34-deployment-storage-posture"'), 'package.json must expose the Gate 4 generator.'); + assertCheck(failures, packageJson.includes('"check:v34-deployment-storage-posture"'), 'package.json must expose the Gate 4 artifact check.'); + assertCheck(failures, packageJson.includes('"check:v34-gate4"'), 'package.json must expose check:v34-gate4.'); + assertCheck(failures, btdPackageJson.includes('"./deployment-storage-posture"'), '@bitcode/btd must export deployment-storage-posture.'); + assertCheck(failures, workflow.includes('check-v34-gate4-deployment-storage-posture.mjs'), 'Gate workflow must run the V34 Gate 4 checker.'); + assertCheck(failures, workflow.includes('deployment-storage-posture.test.ts'), 'Gate workflow must run the focused deployment storage posture test.'); + assertCheck(failures, specifying.includes(ARTIFACT), 'Spec-family profile must include the Gate 4 artifact path.'); + + for (const phrase of [ + 'DeploymentStoragePosture', + 'DEPLOYMENT_STORAGE_CARRIER_IDS', + 'protected_source_locked_until_settlement', + 'ledger_database_projection_drift', + 'database_object_storage_projection_drift', + 'unpaid_source_visibility_attempt', + 'value-bearing-mainnet', + ]) { + assertCheck(failures, source.includes(phrase), `Gate 4 source must include ${phrase}.`); + } + + for (const phrase of [ + 'catalogs ledger-derived state, database projections, object storage, proofs, audit logs, rollback material, and backups', + 'requires durability, disclosure, retention, encryption, backup, rollback, root, repair, validation, and proof fields for each storage carrier', + 'keeps source-bearing AssetPack storage locked before settlement', + 'proves ledger/database and object-storage projection drift repair fixtures', + 'fails closed when a required deployment storage carrier is missing', + 'fails closed when source-bearing AssetPack storage becomes visible before settlement', + 'fails closed when storage posture admits value-bearing mainnet before future canon', + 'fails closed on secret-shaped or non-disclosable source storage text', + ]) { + assertCheck(failures, test.includes(phrase), `Gate 4 test must assert: ${phrase}.`); + } + + if (failures.length) { + process.stderr.write('V34 Gate 4 Ledger Database Object Storage Deployment Posture check failed:\n'); + for (const failure of failures) { + process.stderr.write(`- ${failure}\n`); + } + process.exit(1); + } + + process.stdout.write(`V34 Gate 4 Ledger Database Object Storage Deployment Posture ok ${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-deployment-storage-posture.mjs b/scripts/generate-v34-deployment-storage-posture.mjs new file mode 100644 index 000000000..396dacde0 --- /dev/null +++ b/scripts/generate-v34-deployment-storage-posture.mjs @@ -0,0 +1,533 @@ +#!/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 ARTIFACT_PATH = '.bitcode/v34-deployment-storage-posture.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('_'), + 'raw source', + 'source contents', +]); +const SECRET_PATTERN = new RegExp(SECRET_MARKERS.map(escapeRegex).join('|'), 'iu'); + +const requiredCarrierIds = Object.freeze([ + 'ledger_derived_state', + 'canonical_database_projection', + 'protected_assetpack_object_storage', + 'source_safe_assetpack_preview_storage', + 'generated_proof_artifacts', + 'audit_log_stream', + 'rollback_material', + 'encrypted_backups', +]); + +const nonValueLanes = Object.freeze([ + 'local', + 'regtest', + 'signet', + 'staging-testnet', + 'public-testnet', + 'mainnet-ready-dry-run', +]); + +const carrierRows = Object.freeze([ + { + carrierId: 'ledger_derived_state', + storageClass: 'ledger_derived_state', + ownerHostId: 'ledger_projection', + storageOwnerPackage: 'packages/btd', + supportedLaneIds: nonValueLanes, + durabilityPosture: 'append_only_replayable', + disclosurePolicy: 'ledger_commitment_only', + storesProtectedSourcePayload: false, + preSettlementVisibility: 'source_safe_roots_only', + postSettlementVisibility: 'source_safe_roots_only', + retentionClass: 'testnet-and-mainnet-dry-run-audit-retained', + encryptionPosture: 'hash-chained source-safe roots with provider encryption at rest', + backupPosture: 'replay from ledger roots, database projection roots, and proof artifacts', + rollbackMaterialPosture: 'rollback uses prior ledger projection root and replay command', + requiredRoots: ['ledger', 'proof', 'audit_log'], + driftDetection: 'compare-ledger-root-to-database-projection-root-before-unlock', + repairCommand: 'pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/reconciliation.test.ts', + repairPosture: 'hold AssetPack unlock and replay ledger projection from committed ledger root', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['BtdAssetPackMintReceipt', 'BtdReadReceipt', 'BtdRightsTransferReceipt'], + }, + { + carrierId: 'canonical_database_projection', + storageClass: 'canonical_database_projection', + ownerHostId: 'database_projection', + storageOwnerPackage: 'packages/supabase', + supportedLaneIds: nonValueLanes, + durabilityPosture: 'durable_projection', + disclosurePolicy: 'database_projection_only', + storesProtectedSourcePayload: false, + preSettlementVisibility: 'source_safe_roots_only', + postSettlementVisibility: 'source_safe_roots_only', + retentionClass: 'lane-retained-postgres-projection-with-point-in-time-restore', + encryptionPosture: 'provider encrypted at rest; service access only through server-side policy', + backupPosture: 'Supabase point-in-time restore plus deterministic projection replay', + rollbackMaterialPosture: 'migration rollback requires prior schema root and projection repair root', + requiredRoots: ['database_projection', 'ledger', 'proof', 'audit_log'], + driftDetection: 'compare-database-projection-root-to-ledger-root-and-object-storage-root', + repairCommand: 'pnpm run db:data-health:ci && pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/reconciliation.test.ts', + repairPosture: 'block paid unlock until projection repair writes a new database projection root', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['ledger database reconciliation', 'DeploymentStoragePosture'], + }, + { + carrierId: 'protected_assetpack_object_storage', + storageClass: 'object_storage', + ownerHostId: 'object_storage', + storageOwnerPackage: 'packages/pipeline-hosts', + supportedLaneIds: nonValueLanes, + durabilityPosture: 'encrypted_durable_object', + disclosurePolicy: 'protected_source_locked_until_settlement', + storesProtectedSourcePayload: true, + preSettlementVisibility: 'blocked_before_settlement', + postSettlementVisibility: 'reader_unlocked_after_settlement', + retentionClass: 'rights-retained-until-read-license-expiry-or-operator-deletion', + encryptionPosture: 'encrypted at rest with lane-scoped object key policy and no tracked key material', + backupPosture: 'encrypted protected-object backup with proof-rooted restore command', + rollbackMaterialPosture: 'rollback material may reference encrypted object root but never exposes payload', + requiredRoots: ['object_storage', 'proof', 'audit_log', 'rollback'], + driftDetection: 'deny source visibility unless paid settlement root and object root both verify', + repairCommand: 'pnpm run check:v34-gate4', + repairPosture: 'lock delivery and rewrite from authorized encrypted artifact root after operator approval', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['AssetPackPreview', 'SettlementUnlock', 'object-storage receipt root'], + }, + { + carrierId: 'source_safe_assetpack_preview_storage', + storageClass: 'object_storage', + ownerHostId: 'object_storage', + storageOwnerPackage: 'packages/pipeline-hosts', + supportedLaneIds: nonValueLanes, + durabilityPosture: 'source_safe_durable_object', + disclosurePolicy: 'source_safe_preview_only', + storesProtectedSourcePayload: false, + preSettlementVisibility: 'source_safe_roots_only', + postSettlementVisibility: 'source_safe_roots_only', + retentionClass: 'read-preview-retained-for-transaction-history', + encryptionPosture: 'provider encrypted at rest; payload limited to measurements and roots', + backupPosture: 'preview object can be regenerated from runtime receipt roots', + rollbackMaterialPosture: 'preview rollback rewrites source-safe measurements from receipt output root', + requiredRoots: ['object_storage', 'database_projection', 'proof'], + driftDetection: 'compare-preview-object-root-to-database-preview-root', + repairCommand: 'pnpm --filter @bitcode/pipeline-asset-pack exec jest --config jest.config.cjs --runTestsByPath src/__tests__/asset-pack-disclosure.test.ts --runInBand', + repairPosture: 'withhold preview update until source-safe projection is regenerated', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['AssetPackPreview', 'ReadFitsFindingSynthesis', 'InterfaceTelemetryProofHook'], + }, + { + carrierId: 'generated_proof_artifacts', + storageClass: 'proof_artifact', + ownerHostId: 'proof_services', + storageOwnerPackage: 'packages/protocol', + supportedLaneIds: nonValueLanes, + durabilityPosture: 'generated_replayable_artifact', + disclosurePolicy: 'source_safe_proof_only', + storesProtectedSourcePayload: false, + preSettlementVisibility: 'source_safe_roots_only', + postSettlementVisibility: 'source_safe_roots_only', + retentionClass: 'repository-retained-generated-proof-artifact', + encryptionPosture: 'source-safe generated JSON with secret scanning before commit', + backupPosture: 'recreate from canonical inputs and deterministic generator scripts', + rollbackMaterialPosture: 'rollback restores prior generated artifact root before promotion', + requiredRoots: ['proof', 'audit_log'], + driftDetection: 'generated-artifact-check-compares-current-output-to-tracked-artifact', + repairCommand: 'pnpm run check:spec-quality && pnpm run check:v34-gate4', + repairPosture: 'regenerate proof artifact from canonical source and re-run promotion checks', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['BITCODE_SPEC_V34.md', 'BITCODE_SPEC_V34_PARITY_MATRIX.md'], + }, + { + carrierId: 'audit_log_stream', + storageClass: 'audit_log', + ownerHostId: 'runtime_observers', + storageOwnerPackage: 'packages/observability', + supportedLaneIds: nonValueLanes, + durabilityPosture: 'append_only_log', + disclosurePolicy: 'operator_audit_only', + storesProtectedSourcePayload: false, + preSettlementVisibility: 'operator_source_safe_only', + postSettlementVisibility: 'operator_source_safe_only', + retentionClass: 'lane-audit-log-retention-with-operator-export', + encryptionPosture: 'provider encrypted at rest with redacted structured events', + backupPosture: 'audit log export stores roots and redacted event envelopes', + rollbackMaterialPosture: 'audit rollback is append-only correction event, not deletion', + requiredRoots: ['audit_log', 'proof'], + driftDetection: 'runtime receipt log root must match audit event stream root', + repairCommand: 'pnpm run check:v34-gate3 && pnpm run check:v34-gate4', + repairPosture: 'mark execution blocked and append repair event before replay', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['DistributedExecutionRuntimeReceipt', 'InterfaceTelemetryProofHook'], + }, + { + carrierId: 'rollback_material', + storageClass: 'rollback_material', + ownerHostId: 'repair_jobs', + storageOwnerPackage: 'packages/btd', + supportedLaneIds: nonValueLanes, + durabilityPosture: 'operator_controlled_rollback', + disclosurePolicy: 'operator_rollback_only', + storesProtectedSourcePayload: true, + preSettlementVisibility: 'blocked_before_settlement', + postSettlementVisibility: 'internal_recovery_only', + retentionClass: 'operator-retained-until-deployment-successor-proof', + encryptionPosture: 'encrypted at rest; rollback bundle references roots instead of payload text', + backupPosture: 'rollback bundle copied to encrypted backup carrier with proof root', + rollbackMaterialPosture: 'required for migration, projection, and object-storage repair', + requiredRoots: ['rollback', 'object_storage', 'database_projection', 'ledger', 'proof'], + driftDetection: 'rollback gap blocks deployment promotion and paid unlock', + repairCommand: 'pnpm run check:v34-gate4', + repairPosture: 'block traffic promotion until rollback bundle root and verification command pass', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['RollbackUpgradeRepairPlaybook', 'DeploymentStoragePosture'], + }, + { + carrierId: 'encrypted_backups', + storageClass: 'backup', + ownerHostId: 'object_storage', + storageOwnerPackage: 'packages/pipeline-hosts', + supportedLaneIds: nonValueLanes, + durabilityPosture: 'encrypted_backup', + disclosurePolicy: 'encrypted_recovery_only', + storesProtectedSourcePayload: true, + preSettlementVisibility: 'blocked_before_settlement', + postSettlementVisibility: 'internal_recovery_only', + retentionClass: 'lane-scoped-backup-retention-with-manual-deletion-proof', + encryptionPosture: 'encrypted backup carrier with no tracked key values', + backupPosture: 'required backup root for protected objects, projections, proofs, and rollback bundles', + rollbackMaterialPosture: 'backup restore is admissible only through repair job receipt', + requiredRoots: ['object_storage', 'database_projection', 'ledger', 'proof', 'audit_log', 'rollback'], + driftDetection: 'backup root must match carrier roots before promotion readiness', + repairCommand: 'pnpm run check:v34-gate4', + repairPosture: 'deny restore visibility until backup proof root and repair job receipt verify', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['DeploymentReadinessRehearsal', 'DeploymentStoragePosture'], + }, +]); + +const driftRepairFixtures = Object.freeze([ + { + fixtureId: 'ledger-database-projection-drift', + driftKind: 'ledger_database_projection_drift', + affectedCarrierIds: ['ledger_derived_state', 'canonical_database_projection'], + detectionRoot: 'sha256:ledger-database-projection-drift-root', + blocksUnlock: true, + blocksSourceVisibility: true, + repairCommand: 'pnpm run db:data-health:ci && pnpm --filter @bitcode/btd test -- --runTestsByPath __tests__/reconciliation.test.ts', + repairPosture: 'repair database projection from ledger root before AssetPack unlock', + expectedFinalPosture: 'ledger and database projection roots agree before unlock resumes', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['ledgerProjectionRoot', 'databaseProjectionRoot', 'repairJobReceiptRoot'], + }, + { + fixtureId: 'database-object-storage-projection-drift', + driftKind: 'database_object_storage_projection_drift', + affectedCarrierIds: [ + 'canonical_database_projection', + 'protected_assetpack_object_storage', + 'source_safe_assetpack_preview_storage', + ], + detectionRoot: 'sha256:database-object-storage-projection-drift-root', + blocksUnlock: true, + blocksSourceVisibility: true, + repairCommand: 'pnpm run check:v34-gate4', + repairPosture: 'repair object root projection or rewrite authorized object before source visibility', + expectedFinalPosture: 'database projection and object storage roots agree before delivery resumes', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['databaseProjectionRoot', 'objectStorageRoot', 'repairJobReceiptRoot'], + }, + { + fixtureId: 'unpaid-protected-assetpack-access-attempt', + driftKind: 'unpaid_source_visibility_attempt', + affectedCarrierIds: ['protected_assetpack_object_storage', 'rollback_material', 'encrypted_backups'], + detectionRoot: 'sha256:unpaid-protected-assetpack-access-attempt-root', + blocksUnlock: true, + blocksSourceVisibility: true, + repairCommand: 'pnpm run check:v34-gate4', + repairPosture: 'deny response, append audit event, and require paid settlement root before retry', + expectedFinalPosture: 'protected AssetPack payload remains hidden before settlement', + validationCommand: 'pnpm run check:v34-gate4', + proofRootBasis: ['SettlementUnlock', 'InterfaceAuthorizationPolicy', 'DeploymentStoragePosture'], + }, +]); + +const sourceFiles = Object.freeze([ + 'packages/btd/src/deployment-storage-posture.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-storage-posture.test.ts', + 'scripts/check-v34-gate4-deployment-storage-posture.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 withCarrierRoots(rows) { + return rows.map((row) => ({ + ...row, + carrierRoot: stableRoot('v34-deployment-storage-carrier', [ + row.carrierId, + row.storageClass, + row.ownerHostId, + row.storageOwnerPackage, + row.supportedLaneIds.join(','), + row.durabilityPosture, + row.disclosurePolicy, + String(row.storesProtectedSourcePayload), + row.preSettlementVisibility, + row.postSettlementVisibility, + row.retentionClass, + row.encryptionPosture, + row.backupPosture, + row.rollbackMaterialPosture, + row.requiredRoots.join(','), + row.driftDetection, + row.repairCommand, + row.repairPosture, + row.validationCommand, + row.proofRootBasis.join(','), + ]), + })); +} + +function withDriftRepairFixtureRoots(fixtures) { + return fixtures.map((fixture) => ({ + ...fixture, + fixtureRoot: stableRoot('v34-deployment-storage-drift-repair-fixture', [ + fixture.fixtureId, + fixture.driftKind, + fixture.affectedCarrierIds.join(','), + fixture.detectionRoot, + String(fixture.blocksUnlock), + String(fixture.blocksSourceVisibility), + fixture.repairCommand, + fixture.repairPosture, + fixture.expectedFinalPosture, + fixture.validationCommand, + fixture.proofRootBasis.join(','), + ]), + })); +} + +export function buildV34DeploymentStoragePostureArtifact() { + const carriers = withCarrierRoots(carrierRows); + const driftFixtures = withDriftRepairFixtureRoots(driftRepairFixtures); + const observedCarrierIds = carriers.map((row) => row.carrierId); + const missingCarrierIds = requiredCarrierIds.filter((carrierId) => !observedCarrierIds.includes(carrierId)); + const protectedCarriers = carriers.filter((row) => row.storesProtectedSourcePayload); + const sourceEvidence = [ + scanTokens('packages/btd/src/deployment-storage-posture.ts', [ + 'DeploymentStoragePosture', + 'DEPLOYMENT_STORAGE_CARRIER_IDS', + 'protected_source_locked_until_settlement', + 'ledger_database_projection_drift', + 'database_object_storage_projection_drift', + 'value-bearing-mainnet', + ]), + scanTokens('packages/btd/src/index.ts', ['deployment-storage-posture']), + scanTokens('BITCODE_SPEC_V34.md', [ + ARTIFACT_PATH, + 'DeploymentStoragePosture', + 'protected source is never an unpaid interface payload', + ]), + ]; + const testEvidence = [ + scanTokens('packages/btd/__tests__/deployment-storage-posture.test.ts', [ + 'catalogs ledger-derived state, database projections, object storage, proofs, audit logs, rollback material, and backups', + 'keeps source-bearing AssetPack storage locked before settlement', + 'proves ledger/database and object-storage projection drift repair fixtures', + 'fails closed when source-bearing AssetPack storage becomes visible before settlement', + 'fails closed on secret-shaped or non-disclosable source storage text', + ]), + scanTokens('scripts/check-v34-gate4-deployment-storage-posture.mjs', [ + 'check:v34-deployment-storage-posture', + 'deployment-storage-posture.test.ts', + 'Ledger Database Object Storage Deployment Posture', + ]), + ]; + const sourceEvidenceComplete = sourceEvidence.every(allTokensPresent); + const testEvidenceComplete = testEvidence.every(allTokensPresent); + const ledgerDatabaseDriftRepair = driftFixtures.find( + (fixture) => fixture.driftKind === 'ledger_database_projection_drift', + ); + const objectStorageDriftRepair = driftFixtures.find( + (fixture) => fixture.driftKind === 'database_object_storage_projection_drift', + ); + const sourceLocked = protectedCarriers.every( + (row) => row.preSettlementVisibility === 'blocked_before_settlement', + ); + const noValueBearingMainnet = carriers.every( + (row) => !row.supportedLaneIds.includes('value-bearing-mainnet'), + ); + const passed = + missingCarrierIds.length === 0 && + carriers.length === 8 && + sourceLocked && + noValueBearingMainnet && + Boolean(ledgerDatabaseDriftRepair?.blocksUnlock) && + Boolean(objectStorageDriftRepair?.blocksSourceVisibility) && + sourceEvidenceComplete && + testEvidenceComplete; + + return { + artifactId: 'v34-deployment-storage-posture', + schemaId: 'bitcode.v34.deploymentStoragePosture.v1', + version: 'V34', + currentTarget: 'V33', + generatedAt: GENERATED_AT, + sourceSafetyVerdict: 'source-safe-deployment-storage-posture-metadata', + requiredCarrierIds, + postureRoot: stableRoot('v34-deployment-storage-posture', [ + ...carriers.map((row) => row.carrierRoot), + ...driftFixtures.map((fixture) => fixture.fixtureRoot), + ]), + carriers, + driftRepairFixtures: driftFixtures, + coverage: { + observedCarrierIds, + missingCarrierIds, + carrierCount: carriers.length, + ledgerDerivedStateCovered: observedCarrierIds.includes('ledger_derived_state'), + databaseProjectionCovered: observedCarrierIds.includes('canonical_database_projection'), + objectStorageCovered: + observedCarrierIds.includes('protected_assetpack_object_storage') && + observedCarrierIds.includes('source_safe_assetpack_preview_storage'), + proofArtifactsCovered: observedCarrierIds.includes('generated_proof_artifacts'), + auditLogsCovered: observedCarrierIds.includes('audit_log_stream'), + rollbackMaterialCovered: observedCarrierIds.includes('rollback_material'), + backupsCovered: observedCarrierIds.includes('encrypted_backups'), + retentionCovered: carriers.every((row) => Boolean(row.retentionClass)), + encryptionCovered: carriers.every((row) => Boolean(row.encryptionPosture)), + repairCommandsCovered: carriers.every((row) => Boolean(row.repairCommand)), + protectedSourcePayloadCarrierCount: protectedCarriers.length, + sourceBearingAssetPackLockedBeforeSettlement: sourceLocked, + ledgerDatabaseProjectionDriftRepairable: Boolean(ledgerDatabaseDriftRepair?.blocksUnlock), + objectStorageProjectionDriftRepairable: Boolean(objectStorageDriftRepair?.blocksSourceVisibility), + valueBearingMainnetAdmitted: !noValueBearingMainnet, + protectedSourceVisible: false, + credentialsSerialized: false, + }, + sharedFixtureFiles: [...sourceFiles, ...testFiles], + sourceEvidence, + testEvidence, + passed, + closureCommand: 'pnpm run check:v34-gate4', + }; +} + +function assertSafeArtifact(artifact, artifactPath) { + const serialized = stableStringify(artifact); + if (SECRET_PATTERN.test(serialized)) { + throw new Error(`${artifactPath} contains a secret-shaped or non-disclosable-source 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-deployment-storage-posture.`); + } + const current = readFileSync(artifactFile, 'utf8'); + if (current !== next) { + throw new Error(`${artifactPath} is stale. Run pnpm run generate:v34-deployment-storage-posture.`); + } +} + +function main() { + const mode = process.argv.includes('--check') ? 'check' : 'write'; + const artifact = buildV34DeploymentStoragePostureArtifact(); + + if (mode === 'check') { + checkArtifact(artifact, ARTIFACT_PATH); + process.stdout.write(`V34 deployment storage posture artifact ok ${ARTIFACT_PATH}\n`); + return; + } + + writeArtifact(artifact, ARTIFACT_PATH); + process.stdout.write(`Wrote ${ARTIFACT_PATH}\n`); +} + +if (import.meta.url === `file://${process.argv[1]}`) { + main(); +}