@tokenoftrust/storefront-runner 1.4.2-rc.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/storefront/astro.config.mjs +3 -3
- package/apps/storefront/env.d.ts +54 -5
- package/apps/storefront/integrations/materialize-guard.mjs +12 -14
- package/apps/storefront/migrations-apps/0004_deep_morph.sql +36 -0
- package/apps/storefront/migrations-apps/0005_common_mystique.sql +23 -0
- package/apps/storefront/migrations-apps/0006_broad_microchip.sql +16 -0
- package/apps/storefront/migrations-apps/0007_brave_outlaw_kid.sql +15 -0
- package/apps/storefront/migrations-apps/0008_blue_dazzler.sql +11 -0
- package/apps/storefront/migrations-apps/0009_bored_shinobi_shaw.sql +15 -0
- package/apps/storefront/migrations-apps/0010_ws4_verified_provider_measurement.sql +35 -0
- package/apps/storefront/migrations-apps/meta/0004_snapshot.json +1474 -0
- package/apps/storefront/migrations-apps/meta/0005_snapshot.json +1617 -0
- package/apps/storefront/migrations-apps/meta/0006_snapshot.json +1716 -0
- package/apps/storefront/migrations-apps/meta/0007_snapshot.json +1812 -0
- package/apps/storefront/migrations-apps/meta/0008_snapshot.json +1881 -0
- package/apps/storefront/migrations-apps/meta/0009_snapshot.json +1979 -0
- package/apps/storefront/migrations-apps/meta/_journal.json +42 -0
- package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
- package/apps/storefront/public/platform/amplitude-http/v1/entry.mjs +19 -0
- package/apps/storefront/public/platform/google-measurement/v1/entry.mjs +19 -0
- package/apps/storefront/public/shared/commerce-chrome.css +20 -2
- package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +68 -0
- package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +111 -0
- package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +2 -0
- package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +247 -0
- package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +73 -0
- package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +79 -0
- package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +10982 -0
- package/apps/storefront/scripts/import-v84-through-ws2.ts +38 -0
- package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +2 -0
- package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +4 -0
- package/apps/storefront/scripts/install-v84-native-through-ws1.ts +185 -0
- package/apps/storefront/scripts/prove-clone-isolation.ts +506 -0
- package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +148 -0
- package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +35 -0
- package/apps/storefront/scripts/replay-v84-clean-clone.ts +214 -0
- package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +18 -0
- package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +18 -0
- package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +18 -0
- package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +17 -0
- package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +15 -0
- package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +21 -0
- package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +42 -0
- package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +145 -0
- package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +18 -0
- package/apps/storefront/src/components/Img.astro +1 -1
- package/apps/storefront/src/components/admin/AdminBlogTab.astro +19 -16
- package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
- package/apps/storefront/src/components/admin/AdminPublishTab.astro +1057 -412
- package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
- package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
- package/apps/storefront/src/components/chrome/NavDropdown.astro +12 -0
- package/apps/storefront/src/components/chrome/SiteFooter.astro +7 -2
- package/apps/storefront/src/components/chrome/SiteHeader.astro +46 -10
- package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +1 -1
- package/apps/storefront/src/components/compliance/ComplianceNotice.astro +1 -2
- package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +1 -2
- package/apps/storefront/src/components/compliance/PactActNotice.astro +1 -1
- package/apps/storefront/src/components/compliance/Prop65Warning.astro +1 -2
- package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +2 -3
- package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +2 -2
- package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +2 -3
- package/apps/storefront/src/components/content/TrustStrip.astro +4 -4
- package/apps/storefront/src/components/islands/MobileNav.tsx +26 -1
- package/apps/storefront/src/components/islands/VariantSelector.tsx +27 -2
- package/apps/storefront/src/components/islands/useCheckoutHandoffCorrelation.ts +75 -0
- package/apps/storefront/src/components/membership/IllustrativeLabel.astro +1 -2
- package/apps/storefront/src/components/membership/TierCards.astro +2 -2
- package/apps/storefront/src/components/membership/TierComparisonTable.astro +2 -2
- package/apps/storefront/src/components/subscription/SavingsExplainer.astro +2 -2
- package/apps/storefront/src/config/adminTenantLookup.ts +35 -0
- package/apps/storefront/src/config/devTenantSeed.ts +9 -9
- package/apps/storefront/src/config/resolver.ts +4 -4
- package/apps/storefront/src/layouts/Layout.astro +100 -20
- package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
- package/apps/storefront/src/lib/activity/changeActorAttribution.ts +86 -6
- package/apps/storefront/src/lib/activity/deployVersion.ts +1 -1
- package/apps/storefront/src/lib/admin/adminShell.ts +2 -0
- package/apps/storefront/src/lib/admin/envelope.ts +1 -2
- package/apps/storefront/src/lib/apps/adminService.ts +10 -0
- package/apps/storefront/src/lib/apps/adminSession.ts +1 -1
- package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
- package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
- package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +447 -0
- package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +367 -0
- package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +315 -0
- package/apps/storefront/src/lib/apps/integrationLifecycle.ts +614 -0
- package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +125 -0
- package/apps/storefront/src/lib/apps/integrationReadModel.ts +563 -0
- package/apps/storefront/src/lib/apps/integrationsTab.ts +547 -0
- package/apps/storefront/src/lib/apps/integrationsView.ts +106 -0
- package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +599 -0
- package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
- package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +8 -0
- package/apps/storefront/src/lib/apps/orders/ordersStore.ts +1 -1
- package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
- package/apps/storefront/src/lib/apps/registryService.ts +264 -10
- package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
- package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +16 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +105 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +177 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +251 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +241 -0
- package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +211 -0
- package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +409 -0
- package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +368 -0
- package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +188 -0
- package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +291 -0
- package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +95 -0
- package/apps/storefront/src/lib/auth/adminApiGuard.ts +66 -0
- package/apps/storefront/src/lib/auth/adminEntry.ts +2 -2
- package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
- package/apps/storefront/src/lib/blog/collection.ts +97 -10
- package/apps/storefront/src/lib/blog/markdown.ts +1 -1
- package/apps/storefront/src/lib/blog/provider.ts +57 -3
- package/apps/storefront/src/lib/blog/types.ts +9 -9
- package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome-assignments.example.json +6 -0
- package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +79 -0
- package/apps/storefront/src/lib/chrome/model.ts +48 -253
- package/apps/storefront/src/lib/chrome/navCurrent.ts +29 -0
- package/apps/storefront/src/lib/chrome/parseConfig.ts +18 -32
- package/apps/storefront/src/lib/chrome/variant.ts +39 -0
- package/apps/storefront/src/lib/commerce/checkoutHandoffCorrelation.ts +105 -0
- package/apps/storefront/src/lib/compliance/tamper-sentinel-boot.ts +152 -0
- package/apps/storefront/src/lib/compliance/tamper-sentinel.ts +327 -0
- package/apps/storefront/src/lib/content/index.ts +5 -5
- package/apps/storefront/src/lib/content-edit/applyDiff.ts +1 -1
- package/apps/storefront/src/lib/content-edit/route.ts +1 -1
- package/apps/storefront/src/lib/d1/catalog.ts +12 -0
- package/apps/storefront/src/lib/d1/schema-apps.ts +276 -0
- package/apps/storefront/src/lib/dev/cockpit/ask.ts +49 -0
- package/apps/storefront/src/lib/dev/cockpit/clickToSource.ts +71 -0
- package/apps/storefront/src/lib/dev/cockpit/copyCommand.ts +227 -0
- package/apps/storefront/src/lib/dev/cockpit/files.ts +249 -0
- package/apps/storefront/src/lib/dev/cockpit/hostedActivity.ts +500 -0
- package/apps/storefront/src/lib/dev/cockpit/infoPopover.ts +12 -0
- package/apps/storefront/src/lib/dev/cockpit/mcpCopy.ts +34 -0
- package/apps/storefront/src/lib/dev/cockpit/monitor.ts +107 -0
- package/apps/storefront/src/lib/dev/cockpit/stepper.ts +116 -0
- package/apps/storefront/src/lib/dev/cockpit/themeToggle.ts +23 -0
- package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
- package/apps/storefront/src/lib/edgeCache.ts +18 -6
- package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
- package/apps/storefront/src/lib/integration/browserEnvironment.ts +137 -0
- package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +72 -0
- package/apps/storefront/src/lib/integration/nonceStamp.ts +49 -0
- package/apps/storefront/src/lib/integration/tenantDeclarations.ts +116 -0
- package/apps/storefront/src/lib/membership/eligibility.ts +1 -1
- package/apps/storefront/src/lib/membership/model.ts +1 -2
- package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +2 -2
- package/apps/storefront/src/lib/newsletter/consent.ts +2 -2
- package/apps/storefront/src/lib/newsletter/send.ts +3 -3
- package/apps/storefront/src/lib/previewReload.ts +57 -1
- package/apps/storefront/src/lib/publish/adminPublishTab.ts +1775 -669
- package/apps/storefront/src/lib/publish/buildStream.ts +269 -0
- package/apps/storefront/src/lib/publish/collapsePersistence.ts +72 -0
- package/apps/storefront/src/lib/publish/lifecycleState.ts +369 -0
- package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +29 -0
- package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +78 -0
- package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +62 -0
- package/apps/storefront/src/lib/publish/mergeRowFindings.ts +124 -0
- package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +75 -0
- package/apps/storefront/src/lib/publish/publish-guard.ts +88 -0
- package/apps/storefront/src/lib/publish/publishFormat.ts +165 -0
- package/apps/storefront/src/lib/publish/publishHero.ts +177 -0
- package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +66 -0
- package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +141 -0
- package/apps/storefront/src/lib/publish/reviewRelease/index.ts +41 -0
- package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +47 -0
- package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +42 -0
- package/apps/storefront/src/lib/publish/reviewRelease/types.ts +59 -0
- package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +440 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +161 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +325 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +241 -0
- package/apps/storefront/src/lib/publish/shipWorkspace.ts +22 -3
- package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
- package/apps/storefront/src/lib/publish/streamLogConsole.ts +142 -0
- package/apps/storefront/src/lib/publish/versionNumber.ts +52 -0
- package/apps/storefront/src/lib/rawChrome.ts +125 -47
- package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
- package/apps/storefront/src/lib/seo/alternates.ts +1 -1
- package/apps/storefront/src/lib/social/notify.ts +4 -4
- package/apps/storefront/src/lib/storyblok/content-model.ts +7 -33
- package/apps/storefront/src/lib/storyblok/provider.ts +168 -60
- package/apps/storefront/src/lib/subscription/model.ts +4 -4
- package/apps/storefront/src/lib/the-build/provider.ts +9 -4
- package/apps/storefront/src/middleware/index.ts +273 -51
- package/apps/storefront/src/pages/[...slug].astro +9 -4
- package/apps/storefront/src/pages/admin/review-release.astro +265 -0
- package/apps/storefront/src/pages/admin/select.astro +96 -0
- package/apps/storefront/src/pages/admin.astro +16 -2
- package/apps/storefront/src/pages/api/activity.ts +8 -8
- package/apps/storefront/src/pages/api/admin/activity-alerts.ts +5 -20
- package/apps/storefront/src/pages/api/admin/orders/[id].ts +7 -2
- package/apps/storefront/src/pages/api/admin/orders.ts +8 -2
- package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +174 -0
- package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +96 -0
- package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +118 -0
- package/apps/storefront/src/pages/api/apps/admin/integrations.ts +89 -0
- package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +85 -0
- package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +74 -2
- package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +77 -0
- package/apps/storefront/src/pages/api/compliance/tamper.ts +88 -0
- package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +108 -0
- package/apps/storefront/src/pages/api/rum/vitals.ts +1 -2
- package/apps/storefront/src/pages/blog/[slug].astro +5 -5
- package/apps/storefront/src/pages/blog/author/[author].astro +3 -3
- package/apps/storefront/src/pages/blog/category/[category].astro +2 -2
- package/apps/storefront/src/pages/blog/index.astro +2 -2
- package/apps/storefront/src/pages/blog/tag/[tag].astro +2 -2
- package/apps/storefront/src/pages/cockpit.astro +61 -1362
- package/apps/storefront/src/pages/index.astro +10 -5
- package/apps/storefront/src/pages/llms.txt.ts +1 -1
- package/apps/storefront/src/pages/products/[handle].astro +7 -6
- package/apps/storefront/src/pages/sitemap.xml.ts +2 -2
- package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +31 -27
- package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +1 -1
- package/apps/storefront/src/pages/tenants/[id]/[...path].ts +38 -2
- package/apps/storefront/src/pages/the-build/[slug].astro +4 -4
- package/apps/storefront/src/pages/the-build/index.astro +2 -2
- package/apps/storefront/src/pages/the-build/rss.xml.ts +2 -2
- package/apps/storefront/src/styles/admin.css +27 -1
- package/apps/storefront/tsconfig.json +1 -1
- package/package.json +1 -1
- package/packages/public-runtime/package.json +2 -1
- package/packages/public-runtime/schemas/managed-analytics/v1/adapter-definition.schema.json +427 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +161 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +157 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/delete-integration-install-request.schema.json +84 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/delete-migration-draft-request.schema.json +84 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/error-envelope.schema.json +180 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-adapter-definition-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-install-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-revision-request.schema.json +79 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-migration-draft-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/integration-install.schema.json +167 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +208 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-adapter-definitions-request.schema.json +53 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-installs-request.schema.json +53 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-revisions-request.schema.json +82 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +218 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/mutation-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/mutation-success-envelope.schema.json +49 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/read-request.schema.json +20 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/read-success-envelope.schema.json +45 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +156 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/set-integration-install-status-request.schema.json +92 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +184 -0
- package/packages/public-runtime/scripts/generate-managed-analytics-api-v1-schemas.mjs +13 -0
- package/packages/public-runtime/src/activity/catalog.ts +41 -37
- package/packages/public-runtime/src/activity/event.ts +6 -6
- package/packages/public-runtime/src/activity/index.ts +2 -2
- package/packages/public-runtime/src/activity/redaction.ts +4 -4
- package/packages/public-runtime/src/candidate-index.ts +42 -8
- package/packages/public-runtime/src/checkout-orders.ts +2 -2
- package/packages/public-runtime/src/checkout-style-publish.ts +2 -2
- package/packages/public-runtime/src/checkout-style.ts +7 -7
- package/packages/public-runtime/src/checkout.ts +3 -3
- package/packages/public-runtime/src/chrome.ts +777 -0
- package/packages/public-runtime/src/compliance-obligations.ts +310 -0
- package/packages/public-runtime/src/csp.ts +99 -0
- package/packages/public-runtime/src/customization-runtime.ts +32 -1
- package/packages/public-runtime/src/destination-webhook/contract.ts +353 -0
- package/packages/public-runtime/src/embed-catalog.d.mts +9 -0
- package/packages/public-runtime/src/embed-catalog.mjs +40 -0
- package/packages/public-runtime/src/evidence.ts +288 -0
- package/packages/public-runtime/src/gtm-migration/bounds.ts +138 -0
- package/packages/public-runtime/src/gtm-migration/disposition-rules.ts +361 -0
- package/packages/public-runtime/src/gtm-migration/dispositions.ts +181 -0
- package/packages/public-runtime/src/gtm-migration/draft-generator.ts +400 -0
- package/packages/public-runtime/src/gtm-migration/graph.ts +468 -0
- package/packages/public-runtime/src/gtm-migration/index.ts +95 -0
- package/packages/public-runtime/src/gtm-migration/parse.ts +410 -0
- package/packages/public-runtime/src/gtm-migration/references.ts +266 -0
- package/packages/public-runtime/src/gtm-migration/types.ts +86 -0
- package/packages/public-runtime/src/index.ts +16 -1
- package/packages/public-runtime/src/integration/assurance-decision.ts +259 -0
- package/packages/public-runtime/src/integration/assurance.ts +228 -0
- package/packages/public-runtime/src/integration/candidate-evidence.ts +377 -0
- package/packages/public-runtime/src/integration/commerce-broker.ts +497 -0
- package/packages/public-runtime/src/integration/consent-broker.ts +337 -0
- package/packages/public-runtime/src/integration/consent-cmp-demand.ts +115 -0
- package/packages/public-runtime/src/integration/consent-config.ts +260 -0
- package/packages/public-runtime/src/integration/consent-preview.ts +125 -0
- package/packages/public-runtime/src/integration/correlation-token.ts +444 -0
- package/packages/public-runtime/src/integration/csp-compiler.ts +267 -0
- package/packages/public-runtime/src/integration/csp.ts +105 -0
- package/packages/public-runtime/src/integration/digest.ts +43 -0
- package/packages/public-runtime/src/integration/environment.ts +228 -0
- package/packages/public-runtime/src/integration/events-catalog.ts +186 -0
- package/packages/public-runtime/src/integration/events.ts +701 -0
- package/packages/public-runtime/src/integration/g2-action-boundary.ts +106 -0
- package/packages/public-runtime/src/integration/index.ts +90 -0
- package/packages/public-runtime/src/integration/interim-bridge.ts +138 -0
- package/packages/public-runtime/src/integration/lifecycle-boot.ts +317 -0
- package/packages/public-runtime/src/integration/lifecycle-runtime.ts +393 -0
- package/packages/public-runtime/src/integration/lifecycle.ts +94 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.d.mts +2 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.mjs +10 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +382 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +684 -0
- package/packages/public-runtime/src/integration/managed-analytics-bridge.ts +383 -0
- package/packages/public-runtime/src/integration/managed-analytics-value-safety.ts +14 -0
- package/packages/public-runtime/src/integration/managed-analytics.ts +277 -0
- package/packages/public-runtime/src/integration/manifest.ts +470 -0
- package/packages/public-runtime/src/integration/native-amplitude.ts +362 -0
- package/packages/public-runtime/src/integration/native-ga4.ts +352 -0
- package/packages/public-runtime/src/integration/observability.ts +451 -0
- package/packages/public-runtime/src/integration/order-conversion.ts +0 -0
- package/packages/public-runtime/src/integration/phases.ts +182 -0
- package/packages/public-runtime/src/integration/plan.ts +112 -0
- package/packages/public-runtime/src/integration/platform-adapter-runtime.ts +146 -0
- package/packages/public-runtime/src/integration/platform-adapters.ts +671 -0
- package/packages/public-runtime/src/integration/promotion-gate.ts +451 -0
- package/packages/public-runtime/src/integration/provider-checkout-extension.ts +425 -0
- package/packages/public-runtime/src/integration/receipt-measurement.ts +370 -0
- package/packages/public-runtime/src/integration/regulated-parent-cert.ts +311 -0
- package/packages/public-runtime/src/integration/render-phases.ts +267 -0
- package/packages/public-runtime/src/integration/resolve.ts +465 -0
- package/packages/public-runtime/src/integration/result.ts +239 -0
- package/packages/public-runtime/src/integration/secrets.ts +92 -0
- package/packages/public-runtime/src/integration/tamper-evidence.ts +205 -0
- package/packages/public-runtime/src/integration/tamper-recovery.ts +127 -0
- package/packages/public-runtime/src/integration/version.ts +92 -0
- package/packages/public-runtime/src/integration-capabilities.ts +520 -0
- package/packages/public-runtime/src/membership.ts +2 -2
- package/packages/public-runtime/src/raw-html-policy.ts +2 -4
- package/packages/public-runtime/src/regulated-commerce/broker/consent-dedupe.ts +144 -0
- package/packages/public-runtime/src/regulated-commerce/correlation/contract.ts +177 -0
- package/packages/public-runtime/src/regulated-commerce/diagnostics/local-dispatch.ts +24 -0
- package/packages/public-runtime/src/regulated-commerce/diagnostics/read-model.ts +69 -0
- package/packages/public-runtime/src/regulated-commerce/dispatch/core.ts +134 -0
- package/packages/public-runtime/src/regulated-commerce/events/contract.ts +113 -0
- package/packages/public-runtime/src/regulated-commerce/ledger/core.ts +81 -0
- package/packages/public-runtime/src/regulated-commerce/mappings/ga4-ads.ts +87 -0
- package/packages/public-runtime/src/regulated-commerce/provider-order/bridge.ts +52 -0
- package/packages/public-runtime/src/regulated-commerce/purchase-truth/contract.ts +252 -0
- package/packages/public-runtime/src/regulated-commerce/sources/accepted-browser-operation.ts +67 -0
- package/packages/public-runtime/src/regulated-commerce/testing/fake-destination.ts +93 -0
- package/packages/public-runtime/src/static-bundle.ts +5 -5
- package/packages/public-runtime/src/tenant-assets.ts +143 -4
- package/packages/public-runtime/src/tenant.ts +45 -9
- package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
- package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
- package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
- package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
- package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
- package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
- package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
- package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
- package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
- package/scripts/dev/file-browser.mjs +2 -2
- package/scripts/dev/file-writer.mjs +2 -2
- package/scripts/dev/integration-guardrails.mjs +498 -0
- package/scripts/dev/safe-path.mjs +2 -2
- package/scripts/dev/tenant-source-root.mjs +1 -1
- package/scripts/tot-dev.mjs +2 -2
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The unifying, fail-closed integration PROMOTION gate (ext-policy-06).
|
|
3
|
+
*
|
|
4
|
+
* ONE gate that an integration promotion must pass before its plan can reach a
|
|
5
|
+
* staged/live pointer. It does not invent a second opinion — it COMPOSES the
|
|
6
|
+
* fail-closed verifiers each upstream card already owns and refuses promotion on
|
|
7
|
+
* ANY of their failures:
|
|
8
|
+
*
|
|
9
|
+
* - ext-policy-02 {@link compileIntegrationCspFromPlan} — the plan's curated
|
|
10
|
+
* profiles + advanced origins must compile under the exact-origin grammar; a
|
|
11
|
+
* wildcard / bare scheme / `unsafe-*` / `strict-dynamic` / unknown directive is
|
|
12
|
+
* an INVALID policy and is rejected here, before any flip.
|
|
13
|
+
* - ext-policy-03 the consent broker's {@link ConsentSnapshot} — every
|
|
14
|
+
* non-essential purpose the plan depends on must be eligible under a well-formed
|
|
15
|
+
* (default-deny, `unknownState:"block"`) snapshot; a forged/absent snapshot
|
|
16
|
+
* fails closed.
|
|
17
|
+
* - ext-policy-04 {@link verifyObligationCoverage} — the SERVER-derived required
|
|
18
|
+
* obligation set (never the candidate's declaration) must be completely,
|
|
19
|
+
* unambiguously covered; a regulated tenant cannot ship with an omitted duty.
|
|
20
|
+
* - ext-policy-05 {@link decideAssurance} — an elevated-parent integration needs
|
|
21
|
+
* an approvable assurance record; the regulated floor is non-waivable.
|
|
22
|
+
* - ext-kernel-06 {@link verifyCandidatePromotable} (which calls
|
|
23
|
+
* {@link verifyServedParity}) — the evidence must EQUAL what a mount actually
|
|
24
|
+
* served; an unobserved served output, a forged asset, secret material, or a
|
|
25
|
+
* `live` candidate blocks.
|
|
26
|
+
* - ext-prereq-03 {@link verifyPromotionEvidence} — the compliance-evidence check
|
|
27
|
+
* set (age_gate / excise / jurisdiction) must be complete, matching, fresh, and
|
|
28
|
+
* all-passing against the server-derived {@link REQUIRED_FOR_PROMOTE}.
|
|
29
|
+
*
|
|
30
|
+
* Fail-closed and non-short-circuiting: every composed verdict runs and EVERY reason
|
|
31
|
+
* is accumulated, so the diagnostics are complete and actionable. `ok` is true only
|
|
32
|
+
* when nothing objected.
|
|
33
|
+
*
|
|
34
|
+
* VERSION-BOUND (the rollback-safety property): every artifact the gate reads — the
|
|
35
|
+
* plan, its freshly-compiled CSP, and the candidate evidence — must bind to the SAME
|
|
36
|
+
* `planRevision` (and the caller's `expectedPlanRevision`, when given). A stale
|
|
37
|
+
* artifact left over from a superseded version carries a different revision and is
|
|
38
|
+
* rejected, so a promote — or a rollback that re-promotes a prior version — can never
|
|
39
|
+
* ride another version's cached CSP/evidence.
|
|
40
|
+
*
|
|
41
|
+
* SECRET-SAFE DIAGNOSTICS: the assembled diagnostics are scanned with
|
|
42
|
+
* {@link findForbiddenFields} before they leave the gate; a diagnostic payload that
|
|
43
|
+
* names secret material is redacted AND turns the verdict closed, so an operator
|
|
44
|
+
* report can never leak a nonce/token/credential.
|
|
45
|
+
*/
|
|
46
|
+
import {
|
|
47
|
+
verifyObligationCoverage,
|
|
48
|
+
resolveObligationIds,
|
|
49
|
+
isRegulated,
|
|
50
|
+
type ObligationTenant,
|
|
51
|
+
} from "../compliance-obligations.js";
|
|
52
|
+
import { verifyCandidatePromotable } from "./candidate-evidence.js";
|
|
53
|
+
import type {
|
|
54
|
+
IntegrationCandidateEvidence,
|
|
55
|
+
ServedObservation,
|
|
56
|
+
} from "./candidate-evidence.js";
|
|
57
|
+
import { decideAssurance } from "./assurance-decision.js";
|
|
58
|
+
import type { AssuranceDecisionContext } from "./assurance-decision.js";
|
|
59
|
+
import {
|
|
60
|
+
decideRegulatedParentCert,
|
|
61
|
+
type RegulatedParentCertification,
|
|
62
|
+
} from "./regulated-parent-cert.js";
|
|
63
|
+
import {
|
|
64
|
+
compileIntegrationCspFromPlan,
|
|
65
|
+
type IntegrationCspAdditions,
|
|
66
|
+
} from "./csp-compiler.js";
|
|
67
|
+
import { findForbiddenFields } from "./secrets.js";
|
|
68
|
+
import {
|
|
69
|
+
defaultConsentSnapshot,
|
|
70
|
+
parseConsentSnapshot,
|
|
71
|
+
isPurposeEligible,
|
|
72
|
+
type ConsentSnapshot,
|
|
73
|
+
} from "./consent-broker.js";
|
|
74
|
+
import type { EffectivePlan } from "./plan.js";
|
|
75
|
+
import type { ComplianceAssuranceRecord } from "./assurance.js";
|
|
76
|
+
import type { ContractViolation } from "./result.js";
|
|
77
|
+
import {
|
|
78
|
+
verifyPromotionEvidence,
|
|
79
|
+
REQUIRED_FOR_PROMOTE,
|
|
80
|
+
PROMOTION_EVIDENCE_MAX_AGE_MS,
|
|
81
|
+
type PromotionEvidenceCheck,
|
|
82
|
+
} from "../evidence.js";
|
|
83
|
+
|
|
84
|
+
/** Everything the unifying gate needs to decide whether an integration may promote. */
|
|
85
|
+
export interface IntegrationPromotionGateInput {
|
|
86
|
+
/**
|
|
87
|
+
* The version this promotion is FOR. When set, the plan, its compiled CSP, and the
|
|
88
|
+
* candidate evidence must all bind to this exact revision — the rollback-safety
|
|
89
|
+
* anchor. Omit to only require the three artifacts agree with each other.
|
|
90
|
+
*/
|
|
91
|
+
expectedPlanRevision?: string;
|
|
92
|
+
|
|
93
|
+
/** The tenant's authoritative config — the ONLY source of the required obligations + regulated classification. */
|
|
94
|
+
tenant: ObligationTenant;
|
|
95
|
+
|
|
96
|
+
/** The resolved effective plan for the candidate being promoted (ext-kernel-03). */
|
|
97
|
+
plan: EffectivePlan | null | undefined;
|
|
98
|
+
|
|
99
|
+
/** Curated CSP profile slugs declared across the plan's integrations (ext-policy-02). */
|
|
100
|
+
cspProfiles?: readonly string[];
|
|
101
|
+
|
|
102
|
+
/** The candidate evidence derived from the plan (ext-kernel-06). */
|
|
103
|
+
candidateEvidence: IntegrationCandidateEvidence | null | undefined;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* What a mount ACTUALLY served for this candidate — the parity anchor
|
|
107
|
+
* {@link verifyServedParity} re-derives against (ext-kernel-06). `null` = the
|
|
108
|
+
* served output was not observed, itself a blocker (parity cannot be asserted).
|
|
109
|
+
*/
|
|
110
|
+
served: ServedObservation | null;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* A forge-response integrity failure from the reconcile that produced this
|
|
114
|
+
* candidate (ext-prereq-02), if one occurred. A forged/unreadable forge response
|
|
115
|
+
* can never become promotable.
|
|
116
|
+
*/
|
|
117
|
+
forgeIntegrityError?: string | null;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The obligation ids the candidate/app DECLARES it satisfies. Verified against the
|
|
121
|
+
* server-derived floor ({@link resolveObligationIds}) — never trusted as the
|
|
122
|
+
* required set itself (ext-policy-04).
|
|
123
|
+
*/
|
|
124
|
+
declaredObligationIds: readonly string[] | null | undefined;
|
|
125
|
+
|
|
126
|
+
/** The compliance-evidence checks measured for this candidate (ext-prereq-03). */
|
|
127
|
+
complianceChecks: readonly PromotionEvidenceCheck[] | null | undefined;
|
|
128
|
+
/** The compliance evidence's `generated_at` (ISO) — required to evaluate freshness. */
|
|
129
|
+
complianceGeneratedAt?: string;
|
|
130
|
+
|
|
131
|
+
/** The promote instant (ms epoch) freshness is measured against. */
|
|
132
|
+
now?: number;
|
|
133
|
+
/** Max compliance-evidence age before STALE (ms). Defaults to {@link PROMOTION_EVIDENCE_MAX_AGE_MS}. */
|
|
134
|
+
maxAgeMs?: number;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* The SERVER-derived consent snapshot the plan's non-essential `consentNeeds` must
|
|
138
|
+
* each be eligible under (ext-policy-03). Absent ⇒ the default-deny snapshot, so
|
|
139
|
+
* any dependent purpose blocks — consent defaults precede dependent code.
|
|
140
|
+
*/
|
|
141
|
+
consentSnapshot?: ConsentSnapshot;
|
|
142
|
+
|
|
143
|
+
/** The elevated-integration assurance record, when one resolved (ext-policy-05). */
|
|
144
|
+
assurance?: ComplianceAssuranceRecord | null;
|
|
145
|
+
/**
|
|
146
|
+
* The decision context for {@link decideAssurance} (wall clock, counsel flag, the
|
|
147
|
+
* resolved material state). Required when an `assurance` record is present — an
|
|
148
|
+
* expiry/material-change verdict cannot be reached without it.
|
|
149
|
+
*
|
|
150
|
+
* NOTE: its `certifiedImmutable` is NOT read as a raw external input. The gate
|
|
151
|
+
* OVERRIDES it with the value {@link decideRegulatedParentCert} derives from
|
|
152
|
+
* `regulatedParentCert` below, so no caller can assert a regulated elevated-parent
|
|
153
|
+
* integration is certified without a cert that actually clears every clause.
|
|
154
|
+
*/
|
|
155
|
+
assuranceContext?: AssuranceDecisionContext;
|
|
156
|
+
/**
|
|
157
|
+
* The regulated elevated-parent certification artifact (ext-policy-07), when one is
|
|
158
|
+
* supplied. It is the SINGLE ORIGIN of `certifiedImmutable`: the gate runs
|
|
159
|
+
* {@link decideRegulatedParentCert} against the plan's elevated-parent integration
|
|
160
|
+
* (its served content digest as the drift anchor, its app/version as the binding) and
|
|
161
|
+
* feeds the decision's boolean into the assurance context. Absent/failed ⇒ false ⇒
|
|
162
|
+
* a regulated elevated-parent integration is denied `regulated-cert-missing`.
|
|
163
|
+
*/
|
|
164
|
+
regulatedParentCert?: RegulatedParentCertification | null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** One composed verdict, flattened to the `{ ok, blockers }` family the gate aggregates. */
|
|
168
|
+
export interface IntegrationGateSubVerdict {
|
|
169
|
+
ok: boolean;
|
|
170
|
+
blockers: string[];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** The structured, secret-scanned diagnostics an operator reads to fix a blocked promotion. */
|
|
174
|
+
export interface IntegrationPromotionDiagnostics {
|
|
175
|
+
version: {
|
|
176
|
+
expected: string | null;
|
|
177
|
+
plan: string | null;
|
|
178
|
+
candidate: string | null;
|
|
179
|
+
csp: string | null;
|
|
180
|
+
consistent: boolean;
|
|
181
|
+
};
|
|
182
|
+
csp: IntegrationGateSubVerdict;
|
|
183
|
+
candidate: IntegrationGateSubVerdict;
|
|
184
|
+
obligations: IntegrationGateSubVerdict & { regulated: boolean; required: string[] };
|
|
185
|
+
compliance: IntegrationGateSubVerdict;
|
|
186
|
+
consent: IntegrationGateSubVerdict;
|
|
187
|
+
assurance: IntegrationGateSubVerdict;
|
|
188
|
+
/**
|
|
189
|
+
* The resolved per-integration public config under review — echoed so an operator
|
|
190
|
+
* can inspect exactly what the plan carries. This is the app-declared, nested,
|
|
191
|
+
* open-value structure the diagnostics secret-scan defends: a field naming secret
|
|
192
|
+
* material here (a leak the resolver should have caught) redacts the whole payload.
|
|
193
|
+
*/
|
|
194
|
+
publicConfig: Record<string, Record<string, unknown>>;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export interface IntegrationPromotionVerdict {
|
|
198
|
+
/** True only when EVERY composed verifier passed and the diagnostics carried no secret. */
|
|
199
|
+
ok: boolean;
|
|
200
|
+
/** Every reason this integration may not promote (empty ⇔ ok). */
|
|
201
|
+
blockers: string[];
|
|
202
|
+
/**
|
|
203
|
+
* The structured diagnostics — secret-scanned. Replaced by `{ redacted: true }`
|
|
204
|
+
* (and the verdict forced closed) if any diagnostic field named secret material.
|
|
205
|
+
*/
|
|
206
|
+
diagnostics: IntegrationPromotionDiagnostics | { redacted: true };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* The single fail-CLOSED authority for "may this integration reach a staged/live
|
|
211
|
+
* pointer?". Composes the six upstream verifiers; refuses on any failure. See the
|
|
212
|
+
* file header for the property each clause holds.
|
|
213
|
+
*/
|
|
214
|
+
export function verifyIntegrationPromotable(
|
|
215
|
+
input: IntegrationPromotionGateInput,
|
|
216
|
+
): IntegrationPromotionVerdict {
|
|
217
|
+
const blockers: string[] = [];
|
|
218
|
+
const { plan, candidateEvidence } = input;
|
|
219
|
+
|
|
220
|
+
// ── Version binding (rollback-safety) ────────────────────────────────────
|
|
221
|
+
// A freshly-compiled CSP for THIS plan is the pre-release fail-closed floor
|
|
222
|
+
// (ext-policy-02): an invalid policy yields violations and no additions, and is
|
|
223
|
+
// rejected before any flip. Compiling here (not trusting the evidence's embedded
|
|
224
|
+
// additions) re-validates the plan's CSP independently at promote time.
|
|
225
|
+
const csp = plan
|
|
226
|
+
? compileIntegrationCspFromPlan(plan, input.cspProfiles ?? [])
|
|
227
|
+
: null;
|
|
228
|
+
|
|
229
|
+
const expected = input.expectedPlanRevision ?? null;
|
|
230
|
+
const planRev = plan?.planRevision ?? null;
|
|
231
|
+
const candidateRev = candidateEvidence?.planRevision ?? null;
|
|
232
|
+
const cspRev = csp?.ok ? csp.value.planRevision : null;
|
|
233
|
+
const revs = [planRev, candidateRev, cspRev].filter((r): r is string => r != null);
|
|
234
|
+
const versionConsistent =
|
|
235
|
+
revs.length > 0 &&
|
|
236
|
+
revs.every((r) => r === revs[0]) &&
|
|
237
|
+
(expected == null || revs.every((r) => r === expected));
|
|
238
|
+
if (!versionConsistent) {
|
|
239
|
+
blockers.push(
|
|
240
|
+
`Version mismatch: plan="${planRev ?? "(absent)"}", candidate="${candidateRev ?? "(absent)"}", ` +
|
|
241
|
+
`csp="${cspRev ?? "(absent)"}"${expected ? `, expected="${expected}"` : ""} — every promotion ` +
|
|
242
|
+
`artifact must bind to one plan revision (a stale artifact from a superseded version is rejected).`,
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ── ext-policy-02: CSP fail-closed ───────────────────────────────────────
|
|
247
|
+
const cspSub: IntegrationGateSubVerdict = { ok: true, blockers: [] };
|
|
248
|
+
const cspViolations: ContractViolation[] = [];
|
|
249
|
+
if (!plan) {
|
|
250
|
+
cspSub.ok = false;
|
|
251
|
+
cspSub.blockers.push("No effective plan supplied — nothing to compile or promote.");
|
|
252
|
+
} else if (csp && !csp.ok) {
|
|
253
|
+
cspSub.ok = false;
|
|
254
|
+
for (const v of csp.violations) {
|
|
255
|
+
cspViolations.push(v);
|
|
256
|
+
cspSub.blockers.push(`Invalid integration CSP at ${v.path}: ${v.message}`);
|
|
257
|
+
}
|
|
258
|
+
} else if (csp?.ok && candidateEvidence) {
|
|
259
|
+
// Defense in depth: the promote-time recompile must equal the CSP the evidence
|
|
260
|
+
// (and therefore the served response) carries — otherwise the plan would ship a
|
|
261
|
+
// policy the operator never reviewed as evidence.
|
|
262
|
+
if (canonicalCspKey(csp.value.additions) !== canonicalCspKey(candidateEvidence.cspAdditions)) {
|
|
263
|
+
cspSub.ok = false;
|
|
264
|
+
cspSub.blockers.push(
|
|
265
|
+
"Recompiled integration CSP differs from the candidate evidence's CSP additions.",
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
blockers.push(...cspSub.blockers);
|
|
270
|
+
|
|
271
|
+
// ── ext-kernel-06: candidate ⇔ served parity, assets, secrets, not-live ──
|
|
272
|
+
const candidateSub: IntegrationGateSubVerdict = candidateEvidence
|
|
273
|
+
? verifyCandidatePromotable(candidateEvidence, input.served, {
|
|
274
|
+
forgeIntegrityError: input.forgeIntegrityError,
|
|
275
|
+
})
|
|
276
|
+
: { ok: false, blockers: ["No candidate evidence supplied — parity with the served output is unprovable."] };
|
|
277
|
+
blockers.push(...candidateSub.blockers);
|
|
278
|
+
|
|
279
|
+
// ── ext-policy-04: obligation coverage (server-derived, regulated floor) ──
|
|
280
|
+
const obligationVerdict = verifyObligationCoverage(input.tenant, input.declaredObligationIds);
|
|
281
|
+
const obligationsSub = {
|
|
282
|
+
ok: obligationVerdict.ok,
|
|
283
|
+
blockers: obligationVerdict.blockers,
|
|
284
|
+
regulated: isRegulated(input.tenant),
|
|
285
|
+
required: resolveObligationIds(input.tenant),
|
|
286
|
+
};
|
|
287
|
+
blockers.push(...obligationsSub.blockers);
|
|
288
|
+
|
|
289
|
+
// ── ext-prereq-03: compliance evidence (age_gate / excise / jurisdiction) ─
|
|
290
|
+
const complianceVerdict = verifyPromotionEvidence(input.complianceChecks, {
|
|
291
|
+
requiredIds: REQUIRED_FOR_PROMOTE,
|
|
292
|
+
generatedAt: input.complianceGeneratedAt,
|
|
293
|
+
now: input.now,
|
|
294
|
+
maxAgeMs: input.maxAgeMs ?? PROMOTION_EVIDENCE_MAX_AGE_MS,
|
|
295
|
+
});
|
|
296
|
+
blockers.push(...complianceVerdict.blockers);
|
|
297
|
+
|
|
298
|
+
// ── ext-policy-03: consent order (defaults precede, unknown blocks) ───────
|
|
299
|
+
const consentSub = verifyConsentOrder(plan, input.consentSnapshot);
|
|
300
|
+
blockers.push(...consentSub.blockers);
|
|
301
|
+
|
|
302
|
+
// ── ext-policy-05: assurance for an elevated-parent integration ───────────
|
|
303
|
+
// `certifiedImmutable` has a SINGLE ORIGIN: derive it from the certification decision
|
|
304
|
+
// (never trust a raw external flag on the context) and OVERRIDE the context with it,
|
|
305
|
+
// so no caller can bypass the decision by asserting `certifiedImmutable: true`.
|
|
306
|
+
const derivedCertifiedImmutable = deriveRegulatedParentCertified(
|
|
307
|
+
plan,
|
|
308
|
+
input.tenant,
|
|
309
|
+
input.regulatedParentCert,
|
|
310
|
+
);
|
|
311
|
+
const assuranceContext: AssuranceDecisionContext | undefined = input.assuranceContext
|
|
312
|
+
? { ...input.assuranceContext, certifiedImmutable: derivedCertifiedImmutable }
|
|
313
|
+
: input.assuranceContext;
|
|
314
|
+
const assuranceSub = verifyAssurance(plan, input.assurance, assuranceContext);
|
|
315
|
+
blockers.push(...assuranceSub.blockers);
|
|
316
|
+
|
|
317
|
+
// ── Assemble + secret-scan the diagnostics ───────────────────────────────
|
|
318
|
+
const diagnostics: IntegrationPromotionDiagnostics = {
|
|
319
|
+
version: { expected, plan: planRev, candidate: candidateRev, csp: cspRev, consistent: versionConsistent },
|
|
320
|
+
csp: { ok: cspSub.ok, blockers: cspSub.blockers },
|
|
321
|
+
candidate: candidateSub,
|
|
322
|
+
obligations: obligationsSub,
|
|
323
|
+
compliance: complianceVerdict,
|
|
324
|
+
consent: consentSub,
|
|
325
|
+
assurance: assuranceSub,
|
|
326
|
+
publicConfig: plan?.publicConfig ?? {},
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// The diagnostics are an operator-facing, potentially-serialized surface. Any field
|
|
330
|
+
// whose NAME denotes secret material (a leak from a composed verifier's payload) is
|
|
331
|
+
// never allowed out: redact the whole payload and fail the gate closed.
|
|
332
|
+
const forbidden = findForbiddenFields(diagnostics, "diagnostics");
|
|
333
|
+
if (forbidden.length > 0) {
|
|
334
|
+
blockers.push(
|
|
335
|
+
`Gate diagnostics named secret material (${forbidden.map((f) => f.path).join(", ")}) and were redacted.`,
|
|
336
|
+
);
|
|
337
|
+
return { ok: false, blockers, diagnostics: { redacted: true } };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return { ok: blockers.length === 0, blockers, diagnostics };
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Every non-essential consent purpose the plan depends on must be eligible under a
|
|
345
|
+
* well-formed, default-deny snapshot. An absent snapshot is the default-deny floor
|
|
346
|
+
* (only `essential`), so any dependent purpose blocks — consent defaults precede
|
|
347
|
+
* dependent code. A forged/tampered snapshot (fails the canonical round-trip) blocks.
|
|
348
|
+
*/
|
|
349
|
+
function verifyConsentOrder(
|
|
350
|
+
plan: EffectivePlan | null | undefined,
|
|
351
|
+
snapshot: ConsentSnapshot | undefined,
|
|
352
|
+
): IntegrationGateSubVerdict {
|
|
353
|
+
const blockers: string[] = [];
|
|
354
|
+
if (!plan) return { ok: true, blockers };
|
|
355
|
+
|
|
356
|
+
const effective = snapshot ?? defaultConsentSnapshot();
|
|
357
|
+
// Fail closed on a forged snapshot: the canonical parse recomputes the snapshotId
|
|
358
|
+
// from the eligible set and rejects a mismatched, unknown-state, or unknown-purpose
|
|
359
|
+
// payload — a caller cannot smuggle in a snapshot that grants a purpose it should not.
|
|
360
|
+
if (parseConsentSnapshot(effective) === null) {
|
|
361
|
+
blockers.push("Consent snapshot is malformed or forged — its eligibility cannot be trusted.");
|
|
362
|
+
return { ok: false, blockers };
|
|
363
|
+
}
|
|
364
|
+
if (effective.unknownState !== "block") {
|
|
365
|
+
blockers.push("Consent snapshot does not fail closed on unknown state.");
|
|
366
|
+
}
|
|
367
|
+
for (const purpose of plan.consentNeeds) {
|
|
368
|
+
if (!isPurposeEligible(effective, purpose)) {
|
|
369
|
+
blockers.push(
|
|
370
|
+
`Consent purpose "${purpose}" is required by the plan but not eligible under the approved snapshot.`,
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return { ok: blockers.length === 0, blockers };
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Assurance gates ONLY elevated-parent integrations. A sandbox/broker plan has no
|
|
379
|
+
* assurance obligation (clean pass). An elevated-parent integration with no record
|
|
380
|
+
* fails closed; with a record, `decideAssurance` must approve it (the regulated floor
|
|
381
|
+
* stays non-waivable, the vendor cannot self-approve, expiry/material-change apply).
|
|
382
|
+
*/
|
|
383
|
+
function verifyAssurance(
|
|
384
|
+
plan: EffectivePlan | null | undefined,
|
|
385
|
+
record: ComplianceAssuranceRecord | null | undefined,
|
|
386
|
+
context: AssuranceDecisionContext | undefined,
|
|
387
|
+
): IntegrationGateSubVerdict {
|
|
388
|
+
const blockers: string[] = [];
|
|
389
|
+
const elevated = (plan?.integrations ?? []).some((i) => i.executionTier === "elevated-parent");
|
|
390
|
+
|
|
391
|
+
if (!elevated && !record) return { ok: true, blockers };
|
|
392
|
+
|
|
393
|
+
if (elevated && !record) {
|
|
394
|
+
blockers.push(
|
|
395
|
+
"An elevated-parent integration has no assurance record — full-parent authority cannot promote unacknowledged.",
|
|
396
|
+
);
|
|
397
|
+
return { ok: false, blockers };
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (record) {
|
|
401
|
+
if (!context) {
|
|
402
|
+
blockers.push("An assurance record is present but no decision context was supplied — expiry cannot be evaluated.");
|
|
403
|
+
return { ok: false, blockers };
|
|
404
|
+
}
|
|
405
|
+
const decision = decideAssurance(record, context);
|
|
406
|
+
if (!decision.ok) {
|
|
407
|
+
for (const denial of decision.denials) {
|
|
408
|
+
blockers.push(
|
|
409
|
+
`Assurance denied: ${denial.reason}${denial.subject ? ` (${denial.subject})` : ""}.`,
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return { ok: blockers.length === 0, blockers };
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Derive `certifiedImmutable` from the certification decision — the SINGLE ORIGIN of
|
|
419
|
+
* that flag (never a raw external boolean). Runs {@link decideRegulatedParentCert}
|
|
420
|
+
* against the plan's elevated-parent integration: its served content digest is the
|
|
421
|
+
* drift anchor and its app/version the binding, so a cert for a different artifact, an
|
|
422
|
+
* unpinned/mutable one, a drifted one, or one missing an adversarial obligation proof
|
|
423
|
+
* yields `false`. No elevated-parent integration ⇒ the flag is irrelevant and `false`
|
|
424
|
+
* (fail closed); a non-regulated tenant likewise decides `false` (the decision returns
|
|
425
|
+
* `not-regulated`), which the regulated-only assurance clause never consults.
|
|
426
|
+
*/
|
|
427
|
+
function deriveRegulatedParentCertified(
|
|
428
|
+
plan: EffectivePlan | null | undefined,
|
|
429
|
+
tenant: ObligationTenant,
|
|
430
|
+
cert: RegulatedParentCertification | null | undefined,
|
|
431
|
+
): boolean {
|
|
432
|
+
const elevated = (plan?.integrations ?? []).find((i) => i.executionTier === "elevated-parent");
|
|
433
|
+
if (!elevated) return false;
|
|
434
|
+
return decideRegulatedParentCert(cert, {
|
|
435
|
+
tenant,
|
|
436
|
+
servedDigest: elevated.digest,
|
|
437
|
+
binding: { appId: elevated.appId, version: elevated.version },
|
|
438
|
+
}).certifiedImmutable;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Canonical key for compiled CSP additions. The compiler already sorts + dedupes
|
|
443
|
+
* origins per directive and omits empty directives, so ordering by directive name
|
|
444
|
+
* yields an equality key two identical additions share exactly.
|
|
445
|
+
*/
|
|
446
|
+
function canonicalCspKey(additions: IntegrationCspAdditions): string {
|
|
447
|
+
return Object.keys(additions)
|
|
448
|
+
.sort()
|
|
449
|
+
.map((d) => `${d}:${(additions[d as keyof IntegrationCspAdditions] ?? []).join(",")}`)
|
|
450
|
+
.join("|");
|
|
451
|
+
}
|