@pithy-sh/core 0.1.2 → 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,245 @@
1
+ import { workerVersion } from "../../worker/identity.js";
2
+ import { ControlPlaneConnection } from "../data/connection.js";
3
+ import { CONTROL_PLANE_CONNECTIONS_TABLE } from "../data/tables.js";
4
+ import { healthWire } from "../discovery/healthSummary.js";
5
+ import { readCapabilityHealth } from "../discovery/health.js";
6
+ import { ControlPlaneAuditActions, safeEmit } from "../audit/actions.js";
7
+ import { ControlPlaneInvalidCredentialError, ControlPlaneKeyConflictError } from "../error/errors.js";
8
+ import { appendKey, expireKey, pruneKeys } from "../data/keyLifecycle.js";
9
+ //#region src/controlPlane/http/handlers.ts
10
+ /**
11
+ * The seam's own route handlers.
12
+ *
13
+ * Every one runs behind `requireControlPlane`, so `c.var.controlPlane` is set by the time any of them
14
+ * is entered. They read it rather than re-deriving anything: the verification decision is made once,
15
+ * in `verify.ts`, and a handler that second-guessed it would be a second place for the rules to drift.
16
+ *
17
+ * **Nothing here returns a key's private half, because none is ever held.** `GET /control-plane/keys`
18
+ * returns public keys and their windows, which is exactly what a management client needs to see that a
19
+ * key is ageing and exactly what an attacker learns nothing from.
20
+ *
21
+ * **Every response has an exported schema**, in `responses.ts`, and each `c.json` below is
22
+ * `satisfies`-checked against it. A management client imports the same object and validates with it
23
+ * rather than hand-writing a mirror that drifts. The check is at compile time on purpose: parsing every
24
+ * response would spend a validation pass on values this Worker just built, and would turn a shape
25
+ * mistake into a 500 in production rather than a red build.
26
+ */
27
+ /** A day, in ms — `keyRetentionDays` is expressed in days because that is how anyone reasons about it. */
28
+ const MS_PER_DAY = 864e5;
29
+ /** The verified caller. Never null here — `requireControlPlane` ran first, or this code is unreachable. */
30
+ function caller(c) {
31
+ const context = c.var.controlPlane;
32
+ if (!context) throw new ControlPlaneInvalidCredentialError({ detail: "handler reached with no verified control-plane caller" });
33
+ return context;
34
+ }
35
+ /** Load the connection this call authenticated against. It verified moments ago, so it exists. */
36
+ async function loadConnection(db, connectionId) {
37
+ const row = await db.selectFrom(CONTROL_PLANE_CONNECTIONS_TABLE).selectAll().where("id", "=", connectionId).executeTakeFirst();
38
+ if (!row) throw new ControlPlaneInvalidCredentialError({ detail: `connection ${connectionId} disappeared mid-request` });
39
+ return { connection: ControlPlaneConnection.parse(row) };
40
+ }
41
+ /** Write a connection's key array back, stamping `updatedAt`. One UPDATE — a rotation cannot half-apply. */
42
+ async function saveKeys(db, connectionId, keys, now) {
43
+ const encoded = ControlPlaneConnection.shape.keys.encode([...keys]);
44
+ await db.updateTable(CONTROL_PLANE_CONNECTIONS_TABLE).set({
45
+ keys: encoded,
46
+ updatedAt: ControlPlaneConnection.shape.updatedAt.encode(now)
47
+ }).where("id", "=", connectionId).execute();
48
+ }
49
+ /**
50
+ * The public view of one registered key. Windows and ages, never anything secret.
51
+ *
52
+ * The return type is `z.output` of the exported schema, so what this sends and what a management
53
+ * client validates against are one declaration rather than two that drift. It was
54
+ * `Record<string, unknown>`, which is not a contract at all.
55
+ */
56
+ function publicKeyView(key) {
57
+ return {
58
+ keyId: key.keyId,
59
+ publicKey: key.publicKey,
60
+ validFrom: key.validFrom.toISOString(),
61
+ validUntil: key.validUntil?.toISOString() ?? null,
62
+ revokedAt: key.revokedAt?.toISOString() ?? null
63
+ };
64
+ }
65
+ /**
66
+ * `GET /control-plane/ping` — connectivity and key proof.
67
+ *
68
+ * The most important route here despite doing nothing, because it is what a management client calls to
69
+ * prove a newly registered key **before** the key it replaces is expired. Requires a verified caller
70
+ * and no scope: a connection granted nothing must still be able to prove a key, or rotation has a state
71
+ * it cannot get out of.
72
+ *
73
+ * It echoes the `keyId` that verified the call, so the client can confirm *which* key answered rather
74
+ * than inferring it from a 200.
75
+ */
76
+ function pingHandler(deps) {
77
+ return (c) => {
78
+ const context = caller(c);
79
+ return c.json({
80
+ status: "ok",
81
+ connectionId: context.connectionId,
82
+ environment: context.environment,
83
+ keyId: context.keyId,
84
+ now: deps.now().toISOString()
85
+ });
86
+ };
87
+ }
88
+ /**
89
+ * `GET /control-plane/manifest` — what this Worker is, what it composes, and how each part is doing.
90
+ *
91
+ * Discovery over configuration. A client builds its navigation from this rather than from settings
92
+ * someone maintains, so a Worker with no payments capability has no purchases pane as a matter of fact.
93
+ *
94
+ * **And the numbers a client would otherwise pay a second round trip for** (#317). Each capability's
95
+ * bounded summary is resolved here, behind the scope that capability's own read is behind: a connection
96
+ * without it gets no number rather than a zero, and never costs the adopter's Worker the query.
97
+ *
98
+ * **One capability's failure costs one capability's number** (#350). A producer that throws, or that
99
+ * reports something its declaration cannot name, lands on `unavailable` for that entry alone —
100
+ * `readCapabilityHealth` catches it, so nothing rejects, so `Promise.all` still resolves and every
101
+ * sibling still reports. This route is what the whole management surface is built from; a manifest that
102
+ * 500s takes every pane with it, and the capability at fault is the one thing the screen could not then
103
+ * name.
104
+ */
105
+ function manifestHandler(deps) {
106
+ return async (c) => {
107
+ const context = caller(c);
108
+ const sources = deps.composedHealth();
109
+ const capabilities = await Promise.all(deps.composedCapabilities().map(async (declaration) => ({
110
+ ...declaration,
111
+ ...healthWire(await readCapabilityHealth(sources.get(declaration.name), context.grantedScopes, (source) => source.read(c)))
112
+ })));
113
+ return c.json({
114
+ environment: context.environment,
115
+ connectionId: context.connectionId,
116
+ version: workerVersion(c.env),
117
+ capabilities,
118
+ grantedScopes: [...context.grantedScopes]
119
+ });
120
+ };
121
+ }
122
+ /** `GET /control-plane/keys` — the registration state, so a client can surface a stale key. */
123
+ function listKeysHandler(deps) {
124
+ return async (c) => {
125
+ const context = caller(c);
126
+ const { connection } = await loadConnection(deps.database(c), context.connectionId);
127
+ return c.json({
128
+ connectionId: connection.id,
129
+ environment: connection.environment,
130
+ keys: connection.keys.map(publicKeyView)
131
+ });
132
+ };
133
+ }
134
+ /**
135
+ * `POST /control-plane/keys` — register a new public key, authenticated by the key it succeeds.
136
+ *
137
+ * This is the route the whole rotation story rests on, and its authentication is the subtle part: the
138
+ * request is signed with the key being replaced. That is what makes rotation self-bootstrapping —
139
+ * trust flows forward from existing trust, exactly as a rotated refresh token does — and what makes an
140
+ * unreachable Worker a deferral rather than a lockout.
141
+ *
142
+ * It appends and only appends. The new key's window opens now and no existing key's window moves.
143
+ */
144
+ function registerKeyHandler(deps) {
145
+ return async (c, body) => {
146
+ const context = caller(c);
147
+ const db = deps.database(c);
148
+ const now = deps.now();
149
+ const { connection } = await loadConnection(db, context.connectionId);
150
+ const appended = appendKey(connection.keys, {
151
+ keyId: body.keyId,
152
+ publicKey: body.publicKey,
153
+ validFrom: now,
154
+ validUntil: null,
155
+ revokedAt: null
156
+ }, now);
157
+ const pruned = pruneKeys(appended, {
158
+ now,
159
+ retentionMs: deps.config.keyRetentionDays * MS_PER_DAY,
160
+ maxKeys: deps.config.maxKeys
161
+ });
162
+ await saveKeys(db, connection.id, pruned, now);
163
+ await safeEmit(c.var.emit, {
164
+ action: ControlPlaneAuditActions.keyRegistered,
165
+ outcome: "success",
166
+ severity: "warning",
167
+ actorType: "control-plane",
168
+ actorId: context.subject,
169
+ resourceType: "controlplane_connection",
170
+ resourceId: connection.id,
171
+ requestId: c.req.header("cf-ray"),
172
+ ip: c.req.header("cf-connecting-ip"),
173
+ userAgent: c.req.header("user-agent"),
174
+ metadata: {
175
+ connectionId: connection.id,
176
+ registeredKeyId: body.keyId,
177
+ signedWithKeyId: context.keyId,
178
+ connectionEnvironment: connection.environment
179
+ }
180
+ }, c.var.log);
181
+ return c.json({
182
+ keyId: body.keyId,
183
+ validFrom: now.toISOString(),
184
+ keys: pruned.map(publicKeyView)
185
+ }, 201);
186
+ };
187
+ }
188
+ /**
189
+ * `POST /control-plane/keys/:keyId/expire` — retire a superseded key, once its replacement is proven.
190
+ *
191
+ * A separate call from registration, deliberately. Folding them into one would make append-and-expire
192
+ * atomic, and atomic is precisely wrong here: the point is that the old key survives until the new one
193
+ * has been proven against a live ping. `expireKey` enforces both halves of that — the named successor
194
+ * must currently verify, and the expiry must not empty the connection's live set.
195
+ *
196
+ * **And the successor must be the key that signed this very request.** That is what "proof by use"
197
+ * means, and checking only that `provenKeyId` names *some* live key would not deliver it: a client
198
+ * still signing with the old key could name a successor it has never successfully used and retire the
199
+ * one thing that currently works. The client asserts which key it believes proved itself, the seam
200
+ * compares that against which key actually did, and a client that is wrong about its own rotation gets
201
+ * a 409 instead of a lockout.
202
+ */
203
+ function expireKeyHandler(deps) {
204
+ return async (c, params, body) => {
205
+ const context = caller(c);
206
+ const db = deps.database(c);
207
+ const now = deps.now();
208
+ const { connection } = await loadConnection(db, context.connectionId);
209
+ if (body.provenKeyId !== context.keyId) throw new ControlPlaneKeyConflictError({
210
+ message: "Sign this request with the replacement key you are naming as proven.",
211
+ action: "Rotate to the new key, make a call with it, then expire the old one in that same call.",
212
+ detail: `provenKeyId ${body.provenKeyId} is not the key that signed this request (${context.keyId})`
213
+ });
214
+ const expired = expireKey(connection.keys, params.keyId, {
215
+ provenKeyId: body.provenKeyId,
216
+ now
217
+ });
218
+ await saveKeys(db, connection.id, expired, now);
219
+ await safeEmit(c.var.emit, {
220
+ action: ControlPlaneAuditActions.keyExpired,
221
+ outcome: "success",
222
+ severity: "warning",
223
+ actorType: "control-plane",
224
+ actorId: context.subject,
225
+ resourceType: "controlplane_connection",
226
+ resourceId: connection.id,
227
+ requestId: c.req.header("cf-ray"),
228
+ ip: c.req.header("cf-connecting-ip"),
229
+ userAgent: c.req.header("user-agent"),
230
+ metadata: {
231
+ connectionId: connection.id,
232
+ expiredKeyId: params.keyId,
233
+ provenKeyId: body.provenKeyId,
234
+ connectionEnvironment: connection.environment
235
+ }
236
+ }, c.var.log);
237
+ return c.json({
238
+ keyId: params.keyId,
239
+ validUntil: now.toISOString(),
240
+ keys: expired.map(publicKeyView)
241
+ });
242
+ };
243
+ }
244
+ //#endregion
245
+ export { expireKeyHandler, listKeysHandler, manifestHandler, pingHandler, registerKeyHandler };
@@ -0,0 +1,99 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * What the seam's own routes return, as Zod objects a management client can validate against.
4
+ *
5
+ * `schemas.ts` bounds what a caller may send; this file states what it gets back. Both halves are
6
+ * runtime values for the same reason: a management client reading a customer's Worker is crossing a
7
+ * trust boundary and must validate what comes back, and a TypeScript interface is erased before it
8
+ * can help. `GET /control-plane/manifest` already had a schema — {@link ControlPlaneManifest}, in
9
+ * `discovery/adminRoute.ts`, because it is the discovery contract rather than a projection — and the
10
+ * other four routes had none at all, `publicKeyView` returning a bare `Record<string, unknown>`.
11
+ *
12
+ * **No codecs, and no transform anywhere in this file.** These describe JSON on the wire, so parsing
13
+ * one hands back exactly what went in — which is what lets a test compare the parsed value with a live
14
+ * response and fail on a field either side forgot. A `JsonDate` here would decode an ISO string into a
15
+ * `Date` and make that comparison meaningless.
16
+ *
17
+ * **Nothing here can carry a private key, because none is ever held.** The connection stores public
18
+ * halves only, so `keys` is exactly what a client needs to see a key ageing and exactly what an
19
+ * attacker learns nothing from.
20
+ */
21
+ /** One registered key as a client sees it: the public half and its window. */
22
+ export declare const PublicKeyView: z.ZodObject<{
23
+ keyId: z.ZodString;
24
+ publicKey: z.ZodObject<{
25
+ kty: z.ZodLiteral<"OKP">;
26
+ crv: z.ZodLiteral<"Ed25519">;
27
+ x: z.ZodString;
28
+ }, z.core.$strip>;
29
+ validFrom: z.ZodISODateTime;
30
+ validUntil: z.ZodNullable<z.ZodISODateTime>;
31
+ revokedAt: z.ZodNullable<z.ZodISODateTime>;
32
+ }, z.core.$strip>;
33
+ export type PublicKeyView = z.output<typeof PublicKeyView>;
34
+ /**
35
+ * `GET /control-plane/ping`.
36
+ *
37
+ * It echoes the `keyId` that verified the call, so a client rotating a key can confirm *which* key
38
+ * answered rather than inferring it from a 200 — which is the whole point of the route.
39
+ */
40
+ export declare const ControlPlanePingResponse: z.ZodObject<{
41
+ status: z.ZodLiteral<"ok">;
42
+ connectionId: z.ZodString;
43
+ environment: z.ZodString;
44
+ keyId: z.ZodString;
45
+ now: z.ZodISODateTime;
46
+ }, z.core.$strip>;
47
+ export type ControlPlanePingResponse = z.output<typeof ControlPlanePingResponse>;
48
+ /** `GET /control-plane/keys` — the registration state, so a client can surface a stale key. */
49
+ export declare const ControlPlaneKeysResponse: z.ZodObject<{
50
+ connectionId: z.ZodString;
51
+ environment: z.ZodString;
52
+ keys: z.ZodArray<z.ZodObject<{
53
+ keyId: z.ZodString;
54
+ publicKey: z.ZodObject<{
55
+ kty: z.ZodLiteral<"OKP">;
56
+ crv: z.ZodLiteral<"Ed25519">;
57
+ x: z.ZodString;
58
+ }, z.core.$strip>;
59
+ validFrom: z.ZodISODateTime;
60
+ validUntil: z.ZodNullable<z.ZodISODateTime>;
61
+ revokedAt: z.ZodNullable<z.ZodISODateTime>;
62
+ }, z.core.$strip>>;
63
+ }, z.core.$strip>;
64
+ export type ControlPlaneKeysResponse = z.output<typeof ControlPlaneKeysResponse>;
65
+ /** `POST /control-plane/keys` — the appended key, and the set as it now stands. */
66
+ export declare const RegisterKeyResponse: z.ZodObject<{
67
+ keyId: z.ZodString;
68
+ validFrom: z.ZodISODateTime;
69
+ keys: z.ZodArray<z.ZodObject<{
70
+ keyId: z.ZodString;
71
+ publicKey: z.ZodObject<{
72
+ kty: z.ZodLiteral<"OKP">;
73
+ crv: z.ZodLiteral<"Ed25519">;
74
+ x: z.ZodString;
75
+ }, z.core.$strip>;
76
+ validFrom: z.ZodISODateTime;
77
+ validUntil: z.ZodNullable<z.ZodISODateTime>;
78
+ revokedAt: z.ZodNullable<z.ZodISODateTime>;
79
+ }, z.core.$strip>>;
80
+ }, z.core.$strip>;
81
+ export type RegisterKeyResponse = z.output<typeof RegisterKeyResponse>;
82
+ /** `POST /control-plane/keys/:keyId/expire` — the retired key, and the set as it now stands. */
83
+ export declare const ExpireKeyResponse: z.ZodObject<{
84
+ keyId: z.ZodString;
85
+ validUntil: z.ZodISODateTime;
86
+ keys: z.ZodArray<z.ZodObject<{
87
+ keyId: z.ZodString;
88
+ publicKey: z.ZodObject<{
89
+ kty: z.ZodLiteral<"OKP">;
90
+ crv: z.ZodLiteral<"Ed25519">;
91
+ x: z.ZodString;
92
+ }, z.core.$strip>;
93
+ validFrom: z.ZodISODateTime;
94
+ validUntil: z.ZodNullable<z.ZodISODateTime>;
95
+ revokedAt: z.ZodNullable<z.ZodISODateTime>;
96
+ }, z.core.$strip>>;
97
+ }, z.core.$strip>;
98
+ export type ExpireKeyResponse = z.output<typeof ExpireKeyResponse>;
99
+ //# sourceMappingURL=responses.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"responses.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/http/responses.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;;;;;;;;;GAkBG;AAEH,8EAA8E;AAC9E,eAAO,MAAM,aAAa;;;;;;;;;;iBAcmF,CAAC;AAC9G,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,aAAa,CAAC,CAAC;AAE3D;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB;;;;;;iBAQwB,CAAC;AAC9D,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAEjF,+FAA+F;AAC/F,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;iBAMwC,CAAC;AAC9E,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAEjF,mFAAmF;AACnF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;iBAQyB,CAAC;AAC1D,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEvE,gGAAgG;AAChG,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;iBAM6B,CAAC;AAC5D,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,iBAAiB,CAAC,CAAC"}
@@ -0,0 +1,63 @@
1
+ import { Ed25519PublicJwk } from "../data/connection.js";
2
+ import { z } from "zod";
3
+ //#region src/controlPlane/http/responses.ts
4
+ /**
5
+ * What the seam's own routes return, as Zod objects a management client can validate against.
6
+ *
7
+ * `schemas.ts` bounds what a caller may send; this file states what it gets back. Both halves are
8
+ * runtime values for the same reason: a management client reading a customer's Worker is crossing a
9
+ * trust boundary and must validate what comes back, and a TypeScript interface is erased before it
10
+ * can help. `GET /control-plane/manifest` already had a schema — {@link ControlPlaneManifest}, in
11
+ * `discovery/adminRoute.ts`, because it is the discovery contract rather than a projection — and the
12
+ * other four routes had none at all, `publicKeyView` returning a bare `Record<string, unknown>`.
13
+ *
14
+ * **No codecs, and no transform anywhere in this file.** These describe JSON on the wire, so parsing
15
+ * one hands back exactly what went in — which is what lets a test compare the parsed value with a live
16
+ * response and fail on a field either side forgot. A `JsonDate` here would decode an ISO string into a
17
+ * `Date` and make that comparison meaningless.
18
+ *
19
+ * **Nothing here can carry a private key, because none is ever held.** The connection stores public
20
+ * halves only, so `keys` is exactly what a client needs to see a key ageing and exactly what an
21
+ * attacker learns nothing from.
22
+ */
23
+ /** One registered key as a client sees it: the public half and its window. */
24
+ const PublicKeyView = z.object({
25
+ keyId: z.string().describe("The key's id, matched against a token's `kid` header."),
26
+ publicKey: Ed25519PublicJwk.describe("The public key this id names. The private half is never held here."),
27
+ validFrom: z.iso.datetime().describe("When this key became valid, ISO-8601. A call signed before it is rejected."),
28
+ validUntil: z.iso.datetime().nullable().describe("When it stops being accepted, ISO-8601, or null while open-ended. Set only by the expire route."),
29
+ revokedAt: z.iso.datetime().nullable().describe("When it was revoked outright, ISO-8601, or null. Revocation ignores the window and is immediate.")
30
+ }).describe("One registered public key and its validity window. Two live at once during a rotation overlap.");
31
+ /**
32
+ * `GET /control-plane/ping`.
33
+ *
34
+ * It echoes the `keyId` that verified the call, so a client rotating a key can confirm *which* key
35
+ * answered rather than inferring it from a 200 — which is the whole point of the route.
36
+ */
37
+ const ControlPlanePingResponse = z.object({
38
+ status: z.literal("ok").describe("Always `ok`. A failure is an error payload, never this shape."),
39
+ connectionId: z.string().describe("The connection this call authenticated as."),
40
+ environment: z.string().describe("The environment that connection is bound to."),
41
+ keyId: z.string().describe("The key that verified this call. The proof a newly registered key works."),
42
+ now: z.iso.datetime().describe("This Worker's clock, ISO-8601 — what a token's lifetime is judged against.")
43
+ }).describe("Connectivity, and proof of which key answered.");
44
+ /** `GET /control-plane/keys` — the registration state, so a client can surface a stale key. */
45
+ const ControlPlaneKeysResponse = z.object({
46
+ connectionId: z.string().describe("The connection these keys belong to."),
47
+ environment: z.string().describe("The environment that connection is bound to."),
48
+ keys: z.array(PublicKeyView).describe("Every registered key, expired and revoked ones included.")
49
+ }).describe("Every key registered against this connection, with its window.");
50
+ /** `POST /control-plane/keys` — the appended key, and the set as it now stands. */
51
+ const RegisterKeyResponse = z.object({
52
+ keyId: z.string().describe("The key just registered."),
53
+ validFrom: z.iso.datetime().describe("When its window opened, ISO-8601 — now, by construction."),
54
+ keys: z.array(PublicKeyView).describe("The connection's keys after the append and the prune, so a client sees what it now holds.")
55
+ }).describe("The registered key, and the set it joined.");
56
+ /** `POST /control-plane/keys/:keyId/expire` — the retired key, and the set as it now stands. */
57
+ const ExpireKeyResponse = z.object({
58
+ keyId: z.string().describe("The key just given an end date."),
59
+ validUntil: z.iso.datetime().describe("When its window closed, ISO-8601 — now, by construction."),
60
+ keys: z.array(PublicKeyView).describe("The connection's keys after the expiry.")
61
+ }).describe("The expired key, and the set it left behind.");
62
+ //#endregion
63
+ export { ControlPlaneKeysResponse, ControlPlanePingResponse, ExpireKeyResponse, PublicKeyView, RegisterKeyResponse };
@@ -0,0 +1,40 @@
1
+ import type { Hono } from "hono";
2
+ import type { PithyHonoEnv } from "../../capability/capability";
3
+ import type { AdminRoute } from "../discovery/adminRoute";
4
+ import { type ControlPlaneHandlerDeps } from "./handlers";
5
+ /**
6
+ * The seam's own routes, distinct from the admin routes capabilities contribute.
7
+ *
8
+ * GET /control-plane/ping → connectivity and key proof (any verified caller)
9
+ * GET /control-plane/manifest → what this Worker composes (manifest:read)
10
+ * GET /control-plane/keys → the registration state (keys:rotate)
11
+ * POST /control-plane/keys → register a key (keys:rotate)
12
+ * POST /control-plane/keys/:keyId/expire → retire a superseded key (keys:rotate)
13
+ *
14
+ * Every one is `control-plane` and default-denied — with no connection registered they all answer 403,
15
+ * and that is the shipped state of a Worker nobody has connected.
16
+ *
17
+ * **Guards precede validators on every line.** A validator first would turn a 401 into a 400 and tell a
18
+ * caller with no credential which request shapes were well-formed. On these routes that is a live
19
+ * oracle over key registration, which is the last thing that should be probeable.
20
+ *
21
+ * The three key routes share one scope. Reading which keys are live, registering the next one, and
22
+ * retiring the last are one lifecycle, and an adopter deciding whether a management client may touch
23
+ * their keys is making one decision, not three.
24
+ *
25
+ * The table is declared twice on purpose — once as descriptors a client can read, once as the
26
+ * registrations themselves — and `routeContract.test.ts` proves the two agree.
27
+ */
28
+ /**
29
+ * The seam's own routes, described for `GET /control-plane/manifest`.
30
+ *
31
+ * Derived from the same `basePath` the routes below mount on, so the two cannot disagree — and the
32
+ * seam holds itself to the rule it imposes on every capability: `controlPlaneRouteDescriptors` is
33
+ * checked against the mounted router by `missingAdminRoutes` in `routeContract.test.ts`.
34
+ *
35
+ * `ping` carries a null scope. It requires a verified caller and no authorization, and saying so is
36
+ * how a client knows it can always prove a key — including on a connection granted nothing at all.
37
+ */
38
+ export declare function controlPlaneRouteDescriptors(basePath: string): AdminRoute[];
39
+ export declare function registerControlPlaneRoutes(app: Hono<PithyHonoEnv>, basePath: string, deps: ControlPlaneHandlerDeps): void;
40
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/http/routes.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEhE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAG1D,OAAO,EACL,KAAK,uBAAuB,EAM7B,MAAM,YAAY,CAAC;AAGpB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE,CAiC3E;AAED,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,IAAI,CAAC,YAAY,CAAC,EACvB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,uBAAuB,GAC5B,IAAI,CAqBN"}
@@ -0,0 +1,83 @@
1
+ import { ANY_VERIFIED_CALLER, KEYS_ROTATE_SCOPE, MANIFEST_READ_SCOPE } from "../scope/scope.js";
2
+ import { requireControlPlane } from "./guard.js";
3
+ import { validationHook } from "../../http/validation.js";
4
+ import { expireKeyHandler, listKeysHandler, manifestHandler, pingHandler, registerKeyHandler } from "./handlers.js";
5
+ import { ExpireKeyParams, ExpireKeyRequest, RegisterKeyRequest } from "./schemas.js";
6
+ import { zValidator } from "@hono/zod-validator";
7
+ //#region src/controlPlane/http/routes.ts
8
+ /**
9
+ * The seam's own routes, distinct from the admin routes capabilities contribute.
10
+ *
11
+ * GET /control-plane/ping → connectivity and key proof (any verified caller)
12
+ * GET /control-plane/manifest → what this Worker composes (manifest:read)
13
+ * GET /control-plane/keys → the registration state (keys:rotate)
14
+ * POST /control-plane/keys → register a key (keys:rotate)
15
+ * POST /control-plane/keys/:keyId/expire → retire a superseded key (keys:rotate)
16
+ *
17
+ * Every one is `control-plane` and default-denied — with no connection registered they all answer 403,
18
+ * and that is the shipped state of a Worker nobody has connected.
19
+ *
20
+ * **Guards precede validators on every line.** A validator first would turn a 401 into a 400 and tell a
21
+ * caller with no credential which request shapes were well-formed. On these routes that is a live
22
+ * oracle over key registration, which is the last thing that should be probeable.
23
+ *
24
+ * The three key routes share one scope. Reading which keys are live, registering the next one, and
25
+ * retiring the last are one lifecycle, and an adopter deciding whether a management client may touch
26
+ * their keys is making one decision, not three.
27
+ *
28
+ * The table is declared twice on purpose — once as descriptors a client can read, once as the
29
+ * registrations themselves — and `routeContract.test.ts` proves the two agree.
30
+ */
31
+ /**
32
+ * The seam's own routes, described for `GET /control-plane/manifest`.
33
+ *
34
+ * Derived from the same `basePath` the routes below mount on, so the two cannot disagree — and the
35
+ * seam holds itself to the rule it imposes on every capability: `controlPlaneRouteDescriptors` is
36
+ * checked against the mounted router by `missingAdminRoutes` in `routeContract.test.ts`.
37
+ *
38
+ * `ping` carries a null scope. It requires a verified caller and no authorization, and saying so is
39
+ * how a client knows it can always prove a key — including on a connection granted nothing at all.
40
+ */
41
+ function controlPlaneRouteDescriptors(basePath) {
42
+ return [
43
+ {
44
+ method: "GET",
45
+ path: `${basePath}/ping`,
46
+ scope: null,
47
+ summary: "Prove connectivity and which key answered. Always available to a verified caller."
48
+ },
49
+ {
50
+ method: "GET",
51
+ path: `${basePath}/manifest`,
52
+ scope: MANIFEST_READ_SCOPE,
53
+ summary: "What this Worker composes, and how to call each capability's admin surface."
54
+ },
55
+ {
56
+ method: "GET",
57
+ path: `${basePath}/keys`,
58
+ scope: KEYS_ROTATE_SCOPE,
59
+ summary: "The registered keys, their validity windows, and their ages."
60
+ },
61
+ {
62
+ method: "POST",
63
+ path: `${basePath}/keys`,
64
+ scope: KEYS_ROTATE_SCOPE,
65
+ summary: "Register a successor public key. Signed with the key it replaces; appends only."
66
+ },
67
+ {
68
+ method: "POST",
69
+ path: `${basePath}/keys/:keyId/expire`,
70
+ scope: KEYS_ROTATE_SCOPE,
71
+ summary: "Retire a superseded key. Must be signed with the successor it names as proven."
72
+ }
73
+ ];
74
+ }
75
+ function registerControlPlaneRoutes(app, basePath, deps) {
76
+ app.get(`${basePath}/ping`, requireControlPlane(ANY_VERIFIED_CALLER), pingHandler(deps));
77
+ app.get(`${basePath}/manifest`, requireControlPlane(MANIFEST_READ_SCOPE), manifestHandler(deps));
78
+ app.get(`${basePath}/keys`, requireControlPlane(KEYS_ROTATE_SCOPE), listKeysHandler(deps));
79
+ app.post(`${basePath}/keys`, requireControlPlane(KEYS_ROTATE_SCOPE), zValidator("json", RegisterKeyRequest, validationHook), (c) => registerKeyHandler(deps)(c, c.req.valid("json")));
80
+ app.post(`${basePath}/keys/:keyId/expire`, requireControlPlane(KEYS_ROTATE_SCOPE), zValidator("param", ExpireKeyParams, validationHook), zValidator("json", ExpireKeyRequest, validationHook), (c) => expireKeyHandler(deps)(c, c.req.valid("param"), c.req.valid("json")));
81
+ }
82
+ //#endregion
83
+ export { controlPlaneRouteDescriptors, registerControlPlaneRoutes };
@@ -0,0 +1,39 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * The request contract for the seam's own routes. Every input surface is validated here and read in a
4
+ * handler through `c.req.valid(target)` — a handler never parses input (CLAUDE.md §HTTP).
5
+ *
6
+ * These are the highest-risk routes in the product, so two of the choices below are deliberate and
7
+ * would be wrong to "tighten" later:
8
+ *
9
+ * **`keyId` is a bounded string, not a `z.uuid()`.** A key id is opaque and minted by the management
10
+ * client, so its format is not ours to constrain. More importantly, an unknown-but-well-formed key id
11
+ * must reach the handler and answer `controlplane/key_not_found` rather than bouncing off a 400 — a
12
+ * validator that rejected unfamiliar shapes would turn the route into an enumeration oracle telling a
13
+ * caller which ids exist.
14
+ *
15
+ * **Nothing here validates a scope or a connection id.** Those arrive in the signed token, not the
16
+ * request, and are verified against the adopter's own row before a handler runs. A body that could
17
+ * name its own scope would be a body that could grant itself one.
18
+ */
19
+ /** `POST /control-plane/keys` — register a new public key, signed by the key it succeeds. */
20
+ export declare const RegisterKeyRequest: z.ZodObject<{
21
+ keyId: z.ZodString;
22
+ publicKey: z.ZodObject<{
23
+ kty: z.ZodLiteral<"OKP">;
24
+ crv: z.ZodLiteral<"Ed25519">;
25
+ x: z.ZodString;
26
+ }, z.core.$strip>;
27
+ }, z.core.$strip>;
28
+ export type RegisterKeyRequest = z.infer<typeof RegisterKeyRequest>;
29
+ /** `POST /control-plane/keys/:keyId/expire` — the path segment naming the key to retire. */
30
+ export declare const ExpireKeyParams: z.ZodObject<{
31
+ keyId: z.ZodString;
32
+ }, z.core.$strip>;
33
+ export type ExpireKeyParams = z.infer<typeof ExpireKeyParams>;
34
+ /** `POST /control-plane/keys/:keyId/expire` — the body, carrying the proof that a successor works. */
35
+ export declare const ExpireKeyRequest: z.ZodObject<{
36
+ provenKeyId: z.ZodString;
37
+ }, z.core.$strip>;
38
+ export type ExpireKeyRequest = z.infer<typeof ExpireKeyRequest>;
39
+ //# sourceMappingURL=schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/http/schemas.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;;;;;;;GAgBG;AAEH,6FAA6F;AAC7F,eAAO,MAAM,kBAAkB;;;;;;;iBAa5B,CAAC;AACJ,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,4FAA4F;AAC5F,eAAO,MAAM,eAAe;;iBAU+B,CAAC;AAC5D,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,sGAAsG;AACtG,eAAO,MAAM,gBAAgB;;iBAY1B,CAAC;AACJ,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { Ed25519PublicJwk } from "../data/connection.js";
2
+ import { z } from "zod";
3
+ //#region src/controlPlane/http/schemas.ts
4
+ /**
5
+ * The request contract for the seam's own routes. Every input surface is validated here and read in a
6
+ * handler through `c.req.valid(target)` — a handler never parses input (CLAUDE.md §HTTP).
7
+ *
8
+ * These are the highest-risk routes in the product, so two of the choices below are deliberate and
9
+ * would be wrong to "tighten" later:
10
+ *
11
+ * **`keyId` is a bounded string, not a `z.uuid()`.** A key id is opaque and minted by the management
12
+ * client, so its format is not ours to constrain. More importantly, an unknown-but-well-formed key id
13
+ * must reach the handler and answer `controlplane/key_not_found` rather than bouncing off a 400 — a
14
+ * validator that rejected unfamiliar shapes would turn the route into an enumeration oracle telling a
15
+ * caller which ids exist.
16
+ *
17
+ * **Nothing here validates a scope or a connection id.** Those arrive in the signed token, not the
18
+ * request, and are verified against the adopter's own row before a handler runs. A body that could
19
+ * name its own scope would be a body that could grant itself one.
20
+ */
21
+ /** `POST /control-plane/keys` — register a new public key, signed by the key it succeeds. */
22
+ const RegisterKeyRequest = z.object({
23
+ keyId: z.string().min(1).max(64).describe("The new key's id, which a later token names in its `kid` header. Must not already be registered."),
24
+ publicKey: Ed25519PublicJwk.describe("The Ed25519 public key to trust. Only the public half ever crosses the wire — the management client keeps the private key and the adopter never holds anything worth stealing.")
25
+ }).describe("A request to append one public key to this connection. Appends only: it never touches an existing key's validity, which is what makes a failed rotation harmless.");
26
+ /** `POST /control-plane/keys/:keyId/expire` — the path segment naming the key to retire. */
27
+ const ExpireKeyParams = z.object({ keyId: z.string().min(1).max(64).describe("The key to give an end date. Bounded but unconstrained in shape — an unknown id answers 404, not 400.") }).describe("The path parameters of the key-expiry route.");
28
+ /** `POST /control-plane/keys/:keyId/expire` — the body, carrying the proof that a successor works. */
29
+ const ExpireKeyRequest = z.object({ provenKeyId: z.string().min(1).max(64).describe("The replacement key that has already been proven against a live `ping`. Required, and checked to be currently active: expiring a key without naming a working successor is the one failure mode with no recovery path, so the route refuses rather than trusting the caller to have checked.") }).describe("A request to expire one superseded key. The successor must be named and must already be live — append, verify, then expire, never replace.");
30
+ //#endregion
31
+ export { ExpireKeyParams, ExpireKeyRequest, RegisterKeyRequest };