deepline 0.3.89 → 0.3.91

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.
Files changed (267) hide show
  1. package/contracts/install-commands.json +29 -0
  2. package/dist/bundling-sources/sdk/src/client.ts +221 -43
  3. package/dist/bundling-sources/sdk/src/index.ts +4 -0
  4. package/dist/bundling-sources/sdk/src/monitor-fleet-contract.ts +2 -556
  5. package/dist/bundling-sources/sdk/src/monitor-fleets.ts +2 -2
  6. package/dist/bundling-sources/sdk/src/pagination.ts +25 -0
  7. package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +1 -1
  8. package/dist/bundling-sources/sdk/src/release.ts +11 -9
  9. package/dist/bundling-sources/sdk/src/types.ts +29 -1
  10. package/dist/bundling-sources/shared_libs/integration-contracts/monitor-fleet-contract.ts +549 -0
  11. package/dist/bundling-sources/shared_libs/monitors/analytics-public.ts +10 -0
  12. package/dist/bundling-sources/shared_libs/monitors/fleets/errors.ts +96 -0
  13. package/dist/bundling-sources/shared_libs/monitors/fleets/limits.ts +8 -0
  14. package/dist/bundling-sources/shared_libs/monitors/fleets/plan.ts +320 -0
  15. package/dist/bundling-sources/shared_libs/monitors/fleets/verdict.ts +106 -0
  16. package/dist/bundling-sources/shared_libs/monitors/http-error-response.ts +8 -1
  17. package/dist/bundling-sources/shared_libs/monitors/inbound-queue.ts +4 -4
  18. package/dist/bundling-sources/shared_libs/monitors/inventory-reconciliation.ts +59 -0
  19. package/dist/bundling-sources/shared_libs/monitors/lifecycle-notifications.ts +7 -9
  20. package/dist/bundling-sources/shared_libs/monitors/monitor-fleet-limits.ts +2 -12
  21. package/dist/bundling-sources/shared_libs/monitors/package.json +113 -4
  22. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-batch-operation.ts +56 -9
  23. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-consumer-health.ts +2 -2
  24. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-identity-claims.ts +2 -2
  25. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-public-aliases.ts +1 -1
  26. package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-dead-letter.ts +245 -0
  27. package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-message.ts +38 -0
  28. package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-queue-ack-probe.ts +94 -0
  29. package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-queue-delivery.ts +60 -0
  30. package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-queue.ts +60 -0
  31. package/dist/bundling-sources/shared_libs/monitors/snitcher-install-validation.ts +1 -1
  32. package/dist/bundling-sources/shared_libs/monitors/snitcher-ip2company.ts +2 -2
  33. package/dist/bundling-sources/shared_libs/monitors/snitcher-radar.ts +1 -1
  34. package/dist/bundling-sources/shared_libs/monitors/tracking-provider-integrations.ts +1 -1
  35. package/dist/bundling-sources/shared_libs/observability/build-context.ts +13 -0
  36. package/dist/bundling-sources/shared_libs/observability/log-metadata.ts +160 -0
  37. package/dist/bundling-sources/shared_libs/observability/request-context.ts +68 -0
  38. package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +690 -0
  39. package/dist/bundling-sources/shared_libs/observability/scheduled-work-manual.ts +35 -0
  40. package/dist/bundling-sources/shared_libs/observability/scheduled-work.ts +5 -0
  41. package/dist/bundling-sources/shared_libs/play-data-plane/neon-constants.ts +9 -0
  42. package/dist/bundling-sources/shared_libs/play-data-plane/r2.ts +272 -0
  43. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +46 -1
  44. package/dist/bundling-sources/shared_libs/play-runtime/async-operation.ts +66 -1
  45. package/dist/bundling-sources/shared_libs/play-runtime/async-play-invocation.ts +0 -1
  46. package/dist/bundling-sources/shared_libs/play-runtime/bounceban-batching.ts +137 -0
  47. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +779 -140
  48. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +46 -0
  49. package/dist/bundling-sources/shared_libs/play-runtime/daytona-payload-contract.ts +68 -0
  50. package/dist/bundling-sources/shared_libs/play-runtime/daytona-sandbox-janitor.ts +103 -0
  51. package/dist/bundling-sources/shared_libs/play-runtime/daytona-sandbox-reaper.ts +725 -0
  52. package/dist/bundling-sources/shared_libs/play-runtime/db-session-lease-store.ts +578 -0
  53. package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/roll-execution-relay.ts +521 -0
  54. package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/roll-receipt-gateway.ts +1378 -0
  55. package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/runtime-topology.ts +1024 -0
  56. package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-gateway-route-probe.ts +193 -0
  57. package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machine-lifecycle.ts +104 -0
  58. package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machines.ts +1844 -0
  59. package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-registry.ts +21 -0
  60. package/dist/bundling-sources/shared_libs/play-runtime/deployment/process-tree.ts +40 -0
  61. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-gateway-drain.ts +262 -0
  62. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-surface.ts +98 -0
  63. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-topology.ts +303 -0
  64. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-tuple-publication-guardian.ts +70 -0
  65. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-tuple-recovery.ts +519 -0
  66. package/dist/bundling-sources/shared_libs/play-runtime/deployment/vercel-fly-rollback-alignment.ts +280 -0
  67. package/dist/bundling-sources/shared_libs/play-runtime/deployment/vercel-production-gate.ts +159 -0
  68. package/dist/bundling-sources/shared_libs/play-runtime/durable-call-cache.ts +29 -0
  69. package/dist/bundling-sources/shared_libs/play-runtime/execution-capabilities.ts +23 -0
  70. package/dist/bundling-sources/shared_libs/play-runtime/execution-gateway-invocation-fence.ts +442 -0
  71. package/dist/bundling-sources/shared_libs/play-runtime/execution-protocol.ts +3 -3
  72. package/dist/bundling-sources/shared_libs/play-runtime/fixture-behavior.ts +18 -0
  73. package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +9 -0
  74. package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +1 -1
  75. package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +1 -1
  76. package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/contracts.ts +66 -0
  77. package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/job.ts +145 -0
  78. package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/memory-store.ts +114 -0
  79. package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/ports.ts +21 -0
  80. package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/postgres-store.ts +170 -0
  81. package/dist/bundling-sources/shared_libs/play-runtime/internal-bootstrap-token.ts +1322 -0
  82. package/dist/bundling-sources/shared_libs/play-runtime/internal-step-ids.ts +2 -2
  83. package/dist/bundling-sources/shared_libs/play-runtime/lane-retirement-gate.ts +1 -1
  84. package/dist/bundling-sources/shared_libs/play-runtime/launch-source-policy.ts +112 -0
  85. package/dist/bundling-sources/shared_libs/play-runtime/map-execution-frame.ts +8 -0
  86. package/dist/bundling-sources/shared_libs/play-runtime/perf-trace.ts +127 -0
  87. package/dist/bundling-sources/shared_libs/play-runtime/play-artifact-runtime-contract.ts +5 -0
  88. package/dist/bundling-sources/shared_libs/play-runtime/play-run-recovery-policy.ts +24 -1
  89. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/contracts.ts +56 -0
  90. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/index.ts +13 -0
  91. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/job.ts +41 -0
  92. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/ports.ts +17 -0
  93. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/postgres-adapter.ts +54 -0
  94. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/contracts.ts +114 -0
  95. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/engine-suspension.ts +108 -0
  96. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/index.ts +36 -0
  97. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/job.ts +97 -0
  98. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/ports.ts +104 -0
  99. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/postgres-fence-adapter.ts +263 -0
  100. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/worker-terminal-projection.ts +64 -0
  101. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/contracts.ts +29 -0
  102. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/gateway-adapter.ts +56 -0
  103. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/index.ts +15 -0
  104. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/job.ts +65 -0
  105. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/ports.ts +17 -0
  106. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/contracts.ts +42 -0
  107. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/index.ts +17 -0
  108. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/job.ts +142 -0
  109. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/ports.ts +42 -0
  110. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/postgres-adapter.ts +102 -0
  111. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/contracts.ts +60 -0
  112. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/index.ts +16 -0
  113. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/job.ts +78 -0
  114. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/ports.ts +54 -0
  115. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/postgres-adapter.ts +71 -0
  116. package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-batching-registry.ts +4 -0
  117. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +2 -0
  118. package/dist/bundling-sources/shared_libs/play-runtime/receipt-status.ts +5 -8
  119. package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +3 -5
  120. package/dist/bundling-sources/shared_libs/play-runtime/run-identity.ts +168 -0
  121. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +44 -5
  122. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/child-play-resolver.ts +26 -0
  123. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +3662 -0
  124. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/progress-sidecar.ts +47 -0
  125. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-deadline.ts +84 -0
  126. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-gateway-transport.ts +91 -0
  127. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-heartbeat.ts +424 -0
  128. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-progress-push.ts +321 -0
  129. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-sheet-persistence-retry.ts +110 -0
  130. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +625 -0
  131. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +72 -9
  132. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +6 -12
  133. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +19 -0
  134. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/bundle.ts +0 -1
  135. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runtime-sandbox-reconciliation.ts +14 -44
  136. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +103 -208
  137. package/dist/bundling-sources/shared_libs/play-runtime/runtime-authority.ts +201 -0
  138. package/dist/bundling-sources/shared_libs/play-runtime/runtime-env.ts +6 -0
  139. package/dist/bundling-sources/shared_libs/play-runtime/runtime-operation-contract.ts +67 -0
  140. package/dist/bundling-sources/shared_libs/play-runtime/runtime-release-environment.ts +2 -0
  141. package/dist/bundling-sources/shared_libs/play-runtime/runtime-sandbox-contract.ts +68 -0
  142. package/dist/bundling-sources/shared_libs/play-runtime/runtime-scheduler-topology.ts +14 -2
  143. package/dist/bundling-sources/shared_libs/play-runtime/runtime-telemetry.ts +108 -0
  144. package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/contract.ts +4 -0
  145. package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/runner-backend-adapter.ts +3 -0
  146. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +7 -0
  147. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-deadline-reconciliation.ts +88 -0
  148. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-play-deploy-task.ts +124 -0
  149. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-play-run-task.ts +68 -0
  150. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-pool.ts +193 -0
  151. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-queue-lifecycle.ts +269 -0
  152. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-queue-suffix.ts +73 -0
  153. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +6642 -0
  154. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +897 -0
  155. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-worker-metrics.ts +120 -0
  156. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-worker-required-env.ts +76 -0
  157. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd.ts +476 -0
  158. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/gateway-heavy-request-admission.ts +47 -0
  159. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/gateway-progress-registry.ts +147 -0
  160. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/index.ts +27 -0
  161. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/lazy.ts +50 -0
  162. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/play-run-recovery.ts +98 -0
  163. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-budget-state.ts +260 -0
  164. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +1416 -0
  165. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +1255 -0
  166. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +942 -0
  167. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +14636 -0
  168. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +888 -0
  169. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +355 -0
  170. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-terminal-transition.ts +46 -0
  171. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runner-push.ts +60 -0
  172. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-api-origin.ts +33 -0
  173. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-gateway-health.ts +64 -0
  174. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-capacity.ts +835 -0
  175. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-ledger-reconciliation.ts +1218 -0
  176. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-operation.ts +207 -0
  177. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-scheduler-schema.ts +10 -0
  178. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/scheduler-capacity.ts +123 -0
  179. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/scheduler-launch-read-observability.ts +78 -0
  180. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/submit-admission.ts +169 -0
  181. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/terminal-contract.ts +20 -0
  182. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/worker-queue-metrics.ts +34 -0
  183. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/worker-resource-metrics.ts +249 -0
  184. package/dist/bundling-sources/shared_libs/play-runtime/sheet-attempt-sql.ts +7 -0
  185. package/dist/bundling-sources/shared_libs/play-runtime/step-progress.ts +296 -0
  186. package/dist/bundling-sources/shared_libs/play-runtime/tool-call/native-batch.ts +3 -3
  187. package/dist/bundling-sources/shared_libs/play-runtime/tool-call/runtime-step-receipts-adapter.ts +20 -1
  188. package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +60 -15
  189. package/dist/bundling-sources/shared_libs/play-runtime/tool-execution-outcome.ts +20 -0
  190. package/dist/bundling-sources/shared_libs/play-runtime/transient-service-error.ts +12 -0
  191. package/dist/bundling-sources/shared_libs/play-runtime/vercel-protection.ts +14 -1
  192. package/dist/bundling-sources/shared_libs/play-runtime/webhook-receipt-retry.ts +50 -0
  193. package/dist/bundling-sources/shared_libs/play-runtime/work-receipt-storage-contract.ts +165 -0
  194. package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +15 -23
  195. package/dist/bundling-sources/shared_libs/play-runtime/workflow-executor-auth.ts +169 -0
  196. package/dist/bundling-sources/shared_libs/play-runtime/zerobounce-batching.ts +122 -0
  197. package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +2 -0
  198. package/dist/bundling-sources/shared_libs/plays/bundling/repository-adapter.ts +24 -0
  199. package/dist/bundling-sources/shared_libs/plays/child-run-projection.ts +174 -0
  200. package/dist/bundling-sources/shared_libs/plays/contracts.ts +2 -0
  201. package/dist/bundling-sources/shared_libs/plays/deploy/api-client.ts +267 -0
  202. package/dist/bundling-sources/shared_libs/plays/deploy/control-plane-types.ts +73 -0
  203. package/dist/bundling-sources/shared_libs/plays/deploy/manifest.ts +162 -0
  204. package/dist/bundling-sources/shared_libs/plays/deploy/project-serializer.ts +87 -0
  205. package/dist/bundling-sources/shared_libs/plays/deploy/source-file-path.ts +26 -0
  206. package/dist/bundling-sources/shared_libs/plays/deploy/target-paths.ts +36 -0
  207. package/dist/bundling-sources/shared_libs/plays/deployment-status.ts +1 -1
  208. package/dist/bundling-sources/shared_libs/plays/docflow.ts +1 -1
  209. package/dist/bundling-sources/shared_libs/plays/launch/input-contract.ts +400 -0
  210. package/dist/bundling-sources/shared_libs/plays/launch/prepare.ts +96 -0
  211. package/dist/bundling-sources/shared_libs/plays/local-file-discovery.ts +385 -0
  212. package/dist/bundling-sources/shared_libs/plays/play-grep-search.ts +112 -0
  213. package/dist/bundling-sources/shared_libs/plays/play-reference.ts +117 -0
  214. package/dist/bundling-sources/shared_libs/plays/run-input-summary.ts +260 -0
  215. package/dist/bundling-sources/shared_libs/plays/run-summary-types.ts +49 -0
  216. package/dist/bundling-sources/shared_libs/plays/secrets/errors.ts +19 -0
  217. package/dist/bundling-sources/shared_libs/plays/share/copy.ts +271 -0
  218. package/dist/bundling-sources/shared_libs/plays/share/metrics.ts +140 -0
  219. package/dist/bundling-sources/shared_libs/plays/share/public-read-model.ts +86 -0
  220. package/dist/bundling-sources/shared_libs/plays/share/seo-quality.ts +181 -0
  221. package/dist/bundling-sources/shared_libs/plays/share/static-pipeline-summary.ts +658 -0
  222. package/dist/bundling-sources/shared_libs/plays/share/types.ts +197 -0
  223. package/dist/bundling-sources/shared_libs/plays/sheet-filter.ts +347 -0
  224. package/dist/bundling-sources/shared_libs/plays/slack-hitl-timeout-error.ts +37 -0
  225. package/dist/bundling-sources/shared_libs/plays/standard-webhooks.ts +117 -0
  226. package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +6 -1
  227. package/dist/bundling-sources/shared_libs/plays/status-result.ts +388 -0
  228. package/dist/bundling-sources/shared_libs/plays/tool-result-types.ts +1 -1
  229. package/dist/bundling-sources/shared_libs/plays/transform-factor-structure.ts +377 -0
  230. package/dist/bundling-sources/shared_libs/plays/webhook-payload.ts +113 -0
  231. package/dist/bundling-sources/shared_libs/product-notifications/configuration.ts +7 -1
  232. package/dist/bundling-sources/shared_libs/product-notifications/default-policy.ts +3 -2
  233. package/dist/bundling-sources/shared_libs/product-notifications/overflow.ts +18 -0
  234. package/dist/bundling-sources/shared_libs/product-notifications/rolling-limit.ts +24 -0
  235. package/dist/bundling-sources/shared_libs/product-notifications/slack-delivery.ts +624 -0
  236. package/dist/bundling-sources/shared_libs/product-notifications/slack-payload.ts +140 -0
  237. package/dist/bundling-sources/shared_libs/product-notifications/slack-transport.ts +75 -0
  238. package/dist/bundling-sources/shared_libs/runtime-env.ts +6 -0
  239. package/dist/bundling-sources/shared_libs/security/email-blacklist.ts +42 -0
  240. package/dist/bundling-sources/shared_libs/security/header-safe-env.ts +10 -3
  241. package/dist/bundling-sources/shared_libs/security/slack-sanitize.ts +133 -0
  242. package/dist/cli/getting-started.d.mts +52 -0
  243. package/dist/cli/getting-started.d.ts +52 -0
  244. package/dist/cli/getting-started.js +65 -0
  245. package/dist/cli/getting-started.mjs +36 -0
  246. package/dist/cli/index.d.mts +9 -0
  247. package/dist/cli/index.d.ts +9 -0
  248. package/dist/cli/index.js +895 -407
  249. package/dist/cli/index.mjs +893 -414
  250. package/dist/{compiler-manifest-DsLbQaew.d.mts → compiler-manifest-x14KL83E.d.mts} +4 -2
  251. package/dist/{compiler-manifest-DsLbQaew.d.ts → compiler-manifest-x14KL83E.d.ts} +4 -2
  252. package/dist/index.d.mts +124 -154
  253. package/dist/index.d.ts +124 -154
  254. package/dist/index.js +132 -28
  255. package/dist/index.mjs +132 -28
  256. package/dist/install-integrity.json +182 -2
  257. package/dist/monitor-fleet-contract.d.mts +131 -0
  258. package/dist/monitor-fleet-contract.d.ts +131 -0
  259. package/dist/monitor-fleet-contract.js +406 -0
  260. package/dist/monitor-fleet-contract.mjs +367 -0
  261. package/dist/plays/bundle-play-file.d.mts +2 -2
  262. package/dist/plays/bundle-play-file.d.ts +2 -2
  263. package/dist/release.d.mts +255 -0
  264. package/dist/release.d.ts +255 -0
  265. package/dist/release.js +210 -0
  266. package/dist/release.mjs +183 -0
  267. package/package.json +21 -4
@@ -0,0 +1,1322 @@
1
+ import { WORKFLOW_EXECUTOR_TOKEN_TTL_SECONDS } from './/runtime-constants';
2
+ import {
3
+ DEFAULT_RUNTIME_EXECUTION_CAPABILITIES,
4
+ isRuntimeExecutionCapability,
5
+ normalizeRuntimeExecutionCapabilities,
6
+ type RuntimeExecutionCapability,
7
+ } from './/execution-capabilities';
8
+ import {
9
+ validateFixtureBehavior,
10
+ type FixtureBehavior,
11
+ } from './/fixture-behavior';
12
+ import { normalizeRuntimeSchedulerSchema } from './/runtime-scheduler-topology';
13
+ import { SignJWT, exportJWK, importJWK, jwtVerify, type JWK } from 'jose';
14
+ import {
15
+ directCompletedTerminalContract,
16
+ type DirectCompletedTerminalContract,
17
+ } from './/scheduler-backends/terminal-contract';
18
+
19
+ // Frozen at 1: the content-addressed `kid` is the real rotation mechanism, so
20
+ // this claim never moves and is not a place to encode a token change.
21
+ const INTERNAL_BOOTSTRAP_TOKEN_VERSION = 1 as const;
22
+ const INTERNAL_BOOTSTRAP_TOKEN_TTL_SECONDS = 60 * 5;
23
+ // Workflow executor token TTL.
24
+ //
25
+ // HISTORY: This used to be 7 days, with a comment that said "HITL workflow
26
+ // steps can legitimately wait days for a human response." That comment was
27
+ // describing an earlier design where a single activity blocked while waiting
28
+ // on a human. It is no longer true.
29
+ //
30
+ // CURRENT DESIGN: HITL pauses happen at the *workflow* level via
31
+ // `workflowSleep` + signal handlers.
32
+ // When a signal fires (or a timer elapses), the workflow schedules a NEW
33
+ // `executePlay` activity, and that activity mints a fresh executor token at
34
+ // start. So the token only needs to outlive a single activity invocation,
35
+ // which is bounded by `PLAY_ACTIVITY_TIMEOUT_SECONDS`. Matching the two
36
+ // constants makes that contract explicit: an executor token cannot outlive
37
+ // the activity it was minted for.
38
+ //
39
+ // CHANGING THIS: If you ever revert to a model where one activity blocks on
40
+ // a long-running external wait, this TTL must grow accordingly — and the
41
+ // activity startToCloseTimeout must grow with it. Do not bump one without
42
+ // the other; the runtime API depends on the token being valid for the entire
43
+ // lifetime of the activity that minted it.
44
+ const INTERNAL_WORKFLOW_TOKEN_TTL_SECONDS = WORKFLOW_EXECUTOR_TOKEN_TTL_SECONDS;
45
+ const INTERNAL_BOOTSTRAP_TOKEN_AUDIENCE =
46
+ 'deepline.ingestion.provider_sync_bootstrap' as const;
47
+ const INTERNAL_BOOTSTRAP_TOKEN_ISSUER = 'deepline.internal' as const;
48
+ const INTERNAL_BOOTSTRAP_BOOTSTRAP_PURPOSE =
49
+ 'ingestion_provider_sync_bootstrap' as const;
50
+ const INTERNAL_BOOTSTRAP_EXECUTION_PURPOSE =
51
+ 'ingestion_subscription_execution' as const;
52
+ const INTERNAL_WORKFLOW_EXECUTION_AUDIENCE =
53
+ 'deepline.workflow.execution' as const;
54
+ const INTERNAL_WORKFLOW_EXECUTION_PURPOSE = 'workflow_run_execution' as const;
55
+ const RUNTIME_RATE_SCOPE_AUDIENCE = 'deepline.runtime.rate_scope' as const;
56
+ const RUNTIME_RATE_SCOPE_PURPOSE = 'runtime_rate_scope' as const;
57
+ // The runner resolves a rate scope once per tool and retains it for the
58
+ // executor activity. Keep this narrower, run-scoped capability valid for the
59
+ // same bounded activity as the executor token that is required to use it.
60
+ const RUNTIME_RATE_SCOPE_TOKEN_TTL_SECONDS =
61
+ WORKFLOW_EXECUTOR_TOKEN_TTL_SECONDS;
62
+ const PLAY_DEPLOY_AUDIENCE = 'deepline.play.deploy' as const;
63
+ const PLAY_DEPLOY_PURPOSE = 'play_deploy' as const;
64
+ // A deploy token covers one GitHub deploy-on-push build task: tarball fetch,
65
+ // bundling, register, promote, and status reporting. Deliberately its own
66
+ // constant — the workflow executor TTL is contractually tied to a play
67
+ // activity's runtime limits and must not be reused here.
68
+ //
69
+ // Two hours, not build-length: the token is minted at webhook intake and the
70
+ // task may sit behind a queue backlog plus five exponential retries before
71
+ // it runs, and an expired token would leave the deployment stuck in a
72
+ // non-terminal state (the worker cannot even report failure without it).
73
+ // Real revocation is the live-state check in requirePlayDeployAuth
74
+ // (superseded/terminal deployment, disabled project), not this expiry.
75
+ const PLAY_DEPLOY_TOKEN_TTL_SECONDS = 2 * 60 * 60;
76
+ const INTERNAL_OAUTH_REFRESH_AUDIENCE = 'deepline.oauth.token_refresh' as const;
77
+ const INTERNAL_OAUTH_REFRESH_PURPOSE = 'oauth_token_refresh' as const;
78
+ const PRODUCT_NOTIFICATION_DELIVERY_AUDIENCE =
79
+ 'deepline.product_notification.delivery' as const;
80
+ const PRODUCT_NOTIFICATION_DELIVERY_PURPOSE =
81
+ 'product_notification_delivery' as const;
82
+ const PRODUCT_NOTIFICATION_UNSUBSCRIBE_AUDIENCE =
83
+ 'deepline.product_notification.unsubscribe' as const;
84
+ const PRODUCT_NOTIFICATION_UNSUBSCRIBE_PURPOSE =
85
+ 'product_notification_unsubscribe' as const;
86
+ const MONITOR_FLEET_DISPATCH_AUDIENCE =
87
+ 'deepline.monitor_fleet.dispatch' as const;
88
+ const MONITOR_FLEET_DISPATCH_PURPOSE = 'monitor_fleet_dispatch' as const;
89
+ const INTERNAL_BOOTSTRAP_TOKEN_DERIVATION_LABEL =
90
+ 'deepline:internal-bootstrap-token:v1';
91
+ const WORKFLOW_EXECUTOR_KEY_DERIVATION_LABEL =
92
+ 'deepline:workflow-executor-ed25519:v1';
93
+ const ED25519_PKCS8_SEED_PREFIX = Uint8Array.from([
94
+ 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04,
95
+ 0x22, 0x04, 0x20,
96
+ ]);
97
+
98
+ type IngestionInternalTokenBaseClaims = {
99
+ aud: typeof INTERNAL_BOOTSTRAP_TOKEN_AUDIENCE;
100
+ exp: number;
101
+ iat: number;
102
+ iss: typeof INTERNAL_BOOTSTRAP_TOKEN_ISSUER;
103
+ org_id: string;
104
+ provider: string;
105
+ sub: string;
106
+ v: typeof INTERNAL_BOOTSTRAP_TOKEN_VERSION;
107
+ };
108
+
109
+ export type IngestionProviderSyncBootstrapTokenClaims =
110
+ IngestionInternalTokenBaseClaims & {
111
+ purpose: typeof INTERNAL_BOOTSTRAP_BOOTSTRAP_PURPOSE;
112
+ request_token: string;
113
+ };
114
+
115
+ export type IngestionSubscriptionExecutionTokenClaims =
116
+ IngestionInternalTokenBaseClaims & {
117
+ purpose: typeof INTERNAL_BOOTSTRAP_EXECUTION_PURPOSE;
118
+ subscription_id: string;
119
+ };
120
+
121
+ export type IngestionInternalTokenClaims =
122
+ | IngestionProviderSyncBootstrapTokenClaims
123
+ | IngestionSubscriptionExecutionTokenClaims;
124
+
125
+ export type OAuthTokenRefreshTokenClaims = {
126
+ aud: typeof INTERNAL_OAUTH_REFRESH_AUDIENCE;
127
+ exp: number;
128
+ iat: number;
129
+ iss: typeof INTERNAL_BOOTSTRAP_TOKEN_ISSUER;
130
+ org_id: string;
131
+ provider: string;
132
+ purpose: typeof INTERNAL_OAUTH_REFRESH_PURPOSE;
133
+ sub: string;
134
+ v: typeof INTERNAL_BOOTSTRAP_TOKEN_VERSION;
135
+ };
136
+
137
+ export type ProductNotificationDeliveryTokenClaims = {
138
+ aud: typeof PRODUCT_NOTIFICATION_DELIVERY_AUDIENCE;
139
+ delivery_id: string;
140
+ exp: number;
141
+ iat: number;
142
+ iss: typeof INTERNAL_BOOTSTRAP_TOKEN_ISSUER;
143
+ org_id: string;
144
+ purpose: typeof PRODUCT_NOTIFICATION_DELIVERY_PURPOSE;
145
+ sub: 'product_notification_dispatcher';
146
+ v: typeof INTERNAL_BOOTSTRAP_TOKEN_VERSION;
147
+ };
148
+
149
+ export type ProductNotificationUnsubscribeTokenClaims = {
150
+ aud: typeof PRODUCT_NOTIFICATION_UNSUBSCRIBE_AUDIENCE;
151
+ group_key: string;
152
+ iat: number;
153
+ iss: typeof INTERNAL_BOOTSTRAP_TOKEN_ISSUER;
154
+ member_id: string;
155
+ org_id: string;
156
+ purpose: typeof PRODUCT_NOTIFICATION_UNSUBSCRIBE_PURPOSE;
157
+ sub: 'product_notification_recipient';
158
+ v: typeof INTERNAL_BOOTSTRAP_TOKEN_VERSION;
159
+ };
160
+
161
+ /** Capability scoped to one durable Monitor Fleet control-plane operation. */
162
+ export type MonitorFleetDispatchTokenClaims = {
163
+ aud: typeof MONITOR_FLEET_DISPATCH_AUDIENCE;
164
+ exp: number;
165
+ iat: number;
166
+ iss: typeof INTERNAL_BOOTSTRAP_TOKEN_ISSUER;
167
+ org_id: string;
168
+ purpose: typeof MONITOR_FLEET_DISPATCH_PURPOSE;
169
+ fleet_id?: string;
170
+ operation: 'dispatch' | 'daily_sync';
171
+ sub: 'monitor_fleet_dispatcher';
172
+ v: typeof INTERNAL_BOOTSTRAP_TOKEN_VERSION;
173
+ };
174
+
175
+ export type PlayDeployTokenClaims = {
176
+ aud: typeof PLAY_DEPLOY_AUDIENCE;
177
+ deployment_id: string;
178
+ exp: number;
179
+ granted_by_user_id: string;
180
+ iat: number;
181
+ iss: typeof INTERNAL_BOOTSTRAP_TOKEN_ISSUER;
182
+ org_id: string;
183
+ play_project_id: string;
184
+ purpose: typeof PLAY_DEPLOY_PURPOSE;
185
+ sub: 'play_deploy_worker';
186
+ v: typeof INTERNAL_BOOTSTRAP_TOKEN_VERSION;
187
+ };
188
+
189
+ type WorkflowInternalTokenBaseClaims = {
190
+ aud: typeof INTERNAL_WORKFLOW_EXECUTION_AUDIENCE;
191
+ exp: number;
192
+ iat: number;
193
+ iss: typeof INTERNAL_BOOTSTRAP_TOKEN_ISSUER;
194
+ org_id: string;
195
+ sub: string;
196
+ v: typeof INTERNAL_BOOTSTRAP_TOKEN_VERSION;
197
+ };
198
+
199
+ export type WorkflowRunExecutionTokenClaims =
200
+ WorkflowInternalTokenBaseClaims & {
201
+ purpose: typeof INTERNAL_WORKFLOW_EXECUTION_PURPOSE;
202
+ workflow_id: string;
203
+ run_id: string;
204
+ // Billing owner for this execution. Defaults to run_id on older tokens and
205
+ // root/top-level run id for child play executor tokens.
206
+ billing_run_id?: string;
207
+ // Required by the runtime storage endpoint. Do not make this optional:
208
+ // play/table names are caller-provided in runtime API requests, so the
209
+ // signed executor token must carry the authoritative play scope.
210
+ play_name: string;
211
+ allowed_secrets?: string[];
212
+ max_credits_per_run?: number;
213
+ integration_mode?: 'live' | 'eval_stub' | 'fixture';
214
+ fixture_behavior?: FixtureBehavior;
215
+ // Marks a run launched by an automated test harness (set in non-prod only).
216
+ // Carried on the signed token so the terminal runtime callback — which only
217
+ // sees executor-token auth, not the original CLI request — can suppress SDK
218
+ // CLI error alerts for intentionally failing test plays. Absent on real runs.
219
+ synthetic?: true;
220
+ // Identity of the actor that launched the run, carried on the signed token
221
+ // for the same reason as `synthetic`: the terminal runtime callback only
222
+ // sees executor-token auth, so without these claims it cannot attribute the
223
+ // run and emits "(unknown actor)" alerts. Optional — trigger/cron/system
224
+ // launches have no human actor, and older tokens predate the claims.
225
+ actor_user_id?: string;
226
+ actor_email?: string;
227
+ /** Durable scheduler topology attested by the launch-time app. */
228
+ scheduler_schema?: string;
229
+ /** Scheduler generation that owns durable writes for this execution leg. */
230
+ run_attempt?: number;
231
+ /** Opaque scheduler lease nonce bound to this exact execution leg. */
232
+ run_lease_token?: string;
233
+ /**
234
+ * Terminal transition contract declared by the worker release that asked
235
+ * for this token. Typed as a string, not a union: a token minted by an
236
+ * older or newer build may carry a value this build does not know, and
237
+ * that must degrade to the compatibility wake path rather than make an
238
+ * otherwise valid execution token fail verification.
239
+ */
240
+ terminal_contract?: string;
241
+ /** Explicit allow-list. Tokens without capabilities fail validation. */
242
+ capabilities: RuntimeExecutionCapability[];
243
+ /** Trusted deployment origin that issued this run token. */
244
+ runtime_origin?: string;
245
+ };
246
+
247
+ export type RuntimeRateScopeTokenClaims = {
248
+ aud: typeof RUNTIME_RATE_SCOPE_AUDIENCE;
249
+ exp: number;
250
+ iat: number;
251
+ iss: typeof INTERNAL_BOOTSTRAP_TOKEN_ISSUER;
252
+ org_id: string;
253
+ run_id: string;
254
+ provider: string;
255
+ bucket_id: string;
256
+ scope_kind: 'fleet_managed' | 'customer_credential' | 'org';
257
+ purpose: typeof RUNTIME_RATE_SCOPE_PURPOSE;
258
+ sub: 'runtime_rate_scope';
259
+ v: typeof INTERNAL_BOOTSTRAP_TOKEN_VERSION;
260
+ };
261
+
262
+ function isValidWorkflowIntegrationMode(
263
+ value: unknown,
264
+ ): value is NonNullable<WorkflowRunExecutionTokenClaims['integration_mode']> {
265
+ return value === 'live' || value === 'eval_stub' || value === 'fixture';
266
+ }
267
+
268
+ function getRootSecret(): string {
269
+ const secretFromEnv = process.env.BETTER_AUTH_SECRET;
270
+ if (!secretFromEnv) {
271
+ throw new Error('BETTER_AUTH_SECRET is not set.');
272
+ }
273
+ return `${secretFromEnv}:${INTERNAL_BOOTSTRAP_TOKEN_DERIVATION_LABEL}`;
274
+ }
275
+
276
+ let workflowKeyCache: {
277
+ secret: string;
278
+ privateKey: CryptoKey;
279
+ publicJwk: JWK & { kid: string };
280
+ } | null = null;
281
+
282
+ async function deriveWorkflowExecutorSeed(secret: string): Promise<Uint8Array> {
283
+ // Convex WebCrypto does not implement HKDF. Implement the RFC 5869
284
+ // extract/expand operations with HMAC-SHA-256 instead, so this stays
285
+ // byte-for-byte compatible with the prior HKDF(seed, zero salt, label).
286
+ const toArrayBuffer = (bytes: Uint8Array): ArrayBuffer => {
287
+ const copy = new Uint8Array(bytes.byteLength);
288
+ copy.set(bytes);
289
+ return copy.buffer;
290
+ };
291
+ const hmac = async (
292
+ keyMaterial: Uint8Array,
293
+ message: Uint8Array,
294
+ ): Promise<Uint8Array> => {
295
+ const key = await crypto.subtle.importKey(
296
+ 'raw',
297
+ toArrayBuffer(keyMaterial),
298
+ { name: 'HMAC', hash: 'SHA-256' },
299
+ false,
300
+ ['sign'],
301
+ );
302
+ return new Uint8Array(
303
+ await crypto.subtle.sign('HMAC', key, toArrayBuffer(message)),
304
+ );
305
+ };
306
+ const info = new TextEncoder().encode(WORKFLOW_EXECUTOR_KEY_DERIVATION_LABEL);
307
+ const prk = await hmac(new Uint8Array(32), new TextEncoder().encode(secret));
308
+ const expandInput = new Uint8Array(info.length + 1);
309
+ expandInput.set(info);
310
+ expandInput[info.length] = 1;
311
+ return await hmac(prk, expandInput);
312
+ }
313
+
314
+ async function workflowExecutorKeyPair(): Promise<{
315
+ privateKey: CryptoKey;
316
+ publicJwk: JWK & { kid: string };
317
+ }> {
318
+ const secret = process.env.BETTER_AUTH_SECRET?.trim();
319
+ if (!secret) throw new Error('BETTER_AUTH_SECRET is not set.');
320
+ if (workflowKeyCache?.secret === secret) return workflowKeyCache;
321
+ const seed = await deriveWorkflowExecutorSeed(secret);
322
+ const pkcs8 = new Uint8Array(ED25519_PKCS8_SEED_PREFIX.length + seed.length);
323
+ pkcs8.set(ED25519_PKCS8_SEED_PREFIX);
324
+ pkcs8.set(seed, ED25519_PKCS8_SEED_PREFIX.length);
325
+ const privateKey = await crypto.subtle.importKey(
326
+ 'pkcs8',
327
+ pkcs8,
328
+ { name: 'Ed25519' },
329
+ true,
330
+ ['sign'],
331
+ );
332
+ const exported = await exportJWK(privateKey);
333
+ const publicJwk = {
334
+ kty: exported.kty,
335
+ crv: exported.crv,
336
+ x: exported.x,
337
+ use: 'sig',
338
+ alg: 'EdDSA',
339
+ kid: `runtime-${String(exported.x).slice(0, 16)}`,
340
+ } satisfies JWK & { kid: string };
341
+ workflowKeyCache = { secret, privateKey, publicJwk };
342
+ return workflowKeyCache;
343
+ }
344
+
345
+ export async function workflowExecutorPublicJwks(): Promise<{
346
+ keys: Array<JWK & { kid: string }>;
347
+ }> {
348
+ const { publicJwk } = await workflowExecutorKeyPair();
349
+ return { keys: [publicJwk] };
350
+ }
351
+
352
+ function bytesToBase64(bytes: Uint8Array): string {
353
+ if (typeof Buffer !== 'undefined') {
354
+ return Buffer.from(bytes).toString('base64');
355
+ }
356
+
357
+ let binary = '';
358
+ for (const byte of bytes) {
359
+ binary += String.fromCharCode(byte);
360
+ }
361
+ return btoa(binary);
362
+ }
363
+
364
+ function base64ToBytes(value: string): Uint8Array {
365
+ if (typeof Buffer !== 'undefined') {
366
+ return new Uint8Array(Buffer.from(value, 'base64'));
367
+ }
368
+
369
+ const binary = atob(value);
370
+ const bytes = new Uint8Array(binary.length);
371
+ for (let index = 0; index < binary.length; index += 1) {
372
+ bytes[index] = binary.charCodeAt(index);
373
+ }
374
+ return bytes;
375
+ }
376
+
377
+ function toBase64Url(bytes: Uint8Array): string {
378
+ return bytesToBase64(bytes)
379
+ .replace(/\+/g, '-')
380
+ .replace(/\//g, '_')
381
+ .replace(/=+$/g, '');
382
+ }
383
+
384
+ function fromBase64Url(value: string): Uint8Array {
385
+ const padding =
386
+ value.length % 4 === 0 ? '' : '='.repeat(4 - (value.length % 4));
387
+ const normalized = value.replace(/-/g, '+').replace(/_/g, '/') + padding;
388
+ return base64ToBytes(normalized);
389
+ }
390
+
391
+ function encodeJson(value: unknown): string {
392
+ return toBase64Url(new TextEncoder().encode(JSON.stringify(value)));
393
+ }
394
+
395
+ function decodeJson<T>(value: string): T {
396
+ const decoded = new TextDecoder().decode(fromBase64Url(value));
397
+ return JSON.parse(decoded) as T;
398
+ }
399
+
400
+ async function signTokenMaterial(value: string): Promise<string> {
401
+ const key = await crypto.subtle.importKey(
402
+ 'raw',
403
+ new TextEncoder().encode(getRootSecret()),
404
+ {
405
+ name: 'HMAC',
406
+ hash: 'SHA-256',
407
+ },
408
+ false,
409
+ ['sign'],
410
+ );
411
+ const signature = await crypto.subtle.sign(
412
+ 'HMAC',
413
+ key,
414
+ new TextEncoder().encode(value),
415
+ );
416
+ return toBase64Url(new Uint8Array(signature));
417
+ }
418
+
419
+ function timingSafeEqual(left: Uint8Array, right: Uint8Array): boolean {
420
+ if (left.length !== right.length) {
421
+ return false;
422
+ }
423
+
424
+ let mismatch = 0;
425
+ for (let index = 0; index < left.length; index += 1) {
426
+ mismatch |= left[index] ^ right[index];
427
+ }
428
+ return mismatch === 0;
429
+ }
430
+
431
+ function isValidClaimsShape(
432
+ claims: Record<string, unknown>,
433
+ ): claims is IngestionInternalTokenClaims {
434
+ const hasBaseShape =
435
+ claims.v === INTERNAL_BOOTSTRAP_TOKEN_VERSION &&
436
+ claims.iss === INTERNAL_BOOTSTRAP_TOKEN_ISSUER &&
437
+ claims.aud === INTERNAL_BOOTSTRAP_TOKEN_AUDIENCE &&
438
+ typeof claims.org_id === 'string' &&
439
+ claims.org_id.length > 0 &&
440
+ typeof claims.provider === 'string' &&
441
+ claims.provider.length > 0 &&
442
+ typeof claims.sub === 'string' &&
443
+ claims.sub.length > 0 &&
444
+ typeof claims.iat === 'number' &&
445
+ Number.isFinite(claims.iat) &&
446
+ typeof claims.exp === 'number' &&
447
+ Number.isFinite(claims.exp);
448
+
449
+ if (!hasBaseShape) {
450
+ return false;
451
+ }
452
+
453
+ if (claims.purpose === INTERNAL_BOOTSTRAP_BOOTSTRAP_PURPOSE) {
454
+ return (
455
+ typeof claims.request_token === 'string' &&
456
+ claims.request_token.length > 0
457
+ );
458
+ }
459
+
460
+ if (claims.purpose === INTERNAL_BOOTSTRAP_EXECUTION_PURPOSE) {
461
+ return (
462
+ typeof claims.subscription_id === 'string' &&
463
+ claims.subscription_id.length > 0
464
+ );
465
+ }
466
+
467
+ return false;
468
+ }
469
+
470
+ function isValidOAuthRefreshClaimsShape(
471
+ claims: Record<string, unknown>,
472
+ ): claims is OAuthTokenRefreshTokenClaims {
473
+ return (
474
+ claims.v === INTERNAL_BOOTSTRAP_TOKEN_VERSION &&
475
+ claims.iss === INTERNAL_BOOTSTRAP_TOKEN_ISSUER &&
476
+ claims.aud === INTERNAL_OAUTH_REFRESH_AUDIENCE &&
477
+ claims.purpose === INTERNAL_OAUTH_REFRESH_PURPOSE &&
478
+ typeof claims.org_id === 'string' &&
479
+ claims.org_id.length > 0 &&
480
+ typeof claims.provider === 'string' &&
481
+ claims.provider.length > 0 &&
482
+ typeof claims.sub === 'string' &&
483
+ claims.sub.length > 0 &&
484
+ typeof claims.iat === 'number' &&
485
+ Number.isFinite(claims.iat) &&
486
+ typeof claims.exp === 'number' &&
487
+ Number.isFinite(claims.exp)
488
+ );
489
+ }
490
+
491
+ function isValidRuntimeOrigin(value: unknown): value is string {
492
+ if (typeof value !== 'string') return false;
493
+ try {
494
+ const url = new URL(value);
495
+ if (url.origin !== value || url.username || url.password) return false;
496
+ if (url.protocol === 'https:') return true;
497
+ return (
498
+ url.protocol === 'http:' &&
499
+ (url.hostname === 'localhost' ||
500
+ url.hostname === '127.0.0.1' ||
501
+ url.hostname === '[::1]')
502
+ );
503
+ } catch {
504
+ return false;
505
+ }
506
+ }
507
+
508
+ function isValidWorkflowClaimsShape(
509
+ claims: Record<string, unknown>,
510
+ ): claims is WorkflowRunExecutionTokenClaims {
511
+ return (
512
+ claims.v === INTERNAL_BOOTSTRAP_TOKEN_VERSION &&
513
+ claims.iss === INTERNAL_BOOTSTRAP_TOKEN_ISSUER &&
514
+ claims.aud === INTERNAL_WORKFLOW_EXECUTION_AUDIENCE &&
515
+ claims.purpose === INTERNAL_WORKFLOW_EXECUTION_PURPOSE &&
516
+ typeof claims.org_id === 'string' &&
517
+ claims.org_id.length > 0 &&
518
+ typeof claims.workflow_id === 'string' &&
519
+ claims.workflow_id.length > 0 &&
520
+ typeof claims.run_id === 'string' &&
521
+ claims.run_id.length > 0 &&
522
+ (claims.billing_run_id === undefined ||
523
+ (typeof claims.billing_run_id === 'string' &&
524
+ claims.billing_run_id.length > 0)) &&
525
+ // Hard cutover invariant: old workflow tokens without play_name are not
526
+ // accepted. If this is loosened, runtime storage minting can no longer
527
+ // prove that the requested playName came from the signed run contract.
528
+ typeof claims.play_name === 'string' &&
529
+ claims.play_name.length > 0 &&
530
+ (claims.max_credits_per_run === undefined ||
531
+ (typeof claims.max_credits_per_run === 'number' &&
532
+ Number.isFinite(claims.max_credits_per_run) &&
533
+ claims.max_credits_per_run > 0)) &&
534
+ (claims.integration_mode === undefined ||
535
+ isValidWorkflowIntegrationMode(claims.integration_mode)) &&
536
+ (claims.fixture_behavior === undefined ||
537
+ (claims.integration_mode === 'fixture' &&
538
+ validateFixtureBehavior(claims.fixture_behavior).ok)) &&
539
+ (claims.synthetic === undefined || claims.synthetic === true) &&
540
+ (claims.actor_user_id === undefined ||
541
+ typeof claims.actor_user_id === 'string') &&
542
+ (claims.actor_email === undefined ||
543
+ typeof claims.actor_email === 'string') &&
544
+ (claims.scheduler_schema === undefined ||
545
+ normalizeRuntimeSchedulerSchema(claims.scheduler_schema) !== null) &&
546
+ (claims.run_attempt === undefined ||
547
+ (typeof claims.run_attempt === 'number' &&
548
+ Number.isSafeInteger(claims.run_attempt) &&
549
+ claims.run_attempt >= 0)) &&
550
+ (claims.run_lease_token === undefined ||
551
+ (typeof claims.run_lease_token === 'string' &&
552
+ claims.run_lease_token.length > 0)) &&
553
+ (claims.terminal_contract === undefined ||
554
+ typeof claims.terminal_contract === 'string') &&
555
+ (claims.runtime_origin === undefined ||
556
+ isValidRuntimeOrigin(claims.runtime_origin)) &&
557
+ Array.isArray(claims.capabilities) &&
558
+ claims.capabilities.length > 0 &&
559
+ claims.capabilities.every(isRuntimeExecutionCapability) &&
560
+ typeof claims.sub === 'string' &&
561
+ claims.sub.length > 0 &&
562
+ typeof claims.iat === 'number' &&
563
+ Number.isFinite(claims.iat) &&
564
+ typeof claims.exp === 'number' &&
565
+ Number.isFinite(claims.exp)
566
+ );
567
+ }
568
+
569
+ export async function mintIngestionProviderSyncBootstrapToken(input: {
570
+ orgId: string;
571
+ provider: string;
572
+ requestToken: string;
573
+ requestedByUserId: string | null;
574
+ nowMs?: number;
575
+ }): Promise<string> {
576
+ const issuedAt = Math.floor((input.nowMs ?? Date.now()) / 1000);
577
+ const claims: IngestionProviderSyncBootstrapTokenClaims = {
578
+ v: INTERNAL_BOOTSTRAP_TOKEN_VERSION,
579
+ iss: INTERNAL_BOOTSTRAP_TOKEN_ISSUER,
580
+ aud: INTERNAL_BOOTSTRAP_TOKEN_AUDIENCE,
581
+ purpose: INTERNAL_BOOTSTRAP_BOOTSTRAP_PURPOSE,
582
+ org_id: input.orgId,
583
+ provider: input.provider,
584
+ request_token: input.requestToken,
585
+ sub: input.requestedByUserId ?? 'system',
586
+ iat: issuedAt,
587
+ exp: issuedAt + INTERNAL_BOOTSTRAP_TOKEN_TTL_SECONDS,
588
+ };
589
+ const header = {
590
+ alg: 'HS256',
591
+ typ: 'JWT',
592
+ };
593
+ const encodedHeader = encodeJson(header);
594
+ const encodedClaims = encodeJson(claims);
595
+ const signingInput = `${encodedHeader}.${encodedClaims}`;
596
+ const signature = await signTokenMaterial(signingInput);
597
+ return `${signingInput}.${signature}`;
598
+ }
599
+
600
+ export async function mintIngestionSubscriptionExecutionToken(input: {
601
+ orgId: string;
602
+ provider: string;
603
+ subscriptionId: string;
604
+ subject?: string | null;
605
+ nowMs?: number;
606
+ }): Promise<string> {
607
+ const issuedAt = Math.floor((input.nowMs ?? Date.now()) / 1000);
608
+ const claims: IngestionSubscriptionExecutionTokenClaims = {
609
+ v: INTERNAL_BOOTSTRAP_TOKEN_VERSION,
610
+ iss: INTERNAL_BOOTSTRAP_TOKEN_ISSUER,
611
+ aud: INTERNAL_BOOTSTRAP_TOKEN_AUDIENCE,
612
+ purpose: INTERNAL_BOOTSTRAP_EXECUTION_PURPOSE,
613
+ org_id: input.orgId,
614
+ provider: input.provider,
615
+ subscription_id: input.subscriptionId,
616
+ sub: input.subject ?? 'ingestion_executor',
617
+ iat: issuedAt,
618
+ exp: issuedAt + INTERNAL_BOOTSTRAP_TOKEN_TTL_SECONDS,
619
+ };
620
+ const header = {
621
+ alg: 'HS256',
622
+ typ: 'JWT',
623
+ };
624
+ const encodedHeader = encodeJson(header);
625
+ const encodedClaims = encodeJson(claims);
626
+ const signingInput = `${encodedHeader}.${encodedClaims}`;
627
+ const signature = await signTokenMaterial(signingInput);
628
+ return `${signingInput}.${signature}`;
629
+ }
630
+
631
+ export async function mintWorkflowRunExecutionToken(input: {
632
+ orgId: string;
633
+ workflowId: string;
634
+ runId: string;
635
+ billingRunId?: string | null;
636
+ playName: string;
637
+ allowedSecrets?: readonly string[] | null;
638
+ maxCreditsPerRun?: number | null;
639
+ integrationMode?: 'live' | 'eval_stub' | 'fixture' | null;
640
+ fixtureBehavior?: FixtureBehavior | null;
641
+ synthetic?: boolean | null;
642
+ actorUserId?: string | null;
643
+ actorEmail?: string | null;
644
+ schedulerSchema?: string | null;
645
+ runAttempt?: number | null;
646
+ runLeaseToken?: string | null;
647
+ terminalContract?: DirectCompletedTerminalContract | null;
648
+ capabilities?: readonly RuntimeExecutionCapability[];
649
+ runtimeOrigin?: string | null;
650
+ subject?: string | null;
651
+ nowMs?: number;
652
+ }): Promise<string> {
653
+ const fixtureBehavior = validateFixtureBehavior(input.fixtureBehavior);
654
+ if (!fixtureBehavior.ok) throw new Error(fixtureBehavior.error);
655
+ if (fixtureBehavior.behavior && input.integrationMode !== 'fixture') {
656
+ throw new Error(
657
+ 'fixtureBehavior requires workflow integration mode "fixture".',
658
+ );
659
+ }
660
+ const actorUserId = input.actorUserId?.trim() || null;
661
+ const actorEmail = input.actorEmail?.trim() || null;
662
+ const schedulerSchema = normalizeRuntimeSchedulerSchema(
663
+ input.schedulerSchema,
664
+ );
665
+ const issuedAt = Math.floor((input.nowMs ?? Date.now()) / 1000);
666
+ // A retired contract value mints no claim; the verifier then selects the wake path.
667
+ const mintedTerminalContract = directCompletedTerminalContract(
668
+ input.terminalContract,
669
+ );
670
+ const claims: WorkflowRunExecutionTokenClaims = {
671
+ v: INTERNAL_BOOTSTRAP_TOKEN_VERSION,
672
+ iss: INTERNAL_BOOTSTRAP_TOKEN_ISSUER,
673
+ aud: INTERNAL_WORKFLOW_EXECUTION_AUDIENCE,
674
+ purpose: INTERNAL_WORKFLOW_EXECUTION_PURPOSE,
675
+ org_id: input.orgId,
676
+ workflow_id: input.workflowId,
677
+ run_id: input.runId,
678
+ ...(input.billingRunId?.trim()
679
+ ? { billing_run_id: input.billingRunId.trim() }
680
+ : {}),
681
+ play_name: input.playName,
682
+ ...(input.allowedSecrets?.length
683
+ ? {
684
+ allowed_secrets: [
685
+ ...new Set(
686
+ input.allowedSecrets
687
+ .filter((name): name is string => typeof name === 'string')
688
+ .map((name) => name.trim().toUpperCase())
689
+ .filter(Boolean),
690
+ ),
691
+ ],
692
+ }
693
+ : {}),
694
+ ...(typeof input.maxCreditsPerRun === 'number' &&
695
+ Number.isFinite(input.maxCreditsPerRun) &&
696
+ input.maxCreditsPerRun > 0
697
+ ? { max_credits_per_run: input.maxCreditsPerRun }
698
+ : {}),
699
+ ...(isValidWorkflowIntegrationMode(input.integrationMode)
700
+ ? { integration_mode: input.integrationMode }
701
+ : {}),
702
+ ...(fixtureBehavior.behavior
703
+ ? { fixture_behavior: fixtureBehavior.behavior }
704
+ : {}),
705
+ ...(input.synthetic ? { synthetic: true as const } : {}),
706
+ ...(actorUserId ? { actor_user_id: actorUserId } : {}),
707
+ ...(actorEmail ? { actor_email: actorEmail } : {}),
708
+ ...(schedulerSchema ? { scheduler_schema: schedulerSchema } : {}),
709
+ ...(typeof input.runAttempt === 'number' &&
710
+ Number.isSafeInteger(input.runAttempt) &&
711
+ input.runAttempt >= 0
712
+ ? { run_attempt: input.runAttempt }
713
+ : {}),
714
+ ...(input.runLeaseToken?.trim()
715
+ ? { run_lease_token: input.runLeaseToken.trim() }
716
+ : {}),
717
+ ...(mintedTerminalContract
718
+ ? { terminal_contract: mintedTerminalContract }
719
+ : {}),
720
+ capabilities: normalizeRuntimeExecutionCapabilities(
721
+ input.capabilities ?? DEFAULT_RUNTIME_EXECUTION_CAPABILITIES,
722
+ ),
723
+ ...(input.runtimeOrigin?.trim()
724
+ ? { runtime_origin: new URL(input.runtimeOrigin).origin }
725
+ : {}),
726
+ sub: input.subject ?? 'workflow_executor',
727
+ iat: issuedAt,
728
+ exp: issuedAt + INTERNAL_WORKFLOW_TOKEN_TTL_SECONDS,
729
+ };
730
+ const { privateKey, publicJwk } = await workflowExecutorKeyPair();
731
+ return await new SignJWT(claims)
732
+ .setProtectedHeader({ alg: 'EdDSA', typ: 'JWT', kid: publicJwk.kid })
733
+ .sign(privateKey);
734
+ }
735
+
736
+ export async function mintRuntimeRateScopeToken(input: {
737
+ orgId: string;
738
+ runId: string;
739
+ provider: string;
740
+ bucketId: string;
741
+ scopeKind: RuntimeRateScopeTokenClaims['scope_kind'];
742
+ nowMs?: number;
743
+ }): Promise<string> {
744
+ const issuedAt = Math.floor((input.nowMs ?? Date.now()) / 1000);
745
+ const claims: RuntimeRateScopeTokenClaims = {
746
+ v: INTERNAL_BOOTSTRAP_TOKEN_VERSION,
747
+ iss: INTERNAL_BOOTSTRAP_TOKEN_ISSUER,
748
+ aud: RUNTIME_RATE_SCOPE_AUDIENCE,
749
+ purpose: RUNTIME_RATE_SCOPE_PURPOSE,
750
+ org_id: input.orgId,
751
+ run_id: input.runId,
752
+ provider: input.provider,
753
+ bucket_id: input.bucketId,
754
+ scope_kind: input.scopeKind,
755
+ sub: 'runtime_rate_scope',
756
+ iat: issuedAt,
757
+ exp: issuedAt + RUNTIME_RATE_SCOPE_TOKEN_TTL_SECONDS,
758
+ };
759
+ const { privateKey, publicJwk } = await workflowExecutorKeyPair();
760
+ return await new SignJWT(claims)
761
+ .setProtectedHeader({ alg: 'EdDSA', typ: 'JWT', kid: publicJwk.kid })
762
+ .sign(privateKey);
763
+ }
764
+
765
+ export async function verifyRuntimeRateScopeTokenWithJwks(
766
+ token: string,
767
+ jwks: { keys: JWK[] },
768
+ nowMs = Date.now(),
769
+ ): Promise<RuntimeRateScopeTokenClaims> {
770
+ const headerKid = decodeJson<Record<string, unknown>>(
771
+ token.split('.')[0] ?? '',
772
+ ).kid;
773
+ const jwk = jwks.keys.find((candidate) => candidate.kid === headerKid);
774
+ if (!jwk) throw new Error('Invalid runtime rate-scope token key.');
775
+ const key = await importJWK(jwk, 'EdDSA');
776
+ const verified = await jwtVerify(token, key, {
777
+ algorithms: ['EdDSA'],
778
+ issuer: INTERNAL_BOOTSTRAP_TOKEN_ISSUER,
779
+ audience: RUNTIME_RATE_SCOPE_AUDIENCE,
780
+ currentDate: new Date(nowMs),
781
+ typ: 'JWT',
782
+ });
783
+ const claims = verified.payload as Record<string, unknown>;
784
+ if (
785
+ claims.v !== INTERNAL_BOOTSTRAP_TOKEN_VERSION ||
786
+ claims.purpose !== RUNTIME_RATE_SCOPE_PURPOSE ||
787
+ claims.sub !== 'runtime_rate_scope' ||
788
+ typeof claims.org_id !== 'string' ||
789
+ typeof claims.run_id !== 'string' ||
790
+ typeof claims.provider !== 'string' ||
791
+ typeof claims.bucket_id !== 'string' ||
792
+ !['fleet_managed', 'customer_credential', 'org'].includes(
793
+ String(claims.scope_kind),
794
+ )
795
+ ) {
796
+ throw new Error('Invalid runtime rate-scope token.');
797
+ }
798
+ return claims as RuntimeRateScopeTokenClaims;
799
+ }
800
+
801
+ export async function verifyRuntimeRateScopeToken(
802
+ token: string,
803
+ nowMs = Date.now(),
804
+ ): Promise<RuntimeRateScopeTokenClaims> {
805
+ const { publicJwk } = await workflowExecutorKeyPair();
806
+ return await verifyRuntimeRateScopeTokenWithJwks(
807
+ token,
808
+ { keys: [publicJwk] },
809
+ nowMs,
810
+ );
811
+ }
812
+
813
+ /**
814
+ * Deploy tokens authorize exactly one GitHub deploy-on-push deployment. The
815
+ * control plane mints one at webhook-intake time (the deploy worker holds no
816
+ * signing material) and only the /api/v2/deployments/* endpoints accept it.
817
+ * The granting principal is the play project's binding admin; the grant at
818
+ * bind time is the authorization event.
819
+ */
820
+ export async function mintPlayDeployToken(input: {
821
+ orgId: string;
822
+ playProjectId: string;
823
+ deploymentId: string;
824
+ grantedByUserId: string;
825
+ nowMs?: number;
826
+ }): Promise<string> {
827
+ const issuedAt = Math.floor((input.nowMs ?? Date.now()) / 1000);
828
+ const claims: PlayDeployTokenClaims = {
829
+ v: INTERNAL_BOOTSTRAP_TOKEN_VERSION,
830
+ iss: INTERNAL_BOOTSTRAP_TOKEN_ISSUER,
831
+ aud: PLAY_DEPLOY_AUDIENCE,
832
+ purpose: PLAY_DEPLOY_PURPOSE,
833
+ org_id: input.orgId,
834
+ play_project_id: input.playProjectId,
835
+ deployment_id: input.deploymentId,
836
+ granted_by_user_id: input.grantedByUserId,
837
+ sub: 'play_deploy_worker',
838
+ iat: issuedAt,
839
+ exp: issuedAt + PLAY_DEPLOY_TOKEN_TTL_SECONDS,
840
+ };
841
+ const { privateKey, publicJwk } = await workflowExecutorKeyPair();
842
+ return await new SignJWT(claims)
843
+ .setProtectedHeader({ alg: 'EdDSA', typ: 'JWT', kid: publicJwk.kid })
844
+ .sign(privateKey);
845
+ }
846
+
847
+ /**
848
+ * Cheap pre-verification discriminator: does this bearer token claim to be a
849
+ * play deploy token? Decodes without verifying — callers must still verify.
850
+ */
851
+ export function isPlayDeployTokenCandidate(token: string): boolean {
852
+ const segments = token.split('.');
853
+ if (segments.length !== 3) return false;
854
+ try {
855
+ const claims = decodeJson<Record<string, unknown>>(segments[1] ?? '');
856
+ return claims.aud === PLAY_DEPLOY_AUDIENCE;
857
+ } catch {
858
+ return false;
859
+ }
860
+ }
861
+
862
+ export async function verifyPlayDeployToken(
863
+ token: string,
864
+ nowMs = Date.now(),
865
+ ): Promise<PlayDeployTokenClaims> {
866
+ const { publicJwk } = await workflowExecutorKeyPair();
867
+ const headerKid = decodeJson<Record<string, unknown>>(
868
+ token.split('.')[0] ?? '',
869
+ ).kid;
870
+ if (headerKid !== publicJwk.kid) {
871
+ throw new Error('Invalid play deploy token key.');
872
+ }
873
+ const key = await importJWK(publicJwk, 'EdDSA');
874
+ const verified = await jwtVerify(token, key, {
875
+ algorithms: ['EdDSA'],
876
+ issuer: INTERNAL_BOOTSTRAP_TOKEN_ISSUER,
877
+ audience: PLAY_DEPLOY_AUDIENCE,
878
+ currentDate: new Date(nowMs),
879
+ typ: 'JWT',
880
+ });
881
+ const claims = verified.payload as Record<string, unknown>;
882
+ if (
883
+ claims.v !== INTERNAL_BOOTSTRAP_TOKEN_VERSION ||
884
+ claims.purpose !== PLAY_DEPLOY_PURPOSE ||
885
+ claims.sub !== 'play_deploy_worker' ||
886
+ typeof claims.org_id !== 'string' ||
887
+ !claims.org_id ||
888
+ typeof claims.play_project_id !== 'string' ||
889
+ !claims.play_project_id ||
890
+ typeof claims.deployment_id !== 'string' ||
891
+ !claims.deployment_id ||
892
+ typeof claims.granted_by_user_id !== 'string' ||
893
+ !claims.granted_by_user_id
894
+ ) {
895
+ throw new Error('Invalid play deploy token.');
896
+ }
897
+ return claims as PlayDeployTokenClaims;
898
+ }
899
+
900
+ // Server-only authorization for runtime OAuth token-refresh writes. Minted by
901
+ // the server during integration execution (which holds the signing secret) and
902
+ // verified by the Convex refresh mutation, so a member API key can never
903
+ // authorize a connection write — it can only execute, which triggers the
904
+ // server-side refresh that mints this token.
905
+ export async function mintOAuthTokenRefreshToken(input: {
906
+ orgId: string;
907
+ provider: string;
908
+ subject?: string | null;
909
+ nowMs?: number;
910
+ }): Promise<string> {
911
+ const issuedAt = Math.floor((input.nowMs ?? Date.now()) / 1000);
912
+ const claims: OAuthTokenRefreshTokenClaims = {
913
+ v: INTERNAL_BOOTSTRAP_TOKEN_VERSION,
914
+ iss: INTERNAL_BOOTSTRAP_TOKEN_ISSUER,
915
+ aud: INTERNAL_OAUTH_REFRESH_AUDIENCE,
916
+ purpose: INTERNAL_OAUTH_REFRESH_PURPOSE,
917
+ org_id: input.orgId,
918
+ provider: input.provider,
919
+ sub: input.subject ?? 'oauth_token_refresh',
920
+ iat: issuedAt,
921
+ exp: issuedAt + INTERNAL_BOOTSTRAP_TOKEN_TTL_SECONDS,
922
+ };
923
+ const header = {
924
+ alg: 'HS256',
925
+ typ: 'JWT',
926
+ };
927
+ const encodedHeader = encodeJson(header);
928
+ const encodedClaims = encodeJson(claims);
929
+ const signingInput = `${encodedHeader}.${encodedClaims}`;
930
+ const signature = await signTokenMaterial(signingInput);
931
+ return `${signingInput}.${signature}`;
932
+ }
933
+
934
+ export async function verifyOAuthTokenRefreshToken(
935
+ token: string,
936
+ nowMs = Date.now(),
937
+ ): Promise<OAuthTokenRefreshTokenClaims> {
938
+ const segments = token.split('.');
939
+ if (segments.length !== 3) {
940
+ throw new Error('Invalid OAuth refresh token.');
941
+ }
942
+
943
+ const [encodedHeader, encodedClaims, signature] = segments;
944
+ const header = decodeJson<Record<string, unknown>>(encodedHeader);
945
+ if (header.alg !== 'HS256' || header.typ !== 'JWT') {
946
+ throw new Error('Invalid OAuth refresh token.');
947
+ }
948
+
949
+ const expectedSignature = await signTokenMaterial(
950
+ `${encodedHeader}.${encodedClaims}`,
951
+ );
952
+ if (
953
+ !timingSafeEqual(fromBase64Url(signature), fromBase64Url(expectedSignature))
954
+ ) {
955
+ throw new Error('Invalid OAuth refresh token.');
956
+ }
957
+
958
+ const claims = decodeJson<Record<string, unknown>>(encodedClaims);
959
+ if (!isValidOAuthRefreshClaimsShape(claims)) {
960
+ throw new Error('Invalid OAuth refresh token.');
961
+ }
962
+
963
+ const nowSeconds = Math.floor(nowMs / 1000);
964
+ if (claims.exp <= nowSeconds || claims.iat > nowSeconds + 30) {
965
+ throw new Error('OAuth refresh token expired.');
966
+ }
967
+
968
+ return claims;
969
+ }
970
+
971
+ /**
972
+ * Mints a short-lived capability for one product-notification delivery. The
973
+ * delivery id is part of the signed scope so the receiving route cannot use a
974
+ * valid token to read another org delivery or OAuth connection.
975
+ */
976
+ export async function mintProductNotificationDeliveryToken(input: {
977
+ orgId: string;
978
+ deliveryId: string;
979
+ nowMs?: number;
980
+ }): Promise<string> {
981
+ const issuedAt = Math.floor((input.nowMs ?? Date.now()) / 1000);
982
+ const claims: ProductNotificationDeliveryTokenClaims = {
983
+ v: INTERNAL_BOOTSTRAP_TOKEN_VERSION,
984
+ iss: INTERNAL_BOOTSTRAP_TOKEN_ISSUER,
985
+ aud: PRODUCT_NOTIFICATION_DELIVERY_AUDIENCE,
986
+ purpose: PRODUCT_NOTIFICATION_DELIVERY_PURPOSE,
987
+ org_id: input.orgId,
988
+ delivery_id: input.deliveryId,
989
+ sub: 'product_notification_dispatcher',
990
+ iat: issuedAt,
991
+ exp: issuedAt + INTERNAL_BOOTSTRAP_TOKEN_TTL_SECONDS,
992
+ };
993
+ const encodedHeader = encodeJson({ alg: 'HS256', typ: 'JWT' });
994
+ const encodedClaims = encodeJson(claims);
995
+ const signingInput = `${encodedHeader}.${encodedClaims}`;
996
+ const signature = await signTokenMaterial(signingInput);
997
+ return `${signingInput}.${signature}`;
998
+ }
999
+
1000
+ export async function verifyProductNotificationDeliveryToken(
1001
+ token: string,
1002
+ nowMs = Date.now(),
1003
+ ): Promise<ProductNotificationDeliveryTokenClaims> {
1004
+ const segments = token.split('.');
1005
+ if (segments.length !== 3) {
1006
+ throw new Error('Invalid product notification delivery token.');
1007
+ }
1008
+ const [encodedHeader, encodedClaims, signature] = segments;
1009
+ const header = decodeJson<Record<string, unknown>>(encodedHeader);
1010
+ if (header.alg !== 'HS256' || header.typ !== 'JWT') {
1011
+ throw new Error('Invalid product notification delivery token.');
1012
+ }
1013
+ const expectedSignature = await signTokenMaterial(
1014
+ `${encodedHeader}.${encodedClaims}`,
1015
+ );
1016
+ if (
1017
+ !timingSafeEqual(fromBase64Url(signature), fromBase64Url(expectedSignature))
1018
+ ) {
1019
+ throw new Error('Invalid product notification delivery token.');
1020
+ }
1021
+ const claims = decodeJson<Record<string, unknown>>(encodedClaims);
1022
+ if (
1023
+ claims.v !== INTERNAL_BOOTSTRAP_TOKEN_VERSION ||
1024
+ claims.iss !== INTERNAL_BOOTSTRAP_TOKEN_ISSUER ||
1025
+ claims.aud !== PRODUCT_NOTIFICATION_DELIVERY_AUDIENCE ||
1026
+ claims.purpose !== PRODUCT_NOTIFICATION_DELIVERY_PURPOSE ||
1027
+ claims.sub !== 'product_notification_dispatcher' ||
1028
+ typeof claims.org_id !== 'string' ||
1029
+ claims.org_id.length === 0 ||
1030
+ typeof claims.delivery_id !== 'string' ||
1031
+ claims.delivery_id.length === 0 ||
1032
+ typeof claims.iat !== 'number' ||
1033
+ !Number.isFinite(claims.iat) ||
1034
+ typeof claims.exp !== 'number' ||
1035
+ !Number.isFinite(claims.exp)
1036
+ ) {
1037
+ throw new Error('Invalid product notification delivery token.');
1038
+ }
1039
+ const nowSeconds = Math.floor(nowMs / 1000);
1040
+ if (claims.exp <= nowSeconds || claims.iat > nowSeconds + 30) {
1041
+ throw new Error('Product notification delivery token expired.');
1042
+ }
1043
+ return claims as ProductNotificationDeliveryTokenClaims;
1044
+ }
1045
+
1046
+ /**
1047
+ * Mints a durable, narrowly scoped opt-out command. It never expires because
1048
+ * an old notification must remain usable to stop future email; rotating the
1049
+ * application secret revokes every outstanding link.
1050
+ */
1051
+ export async function mintProductNotificationUnsubscribeToken(input: {
1052
+ orgId: string;
1053
+ groupKey: string;
1054
+ memberId: string;
1055
+ nowMs?: number;
1056
+ }): Promise<string> {
1057
+ const claims: ProductNotificationUnsubscribeTokenClaims = {
1058
+ v: INTERNAL_BOOTSTRAP_TOKEN_VERSION,
1059
+ iss: INTERNAL_BOOTSTRAP_TOKEN_ISSUER,
1060
+ aud: PRODUCT_NOTIFICATION_UNSUBSCRIBE_AUDIENCE,
1061
+ purpose: PRODUCT_NOTIFICATION_UNSUBSCRIBE_PURPOSE,
1062
+ org_id: input.orgId,
1063
+ group_key: input.groupKey,
1064
+ member_id: input.memberId,
1065
+ sub: 'product_notification_recipient',
1066
+ iat: Math.floor((input.nowMs ?? Date.now()) / 1000),
1067
+ };
1068
+ const encodedHeader = encodeJson({ alg: 'HS256', typ: 'JWT' });
1069
+ const encodedClaims = encodeJson(claims);
1070
+ const signingInput = `${encodedHeader}.${encodedClaims}`;
1071
+ return `${signingInput}.${await signTokenMaterial(signingInput)}`;
1072
+ }
1073
+
1074
+ export async function verifyProductNotificationUnsubscribeToken(
1075
+ token: string,
1076
+ nowMs = Date.now(),
1077
+ ): Promise<ProductNotificationUnsubscribeTokenClaims> {
1078
+ const segments = token.split('.');
1079
+ if (segments.length !== 3) {
1080
+ throw new Error('Invalid product notification unsubscribe token.');
1081
+ }
1082
+ const [encodedHeader, encodedClaims, signature] = segments;
1083
+ const header = decodeJson<Record<string, unknown>>(encodedHeader);
1084
+ if (header.alg !== 'HS256' || header.typ !== 'JWT') {
1085
+ throw new Error('Invalid product notification unsubscribe token.');
1086
+ }
1087
+ const expectedSignature = await signTokenMaterial(
1088
+ `${encodedHeader}.${encodedClaims}`,
1089
+ );
1090
+ if (
1091
+ !timingSafeEqual(fromBase64Url(signature), fromBase64Url(expectedSignature))
1092
+ ) {
1093
+ throw new Error('Invalid product notification unsubscribe token.');
1094
+ }
1095
+ const claims = decodeJson<Record<string, unknown>>(encodedClaims);
1096
+ if (
1097
+ claims.v !== INTERNAL_BOOTSTRAP_TOKEN_VERSION ||
1098
+ claims.iss !== INTERNAL_BOOTSTRAP_TOKEN_ISSUER ||
1099
+ claims.aud !== PRODUCT_NOTIFICATION_UNSUBSCRIBE_AUDIENCE ||
1100
+ claims.purpose !== PRODUCT_NOTIFICATION_UNSUBSCRIBE_PURPOSE ||
1101
+ claims.sub !== 'product_notification_recipient' ||
1102
+ typeof claims.org_id !== 'string' ||
1103
+ !claims.org_id ||
1104
+ typeof claims.group_key !== 'string' ||
1105
+ !claims.group_key ||
1106
+ typeof claims.member_id !== 'string' ||
1107
+ !claims.member_id ||
1108
+ typeof claims.iat !== 'number' ||
1109
+ !Number.isFinite(claims.iat) ||
1110
+ claims.iat > Math.floor(nowMs / 1000) + 30
1111
+ ) {
1112
+ throw new Error('Invalid product notification unsubscribe token.');
1113
+ }
1114
+ return claims as ProductNotificationUnsubscribeTokenClaims;
1115
+ }
1116
+
1117
+ export async function mintMonitorFleetDispatchToken(input: {
1118
+ orgId: string;
1119
+ operation: 'dispatch' | 'daily_sync';
1120
+ fleetId?: string;
1121
+ nowMs?: number;
1122
+ }): Promise<string> {
1123
+ const issuedAt = Math.floor((input.nowMs ?? Date.now()) / 1000);
1124
+ const claims: MonitorFleetDispatchTokenClaims = {
1125
+ v: INTERNAL_BOOTSTRAP_TOKEN_VERSION,
1126
+ iss: INTERNAL_BOOTSTRAP_TOKEN_ISSUER,
1127
+ aud: MONITOR_FLEET_DISPATCH_AUDIENCE,
1128
+ purpose: MONITOR_FLEET_DISPATCH_PURPOSE,
1129
+ org_id: input.orgId,
1130
+ operation: input.operation,
1131
+ ...(input.fleetId ? { fleet_id: input.fleetId } : {}),
1132
+ sub: 'monitor_fleet_dispatcher',
1133
+ iat: issuedAt,
1134
+ exp: issuedAt + INTERNAL_BOOTSTRAP_TOKEN_TTL_SECONDS,
1135
+ };
1136
+ const encodedHeader = encodeJson({ alg: 'HS256', typ: 'JWT' });
1137
+ const encodedClaims = encodeJson(claims);
1138
+ const signingInput = `${encodedHeader}.${encodedClaims}`;
1139
+ return `${signingInput}.${await signTokenMaterial(signingInput)}`;
1140
+ }
1141
+
1142
+ export async function verifyMonitorFleetDispatchToken(
1143
+ token: string,
1144
+ nowMs = Date.now(),
1145
+ ): Promise<MonitorFleetDispatchTokenClaims> {
1146
+ const segments = token.split('.');
1147
+ if (segments.length !== 3)
1148
+ throw new Error('Invalid monitor fleet dispatch token.');
1149
+ const [encodedHeader, encodedClaims, signature] = segments;
1150
+ const header = decodeJson<Record<string, unknown>>(encodedHeader);
1151
+ if (header.alg !== 'HS256' || header.typ !== 'JWT') {
1152
+ throw new Error('Invalid monitor fleet dispatch token.');
1153
+ }
1154
+ const expectedSignature = await signTokenMaterial(
1155
+ `${encodedHeader}.${encodedClaims}`,
1156
+ );
1157
+ if (
1158
+ !timingSafeEqual(fromBase64Url(signature), fromBase64Url(expectedSignature))
1159
+ ) {
1160
+ throw new Error('Invalid monitor fleet dispatch token.');
1161
+ }
1162
+ const claims = decodeJson<Record<string, unknown>>(encodedClaims);
1163
+ if (
1164
+ claims.v !== INTERNAL_BOOTSTRAP_TOKEN_VERSION ||
1165
+ claims.iss !== INTERNAL_BOOTSTRAP_TOKEN_ISSUER ||
1166
+ claims.aud !== MONITOR_FLEET_DISPATCH_AUDIENCE ||
1167
+ claims.purpose !== MONITOR_FLEET_DISPATCH_PURPOSE ||
1168
+ claims.sub !== 'monitor_fleet_dispatcher' ||
1169
+ typeof claims.org_id !== 'string' ||
1170
+ !claims.org_id ||
1171
+ !['dispatch', 'daily_sync'].includes(String(claims.operation)) ||
1172
+ (claims.fleet_id !== undefined && typeof claims.fleet_id !== 'string') ||
1173
+ typeof claims.iat !== 'number' ||
1174
+ !Number.isFinite(claims.iat) ||
1175
+ typeof claims.exp !== 'number' ||
1176
+ !Number.isFinite(claims.exp)
1177
+ ) {
1178
+ throw new Error('Invalid monitor fleet dispatch token.');
1179
+ }
1180
+ const nowSeconds = Math.floor(nowMs / 1000);
1181
+ if (claims.exp <= nowSeconds || claims.iat > nowSeconds + 30) {
1182
+ throw new Error('Monitor fleet dispatch token expired.');
1183
+ }
1184
+ return claims as MonitorFleetDispatchTokenClaims;
1185
+ }
1186
+
1187
+ export async function verifyIngestionInternalToken(
1188
+ token: string,
1189
+ nowMs = Date.now(),
1190
+ ): Promise<IngestionInternalTokenClaims> {
1191
+ const segments = token.split('.');
1192
+ if (segments.length !== 3) {
1193
+ throw new Error('Invalid ingestion token.');
1194
+ }
1195
+
1196
+ const [encodedHeader, encodedClaims, signature] = segments;
1197
+ const header = decodeJson<Record<string, unknown>>(encodedHeader);
1198
+ if (header.alg !== 'HS256' || header.typ !== 'JWT') {
1199
+ throw new Error('Invalid ingestion token.');
1200
+ }
1201
+
1202
+ const expectedSignature = await signTokenMaterial(
1203
+ `${encodedHeader}.${encodedClaims}`,
1204
+ );
1205
+ if (
1206
+ !timingSafeEqual(fromBase64Url(signature), fromBase64Url(expectedSignature))
1207
+ ) {
1208
+ throw new Error('Invalid ingestion token.');
1209
+ }
1210
+
1211
+ const claims = decodeJson<Record<string, unknown>>(encodedClaims);
1212
+ if (!isValidClaimsShape(claims)) {
1213
+ throw new Error('Invalid ingestion token.');
1214
+ }
1215
+
1216
+ if (claims.exp <= Math.floor(nowMs / 1000)) {
1217
+ throw new Error('Ingestion token expired.');
1218
+ }
1219
+
1220
+ return claims;
1221
+ }
1222
+
1223
+ export async function verifyWorkflowInternalToken(
1224
+ token: string,
1225
+ nowMs = Date.now(),
1226
+ ): Promise<WorkflowRunExecutionTokenClaims> {
1227
+ const { publicJwk } = await workflowExecutorKeyPair();
1228
+ return await verifyWorkflowInternalTokenWithJwks(
1229
+ token,
1230
+ { keys: [publicJwk] },
1231
+ nowMs,
1232
+ );
1233
+ }
1234
+
1235
+ export async function verifyWorkflowInternalTokenWithJwks(
1236
+ token: string,
1237
+ jwks: { keys: JWK[] },
1238
+ nowMs = Date.now(),
1239
+ ): Promise<WorkflowRunExecutionTokenClaims> {
1240
+ const headerKid = decodeJson<Record<string, unknown>>(
1241
+ token.split('.')[0] ?? '',
1242
+ ).kid;
1243
+ const jwk = jwks.keys.find((candidate) => candidate.kid === headerKid);
1244
+ if (!jwk) throw new Error('Invalid workflow token key.');
1245
+ const key = await importJWK(jwk, 'EdDSA');
1246
+ let claims: Record<string, unknown>;
1247
+ try {
1248
+ const verified = await jwtVerify(token, key, {
1249
+ algorithms: ['EdDSA'],
1250
+ issuer: INTERNAL_BOOTSTRAP_TOKEN_ISSUER,
1251
+ audience: INTERNAL_WORKFLOW_EXECUTION_AUDIENCE,
1252
+ currentDate: new Date(nowMs),
1253
+ typ: 'JWT',
1254
+ });
1255
+ claims = verified.payload as Record<string, unknown>;
1256
+ } catch (error) {
1257
+ if (
1258
+ error &&
1259
+ typeof error === 'object' &&
1260
+ 'code' in error &&
1261
+ (error as { code?: unknown }).code === 'ERR_JWT_EXPIRED'
1262
+ ) {
1263
+ throw new Error('Workflow token expired.');
1264
+ }
1265
+ throw error;
1266
+ }
1267
+ if (!isValidWorkflowClaimsShape(claims)) {
1268
+ throw new Error('Invalid workflow token.');
1269
+ }
1270
+
1271
+ const nowSeconds = Math.floor(nowMs / 1000);
1272
+ if (claims.exp <= nowSeconds || claims.iat > nowSeconds + 30) {
1273
+ throw new Error('Workflow token expired.');
1274
+ }
1275
+
1276
+ return claims;
1277
+ }
1278
+
1279
+ export async function verifyIngestionProviderSyncBootstrapToken(
1280
+ token: string,
1281
+ nowMs = Date.now(),
1282
+ ): Promise<IngestionProviderSyncBootstrapTokenClaims> {
1283
+ const claims = await verifyIngestionInternalToken(token, nowMs);
1284
+ if (claims.purpose !== INTERNAL_BOOTSTRAP_BOOTSTRAP_PURPOSE) {
1285
+ throw new Error('Invalid bootstrap token.');
1286
+ }
1287
+ return claims;
1288
+ }
1289
+
1290
+ export async function verifyIngestionSubscriptionExecutionToken(
1291
+ token: string,
1292
+ nowMs = Date.now(),
1293
+ ): Promise<IngestionSubscriptionExecutionTokenClaims> {
1294
+ const claims = await verifyIngestionInternalToken(token, nowMs);
1295
+ if (claims.purpose !== INTERNAL_BOOTSTRAP_EXECUTION_PURPOSE) {
1296
+ throw new Error('Invalid execution token.');
1297
+ }
1298
+ return claims;
1299
+ }
1300
+
1301
+ export async function verifyWorkflowRunExecutionToken(
1302
+ token: string,
1303
+ nowMs = Date.now(),
1304
+ ): Promise<WorkflowRunExecutionTokenClaims> {
1305
+ const claims = await verifyWorkflowInternalToken(token, nowMs);
1306
+ if (claims.purpose !== INTERNAL_WORKFLOW_EXECUTION_PURPOSE) {
1307
+ throw new Error('Invalid workflow execution token.');
1308
+ }
1309
+ return claims;
1310
+ }
1311
+
1312
+ export async function verifyWorkflowRunExecutionTokenWithJwks(
1313
+ token: string,
1314
+ jwks: { keys: JWK[] },
1315
+ nowMs = Date.now(),
1316
+ ): Promise<WorkflowRunExecutionTokenClaims> {
1317
+ const claims = await verifyWorkflowInternalTokenWithJwks(token, jwks, nowMs);
1318
+ if (claims.purpose !== INTERNAL_WORKFLOW_EXECUTION_PURPOSE) {
1319
+ throw new Error('Invalid workflow execution token.');
1320
+ }
1321
+ return claims;
1322
+ }