@pithy-sh/core 0.1.1 → 0.1.3

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 (393) hide show
  1. package/dist/address/address.d.ts +57 -0
  2. package/dist/address/address.d.ts.map +1 -0
  3. package/dist/address/address.js +71 -0
  4. package/dist/audit/auditEvent.d.ts +89 -0
  5. package/dist/audit/auditEvent.d.ts.map +1 -0
  6. package/dist/audit/auditEvent.js +69 -0
  7. package/dist/audit/recorder.d.ts +18 -0
  8. package/dist/audit/recorder.d.ts.map +1 -0
  9. package/dist/audit/recorder.js +8 -0
  10. package/dist/capability/bindings.d.ts +62 -0
  11. package/dist/capability/bindings.d.ts.map +1 -0
  12. package/dist/capability/bindings.js +97 -0
  13. package/dist/capability/capability.d.ts +501 -0
  14. package/dist/capability/capability.d.ts.map +1 -0
  15. package/dist/capability/capability.js +16 -0
  16. package/dist/capability/client.d.ts +70 -0
  17. package/dist/capability/client.d.ts.map +1 -0
  18. package/dist/capability/client.js +86 -0
  19. package/dist/capability/compose.d.ts +25 -0
  20. package/dist/capability/compose.d.ts.map +1 -0
  21. package/dist/capability/compose.js +54 -0
  22. package/dist/capability/composition.d.ts +26 -0
  23. package/dist/capability/composition.d.ts.map +1 -0
  24. package/dist/capability/composition.js +84 -0
  25. package/dist/capability/config.d.ts +7 -0
  26. package/dist/capability/config.d.ts.map +1 -0
  27. package/dist/capability/config.js +14 -0
  28. package/dist/capability/devSecret.d.ts +32 -0
  29. package/dist/capability/devSecret.d.ts.map +1 -0
  30. package/dist/capability/devSecret.js +28 -0
  31. package/dist/capability/manifest.d.ts +298 -0
  32. package/dist/capability/manifest.d.ts.map +1 -0
  33. package/dist/capability/manifest.js +356 -0
  34. package/dist/capability/secretOrigin.d.ts +121 -0
  35. package/dist/capability/secretOrigin.d.ts.map +1 -0
  36. package/dist/capability/secretOrigin.js +134 -0
  37. package/dist/capability/settings.d.ts +119 -0
  38. package/dist/capability/settings.d.ts.map +1 -0
  39. package/dist/capability/settings.js +66 -0
  40. package/dist/capability/validateBindings.d.ts +4 -0
  41. package/dist/capability/validateBindings.d.ts.map +1 -0
  42. package/dist/capability/validateBindings.js +34 -0
  43. package/dist/capability/vanishingKey.d.ts +31 -0
  44. package/dist/capability/vanishingKey.d.ts.map +1 -0
  45. package/dist/capability/vanishingKey.js +79 -0
  46. package/dist/controlPlane/audit/actions.d.ts +70 -0
  47. package/dist/controlPlane/audit/actions.d.ts.map +1 -0
  48. package/dist/controlPlane/audit/actions.js +76 -0
  49. package/dist/controlPlane/capability.d.ts +47 -0
  50. package/dist/controlPlane/capability.d.ts.map +1 -0
  51. package/dist/controlPlane/capability.js +138 -0
  52. package/dist/controlPlane/config/config.d.ts +19 -0
  53. package/dist/controlPlane/config/config.d.ts.map +1 -0
  54. package/dist/controlPlane/config/config.js +85 -0
  55. package/dist/controlPlane/context.d.ts +27 -0
  56. package/dist/controlPlane/context.d.ts.map +1 -0
  57. package/dist/controlPlane/context.js +29 -0
  58. package/dist/controlPlane/data/connection.d.ts +79 -0
  59. package/dist/controlPlane/data/connection.d.ts.map +1 -0
  60. package/dist/controlPlane/data/connection.js +55 -0
  61. package/dist/controlPlane/data/keyLifecycle.d.ts +62 -0
  62. package/dist/controlPlane/data/keyLifecycle.d.ts.map +1 -0
  63. package/dist/controlPlane/data/keyLifecycle.js +126 -0
  64. package/dist/controlPlane/data/replay.d.ts +18 -0
  65. package/dist/controlPlane/data/replay.d.ts.map +1 -0
  66. package/dist/controlPlane/data/replay.js +20 -0
  67. package/dist/controlPlane/data/tables.d.ts +34 -0
  68. package/dist/controlPlane/data/tables.d.ts.map +1 -0
  69. package/dist/controlPlane/data/tables.js +37 -0
  70. package/dist/controlPlane/discovery/adminRoute.d.ts +313 -0
  71. package/dist/controlPlane/discovery/adminRoute.d.ts.map +1 -0
  72. package/dist/controlPlane/discovery/adminRoute.js +136 -0
  73. package/dist/controlPlane/discovery/configuration.d.ts +108 -0
  74. package/dist/controlPlane/discovery/configuration.d.ts.map +1 -0
  75. package/dist/controlPlane/discovery/configuration.js +187 -0
  76. package/dist/controlPlane/discovery/drift.d.ts +64 -0
  77. package/dist/controlPlane/discovery/drift.d.ts.map +1 -0
  78. package/dist/controlPlane/discovery/drift.js +58 -0
  79. package/dist/controlPlane/discovery/health.d.ts +99 -0
  80. package/dist/controlPlane/discovery/health.d.ts.map +1 -0
  81. package/dist/controlPlane/discovery/health.js +151 -0
  82. package/dist/controlPlane/discovery/healthSummary.d.ts +236 -0
  83. package/dist/controlPlane/discovery/healthSummary.d.ts.map +1 -0
  84. package/dist/controlPlane/discovery/healthSummary.js +278 -0
  85. package/dist/controlPlane/error/errors.d.ts +67 -0
  86. package/dist/controlPlane/error/errors.d.ts.map +1 -0
  87. package/dist/controlPlane/error/errors.js +79 -0
  88. package/dist/controlPlane/http/cors.d.ts +112 -0
  89. package/dist/controlPlane/http/cors.d.ts.map +1 -0
  90. package/dist/controlPlane/http/cors.js +192 -0
  91. package/dist/controlPlane/http/guard.d.ts +77 -0
  92. package/dist/controlPlane/http/guard.d.ts.map +1 -0
  93. package/dist/controlPlane/http/guard.js +142 -0
  94. package/dist/controlPlane/http/handlers.d.ts +179 -0
  95. package/dist/controlPlane/http/handlers.d.ts.map +1 -0
  96. package/dist/controlPlane/http/handlers.js +245 -0
  97. package/dist/controlPlane/http/responses.d.ts +99 -0
  98. package/dist/controlPlane/http/responses.d.ts.map +1 -0
  99. package/dist/controlPlane/http/responses.js +63 -0
  100. package/dist/controlPlane/http/routes.d.ts +40 -0
  101. package/dist/controlPlane/http/routes.d.ts.map +1 -0
  102. package/dist/controlPlane/http/routes.js +83 -0
  103. package/dist/controlPlane/http/schemas.d.ts +39 -0
  104. package/dist/controlPlane/http/schemas.d.ts.map +1 -0
  105. package/dist/controlPlane/http/schemas.js +31 -0
  106. package/dist/controlPlane/http/verify.d.ts +84 -0
  107. package/dist/controlPlane/http/verify.d.ts.map +1 -0
  108. package/dist/controlPlane/http/verify.js +65 -0
  109. package/dist/controlPlane/migrations/0001_init.d.ts +51 -0
  110. package/dist/controlPlane/migrations/0001_init.d.ts.map +1 -0
  111. package/dist/controlPlane/migrations/0001_init.js +65 -0
  112. package/dist/controlPlane/replay/d1Guard.d.ts +29 -0
  113. package/dist/controlPlane/replay/d1Guard.d.ts.map +1 -0
  114. package/dist/controlPlane/replay/d1Guard.js +39 -0
  115. package/dist/controlPlane/replay/guard.d.ts +52 -0
  116. package/dist/controlPlane/replay/guard.d.ts.map +1 -0
  117. package/dist/controlPlane/replay/guard.js +0 -0
  118. package/dist/controlPlane/replay/kvGuard.d.ts +99 -0
  119. package/dist/controlPlane/replay/kvGuard.d.ts.map +1 -0
  120. package/dist/controlPlane/replay/kvGuard.js +101 -0
  121. package/dist/controlPlane/scope/scope.d.ts +68 -0
  122. package/dist/controlPlane/scope/scope.d.ts.map +1 -0
  123. package/dist/controlPlane/scope/scope.js +63 -0
  124. package/dist/controlPlane/token/base64url.d.ts +10 -0
  125. package/dist/controlPlane/token/base64url.d.ts.map +1 -0
  126. package/dist/controlPlane/token/base64url.js +48 -0
  127. package/dist/controlPlane/token/claims.d.ts +83 -0
  128. package/dist/controlPlane/token/claims.d.ts.map +1 -0
  129. package/dist/controlPlane/token/claims.js +86 -0
  130. package/dist/controlPlane/token/digest.d.ts +44 -0
  131. package/dist/controlPlane/token/digest.d.ts.map +1 -0
  132. package/dist/controlPlane/token/digest.js +56 -0
  133. package/dist/controlPlane/token/jws.d.ts +52 -0
  134. package/dist/controlPlane/token/jws.d.ts.map +1 -0
  135. package/dist/controlPlane/token/jws.js +65 -0
  136. package/dist/controlPlane/token/mint.d.ts +56 -0
  137. package/dist/controlPlane/token/mint.d.ts.map +1 -0
  138. package/dist/controlPlane/token/mint.js +55 -0
  139. package/dist/controlPlane/wire.d.ts +122 -0
  140. package/dist/controlPlane/wire.d.ts.map +1 -0
  141. package/dist/controlPlane/wire.js +123 -0
  142. package/dist/createBackend.d.ts +58 -0
  143. package/dist/createBackend.d.ts.map +1 -0
  144. package/dist/createBackend.js +170 -0
  145. package/dist/createEntrypoint.d.ts +53 -0
  146. package/dist/createEntrypoint.d.ts.map +1 -0
  147. package/dist/createEntrypoint.js +52 -0
  148. package/dist/data/boundParameters.d.ts +83 -0
  149. package/dist/data/boundParameters.d.ts.map +1 -0
  150. package/dist/data/boundParameters.js +162 -0
  151. package/dist/data/codecs.d.ts +34 -0
  152. package/dist/data/codecs.d.ts.map +1 -0
  153. package/dist/data/codecs.js +135 -0
  154. package/dist/data/cursor.d.ts +81 -0
  155. package/dist/data/cursor.d.ts.map +1 -0
  156. package/dist/data/cursor.js +104 -0
  157. package/dist/data/databases.d.ts +55 -0
  158. package/dist/data/databases.d.ts.map +1 -0
  159. package/dist/data/databases.js +32 -0
  160. package/dist/data/db.d.ts +42 -0
  161. package/dist/data/db.d.ts.map +1 -0
  162. package/dist/data/db.js +24 -0
  163. package/dist/data/withD1Retry.d.ts +91 -0
  164. package/dist/data/withD1Retry.d.ts.map +1 -0
  165. package/dist/data/withD1Retry.js +152 -0
  166. package/dist/entitlement/entitlement.d.ts +118 -0
  167. package/dist/entitlement/entitlement.d.ts.map +1 -0
  168. package/dist/entitlement/entitlement.js +59 -0
  169. package/dist/entitlement/gateScan.d.ts +26 -0
  170. package/dist/entitlement/gateScan.d.ts.map +1 -0
  171. package/dist/entitlement/gateScan.js +96 -0
  172. package/dist/entitlement/require.d.ts +12 -0
  173. package/dist/entitlement/require.d.ts.map +1 -0
  174. package/dist/entitlement/require.js +123 -0
  175. package/dist/env/ambient.d.ts +52 -0
  176. package/dist/env/ambient.d.ts.map +1 -0
  177. package/dist/env/ambient.js +35 -0
  178. package/dist/env/ci.d.ts +36 -0
  179. package/dist/env/ci.d.ts.map +1 -0
  180. package/dist/env/ci.js +40 -0
  181. package/dist/env/stem.d.ts +23 -0
  182. package/dist/env/stem.d.ts.map +1 -0
  183. package/dist/env/stem.js +29 -0
  184. package/dist/error/cause.d.ts +143 -0
  185. package/dist/error/cause.d.ts.map +1 -0
  186. package/dist/error/cause.js +192 -0
  187. package/dist/error/client.d.ts +35 -0
  188. package/dist/error/client.d.ts.map +1 -0
  189. package/dist/error/client.js +38 -0
  190. package/dist/error/extend.d.ts +101 -0
  191. package/dist/error/extend.d.ts.map +1 -0
  192. package/dist/error/extend.js +76 -0
  193. package/dist/error/http.d.ts +1516 -0
  194. package/dist/error/http.d.ts.map +1 -0
  195. package/dist/error/http.js +72 -0
  196. package/dist/error/payload.d.ts +3034 -0
  197. package/dist/error/payload.d.ts.map +1 -0
  198. package/dist/error/payload.js +1110 -0
  199. package/dist/error/pithyError.d.ts +154 -0
  200. package/dist/error/pithyError.d.ts.map +1 -0
  201. package/dist/error/pithyError.js +197 -0
  202. package/dist/error/terminal.d.ts +28 -0
  203. package/dist/error/terminal.d.ts.map +1 -0
  204. package/dist/error/terminal.js +33 -0
  205. package/dist/http/authContext.d.ts +21 -0
  206. package/dist/http/authContext.d.ts.map +1 -0
  207. package/dist/http/authContext.js +23 -0
  208. package/dist/http/routeContract.d.ts +39 -0
  209. package/dist/http/routeContract.d.ts.map +1 -0
  210. package/dist/http/routeContract.js +82 -0
  211. package/dist/http/sameOrigin.d.ts +47 -0
  212. package/dist/http/sameOrigin.d.ts.map +1 -0
  213. package/dist/http/sameOrigin.js +24 -0
  214. package/dist/http/signedWebhook.d.ts +225 -0
  215. package/dist/http/signedWebhook.d.ts.map +1 -0
  216. package/dist/http/signedWebhook.js +247 -0
  217. package/dist/http/validation.d.ts +37 -0
  218. package/dist/http/validation.d.ts.map +1 -0
  219. package/dist/http/validation.js +14 -0
  220. package/dist/http/verification.d.ts +10 -0
  221. package/dist/http/verification.d.ts.map +1 -0
  222. package/dist/http/verification.js +17 -0
  223. package/dist/i18n/acceptLanguage.d.ts +22 -0
  224. package/dist/i18n/acceptLanguage.d.ts.map +1 -0
  225. package/dist/i18n/acceptLanguage.js +55 -0
  226. package/dist/i18n/catalog.d.ts +39 -0
  227. package/dist/i18n/catalog.d.ts.map +1 -0
  228. package/dist/i18n/catalog.js +57 -0
  229. package/dist/i18n/locale.d.ts +74 -0
  230. package/dist/i18n/locale.d.ts.map +1 -0
  231. package/dist/i18n/locale.js +109 -0
  232. package/dist/i18n/localeMarker.d.ts +29 -0
  233. package/dist/i18n/localeMarker.d.ts.map +1 -0
  234. package/dist/i18n/localeMarker.js +112 -0
  235. package/dist/i18n/match.d.ts +26 -0
  236. package/dist/i18n/match.d.ts.map +1 -0
  237. package/dist/i18n/match.js +88 -0
  238. package/dist/i18n/registry.d.ts +25 -0
  239. package/dist/i18n/registry.d.ts.map +1 -0
  240. package/dist/i18n/registry.js +51 -0
  241. package/dist/i18n/translator.d.ts +107 -0
  242. package/dist/i18n/translator.d.ts.map +1 -0
  243. package/dist/i18n/translator.js +80 -0
  244. package/dist/index.d.ts +32 -0
  245. package/dist/index.d.ts.map +1 -0
  246. package/dist/index.js +17 -0
  247. package/dist/kv/kv.d.ts +166 -0
  248. package/dist/kv/kv.d.ts.map +1 -0
  249. package/dist/kv/kv.js +261 -0
  250. package/dist/kv/namespaces.d.ts +58 -0
  251. package/dist/kv/namespaces.d.ts.map +1 -0
  252. package/dist/kv/namespaces.js +40 -0
  253. package/dist/logger/local.d.ts +41 -0
  254. package/dist/logger/local.d.ts.map +1 -0
  255. package/dist/logger/local.js +45 -0
  256. package/dist/logger/logger.d.ts +56 -0
  257. package/dist/logger/logger.d.ts.map +1 -0
  258. package/dist/logger/logger.js +83 -0
  259. package/dist/logger/record.d.ts +919 -0
  260. package/dist/logger/record.d.ts.map +1 -0
  261. package/dist/logger/record.js +79 -0
  262. package/dist/logger/worker.d.ts +74 -0
  263. package/dist/logger/worker.d.ts.map +1 -0
  264. package/dist/logger/worker.js +54 -0
  265. package/dist/migrations/batch.d.ts +10 -0
  266. package/dist/migrations/batch.d.ts.map +1 -0
  267. package/dist/migrations/batch.js +193 -0
  268. package/dist/migrations/bookkeeping.d.ts +24 -0
  269. package/dist/migrations/bookkeeping.d.ts.map +1 -0
  270. package/dist/migrations/bookkeeping.js +57 -0
  271. package/dist/migrations/owner.d.ts +41 -0
  272. package/dist/migrations/owner.d.ts.map +1 -0
  273. package/dist/migrations/owner.js +105 -0
  274. package/dist/migrations/registry.d.ts +36 -0
  275. package/dist/migrations/registry.d.ts.map +1 -0
  276. package/dist/migrations/registry.js +70 -0
  277. package/dist/migrations/runner.d.ts +67 -0
  278. package/dist/migrations/runner.d.ts.map +1 -0
  279. package/dist/migrations/runner.js +218 -0
  280. package/dist/naming/domains.d.ts +108 -0
  281. package/dist/naming/domains.d.ts.map +1 -0
  282. package/dist/naming/domains.js +156 -0
  283. package/dist/naming/environment.d.ts +118 -0
  284. package/dist/naming/environment.d.ts.map +1 -0
  285. package/dist/naming/environment.js +190 -0
  286. package/dist/naming/feature.d.ts +68 -0
  287. package/dist/naming/feature.d.ts.map +1 -0
  288. package/dist/naming/feature.js +106 -0
  289. package/dist/naming/limits.d.ts +229 -0
  290. package/dist/naming/limits.d.ts.map +1 -0
  291. package/dist/naming/limits.js +187 -0
  292. package/dist/naming/provisionScope.d.ts +91 -0
  293. package/dist/naming/provisionScope.d.ts.map +1 -0
  294. package/dist/naming/provisionScope.js +65 -0
  295. package/dist/naming/resource.d.ts +169 -0
  296. package/dist/naming/resource.d.ts.map +1 -0
  297. package/dist/naming/resource.js +220 -0
  298. package/dist/naming/resourceNames.d.ts +83 -0
  299. package/dist/naming/resourceNames.d.ts.map +1 -0
  300. package/dist/naming/resourceNames.js +102 -0
  301. package/dist/naming/segment.d.ts +29 -0
  302. package/dist/naming/segment.d.ts.map +1 -0
  303. package/dist/naming/segment.js +31 -0
  304. package/dist/projection/asRead.d.ts +72 -0
  305. package/dist/projection/asRead.d.ts.map +1 -0
  306. package/dist/projection/asRead.js +169 -0
  307. package/dist/projection/published.d.ts +95 -0
  308. package/dist/projection/published.d.ts.map +1 -0
  309. package/dist/projection/published.js +114 -0
  310. package/dist/schema/describedness.d.ts +55 -0
  311. package/dist/schema/describedness.d.ts.map +1 -0
  312. package/dist/schema/describedness.js +180 -0
  313. package/dist/seed/compose.d.ts +41 -0
  314. package/dist/seed/compose.d.ts.map +1 -0
  315. package/dist/seed/compose.js +51 -0
  316. package/dist/seed/devLogin.d.ts +50 -0
  317. package/dist/seed/devLogin.d.ts.map +1 -0
  318. package/dist/seed/devLogin.js +52 -0
  319. package/dist/seed/exampleIdentities.d.ts +58 -0
  320. package/dist/seed/exampleIdentities.d.ts.map +1 -0
  321. package/dist/seed/exampleIdentities.js +26 -0
  322. package/dist/seed/metadata.d.ts +20 -0
  323. package/dist/seed/metadata.d.ts.map +1 -0
  324. package/dist/seed/metadata.js +27 -0
  325. package/dist/seed/seed.d.ts +264 -0
  326. package/dist/seed/seed.d.ts.map +1 -0
  327. package/dist/seed/seed.js +37 -0
  328. package/dist/seed/seededRows.d.ts +26 -0
  329. package/dist/seed/seededRows.d.ts.map +1 -0
  330. package/dist/seed/seededRows.js +20 -0
  331. package/dist/seed/writeD1.d.ts +43 -0
  332. package/dist/seed/writeD1.d.ts.map +1 -0
  333. package/dist/seed/writeD1.js +73 -0
  334. package/dist/seed/writeKv.d.ts +29 -0
  335. package/dist/seed/writeKv.d.ts.map +1 -0
  336. package/dist/seed/writeKv.js +63 -0
  337. package/dist/semver/semver.d.ts +62 -0
  338. package/dist/semver/semver.d.ts.map +1 -0
  339. package/dist/semver/semver.js +137 -0
  340. package/dist/text/comments.d.ts +62 -0
  341. package/dist/text/comments.d.ts.map +1 -0
  342. package/dist/text/comments.js +161 -0
  343. package/dist/version.generated.d.ts +5 -0
  344. package/dist/version.generated.d.ts.map +1 -0
  345. package/dist/version.generated.js +7 -0
  346. package/dist/worker/health.d.ts +39 -0
  347. package/dist/worker/health.d.ts.map +1 -0
  348. package/dist/worker/health.js +41 -0
  349. package/dist/worker/identity.d.ts +191 -0
  350. package/dist/worker/identity.d.ts.map +1 -0
  351. package/dist/worker/identity.js +162 -0
  352. package/dist/workflow/bindings.d.ts +41 -0
  353. package/dist/workflow/bindings.d.ts.map +1 -0
  354. package/dist/workflow/bindings.js +53 -0
  355. package/dist/workflow/dispatch.d.ts +98 -0
  356. package/dist/workflow/dispatch.d.ts.map +1 -0
  357. package/dist/workflow/dispatch.js +148 -0
  358. package/dist/workflow/dispatchRoute.d.ts +80 -0
  359. package/dist/workflow/dispatchRoute.d.ts.map +1 -0
  360. package/dist/workflow/dispatchRoute.js +134 -0
  361. package/dist/workflow/faults.d.ts +97 -0
  362. package/dist/workflow/faults.d.ts.map +1 -0
  363. package/dist/workflow/faults.js +111 -0
  364. package/dist/workflow/host.d.ts +176 -0
  365. package/dist/workflow/host.d.ts.map +1 -0
  366. package/dist/workflow/host.js +116 -0
  367. package/dist/workflow/hostEntry.d.ts +50 -0
  368. package/dist/workflow/hostEntry.d.ts.map +1 -0
  369. package/dist/workflow/hostEntry.js +24 -0
  370. package/dist/workflow/hostEnv.d.ts +143 -0
  371. package/dist/workflow/hostEnv.d.ts.map +1 -0
  372. package/dist/workflow/hostEnv.js +170 -0
  373. package/dist/workflow/loopback.d.ts +52 -0
  374. package/dist/workflow/loopback.d.ts.map +1 -0
  375. package/dist/workflow/loopback.js +76 -0
  376. package/dist/workflow/naming.d.ts +79 -0
  377. package/dist/workflow/naming.d.ts.map +1 -0
  378. package/dist/workflow/naming.js +138 -0
  379. package/dist/workflow/register.d.ts +19 -0
  380. package/dist/workflow/register.d.ts.map +1 -0
  381. package/dist/workflow/register.js +40 -0
  382. package/dist/workflow/schemas.d.ts +44 -0
  383. package/dist/workflow/schemas.d.ts.map +1 -0
  384. package/dist/workflow/schemas.js +43 -0
  385. package/dist/workflow/spec.d.ts +81 -0
  386. package/dist/workflow/spec.d.ts.map +1 -0
  387. package/dist/workflow/spec.js +0 -0
  388. package/dist/workflow/stepMessage.d.ts +122 -0
  389. package/dist/workflow/stepMessage.d.ts.map +1 -0
  390. package/dist/workflow/stepMessage.js +152 -0
  391. package/package.json +20 -9
  392. package/src/capability/manifest.ts +21 -0
  393. package/src/version.generated.ts +1 -1
@@ -0,0 +1,192 @@
1
+ import "../../worker/identity.js";
2
+ import { CONTROL_PLANE_HEADER, CONTROL_PLANE_VERSION_CREATED_HEADER, CONTROL_PLANE_VERSION_HEADER } from "../wire.js";
3
+ import "../../naming/environment.js";
4
+ //#region src/controlPlane/http/cors.ts
5
+ /**
6
+ * CORS for the control-plane surface — the one thing standing between a browser and a Worker that is
7
+ * up, healthy, and answering.
8
+ *
9
+ * **Why this exists at all.** A control-plane call carries its token on {@link CONTROL_PLANE_HEADER},
10
+ * which is not a CORS-safelisted request header, so a browser sends `OPTIONS` first and refuses to send
11
+ * the real request until something answers it. Nothing did. The failure surfaced as a `TypeError` from
12
+ * `fetch` naming the host, so a reachable Worker read as an unreachable one — and the browser-direct
13
+ * path is the whole reason the seam is shaped this way, since the alternative is proxying an adopter's
14
+ * data through a management client's origin.
15
+ *
16
+ * **The allow-list is static config and never a connection row.** A preflight is the one request on
17
+ * this surface that is answered before any credential is read: there is nothing to authenticate it
18
+ * with, because the browser sends it before it will send the token. Consulting D1 would therefore turn
19
+ * it into an oracle over which origins are registered, answerable by anyone. So the list is
20
+ * `[issuer, ...allowedOrigins]` off the parsed config, computed once at composition, and this module
21
+ * reads no database.
22
+ *
23
+ * **An unlisted origin is refused by omission.** It gets the same `204` and the same empty body as an
24
+ * allowed one, with no `Access-Control-Allow-Origin` — the browser blocks the read, and the response
25
+ * says nothing about what the list contains. A `403` here would be more honest as an API and would also
26
+ * be the oracle above, so it is deliberately not one; the caller already sees a clear console error.
27
+ *
28
+ * **No `hono/cors`.** `worker-safety.test.ts` freezes the import allowlist and the middleware is not on
29
+ * it (`docs/STACK.md` notes the ReDoS advisory against it). It also cannot express either of the two
30
+ * rules above — a refusal that is byte-identical to an acceptance, and credentials that are never
31
+ * allowed — so hand-rolling costs about forty lines and buys the behavior the seam actually needs.
32
+ */
33
+ /** The hostnames a browser reaches a developer's own machine at, and the only ones §dev will allow. */
34
+ const LOOPBACK_HOSTS = /* @__PURE__ */ new Set([
35
+ "localhost",
36
+ "127.0.0.1",
37
+ "[::1]"
38
+ ]);
39
+ /**
40
+ * Whether this request may use the **local dev** allowance: a loopback origin, in the dev environment.
41
+ *
42
+ * A console an adopter runs against a local Worker sits on `http://localhost:<port>`, on a port the dev
43
+ * port allocator picked. Nobody can write that into `allowedOrigins` and have it stay true across
44
+ * checkouts, so in dev the seam simply allows this machine.
45
+ *
46
+ * **Two guards: `ENVIRONMENT` is `dev`, and the hostname is loopback.** Take the first for what it is
47
+ * — a var the adopter stamps, and `pithy init` writes `dev` into the top-level `wrangler.jsonc` stanza
48
+ * (`cli/src/project/workerScaffold.ts`) that a bare `pithy deploy` publishes. So this is not a claim
49
+ * that the allowance cannot appear on a deployed Worker.
50
+ *
51
+ * It is a claim about what the allowance is *worth*, and that holds wherever it appears. The origins it
52
+ * admits name the caller's **own machine**. No ambient authority crosses, because
53
+ * `Access-Control-Allow-Credentials` is never set on this surface and the token is an explicit header.
54
+ * And every admin route still demands a verified control-plane token. So the most this permits is a
55
+ * local page reading a reply it could have fetched directly, which is not a capability anyone gains.
56
+ *
57
+ * **Do not reach for `<STEM>_ORIGIN` here.** It looks like the precise version of this and it does not
58
+ * work: `pithy dev` publishes those into each child *process*, and the host environment does not cross
59
+ * into workerd — `cli/src/dev/env.ts:157-165`. Only capability *hosts* are forwarded as `--var`
60
+ * (`hostVarArgs`), deliberately (#410), and a UI worker is not one. So a Worker's `env` never carries
61
+ * the console's address, and a version of this that read one allowed exactly the origins no browser
62
+ * calls while reading as though it did more.
63
+ *
64
+ * It is a per-request read rather than part of {@link allowedOriginSet} because in Workers there is no
65
+ * `env` until a request arrives.
66
+ */
67
+ function devLoopbackAllows(env, origin) {
68
+ if (env["ENVIRONMENT"] !== "dev") return false;
69
+ try {
70
+ return LOOPBACK_HOSTS.has(new URL(origin).hostname);
71
+ } catch {
72
+ return false;
73
+ }
74
+ }
75
+ /** The two response headers a client must be allowed to read off an admin response, as one value. */
76
+ const EXPOSE_HEADERS = `${CONTROL_PLANE_VERSION_HEADER}, ${CONTROL_PLANE_VERSION_CREATED_HEADER}`;
77
+ /**
78
+ * One origin, normalized the way a browser spells it, or `null` when the value is not one.
79
+ *
80
+ * Applied to both ends deliberately. `issuer` is a `z.url()` that predates this module and may legally
81
+ * carry a path or a trailing slash, and an `Access-Control-Allow-Origin` carrying either matches no
82
+ * browser `Origin` — so it is normalized rather than trusted. The incoming header is normalized for the
83
+ * same reason from the other direction: it is not ours, and comparing raw strings would turn a spelling
84
+ * difference into a refusal nobody can diagnose.
85
+ */
86
+ function originOf(value) {
87
+ if (!value) return null;
88
+ try {
89
+ const origin = new URL(value).origin;
90
+ return origin === "null" ? null : origin;
91
+ } catch {
92
+ return null;
93
+ }
94
+ }
95
+ /**
96
+ * The effective allow-list: `[issuer, ...allowedOrigins]`, normalized, then deduped.
97
+ *
98
+ * Additive in that order and never replacing, which is the property the whole shape rests on: an
99
+ * adopter who adds their own console cannot silently drop the dashboard that was already working, and
100
+ * a replacing default would have done exactly that the first time someone wrote a single entry.
101
+ * Dedupe happens **after** normalization, so `https://app.pithy.sh` and `https://app.pithy.sh/` are one
102
+ * entry rather than two.
103
+ */
104
+ function allowedOriginSet(config) {
105
+ const origins = /* @__PURE__ */ new Set();
106
+ for (const value of [config.issuer, ...config.allowedOrigins]) {
107
+ const origin = originOf(value);
108
+ if (origin !== null) origins.add(origin);
109
+ }
110
+ return origins;
111
+ }
112
+ /**
113
+ * Fold every composed capability's `adminRoutes` into one entry per distinct path.
114
+ *
115
+ * **The descriptors are the surface.** They already exist, a management client already navigates by
116
+ * them, and `routeContract.test.ts` already proves they agree with the mounted router — so deriving
117
+ * CORS from the same table means a capability that adds an admin route gets its preflight for free and
118
+ * there is no second list to drift. The alternative considered and rejected was an app-wide
119
+ * `app.use("*", …)`, which would attach dashboard origins to the adopter's own API — a different
120
+ * surface, with different origins, that is not the kit's decision to make.
121
+ *
122
+ * **`content-type` keys on the method, not on whether the route takes a body.** {@link AdminRoute}
123
+ * carries no body flag, and adding one is worse than the proxy it would replace: that type is read
124
+ * across a version boundary by the management client, so a new field must be `.optional()` and an
125
+ * absent optional says less than the method already does.
126
+ */
127
+ function corsSurface(capabilities) {
128
+ const methodsByPath = /* @__PURE__ */ new Map();
129
+ for (const capability of capabilities) for (const route of capability.adminRoutes ?? []) {
130
+ const methods = methodsByPath.get(route.path) ?? /* @__PURE__ */ new Set();
131
+ methods.add(route.method);
132
+ methodsByPath.set(route.path, methods);
133
+ }
134
+ return [...methodsByPath.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([path, methods]) => {
135
+ const carriesBody = [...methods].some((method) => method !== "GET");
136
+ return {
137
+ path,
138
+ allowMethods: [...methods, "OPTIONS"].join(", "),
139
+ allowHeaders: carriesBody ? `${CONTROL_PLANE_HEADER}, content-type` : CONTROL_PLANE_HEADER
140
+ };
141
+ });
142
+ }
143
+ /**
144
+ * The per-path middleware: answer the preflight, and mark the real response readable.
145
+ *
146
+ * Both halves are here rather than in an `app.options(…)` of their own, because a preflight that is
147
+ * allowed and a response that cannot be read is the same failure as no preflight at all — the browser
148
+ * blocks a `GET` whose response carries no `Access-Control-Allow-Origin`, `204` or not.
149
+ */
150
+ function corsMiddleware(entry, origins, maxAgeSeconds) {
151
+ return async (c, next) => {
152
+ c.header("Vary", "Origin", { append: true });
153
+ const origin = originOf(c.req.raw.headers.get("origin"));
154
+ const allowed = origin !== null && (origins.has(origin) || devLoopbackAllows(c.env, origin));
155
+ if (allowed) c.header("Access-Control-Allow-Origin", origin);
156
+ if (c.req.method === "OPTIONS") {
157
+ if (allowed) {
158
+ c.header("Access-Control-Allow-Methods", entry.allowMethods);
159
+ c.header("Access-Control-Allow-Headers", entry.allowHeaders);
160
+ c.header("Access-Control-Max-Age", String(maxAgeSeconds));
161
+ }
162
+ return c.body(null, 204);
163
+ }
164
+ c.header("Access-Control-Expose-Headers", EXPOSE_HEADERS);
165
+ await next();
166
+ };
167
+ }
168
+ /**
169
+ * Mount one `app.use(path, …)` per entry.
170
+ *
171
+ * **Called from the capability's `middleware` hook, never its `routes` hook, and that is load-bearing.**
172
+ * `createBackend` runs every capability's `middleware` hook before **any** capability's `routes` hook
173
+ * (`createBackend.ts:262-267`), which is what makes this independent of the order an adopter happens to
174
+ * list their capabilities in.
175
+ *
176
+ * From `routes` it looks like it works and does not. The preflight still answers — an `OPTIONS` has no
177
+ * route handler to lose to, so this `use` is the only thing that matches it — but a capability composed
178
+ * *before* the seam has already registered its terminal handler, and a terminal handler never calls
179
+ * `next()`, so the middleware after it never runs. The real response then carries no
180
+ * `Access-Control-Allow-Origin` and the browser blocks a `200` the caller was entitled to read: a
181
+ * preflight that passes and a read that fails, which is a worse failure than the one this fixes because
182
+ * it looks configured. `cors.test.ts` §"the registration slot" pins it by composing the seam last.
183
+ *
184
+ * Exact paths, never a wildcard: `app.use` on a literal path does not match subpaths, so "nothing
185
+ * outside the control-plane surface gains a CORS header" is structural rather than a check that could
186
+ * be got wrong.
187
+ */
188
+ function registerControlPlaneCors(app, surface, origins, maxAgeSeconds) {
189
+ for (const entry of surface) app.use(entry.path, corsMiddleware(entry, origins, maxAgeSeconds));
190
+ }
191
+ //#endregion
192
+ export { allowedOriginSet, corsMiddleware, corsSurface, devLoopbackAllows, originOf, registerControlPlaneCors };
@@ -0,0 +1,77 @@
1
+ import type { MiddlewareHandler } from "hono";
2
+ import type { PithyHonoEnv } from "../../capability/capability";
3
+ import type { ControlPlaneContext } from "../context";
4
+ import type { ControlPlaneRequirement } from "../scope/scope";
5
+ import { type ControlPlaneVerifyDeps } from "./verify";
6
+ /**
7
+ * `requireControlPlane(scope)` — the gate every control-plane route wears, including the ones
8
+ * capabilities contribute.
9
+ *
10
+ * ## Why a verifier on the request, and not a configured factory
11
+ *
12
+ * `@pithy-sh/payments` writes `requireControlPlane(PAYMENTS_ENTITLEMENT_GRANT_SCOPE)` at module scope. It cannot
13
+ * hold the seam's config — that is the adopter's, resolved when they compose `controlplane()` — and it
14
+ * must not import anything from a sibling package. So the seam contributes one middleware that puts a
15
+ * {@link ControlPlaneVerifier} on the request, and the gate below consumes it. A capability depends on
16
+ * a core seam and nothing else, which is principle 4 doing exactly what it exists for.
17
+ *
18
+ * ## It fails closed, and that is the point
19
+ *
20
+ * With no `controlplane()` composed there is no verifier, and every gate raises
21
+ * `controlplane/not_connected`. A capability that contributes admin routes into a Worker that never
22
+ * enabled the seam therefore denies them, rather than leaving them open because the thing meant to
23
+ * protect them is absent. **A package that imports its authorization from elsewhere must deny when
24
+ * that elsewhere is missing** — the same reason `requireAuth` is copied per capability rather than
25
+ * imported from `@pithy-sh/auth`.
26
+ *
27
+ * ## The requirement is mandatory
28
+ *
29
+ * There is no zero-argument form. An optional scope is a thing you can forget, and forgetting it on an
30
+ * admin route ships an unscoped one; requiring the argument means the only way to write a route with no
31
+ * authorization is to name `ANY_VERIFIED_CALLER` out loud, which is as deliberate as naming a scope.
32
+ */
33
+ /**
34
+ * Verify one call against this Worker's registrations. Contributed by the seam's middleware, consumed
35
+ * by {@link requireControlPlane}, and absent when the seam is not composed.
36
+ */
37
+ export type ControlPlaneVerifier = (request: Request, requirement: ControlPlaneRequirement) => Promise<ControlPlaneContext>;
38
+ /**
39
+ * Build the verifier the seam's middleware publishes. Split out from the middleware so the wiring is
40
+ * testable without a Worker, and so `verify.ts` stays free of Hono.
41
+ */
42
+ export declare function createControlPlaneVerifier(deps: ControlPlaneVerifyDeps): ControlPlaneVerifier;
43
+ /**
44
+ * What this Worker may say about its own build, as headers — everything the binding handed it, and
45
+ * nothing else.
46
+ *
47
+ * A function rather than two lines in the middleware because this is where the seam's one invariant
48
+ * lives and one invariant wants one place to be gated: **every value here was handed to this Worker by
49
+ * the platform, byte for byte.** Nothing is defaulted, nothing is computed at request time, and a field
50
+ * the binding did not carry produces no header at all. That last clause is the load-bearing one — this
51
+ * pair is what a management client invalidates a rendered pane on, and an empty header is a value, so
52
+ * "cannot say" has to arrive as silence rather than as an empty string a client can compare.
53
+ *
54
+ * The tag stays off the wire. It is read by the identity reader and answers neither of the two
55
+ * questions a client asks, and this header crosses to a management client on adopter-authored text.
56
+ */
57
+ export declare function workerVersionHeaders(env: unknown): Record<string, string>;
58
+ /**
59
+ * Require a verified control-plane caller carrying `requirement`.
60
+ *
61
+ * Goes **before** any validator on the route line. A validator ahead of the gate turns a 401 into a
62
+ * 400 and tells an unauthenticated caller which requests were well-formed — on this seam that is a
63
+ * live oracle, letting someone with no credential at all probe the shape of key registration.
64
+ */
65
+ /**
66
+ * The brand every {@link requireControlPlane} gate carries, so a mounted router can be read back.
67
+ *
68
+ * A middleware is an opaque function to Hono, so "which routes are control-plane routes" is knowable
69
+ * only if the gate says so about itself. `undeclaredAdminRoutes` needs exactly that: it walks the
70
+ * router looking for guarded routes nobody declared, and without a mark it would have to guess from
71
+ * path shape. `Symbol.for` so the check still works across duplicated copies of this module.
72
+ */
73
+ export declare const CONTROL_PLANE_GUARD: unique symbol;
74
+ /** Whether a mounted handler is a control-plane gate. */
75
+ export declare function isControlPlaneGuard(handler: unknown): boolean;
76
+ export declare function requireControlPlane(requirement: ControlPlaneRequirement): MiddlewareHandler<PithyHonoEnv>;
77
+ //# sourceMappingURL=guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/http/guard.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAIhE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAE9D,OAAO,EAAE,KAAK,sBAAsB,EAA0B,MAAM,UAAU,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,CACjC,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,uBAAuB,KACjC,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAElC;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,sBAAsB,GAAG,oBAAoB,CAgB7F;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMzE;AAED;;;;;;GAMG;AACH;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,eAAwC,CAAC;AAEzE,yDAAyD;AACzD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAE7D;AAED,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,uBAAuB,GAAG,iBAAiB,CAAC,YAAY,CAAC,CAkEzG"}
@@ -0,0 +1,142 @@
1
+ import { PithyError } from "../../error/pithyError.js";
2
+ import { workerVersionMetadata } from "../../worker/identity.js";
3
+ import { ControlPlaneAuditActions, safeEmit } from "../audit/actions.js";
4
+ import { ControlPlaneNotConnectedError } from "../error/errors.js";
5
+ import { CONTROL_PLANE_VERSION_CREATED_HEADER, CONTROL_PLANE_VERSION_HEADER } from "../wire.js";
6
+ import { verifyControlPlaneCall } from "./verify.js";
7
+ //#region src/controlPlane/http/guard.ts
8
+ /**
9
+ * Build the verifier the seam's middleware publishes. Split out from the middleware so the wiring is
10
+ * testable without a Worker, and so `verify.ts` stays free of Hono.
11
+ */
12
+ function createControlPlaneVerifier(deps) {
13
+ return async (request, requirement) => {
14
+ const body = new Uint8Array(await request.clone().arrayBuffer());
15
+ return await verifyControlPlaneCall({
16
+ token: request.headers.get("pithy-control-plane") ?? void 0,
17
+ body,
18
+ requirement
19
+ }, deps);
20
+ };
21
+ }
22
+ /**
23
+ * What this Worker may say about its own build, as headers — everything the binding handed it, and
24
+ * nothing else.
25
+ *
26
+ * A function rather than two lines in the middleware because this is where the seam's one invariant
27
+ * lives and one invariant wants one place to be gated: **every value here was handed to this Worker by
28
+ * the platform, byte for byte.** Nothing is defaulted, nothing is computed at request time, and a field
29
+ * the binding did not carry produces no header at all. That last clause is the load-bearing one — this
30
+ * pair is what a management client invalidates a rendered pane on, and an empty header is a value, so
31
+ * "cannot say" has to arrive as silence rather than as an empty string a client can compare.
32
+ *
33
+ * The tag stays off the wire. It is read by the identity reader and answers neither of the two
34
+ * questions a client asks, and this header crosses to a management client on adopter-authored text.
35
+ */
36
+ function workerVersionHeaders(env) {
37
+ const meta = workerVersionMetadata(env);
38
+ const headers = {};
39
+ if (meta.id !== null) headers[CONTROL_PLANE_VERSION_HEADER] = meta.id;
40
+ if (meta.createdAt !== null) headers[CONTROL_PLANE_VERSION_CREATED_HEADER] = meta.createdAt;
41
+ return headers;
42
+ }
43
+ /**
44
+ * Require a verified control-plane caller carrying `requirement`.
45
+ *
46
+ * Goes **before** any validator on the route line. A validator ahead of the gate turns a 401 into a
47
+ * 400 and tells an unauthenticated caller which requests were well-formed — on this seam that is a
48
+ * live oracle, letting someone with no credential at all probe the shape of key registration.
49
+ */
50
+ /**
51
+ * The brand every {@link requireControlPlane} gate carries, so a mounted router can be read back.
52
+ *
53
+ * A middleware is an opaque function to Hono, so "which routes are control-plane routes" is knowable
54
+ * only if the gate says so about itself. `undeclaredAdminRoutes` needs exactly that: it walks the
55
+ * router looking for guarded routes nobody declared, and without a mark it would have to guess from
56
+ * path shape. `Symbol.for` so the check still works across duplicated copies of this module.
57
+ */
58
+ const CONTROL_PLANE_GUARD = Symbol.for("pithy.controlPlaneGuard");
59
+ /** Whether a mounted handler is a control-plane gate. */
60
+ function isControlPlaneGuard(handler) {
61
+ return typeof handler === "function" && handler[CONTROL_PLANE_GUARD] === true;
62
+ }
63
+ function requireControlPlane(requirement) {
64
+ const guard = async (c, next) => {
65
+ for (const [name, value] of Object.entries(workerVersionHeaders(c.env))) c.header(name, value);
66
+ const verify = c.var.controlPlaneVerifier;
67
+ if (!verify) {
68
+ const denial = new ControlPlaneNotConnectedError({
69
+ message: "This deployment does not accept management-client calls.",
70
+ action: "Add `controlplane()` to this Worker's pithy.config.ts, then run pithy dashboard connect.",
71
+ detail: "no control-plane verifier on the request — the controlplane capability is not composed"
72
+ });
73
+ await auditDenial(c, requirement, denial);
74
+ throw denial;
75
+ }
76
+ let context;
77
+ try {
78
+ context = await verify(c.req.raw, requirement);
79
+ } catch (cause) {
80
+ await auditDenial(c, requirement, cause);
81
+ throw cause;
82
+ }
83
+ c.set("controlPlane", context);
84
+ await safeEmit(c.var.emit, {
85
+ action: ControlPlaneAuditActions.callAllowed,
86
+ outcome: "success",
87
+ actorType: "control-plane",
88
+ actorId: context.subject,
89
+ resourceType: "controlplane_connection",
90
+ resourceId: context.connectionId,
91
+ requestId: c.req.header("cf-ray"),
92
+ ip: c.req.header("cf-connecting-ip"),
93
+ userAgent: c.req.header("user-agent"),
94
+ metadata: {
95
+ connectionId: context.connectionId,
96
+ connectionEnvironment: context.environment,
97
+ scope: context.scope,
98
+ keyId: context.keyId,
99
+ method: c.req.method,
100
+ path: c.req.path
101
+ }
102
+ }, c.var.log);
103
+ await next();
104
+ };
105
+ guard[CONTROL_PLANE_GUARD] = true;
106
+ return guard;
107
+ }
108
+ /**
109
+ * Record a refused call.
110
+ *
111
+ * **Denials are audited, not only successes.** `denied` is a first-class outcome precisely for this, and
112
+ * this is the surface where an unaudited blocked attempt is least acceptable — a run of them is what a
113
+ * credential being probed looks like, and it is invisible if only the successes are written down.
114
+ *
115
+ * The reason lands in `metadata.reason` from the error's `code`, never from its `detail`: `detail` names
116
+ * the exact verification step, and the audit trail is queryable and long-lived. The step belongs in the
117
+ * log, which is bounded and already carries the whole payload.
118
+ *
119
+ * Nothing here can turn a correct denial into a 500 — `safeEmit` swallows, and the caller rethrows the
120
+ * original error regardless.
121
+ */
122
+ async function auditDenial(c, requirement, cause) {
123
+ const code = cause instanceof PithyError ? cause.payload.code : "core/internal";
124
+ await safeEmit(c.var.emit, {
125
+ action: ControlPlaneAuditActions.callDenied,
126
+ outcome: "denied",
127
+ severity: "warning",
128
+ actorType: "control-plane",
129
+ actorId: null,
130
+ requestId: c.req.header("cf-ray"),
131
+ ip: c.req.header("cf-connecting-ip"),
132
+ userAgent: c.req.header("user-agent"),
133
+ metadata: {
134
+ reason: code,
135
+ required: String(requirement),
136
+ method: c.req.method,
137
+ path: c.req.path
138
+ }
139
+ }, c.var.log);
140
+ }
141
+ //#endregion
142
+ export { CONTROL_PLANE_GUARD, createControlPlaneVerifier, isControlPlaneGuard, requireControlPlane, workerVersionHeaders };
@@ -0,0 +1,179 @@
1
+ import type { Context } from "hono";
2
+ import type { PithyHonoEnv } from "../../capability/capability";
3
+ import type { ControlPlaneConfig } from "../config/config";
4
+ import { type ControlPlaneDatabase } from "../data/tables";
5
+ import type { CapabilityDeclaration } from "../discovery/adminRoute";
6
+ import { type CapabilityHealthSource } from "../discovery/health";
7
+ import type { ExpireKeyParams, ExpireKeyRequest, RegisterKeyRequest } from "./schemas";
8
+ /** The pieces of the composed capability a handler needs. Passed in, so nothing reaches for a global. */
9
+ export interface ControlPlaneHandlerDeps {
10
+ /** The resolved seam config — pruning bounds, mostly. */
11
+ config: ControlPlaneConfig;
12
+ /** The app database, resolved per request from the `DB` binding. */
13
+ database: (c: Context<PithyHonoEnv>) => ControlPlaneDatabase;
14
+ /**
15
+ * The names of every capability this Worker composed, captured by the capability's `compose` hook at
16
+ * assembly. A thunk because that hook runs after the routes closure is built.
17
+ *
18
+ * This is assembly-time knowledge no capability has about its siblings, and it is what makes
19
+ * **discovery over configuration** work: a management client builds its navigation *and its calls*
20
+ * from what a Worker declares, so a Worker without payments simply has no purchases pane, and
21
+ * `pithy add support` produces a working support pane with nothing for either side to configure.
22
+ */
23
+ composedCapabilities: () => readonly CapabilityDeclaration[];
24
+ /**
25
+ * The health summary each capability contributes, by capability name — captured by the same `compose`
26
+ * hook, and checked there against the scopes that capability's own routes require.
27
+ *
28
+ * Separate from {@link composedCapabilities} because a declaration is the same for every caller and a
29
+ * value is not: the numbers are resolved per request, behind the scope each one is declared under.
30
+ */
31
+ composedHealth: () => ReadonlyMap<string, CapabilityHealthSource>;
32
+ /** The clock, injected so a test can stand at any instant. */
33
+ now: () => Date;
34
+ }
35
+ /**
36
+ * `GET /control-plane/ping` — connectivity and key proof.
37
+ *
38
+ * The most important route here despite doing nothing, because it is what a management client calls to
39
+ * prove a newly registered key **before** the key it replaces is expired. Requires a verified caller
40
+ * and no scope: a connection granted nothing must still be able to prove a key, or rotation has a state
41
+ * it cannot get out of.
42
+ *
43
+ * It echoes the `keyId` that verified the call, so the client can confirm *which* key answered rather
44
+ * than inferring it from a 200.
45
+ */
46
+ export declare function pingHandler(deps: ControlPlaneHandlerDeps): (c: Context<PithyHonoEnv>) => Response & import("hono").TypedResponse<{
47
+ status: "ok";
48
+ connectionId: string;
49
+ environment: string;
50
+ keyId: string;
51
+ now: string;
52
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">;
53
+ /**
54
+ * `GET /control-plane/manifest` — what this Worker is, what it composes, and how each part is doing.
55
+ *
56
+ * Discovery over configuration. A client builds its navigation from this rather than from settings
57
+ * someone maintains, so a Worker with no payments capability has no purchases pane as a matter of fact.
58
+ *
59
+ * **And the numbers a client would otherwise pay a second round trip for** (#317). Each capability's
60
+ * bounded summary is resolved here, behind the scope that capability's own read is behind: a connection
61
+ * without it gets no number rather than a zero, and never costs the adopter's Worker the query.
62
+ *
63
+ * **One capability's failure costs one capability's number** (#350). A producer that throws, or that
64
+ * reports something its declaration cannot name, lands on `unavailable` for that entry alone —
65
+ * `readCapabilityHealth` catches it, so nothing rejects, so `Promise.all` still resolves and every
66
+ * sibling still reports. This route is what the whole management surface is built from; a manifest that
67
+ * 500s takes every pane with it, and the capability at fault is the one thing the screen could not then
68
+ * name.
69
+ */
70
+ export declare function manifestHandler(deps: ControlPlaneHandlerDeps): (c: Context<PithyHonoEnv>) => Promise<Response & import("hono").TypedResponse<{
71
+ environment: string;
72
+ connectionId: string;
73
+ version: string | null;
74
+ capabilities: {
75
+ health: import("../..").HealthSummary | null;
76
+ healthUnavailable: boolean;
77
+ name: string;
78
+ version: string | null;
79
+ adminRoutes: {
80
+ method: "DELETE" | "GET" | "PATCH" | "POST" | "PUT";
81
+ path: string;
82
+ scope: string | null;
83
+ summary: string;
84
+ }[];
85
+ healthKeys: {
86
+ key: string;
87
+ kind: "count" | "state";
88
+ states: string[] | null;
89
+ scope: string;
90
+ cost: "indexed" | "memory";
91
+ summary: string;
92
+ nominal: string[] | {
93
+ atMost?: number | undefined;
94
+ atLeast?: number | undefined;
95
+ } | null;
96
+ }[];
97
+ configKeys: {
98
+ key: string;
99
+ choices: string[] | null;
100
+ summary: string;
101
+ }[];
102
+ config: {
103
+ [x: string]: string | number | boolean;
104
+ };
105
+ }[];
106
+ grantedScopes: string[];
107
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">>;
108
+ /** `GET /control-plane/keys` — the registration state, so a client can surface a stale key. */
109
+ export declare function listKeysHandler(deps: ControlPlaneHandlerDeps): (c: Context<PithyHonoEnv>) => Promise<Response & import("hono").TypedResponse<{
110
+ connectionId: string;
111
+ environment: string;
112
+ keys: {
113
+ keyId: string;
114
+ publicKey: {
115
+ kty: "OKP";
116
+ crv: "Ed25519";
117
+ x: string;
118
+ };
119
+ validFrom: string;
120
+ validUntil: string | null;
121
+ revokedAt: string | null;
122
+ }[];
123
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">>;
124
+ /**
125
+ * `POST /control-plane/keys` — register a new public key, authenticated by the key it succeeds.
126
+ *
127
+ * This is the route the whole rotation story rests on, and its authentication is the subtle part: the
128
+ * request is signed with the key being replaced. That is what makes rotation self-bootstrapping —
129
+ * trust flows forward from existing trust, exactly as a rotated refresh token does — and what makes an
130
+ * unreachable Worker a deferral rather than a lockout.
131
+ *
132
+ * It appends and only appends. The new key's window opens now and no existing key's window moves.
133
+ */
134
+ export declare function registerKeyHandler(deps: ControlPlaneHandlerDeps): (c: Context<PithyHonoEnv>, body: RegisterKeyRequest) => Promise<Response & import("hono").TypedResponse<{
135
+ keyId: string;
136
+ validFrom: string;
137
+ keys: {
138
+ keyId: string;
139
+ publicKey: {
140
+ kty: "OKP";
141
+ crv: "Ed25519";
142
+ x: string;
143
+ };
144
+ validFrom: string;
145
+ validUntil: string | null;
146
+ revokedAt: string | null;
147
+ }[];
148
+ }, 201, "json">>;
149
+ /**
150
+ * `POST /control-plane/keys/:keyId/expire` — retire a superseded key, once its replacement is proven.
151
+ *
152
+ * A separate call from registration, deliberately. Folding them into one would make append-and-expire
153
+ * atomic, and atomic is precisely wrong here: the point is that the old key survives until the new one
154
+ * has been proven against a live ping. `expireKey` enforces both halves of that — the named successor
155
+ * must currently verify, and the expiry must not empty the connection's live set.
156
+ *
157
+ * **And the successor must be the key that signed this very request.** That is what "proof by use"
158
+ * means, and checking only that `provenKeyId` names *some* live key would not deliver it: a client
159
+ * still signing with the old key could name a successor it has never successfully used and retire the
160
+ * one thing that currently works. The client asserts which key it believes proved itself, the seam
161
+ * compares that against which key actually did, and a client that is wrong about its own rotation gets
162
+ * a 409 instead of a lockout.
163
+ */
164
+ export declare function expireKeyHandler(deps: ControlPlaneHandlerDeps): (c: Context<PithyHonoEnv>, params: ExpireKeyParams, body: ExpireKeyRequest) => Promise<Response & import("hono").TypedResponse<{
165
+ keyId: string;
166
+ validUntil: string;
167
+ keys: {
168
+ keyId: string;
169
+ publicKey: {
170
+ kty: "OKP";
171
+ crv: "Ed25519";
172
+ x: string;
173
+ };
174
+ validFrom: string;
175
+ validUntil: string | null;
176
+ revokedAt: string | null;
177
+ }[];
178
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">>;
179
+ //# sourceMappingURL=handlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/http/handlers.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAGhE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAI3D,OAAO,EAAmC,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC5F,OAAO,KAAK,EAAE,qBAAqB,EAA4B,MAAM,yBAAyB,CAAC;AAC/F,OAAO,EAAE,KAAK,sBAAsB,EAAwB,MAAM,qBAAqB,CAAC;AAUxF,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAuBvF,yGAAyG;AACzG,MAAM,WAAW,uBAAuB;IACtC,yDAAyD;IACzD,MAAM,EAAE,kBAAkB,CAAC;IAC3B,oEAAoE;IACpE,QAAQ,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,oBAAoB,CAAC;IAC7D;;;;;;;;OAQG;IACH,oBAAoB,EAAE,MAAM,SAAS,qBAAqB,EAAE,CAAC;IAC7D;;;;;;OAMG;IACH,cAAc,EAAE,MAAM,WAAW,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IAClE,8DAA8D;IAC9D,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AAgED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,uBAAuB,OAC5C,OAAO,CAAC,YAAY,CAAC;;;;;;kEAUjC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,uBAAuB,OAC1C,OAAO,CAAC,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mEAyBvC;AAED,+FAA+F;AAC/F,wBAAgB,eAAe,CAAC,IAAI,EAAE,uBAAuB,OAC1C,OAAO,CAAC,YAAY,CAAC;;;;;;;;;;;;;;mEASvC;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,uBAAuB,OAC7C,OAAO,CAAC,YAAY,CAAC,QAAQ,kBAAkB;;;;;;;;;;;;;;iBAoDjE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,OAC3C,OAAO,CAAC,YAAY,CAAC,UAAU,eAAe,QAAQ,gBAAgB;;;;;;;;;;;;;;mEA8CxF"}