@pithy-sh/core 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (354) hide show
  1. package/README.md +54 -0
  2. package/dist/audit/auditEvent.js +1 -1
  3. package/dist/audit/chain.d.ts +289 -0
  4. package/dist/audit/chain.d.ts.map +1 -0
  5. package/dist/audit/chain.js +400 -0
  6. package/dist/audit/citations.d.ts +52 -0
  7. package/dist/audit/citations.d.ts.map +1 -0
  8. package/dist/audit/citations.js +13 -0
  9. package/dist/capability/bindings.d.ts +22 -0
  10. package/dist/capability/bindings.d.ts.map +1 -1
  11. package/dist/capability/bindings.js +32 -2
  12. package/dist/capability/capability.d.ts +123 -16
  13. package/dist/capability/capability.d.ts.map +1 -1
  14. package/dist/capability/capability.js +17 -2
  15. package/dist/capability/erasure.d.ts +75 -0
  16. package/dist/capability/erasure.d.ts.map +1 -0
  17. package/dist/capability/erasure.js +2 -0
  18. package/dist/capability/manifest.d.ts +5 -0
  19. package/dist/capability/manifest.d.ts.map +1 -1
  20. package/dist/capability/personColumns.d.ts +28 -0
  21. package/dist/capability/personColumns.d.ts.map +1 -0
  22. package/dist/capability/personColumns.js +66 -0
  23. package/dist/capability/personRows.d.ts +62 -0
  24. package/dist/capability/personRows.d.ts.map +1 -0
  25. package/dist/capability/personRows.js +59 -0
  26. package/dist/capability/purge.d.ts +86 -19
  27. package/dist/capability/purge.d.ts.map +1 -1
  28. package/dist/capability/purge.js +83 -5
  29. package/dist/capability/settings.d.ts +10 -0
  30. package/dist/capability/settings.d.ts.map +1 -1
  31. package/dist/capability/vectorIndex.d.ts +36 -0
  32. package/dist/capability/vectorIndex.d.ts.map +1 -0
  33. package/dist/capability/vectorIndex.js +25 -0
  34. package/dist/cli/commandCatalog.generated.d.ts +438 -3
  35. package/dist/cli/commandCatalog.generated.d.ts.map +1 -1
  36. package/dist/cli/commandCatalog.generated.js +454 -2
  37. package/dist/cli/commandFacts.d.ts +8 -5
  38. package/dist/cli/commandFacts.d.ts.map +1 -1
  39. package/dist/cli/commandFacts.js +12 -3
  40. package/dist/consent/purpose.d.ts +70 -0
  41. package/dist/consent/purpose.d.ts.map +1 -0
  42. package/dist/consent/purpose.js +97 -0
  43. package/dist/consent/reader.d.ts +99 -0
  44. package/dist/consent/reader.d.ts.map +1 -0
  45. package/dist/consent/reader.js +33 -0
  46. package/dist/controlPlane/audit/actions.d.ts +15 -4
  47. package/dist/controlPlane/audit/actions.d.ts.map +1 -1
  48. package/dist/controlPlane/audit/actions.js +16 -5
  49. package/dist/controlPlane/capability.d.ts +10 -0
  50. package/dist/controlPlane/capability.d.ts.map +1 -1
  51. package/dist/controlPlane/capability.js +56 -8
  52. package/dist/controlPlane/context.d.ts +4 -0
  53. package/dist/controlPlane/context.d.ts.map +1 -1
  54. package/dist/controlPlane/context.js +3 -2
  55. package/dist/controlPlane/data/connection.d.ts +21 -2
  56. package/dist/controlPlane/data/connection.d.ts.map +1 -1
  57. package/dist/controlPlane/data/connection.js +13 -4
  58. package/dist/controlPlane/data/connections.d.ts +94 -0
  59. package/dist/controlPlane/data/connections.d.ts.map +1 -0
  60. package/dist/controlPlane/data/connections.js +151 -0
  61. package/dist/controlPlane/data/hold.d.ts +46 -0
  62. package/dist/controlPlane/data/hold.d.ts.map +1 -0
  63. package/dist/controlPlane/data/hold.js +36 -0
  64. package/dist/controlPlane/data/oidcTrust.d.ts +122 -0
  65. package/dist/controlPlane/data/oidcTrust.d.ts.map +1 -0
  66. package/dist/controlPlane/data/oidcTrust.js +173 -0
  67. package/dist/controlPlane/data/tables.d.ts +4 -0
  68. package/dist/controlPlane/data/tables.d.ts.map +1 -1
  69. package/dist/controlPlane/data/tables.js +9 -4
  70. package/dist/controlPlane/data/trustFirstCall.d.ts +16 -0
  71. package/dist/controlPlane/data/trustFirstCall.d.ts.map +1 -0
  72. package/dist/controlPlane/data/trustFirstCall.js +25 -0
  73. package/dist/controlPlane/discovery/adminRoute.d.ts +165 -2
  74. package/dist/controlPlane/discovery/adminRoute.d.ts.map +1 -1
  75. package/dist/controlPlane/discovery/adminRoute.js +32 -8
  76. package/dist/controlPlane/discovery/routeInput.d.ts +23 -0
  77. package/dist/controlPlane/discovery/routeInput.d.ts.map +1 -0
  78. package/dist/controlPlane/discovery/routeInput.js +80 -0
  79. package/dist/controlPlane/discovery/toolName.d.ts +21 -0
  80. package/dist/controlPlane/discovery/toolName.d.ts.map +1 -0
  81. package/dist/controlPlane/discovery/toolName.js +25 -0
  82. package/dist/controlPlane/erasure/attest.d.ts +78 -0
  83. package/dist/controlPlane/erasure/attest.d.ts.map +1 -0
  84. package/dist/controlPlane/erasure/attest.js +164 -0
  85. package/dist/controlPlane/erasure/read.d.ts +39 -0
  86. package/dist/controlPlane/erasure/read.d.ts.map +1 -0
  87. package/dist/controlPlane/erasure/read.js +140 -0
  88. package/dist/controlPlane/error/errors.d.ts +61 -2
  89. package/dist/controlPlane/error/errors.d.ts.map +1 -1
  90. package/dist/controlPlane/error/errors.js +13 -0
  91. package/dist/controlPlane/evidence/derive.d.ts +124 -0
  92. package/dist/controlPlane/evidence/derive.d.ts.map +1 -0
  93. package/dist/controlPlane/evidence/derive.js +343 -0
  94. package/dist/controlPlane/evidence/mapping.d.ts +85 -0
  95. package/dist/controlPlane/evidence/mapping.d.ts.map +1 -0
  96. package/dist/controlPlane/evidence/mapping.js +114 -0
  97. package/dist/controlPlane/evidence/pack.d.ts +46 -0
  98. package/dist/controlPlane/evidence/pack.d.ts.map +1 -0
  99. package/dist/controlPlane/evidence/pack.js +124 -0
  100. package/dist/controlPlane/http/connections.d.ts +96 -0
  101. package/dist/controlPlane/http/connections.d.ts.map +1 -0
  102. package/dist/controlPlane/http/connections.js +109 -0
  103. package/dist/controlPlane/http/guard.d.ts +23 -2
  104. package/dist/controlPlane/http/guard.d.ts.map +1 -1
  105. package/dist/controlPlane/http/guard.js +69 -12
  106. package/dist/controlPlane/http/handlers.d.ts +212 -10
  107. package/dist/controlPlane/http/handlers.d.ts.map +1 -1
  108. package/dist/controlPlane/http/handlers.js +117 -26
  109. package/dist/controlPlane/http/holds.d.ts +57 -0
  110. package/dist/controlPlane/http/holds.d.ts.map +1 -0
  111. package/dist/controlPlane/http/holds.js +98 -0
  112. package/dist/controlPlane/http/presence.d.ts +27 -0
  113. package/dist/controlPlane/http/presence.d.ts.map +1 -0
  114. package/dist/controlPlane/http/presence.js +67 -0
  115. package/dist/controlPlane/http/processing.d.ts +73 -0
  116. package/dist/controlPlane/http/processing.d.ts.map +1 -0
  117. package/dist/controlPlane/http/processing.js +17 -0
  118. package/dist/controlPlane/http/responses.d.ts +927 -3
  119. package/dist/controlPlane/http/responses.d.ts.map +1 -1
  120. package/dist/controlPlane/http/responses.js +283 -7
  121. package/dist/controlPlane/http/routes.d.ts +12 -2
  122. package/dist/controlPlane/http/routes.d.ts.map +1 -1
  123. package/dist/controlPlane/http/routes.js +182 -5
  124. package/dist/controlPlane/http/schemas.d.ts +63 -0
  125. package/dist/controlPlane/http/schemas.d.ts.map +1 -1
  126. package/dist/controlPlane/http/schemas.js +39 -1
  127. package/dist/controlPlane/http/verify.d.ts.map +1 -1
  128. package/dist/controlPlane/http/verify.js +13 -1
  129. package/dist/controlPlane/http/verifyTrust.d.ts +76 -0
  130. package/dist/controlPlane/http/verifyTrust.d.ts.map +1 -0
  131. package/dist/controlPlane/http/verifyTrust.js +75 -0
  132. package/dist/controlPlane/migrations/0004_connection_trusts.d.ts +15 -0
  133. package/dist/controlPlane/migrations/0004_connection_trusts.d.ts.map +1 -0
  134. package/dist/controlPlane/migrations/0004_connection_trusts.js +23 -0
  135. package/dist/controlPlane/migrations/0005_holds.d.ts +12 -0
  136. package/dist/controlPlane/migrations/0005_holds.d.ts.map +1 -0
  137. package/dist/controlPlane/migrations/0005_holds.js +26 -0
  138. package/dist/controlPlane/processing/derive.d.ts +77 -0
  139. package/dist/controlPlane/processing/derive.d.ts.map +1 -0
  140. package/dist/controlPlane/processing/derive.js +206 -0
  141. package/dist/controlPlane/processing/read.d.ts +10 -0
  142. package/dist/controlPlane/processing/read.d.ts.map +1 -0
  143. package/dist/controlPlane/processing/read.js +89 -0
  144. package/dist/controlPlane/scope/kind.d.ts +46 -0
  145. package/dist/controlPlane/scope/kind.d.ts.map +1 -0
  146. package/dist/controlPlane/scope/kind.js +52 -0
  147. package/dist/controlPlane/scope/scope.d.ts +58 -3
  148. package/dist/controlPlane/scope/scope.d.ts.map +1 -1
  149. package/dist/controlPlane/scope/scope.js +59 -4
  150. package/dist/controlPlane/token/jws.js +1 -1
  151. package/dist/controlPlane/token/mint.js +1 -1
  152. package/dist/controlPlane/wire.d.ts +23 -0
  153. package/dist/controlPlane/wire.d.ts.map +1 -1
  154. package/dist/controlPlane/wire.js +24 -1
  155. package/dist/createBackend.d.ts +13 -0
  156. package/dist/createBackend.d.ts.map +1 -1
  157. package/dist/createBackend.js +11 -3
  158. package/dist/createEntrypoint.d.ts +7 -0
  159. package/dist/createEntrypoint.d.ts.map +1 -1
  160. package/dist/createEntrypoint.js +4 -0
  161. package/dist/error/http.d.ts +168 -0
  162. package/dist/error/http.d.ts.map +1 -1
  163. package/dist/error/payload.d.ts +336 -0
  164. package/dist/error/payload.d.ts.map +1 -1
  165. package/dist/error/payload.js +112 -0
  166. package/dist/error/pithyError.d.ts +22 -0
  167. package/dist/error/pithyError.d.ts.map +1 -1
  168. package/dist/error/pithyError.js +31 -1
  169. package/dist/http/routeSpec.d.ts +46 -1
  170. package/dist/http/routeSpec.d.ts.map +1 -1
  171. package/dist/http/routeSpec.js +39 -5
  172. package/dist/http/verification.d.ts.map +1 -1
  173. package/dist/http/verification.js +1 -1
  174. package/dist/i18n/translator.d.ts +2 -1
  175. package/dist/i18n/translator.d.ts.map +1 -1
  176. package/dist/index.d.ts +1 -1
  177. package/dist/index.d.ts.map +1 -1
  178. package/dist/index.js +3 -3
  179. package/dist/logger/record.d.ts +98 -0
  180. package/dist/logger/record.d.ts.map +1 -1
  181. package/dist/mcp/annotations.d.ts +20 -0
  182. package/dist/mcp/annotations.d.ts.map +1 -0
  183. package/dist/mcp/annotations.js +23 -0
  184. package/dist/mcp/tools.d.ts +75 -0
  185. package/dist/mcp/tools.d.ts.map +1 -0
  186. package/dist/mcp/tools.js +118 -0
  187. package/dist/openapi/document.d.ts.map +1 -1
  188. package/dist/openapi/document.js +8 -26
  189. package/dist/openapi/operations.d.ts +35 -0
  190. package/dist/openapi/operations.d.ts.map +1 -0
  191. package/dist/openapi/operations.js +37 -0
  192. package/dist/presence/channel.d.ts +11 -0
  193. package/dist/presence/channel.d.ts.map +1 -0
  194. package/dist/presence/channel.js +65 -0
  195. package/dist/presence/declaration.d.ts +154 -0
  196. package/dist/presence/declaration.d.ts.map +1 -0
  197. package/dist/presence/declaration.js +164 -0
  198. package/dist/presence/durableObject.d.ts +61 -0
  199. package/dist/presence/durableObject.d.ts.map +1 -0
  200. package/dist/presence/durableObject.js +257 -0
  201. package/dist/presence/frames.d.ts +166 -0
  202. package/dist/presence/frames.d.ts.map +1 -0
  203. package/dist/presence/frames.js +90 -0
  204. package/dist/presence/http/guard.d.ts +11 -0
  205. package/dist/presence/http/guard.d.ts.map +1 -0
  206. package/dist/presence/http/guard.js +20 -0
  207. package/dist/presence/http/responses.d.ts +31 -0
  208. package/dist/presence/http/responses.d.ts.map +1 -0
  209. package/dist/presence/http/responses.js +34 -0
  210. package/dist/presence/http/routes.d.ts +24 -0
  211. package/dist/presence/http/routes.d.ts.map +1 -0
  212. package/dist/presence/http/routes.js +127 -0
  213. package/dist/presence/http/schemas.d.ts +29 -0
  214. package/dist/presence/http/schemas.d.ts.map +1 -0
  215. package/dist/presence/http/schemas.js +28 -0
  216. package/dist/presence/names.d.ts +23 -0
  217. package/dist/presence/names.d.ts.map +1 -0
  218. package/dist/presence/names.js +22 -0
  219. package/dist/presence/room.d.ts +95 -0
  220. package/dist/presence/room.d.ts.map +1 -0
  221. package/dist/presence/room.js +105 -0
  222. package/dist/presence/ticket.d.ts +55 -0
  223. package/dist/presence/ticket.d.ts.map +1 -0
  224. package/dist/presence/ticket.js +109 -0
  225. package/dist/privacy/classify.js +1 -1
  226. package/dist/privacy/encrypted.js +1 -1
  227. package/dist/processing/activity.d.ts +134 -0
  228. package/dist/processing/activity.d.ts.map +1 -0
  229. package/dist/processing/activity.js +161 -0
  230. package/dist/processing/composed.d.ts +18 -0
  231. package/dist/processing/composed.d.ts.map +1 -0
  232. package/dist/processing/composed.js +28 -0
  233. package/dist/processing/vocabulary.d.ts +26 -0
  234. package/dist/processing/vocabulary.d.ts.map +1 -0
  235. package/dist/processing/vocabulary.js +35 -0
  236. package/dist/queue/consumer.d.ts +59 -0
  237. package/dist/queue/consumer.d.ts.map +1 -0
  238. package/dist/queue/consumer.js +46 -0
  239. package/dist/retention/composed.d.ts +37 -0
  240. package/dist/retention/composed.d.ts.map +1 -0
  241. package/dist/retention/composed.js +66 -0
  242. package/dist/retention/holds.d.ts +61 -0
  243. package/dist/retention/holds.d.ts.map +1 -0
  244. package/dist/retention/holds.js +107 -0
  245. package/dist/retention/holdsPurge.d.ts +22 -0
  246. package/dist/retention/holdsPurge.d.ts.map +1 -0
  247. package/dist/retention/holdsPurge.js +50 -0
  248. package/dist/retention/policy.d.ts +132 -0
  249. package/dist/retention/policy.d.ts.map +1 -0
  250. package/dist/retention/policy.js +197 -0
  251. package/dist/session/rotated.d.ts +34 -0
  252. package/dist/session/rotated.d.ts.map +1 -0
  253. package/dist/session/rotated.js +25 -0
  254. package/dist/test-utils/connectionFixtures.d.ts +13 -0
  255. package/dist/test-utils/connectionFixtures.d.ts.map +1 -0
  256. package/dist/test-utils/connectionFixtures.js +58 -0
  257. package/dist/test-utils/interleave.d.ts +12 -0
  258. package/dist/test-utils/interleave.d.ts.map +1 -0
  259. package/dist/test-utils/interleave.js +26 -0
  260. package/dist/testWorker.d.ts +8 -0
  261. package/dist/testWorker.d.ts.map +1 -0
  262. package/dist/testWorker.js +9 -0
  263. package/dist/version.generated.d.ts +1 -1
  264. package/dist/version.generated.js +1 -1
  265. package/dist/workflow/dispatchRoute.js +1 -1
  266. package/package.json +1 -1
  267. package/src/audit/auditEvent.ts +1 -1
  268. package/src/audit/chain.ts +551 -0
  269. package/src/audit/citations.ts +63 -0
  270. package/src/capability/bindings.ts +61 -0
  271. package/src/capability/capability.ts +156 -19
  272. package/src/capability/erasure.ts +82 -0
  273. package/src/capability/personColumns.ts +83 -0
  274. package/src/capability/personRows.ts +125 -0
  275. package/src/capability/purge.ts +158 -21
  276. package/src/capability/settings.ts +7 -0
  277. package/src/capability/vectorIndex.ts +60 -0
  278. package/src/cli/commandCatalog.generated.ts +487 -2
  279. package/src/cli/commandFacts.ts +12 -3
  280. package/src/cloudflare-test.d.ts +3 -1
  281. package/src/consent/purpose.ts +160 -0
  282. package/src/consent/reader.ts +134 -0
  283. package/src/controlPlane/audit/actions.ts +15 -4
  284. package/src/controlPlane/capability.ts +99 -20
  285. package/src/controlPlane/context.ts +10 -2
  286. package/src/controlPlane/data/connection.ts +16 -2
  287. package/src/controlPlane/data/connections.ts +231 -0
  288. package/src/controlPlane/data/hold.ts +52 -0
  289. package/src/controlPlane/data/oidcTrust.ts +249 -0
  290. package/src/controlPlane/data/tables.ts +7 -0
  291. package/src/controlPlane/data/trustFirstCall.ts +37 -0
  292. package/src/controlPlane/discovery/adminRoute.ts +71 -9
  293. package/src/controlPlane/discovery/routeInput.ts +86 -0
  294. package/src/controlPlane/discovery/toolName.ts +23 -0
  295. package/src/controlPlane/erasure/attest.ts +205 -0
  296. package/src/controlPlane/erasure/read.ts +169 -0
  297. package/src/controlPlane/error/errors.ts +69 -2
  298. package/src/controlPlane/evidence/derive.ts +426 -0
  299. package/src/controlPlane/evidence/mapping.ts +118 -0
  300. package/src/controlPlane/evidence/pack.ts +190 -0
  301. package/src/controlPlane/http/connections.ts +129 -0
  302. package/src/controlPlane/http/guard.ts +118 -9
  303. package/src/controlPlane/http/handlers.ts +172 -40
  304. package/src/controlPlane/http/holds.ts +108 -0
  305. package/src/controlPlane/http/presence.ts +71 -0
  306. package/src/controlPlane/http/processing.ts +19 -0
  307. package/src/controlPlane/http/responses.ts +590 -7
  308. package/src/controlPlane/http/routes.ts +302 -3
  309. package/src/controlPlane/http/schemas.ts +126 -0
  310. package/src/controlPlane/http/verify.ts +19 -2
  311. package/src/controlPlane/http/verifyTrust.ts +176 -0
  312. package/src/controlPlane/migrations/0004_connection_trusts.ts +30 -0
  313. package/src/controlPlane/migrations/0005_holds.ts +40 -0
  314. package/src/controlPlane/processing/derive.ts +359 -0
  315. package/src/controlPlane/processing/read.ts +102 -0
  316. package/src/controlPlane/scope/kind.ts +63 -0
  317. package/src/controlPlane/scope/scope.ts +65 -3
  318. package/src/controlPlane/wire.ts +26 -0
  319. package/src/createBackend.ts +31 -6
  320. package/src/createEntrypoint.ts +17 -0
  321. package/src/error/payload.ts +234 -0
  322. package/src/error/pithyError.ts +42 -0
  323. package/src/http/routeSpec.ts +75 -5
  324. package/src/http/verification.ts +3 -1
  325. package/src/i18n/translator.ts +2 -1
  326. package/src/index.ts +2 -0
  327. package/src/mcp/annotations.ts +38 -0
  328. package/src/mcp/tools.ts +220 -0
  329. package/src/openapi/document.ts +8 -28
  330. package/src/openapi/operations.ts +62 -0
  331. package/src/presence/channel.ts +46 -0
  332. package/src/presence/declaration.ts +283 -0
  333. package/src/presence/durableObject.ts +287 -0
  334. package/src/presence/frames.ts +153 -0
  335. package/src/presence/http/guard.ts +23 -0
  336. package/src/presence/http/responses.ts +63 -0
  337. package/src/presence/http/routes.ts +165 -0
  338. package/src/presence/http/schemas.ts +39 -0
  339. package/src/presence/names.ts +45 -0
  340. package/src/presence/room.ts +145 -0
  341. package/src/presence/ticket.ts +145 -0
  342. package/src/processing/activity.ts +289 -0
  343. package/src/processing/composed.ts +33 -0
  344. package/src/processing/vocabulary.ts +43 -0
  345. package/src/queue/consumer.ts +104 -0
  346. package/src/retention/composed.ts +75 -0
  347. package/src/retention/holds.ts +175 -0
  348. package/src/retention/holdsPurge.ts +50 -0
  349. package/src/retention/policy.ts +305 -0
  350. package/src/session/rotated.ts +53 -0
  351. package/src/test-utils/connectionFixtures.ts +58 -0
  352. package/src/test-utils/interleave.ts +31 -0
  353. package/src/testWorker.ts +13 -0
  354. package/src/version.generated.ts +1 -1
@@ -1,15 +1,19 @@
1
1
  // SPDX-FileCopyrightText: 2026 Pithy
2
2
  // SPDX-License-Identifier: MIT
3
3
  import type { Context } from "hono";
4
- import type { PithyHonoEnv } from "../../capability/capability";
4
+ import type { Capability, PithyHonoEnv } from "../../capability/capability";
5
+ import type { RouteGroup } from "../../openapi/document";
6
+ import type { PresenceDeclaration } from "../../presence/declaration";
5
7
  import type { PrivacyInventory } from "../../privacy/report";
6
8
  import type { RetirementVerdict } from "../../privacy/retirement";
9
+ import type { EffectiveRetention } from "../../retention/policy";
7
10
  import type { ControlPlaneConfig } from "../config/config";
8
- import { type ControlPlaneDatabase } from "../data/tables";
11
+ import type { ControlPlaneContext } from "../context";
12
+ import type { ControlPlaneDatabase } from "../data/tables";
9
13
  import type { CapabilityDeclaration } from "../discovery/adminRoute";
10
14
  import { type CapabilityHealthSource } from "../discovery/health";
11
15
  import { type ComposedMigrationDatabase } from "../migrationState/migrationState";
12
- import type { ExpireKeyParams, ExpireKeyRequest, RegisterKeyRequest } from "./schemas";
16
+ import type { ErasurePeriodQuery, ErasureSubjectParams, ExpireKeyParams, ExpireKeyRequest, RegisterKeyRequest } from "./schemas";
13
17
  /** The pieces of the composed capability a handler needs. Passed in, so nothing reaches for a global. */
14
18
  export interface ControlPlaneHandlerDeps {
15
19
  /** The resolved seam config — pruning bounds, mostly. */
@@ -36,22 +40,44 @@ export interface ControlPlaneHandlerDeps {
36
40
  composedHealth: () => ReadonlyMap<string, CapabilityHealthSource>;
37
41
  /**
38
42
  * Every database this Worker binds, with the provider this build composes for it — captured by the same
39
- * `compose` hook through `collectMigrationSets`, the function `pithy migrate` composes with (#729).
43
+ * `compose` hook through `collectMigrationSets`, the function `pithy migrate` composes with.
40
44
  */
41
45
  composedMigrations: () => readonly ComposedMigrationDatabase[];
42
46
  /**
43
- * The privacy inventory of every storage schema this Worker composed (#132), walked on the first call and
47
+ * The privacy inventory of every storage schema this Worker composed, walked on the first call and
44
48
  * cached for the isolate. A thunk, so a Worker whose control plane is never called never walks.
45
49
  */
46
50
  privacyInventory: () => PrivacyInventory;
47
51
  /**
48
- * The field key's retirement verdict (#390): the guard over this Worker's encrypted columns, read through its own
52
+ * The field key's retirement verdict: the guard over this Worker's encrypted columns, read through its own
49
53
  * bindings. Never throws: whatever it cannot read is incomplete coverage.
50
54
  */
51
55
  retirement: (env: Record<string, unknown>) => Promise<RetirementVerdict>;
56
+ /**
57
+ * Every presence room kind this Worker composed, by kind, with the declaration that serves it — captured
58
+ * by the same `compose` hook. A thunk for the same reason as {@link composedCapabilities}.
59
+ */
60
+ composedPresence: () => ReadonlyMap<string, PresenceDeclaration>;
61
+ /**
62
+ * Every capability this Worker composed, as the objects themselves — the evidence pack reads which one records
63
+ * audit events and the bindings it writes through. A thunk for the same reason as the rest.
64
+ */
65
+ composition: () => readonly Capability[];
66
+ /**
67
+ * The route table `createBackend` recorded for the app these routes mounted on, or `undefined` for an app it did
68
+ * not assemble. Read per call, after every capability's routes have mounted, so the evidence pack sees them all.
69
+ */
70
+ routeGroups: () => readonly RouteGroup[] | undefined;
71
+ /**
72
+ * The retention block as this Worker resolved it at assembly, or null when no backend recorded one. The
73
+ * manifest reports it.
74
+ */
75
+ retention?: () => EffectiveRetention | null;
52
76
  /** The clock, injected so a test can stand at any instant. */
53
77
  now: () => Date;
54
78
  }
79
+ /** The verified caller. Never null here — `requireControlPlane` ran first, or this code is unreachable. */
80
+ export declare function caller(c: Context<PithyHonoEnv>): ControlPlaneContext;
55
81
  /**
56
82
  * `GET /control-plane/ping` — connectivity and key proof.
57
83
  *
@@ -76,11 +102,11 @@ export declare function pingHandler(deps: ControlPlaneHandlerDeps): (c: Context<
76
102
  * Discovery over configuration. A client builds its navigation from this rather than from settings
77
103
  * someone maintains, so a Worker with no payments capability has no purchases pane as a matter of fact.
78
104
  *
79
- * **And the numbers a client would otherwise pay a second round trip for** (#317). Each capability's
105
+ * **And the numbers a client would otherwise pay a second round trip for**. Each capability's
80
106
  * bounded summary is resolved here, behind the scope that capability's own read is behind: a connection
81
107
  * without it gets no number rather than a zero, and never costs the adopter's Worker the query.
82
108
  *
83
- * **One capability's failure costs one capability's number** (#350). A producer that throws, or that
109
+ * **One capability's failure costs one capability's number**. A producer that throws, or that
84
110
  * reports something its declaration cannot name, lands on `unavailable` for that entry alone —
85
111
  * `readCapabilityHealth` catches it, so nothing rejects, so `Promise.all` still resolves and every
86
112
  * sibling still reports. This route is what the whole management surface is built from; a manifest that
@@ -121,6 +147,11 @@ export declare function manifestHandler(deps: ControlPlaneHandlerDeps): (c: Cont
121
147
  path: string;
122
148
  scope: string | null;
123
149
  summary: string;
150
+ kind?: "charges" | "credentials" | "deletes" | "reads" | "sends" | "writes" | undefined;
151
+ operation?: string | undefined;
152
+ input?: {
153
+ [x: string]: import("hono/utils/types").JSONValue;
154
+ } | undefined;
124
155
  }[];
125
156
  healthKeys: {
126
157
  key: string;
@@ -142,11 +173,40 @@ export declare function manifestHandler(deps: ControlPlaneHandlerDeps): (c: Cont
142
173
  config: {
143
174
  [x: string]: string | number | boolean;
144
175
  };
176
+ presenceRooms?: {
177
+ kind: string;
178
+ scope: string;
179
+ activities: string[];
180
+ maxParticipants: number;
181
+ sessionTicketPath: string | null;
182
+ socketPath: string;
183
+ }[] | undefined;
145
184
  }[];
146
185
  grantedScopes: string[];
186
+ retention?: {
187
+ purgeWindows: {
188
+ subject: "organization" | "user";
189
+ days: number;
190
+ source: "capability" | "floor" | "policy";
191
+ }[];
192
+ audit: {
193
+ defaultDays: number | null;
194
+ floorDays: number;
195
+ rules: {
196
+ prefix: string;
197
+ days: number;
198
+ source: "capability" | "floor" | "policy";
199
+ }[];
200
+ floored: string[];
201
+ };
202
+ classAges: {
203
+ class: string;
204
+ days: number;
205
+ }[];
206
+ } | undefined;
147
207
  }, import("hono/utils/http-status").ContentfulStatusCode, "json">>;
148
208
  /**
149
- * `GET /control-plane/migrations` — where each database this Worker binds stands (#729).
209
+ * `GET /control-plane/migrations` — where each database this Worker binds stands.
150
210
  *
151
211
  * Read-only: it reports, and nothing over the seam runs, reverses or resets a migration.
152
212
  *
@@ -214,6 +274,18 @@ export declare function listKeysHandler(deps: ControlPlaneHandlerDeps): (c: Cont
214
274
  authorizedBy: string | null;
215
275
  actorKind: "agent" | "dashboard" | "script" | null;
216
276
  purpose: string | null;
277
+ trusts: {
278
+ trustId: string;
279
+ issuer: string;
280
+ audience: string;
281
+ claims: {
282
+ [x: string]: string;
283
+ };
284
+ authorizedBy: string;
285
+ validFrom: string;
286
+ revokedAt: string | null;
287
+ firstCallAt: string | null;
288
+ }[];
217
289
  }, import("hono/utils/http-status").ContentfulStatusCode, "json">>;
218
290
  /**
219
291
  * `POST /control-plane/keys` — register a new public key, authenticated by the key it succeeds.
@@ -271,7 +343,7 @@ export declare function expireKeyHandler(deps: ControlPlaneHandlerDeps): (c: Con
271
343
  }[];
272
344
  }, import("hono/utils/http-status").ContentfulStatusCode, "json">>;
273
345
  /**
274
- * `GET /control-plane/privacy` — the full privacy inventory (#132).
346
+ * `GET /control-plane/privacy` — the full privacy inventory.
275
347
  *
276
348
  * Every storage schema this Worker composed that holds a marked field, each field's path and class, and where it
277
349
  * is stored — derived from the running code, so unmarking a field removes it on the next deploy with nothing in
@@ -318,4 +390,134 @@ export declare function privacyHandler(deps: ControlPlaneHandlerDeps): (c: Conte
318
390
  }[];
319
391
  }[];
320
392
  }, import("hono/utils/http-status").ContentfulStatusCode, "json">>;
393
+ /**
394
+ * `GET /control-plane/evidence` — the evidence pack.
395
+ *
396
+ * SOC 2 and ISO 27001 evidence derived from what this Worker composes, records and declares: each control's
397
+ * references, the facts read and their sources, and `evidenced`, `gap` or `not-derivable` with the reason. It
398
+ * proves controls present and performs nothing. Behind `evidence:read`, and it never widens another scope: what
399
+ * a capability keeps behind its own read stays there. Read-only.
400
+ */
401
+ export declare function evidenceHandler(deps: ControlPlaneHandlerDeps): (c: Context<PithyHonoEnv>) => Promise<Response & import("hono").TypedResponse<{
402
+ environment: string;
403
+ generatedAt: string;
404
+ version: string | null;
405
+ mappingVersion: number;
406
+ controls: {
407
+ control: "audit-logging" | "change-history" | "csrf" | "data-protection" | "key-rotation" | "management-access" | "route-verification";
408
+ title: string;
409
+ soc2: string[];
410
+ iso27001: string[];
411
+ state: "evidenced" | "gap" | "not-derivable";
412
+ reasons: string[];
413
+ facts: {
414
+ fact: string;
415
+ subject?: string | undefined;
416
+ value: string | number | boolean | string[] | null;
417
+ source: "auditRecorder" | "bindings" | "capabilityHealth" | "composition" | "connection" | "connectionKeys" | "encryptedMarkers" | "fieldKeyRetirement" | "migrationLedger" | "privacyAttestation" | "privacyMarkers" | "routeSpec" | "sameOriginGate";
418
+ }[];
419
+ }[];
420
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">>;
421
+ /**
422
+ * `GET /control-plane/erasure/:subjectType/:subjectId` — one subject's erasure, attested.
423
+ *
424
+ * Read from the purge's own lifecycle rows, through the capability that owns the subject, and cited in the audit
425
+ * chain through the composed audit. A subject with no deletion in force is a 404. Read-only.
426
+ */
427
+ export declare function erasureSubjectHandler(deps: ControlPlaneHandlerDeps): (c: Context<PithyHonoEnv>, params: ErasureSubjectParams) => Promise<Response & import("hono").TypedResponse<{
428
+ generatedAt: string;
429
+ attestation: {
430
+ subject: {
431
+ type: "organization" | "user";
432
+ id: string;
433
+ };
434
+ deletion: {
435
+ at: string;
436
+ actorType: string;
437
+ reason: string | null;
438
+ mode: "hard" | "soft" | null;
439
+ purgeAfter: string | null;
440
+ audit: {
441
+ eventId: string;
442
+ action: string;
443
+ occurredAt: string;
444
+ tenant: string | null;
445
+ seq: number | null;
446
+ hash: string | null;
447
+ } | null;
448
+ };
449
+ state: "complete" | "failed" | "in_progress" | "scheduled";
450
+ overdue: boolean;
451
+ stoppedAt: string | null;
452
+ steps: {
453
+ capability: string;
454
+ disposition: "erased" | "retained";
455
+ reason: string | null;
456
+ summary: string | null;
457
+ completedAt: string;
458
+ audit: {
459
+ eventId: string;
460
+ action: string;
461
+ occurredAt: string;
462
+ tenant: string | null;
463
+ seq: number | null;
464
+ hash: string | null;
465
+ } | null;
466
+ }[];
467
+ completedAt: string | null;
468
+ completion: {
469
+ eventId: string;
470
+ action: string;
471
+ occurredAt: string;
472
+ tenant: string | null;
473
+ seq: number | null;
474
+ hash: string | null;
475
+ } | null;
476
+ failure: {
477
+ eventId: string;
478
+ action: string;
479
+ occurredAt: string;
480
+ tenant: string | null;
481
+ seq: number | null;
482
+ hash: string | null;
483
+ } | null;
484
+ trail: "absent" | "recorded";
485
+ };
486
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">>;
487
+ /** `GET /control-plane/erasure?since&until` — every purge in a period, counted. Read-only. */
488
+ export declare function erasurePeriodHandler(deps: ControlPlaneHandlerDeps): (c: Context<PithyHonoEnv>, query: ErasurePeriodQuery) => Promise<Response & import("hono").TypedResponse<{
489
+ generatedAt: string;
490
+ since: string;
491
+ until: string;
492
+ subjects: ("organization" | "user")[];
493
+ counts: {
494
+ scheduled: number;
495
+ inProgress: number;
496
+ complete: number;
497
+ failed: number;
498
+ overdue: number;
499
+ };
500
+ attention: {
501
+ subject: {
502
+ type: "organization" | "user";
503
+ id: string;
504
+ };
505
+ state: "complete" | "failed" | "in_progress" | "scheduled";
506
+ overdue: boolean;
507
+ purgeAfter: string | null;
508
+ completedAt: string | null;
509
+ stoppedAt: string | null;
510
+ }[];
511
+ purges: {
512
+ subject: {
513
+ type: "organization" | "user";
514
+ id: string;
515
+ };
516
+ state: "complete" | "failed" | "in_progress" | "scheduled";
517
+ overdue: boolean;
518
+ purgeAfter: string | null;
519
+ completedAt: string | null;
520
+ stoppedAt: string | null;
521
+ }[];
522
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">>;
321
523
  //# sourceMappingURL=handlers.d.ts.map
@@ -1 +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,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAGlE,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;AAGxF,OAAO,EAAE,KAAK,yBAAyB,EAA0B,MAAM,kCAAkC,CAAC;AAU1G,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;;;OAGG;IACH,kBAAkB,EAAE,MAAM,SAAS,yBAAyB,EAAE,CAAC;IAC/D;;;OAGG;IACH,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;IACzC;;;OAGG;IACH,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACzE,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mEAoCvC;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,uBAAuB,OAC5C,OAAO,CAAC,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mEAgBvC;AAED,+FAA+F;AAC/F,wBAAgB,eAAe,CAAC,IAAI,EAAE,uBAAuB,OAC1C,OAAO,CAAC,YAAY,CAAC;;;;;;;;;;;;;;;;;mEAcvC;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;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,uBAAuB,OACzC,OAAO,CAAC,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mEAiBvC"}
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/http/handlers.ts"],"names":[],"mappings":"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"}
@@ -1,16 +1,19 @@
1
1
  // SPDX-FileCopyrightText: 2026 Pithy
2
2
  // SPDX-License-Identifier: MIT
3
+ import { commandName } from "../../cli/command.js";
4
+ import { ControlPlaneInvalidCredentialError, ControlPlaneKeyConflictError } from "../error/errors.js";
3
5
  import { workerVersionMetadata } from "../../worker/identity.js";
4
- import { ControlPlaneConnection } from "../data/connection.js";
5
- import { CONTROL_PLANE_CONNECTIONS_TABLE } from "../data/tables.js";
6
6
  import { healthWire } from "../discovery/healthSummary.js";
7
7
  import { readCapabilityHealth } from "../discovery/health.js";
8
8
  import { ControlPlaneAuditActions, safeEmit } from "../audit/actions.js";
9
- import { ControlPlaneInvalidCredentialError, ControlPlaneKeyConflictError } from "../error/errors.js";
10
9
  import { appendKey, expireKey, pruneKeys } from "../data/keyLifecycle.js";
11
10
  import { privacyAttestation, privacyPosture } from "../../privacy/posture.js";
11
+ import { readConnectionForWrite, writeKeys } from "../data/connections.js";
12
12
  import { causeMessage } from "../../error/cause.js";
13
+ import { routeInputs, withRouteInputs } from "../discovery/routeInput.js";
14
+ import { readErasurePeriod, readErasureSubject } from "../erasure/read.js";
13
15
  import { readDatabaseMigrations } from "../migrationState/migrationState.js";
16
+ import { readEvidencePack } from "../evidence/pack.js";
14
17
  //#region src/controlPlane/http/handlers.ts
15
18
  /**
16
19
  * The seam's own route handlers.
@@ -37,19 +40,23 @@ function caller(c) {
37
40
  if (!context) throw new ControlPlaneInvalidCredentialError({ detail: "handler reached with no verified control-plane caller" });
38
41
  return context;
39
42
  }
40
- /** Load the connection this call authenticated against. It verified moments ago, so it exists. */
43
+ /** Load the connection this call authenticated against, as stored, for a key write. It verified moments ago. */
41
44
  async function loadConnection(db, connectionId) {
42
- const row = await db.selectFrom(CONTROL_PLANE_CONNECTIONS_TABLE).selectAll().where("id", "=", connectionId).executeTakeFirst();
43
- if (!row) throw new ControlPlaneInvalidCredentialError({ detail: `connection ${connectionId} disappeared mid-request` });
44
- return { connection: ControlPlaneConnection.parse(row) };
45
+ const loaded = await readConnectionForWrite(db, connectionId);
46
+ if (loaded === null) throw new ControlPlaneInvalidCredentialError({ detail: `connection ${connectionId} disappeared mid-request` });
47
+ return loaded;
45
48
  }
46
- /** Write a connection's key array back, stamping `updatedAt`. One UPDATE — a rotation cannot half-apply. */
47
- async function saveKeys(db, connectionId, keys, now) {
48
- const encoded = ControlPlaneConnection.shape.keys.encode([...keys]);
49
- await db.updateTable(CONTROL_PLANE_CONNECTIONS_TABLE).set({
50
- keys: encoded,
51
- updatedAt: ControlPlaneConnection.shape.updatedAt.encode(now)
52
- }).where("id", "=", connectionId).execute();
49
+ /**
50
+ * Write a connection's key array back, stamping `updatedAt`. One UPDATE, so a rotation cannot half-apply, and only
51
+ * while the keys are still what `loaded` read: a revocation landing between the read and this write is never undone.
52
+ */
53
+ async function saveKeys(db, loaded, keys, now) {
54
+ if (await writeKeys(db, loaded, keys, now)) return;
55
+ throw new ControlPlaneKeyConflictError({
56
+ message: "The connection's keys changed while this call ran.",
57
+ action: "Make the call again.",
58
+ detail: `connection ${loaded.connection.id} had its keys written between this call's read and its write`
59
+ });
53
60
  }
54
61
  /**
55
62
  * The public view of one registered key. Windows and ages, never anything secret.
@@ -67,6 +74,35 @@ function publicKeyView(key) {
67
74
  revokedAt: key.revokedAt?.toISOString() ?? null
68
75
  };
69
76
  }
77
+ /** The public view of one OIDC trust: its issuer, audience and claims — the population it admits. */
78
+ function trustView(trust) {
79
+ return {
80
+ trustId: trust.trustId,
81
+ issuer: trust.issuer,
82
+ audience: trust.audience,
83
+ claims: { ...trust.claims },
84
+ authorizedBy: trust.authorizedBy,
85
+ validFrom: trust.validFrom.toISOString(),
86
+ revokedAt: trust.revokedAt?.toISOString() ?? null,
87
+ firstCallAt: trust.firstCallAt?.toISOString() ?? null
88
+ };
89
+ }
90
+ /**
91
+ * Refuse a key write to a caller that verified under an OIDC trust.
92
+ *
93
+ * A trust exists so that no key is held on the workload's host. Letting a trust's caller register a key would
94
+ * turn a short-lived identity into a standing credential at the first call — an exchange by another name, and
95
+ * the custody the trust was registered to avoid. Granting `keys:rotate` to a connection is a statement about
96
+ * its keys, never a license for a workload to mint one.
97
+ */
98
+ function refuseTrustCaller(context, what) {
99
+ if (context.credentialKind !== "trust") return;
100
+ throw new ControlPlaneKeyConflictError({
101
+ message: "A call under a workload trust cannot change this connection's keys.",
102
+ action: `Register or expire a key from a client that holds one, or with ${commandName("dashboard connect")}.`,
103
+ detail: `${what} refused: trust ${context.keyId} verified this call`
104
+ });
105
+ }
70
106
  /**
71
107
  * `GET /control-plane/ping` — connectivity and key proof.
72
108
  *
@@ -96,11 +132,11 @@ function pingHandler(deps) {
96
132
  * Discovery over configuration. A client builds its navigation from this rather than from settings
97
133
  * someone maintains, so a Worker with no payments capability has no purchases pane as a matter of fact.
98
134
  *
99
- * **And the numbers a client would otherwise pay a second round trip for** (#317). Each capability's
135
+ * **And the numbers a client would otherwise pay a second round trip for**. Each capability's
100
136
  * bounded summary is resolved here, behind the scope that capability's own read is behind: a connection
101
137
  * without it gets no number rather than a zero, and never costs the adopter's Worker the query.
102
138
  *
103
- * **One capability's failure costs one capability's number** (#350). A producer that throws, or that
139
+ * **One capability's failure costs one capability's number**. A producer that throws, or that
104
140
  * reports something its declaration cannot name, lands on `unavailable` for that entry alone —
105
141
  * `readCapabilityHealth` catches it, so nothing rejects, so `Promise.all` still resolves and every
106
142
  * sibling still reports. This route is what the whole management surface is built from; a manifest that
@@ -108,10 +144,19 @@ function pingHandler(deps) {
108
144
  * name.
109
145
  */
110
146
  function manifestHandler(deps) {
147
+ let converted;
148
+ const inputsFor = (groups) => {
149
+ if (groups === void 0) return void 0;
150
+ if (converted?.groups !== groups) converted = {
151
+ groups,
152
+ inputs: routeInputs(groups)
153
+ };
154
+ return converted.inputs;
155
+ };
111
156
  return async (c) => {
112
157
  const context = caller(c);
113
158
  const sources = deps.composedHealth();
114
- const capabilities = await Promise.all(deps.composedCapabilities().map(async (declaration) => ({
159
+ const capabilities = await Promise.all(withRouteInputs(deps.composedCapabilities(), inputsFor(deps.routeGroups())).map(async (declaration) => ({
115
160
  ...declaration,
116
161
  ...healthWire(await readCapabilityHealth(sources.get(declaration.name), context.grantedScopes, (source) => source.read(c)))
117
162
  })));
@@ -126,12 +171,18 @@ function manifestHandler(deps) {
126
171
  retirement: await deps.retirement(c.env)
127
172
  },
128
173
  capabilities,
129
- grantedScopes: [...context.grantedScopes]
174
+ grantedScopes: [...context.grantedScopes],
175
+ ...retentionOf(deps)
130
176
  });
131
177
  };
132
178
  }
179
+ /** The resolved retention for the manifest, or nothing where this Worker recorded none. */
180
+ function retentionOf(deps) {
181
+ const retention = deps.retention?.() ?? null;
182
+ return retention === null ? {} : { retention };
183
+ }
133
184
  /**
134
- * `GET /control-plane/migrations` — where each database this Worker binds stands (#729).
185
+ * `GET /control-plane/migrations` — where each database this Worker binds stands.
135
186
  *
136
187
  * Read-only: it reports, and nothing over the seam runs, reverses or resets a migration.
137
188
  *
@@ -165,7 +216,8 @@ function listKeysHandler(deps) {
165
216
  keys: connection.keys.map(publicKeyView),
166
217
  authorizedBy: connection.authorizedBy,
167
218
  actorKind: connection.actorKind,
168
- purpose: connection.purpose
219
+ purpose: connection.purpose,
220
+ trusts: (connection.trusts ?? []).map(trustView)
169
221
  });
170
222
  };
171
223
  }
@@ -182,9 +234,11 @@ function listKeysHandler(deps) {
182
234
  function registerKeyHandler(deps) {
183
235
  return async (c, body) => {
184
236
  const context = caller(c);
237
+ refuseTrustCaller(context, "key registration");
185
238
  const db = deps.database(c);
186
239
  const now = deps.now();
187
- const { connection } = await loadConnection(db, context.connectionId);
240
+ const loaded = await loadConnection(db, context.connectionId);
241
+ const { connection } = loaded;
188
242
  const appended = appendKey(connection.keys, {
189
243
  keyId: body.keyId,
190
244
  publicKey: body.publicKey,
@@ -197,7 +251,7 @@ function registerKeyHandler(deps) {
197
251
  retentionMs: deps.config.keyRetentionDays * MS_PER_DAY,
198
252
  maxKeys: deps.config.maxKeys
199
253
  });
200
- await saveKeys(db, connection.id, pruned, now);
254
+ await saveKeys(db, loaded, pruned, now);
201
255
  await safeEmit(c.var.emit, {
202
256
  action: ControlPlaneAuditActions.keyRegistered,
203
257
  outcome: "success",
@@ -241,9 +295,11 @@ function registerKeyHandler(deps) {
241
295
  function expireKeyHandler(deps) {
242
296
  return async (c, params, body) => {
243
297
  const context = caller(c);
298
+ refuseTrustCaller(context, "key expiry");
244
299
  const db = deps.database(c);
245
300
  const now = deps.now();
246
- const { connection } = await loadConnection(db, context.connectionId);
301
+ const loaded = await loadConnection(db, context.connectionId);
302
+ const { connection } = loaded;
247
303
  if (body.provenKeyId !== context.keyId) throw new ControlPlaneKeyConflictError({
248
304
  message: "Sign this request with the replacement key you are naming as proven.",
249
305
  action: "Rotate to the new key, make a call with it, then expire the old one in that same call.",
@@ -253,7 +309,7 @@ function expireKeyHandler(deps) {
253
309
  provenKeyId: body.provenKeyId,
254
310
  now
255
311
  });
256
- await saveKeys(db, connection.id, expired, now);
312
+ await saveKeys(db, loaded, expired, now);
257
313
  await safeEmit(c.var.emit, {
258
314
  action: ControlPlaneAuditActions.keyExpired,
259
315
  outcome: "success",
@@ -280,7 +336,7 @@ function expireKeyHandler(deps) {
280
336
  };
281
337
  }
282
338
  /**
283
- * `GET /control-plane/privacy` — the full privacy inventory (#132).
339
+ * `GET /control-plane/privacy` — the full privacy inventory.
284
340
  *
285
341
  * Every storage schema this Worker composed that holds a marked field, each field's path and class, and where it
286
342
  * is stored — derived from the running code, so unmarking a field removes it on the next deploy with nothing in
@@ -306,5 +362,40 @@ function privacyHandler(deps) {
306
362
  });
307
363
  };
308
364
  }
365
+ /**
366
+ * `GET /control-plane/evidence` — the evidence pack.
367
+ *
368
+ * SOC 2 and ISO 27001 evidence derived from what this Worker composes, records and declares: each control's
369
+ * references, the facts read and their sources, and `evidenced`, `gap` or `not-derivable` with the reason. It
370
+ * proves controls present and performs nothing. Behind `evidence:read`, and it never widens another scope: what
371
+ * a capability keeps behind its own read stays there. Read-only.
372
+ */
373
+ function evidenceHandler(deps) {
374
+ return async (c) => {
375
+ const context = caller(c);
376
+ return c.json(await readEvidencePack(c, deps, context));
377
+ };
378
+ }
379
+ /**
380
+ * `GET /control-plane/erasure/:subjectType/:subjectId` — one subject's erasure, attested.
381
+ *
382
+ * Read from the purge's own lifecycle rows, through the capability that owns the subject, and cited in the audit
383
+ * chain through the composed audit. A subject with no deletion in force is a 404. Read-only.
384
+ */
385
+ function erasureSubjectHandler(deps) {
386
+ return async (c, params) => {
387
+ caller(c);
388
+ const env = c.env ?? {};
389
+ return c.json(await readErasureSubject(env, deps, params.subjectType, params.subjectId));
390
+ };
391
+ }
392
+ /** `GET /control-plane/erasure?since&until` — every purge in a period, counted. Read-only. */
393
+ function erasurePeriodHandler(deps) {
394
+ return async (c, query) => {
395
+ caller(c);
396
+ const env = c.env ?? {};
397
+ return c.json(await readErasurePeriod(env, deps, query));
398
+ };
399
+ }
309
400
  //#endregion
310
- export { expireKeyHandler, listKeysHandler, manifestHandler, migrationsHandler, pingHandler, privacyHandler, registerKeyHandler };
401
+ export { caller, erasurePeriodHandler, erasureSubjectHandler, evidenceHandler, expireKeyHandler, listKeysHandler, manifestHandler, migrationsHandler, pingHandler, privacyHandler, registerKeyHandler };
@@ -0,0 +1,57 @@
1
+ // SPDX-FileCopyrightText: 2026 Pithy
2
+ // SPDX-License-Identifier: MIT
3
+ import type { Context } from "hono";
4
+ import type { PithyHonoEnv } from "../../capability/capability";
5
+ import type { LegalHold } from "../data/hold";
6
+ import { type ControlPlaneHandlerDeps } from "./handlers";
7
+ import type { LegalHoldView } from "./responses";
8
+ import type { HoldListQuery, HoldParams, PlaceHoldRequest, ReleaseHoldRequest } from "./schemas";
9
+ /** A hold, as the routes answer it. */
10
+ export declare function holdView(hold: LegalHold): LegalHoldView;
11
+ /** `POST /control-plane/holds` — place a hold. */
12
+ export declare function placeHoldHandler(deps: ControlPlaneHandlerDeps): (c: Context<PithyHonoEnv>, body: PlaceHoldRequest) => Promise<Response & import("hono").TypedResponse<{
13
+ hold: {
14
+ id: string;
15
+ subjectType: "audit" | "organization" | "user";
16
+ subjectId: string | null;
17
+ reason: string;
18
+ placedBy: string;
19
+ placedAt: string;
20
+ state: "active" | "released";
21
+ releasedAt: string | null;
22
+ releasedBy: string | null;
23
+ releaseReason: string | null;
24
+ };
25
+ }, 201, "json">>;
26
+ /** `POST /control-plane/holds/:holdId/release` — release a hold, with a reason. */
27
+ export declare function releaseHoldHandler(deps: ControlPlaneHandlerDeps): (c: Context<PithyHonoEnv>, params: HoldParams, body: ReleaseHoldRequest) => Promise<Response & import("hono").TypedResponse<{
28
+ hold: {
29
+ id: string;
30
+ subjectType: "audit" | "organization" | "user";
31
+ subjectId: string | null;
32
+ reason: string;
33
+ placedBy: string;
34
+ placedAt: string;
35
+ state: "active" | "released";
36
+ releasedAt: string | null;
37
+ releasedBy: string | null;
38
+ releaseReason: string | null;
39
+ };
40
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">>;
41
+ /** `GET /control-plane/holds` — every hold, standing first. Read-only. */
42
+ export declare function listHoldsHandler(deps: ControlPlaneHandlerDeps): (c: Context<PithyHonoEnv>, query: HoldListQuery) => Promise<Response & import("hono").TypedResponse<{
43
+ active: number;
44
+ holds: {
45
+ id: string;
46
+ subjectType: "audit" | "organization" | "user";
47
+ subjectId: string | null;
48
+ reason: string;
49
+ placedBy: string;
50
+ placedAt: string;
51
+ state: "active" | "released";
52
+ releasedAt: string | null;
53
+ releasedBy: string | null;
54
+ releaseReason: string | null;
55
+ }[];
56
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">>;
57
+ //# sourceMappingURL=holds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"holds.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/http/holds.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,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,KAAK,uBAAuB,EAAU,MAAM,YAAY,CAAC;AAClE,OAAO,KAAK,EAAuD,aAAa,EAAE,MAAM,aAAa,CAAC;AACtG,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAcjG,uCAAuC;AACvC,wBAAgB,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,aAAa,CAavD;AAwBD,kDAAkD;AAClD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,OAC3C,OAAO,CAAC,YAAY,CAAC,QAAQ,gBAAgB;;;;;;;;;;;;;iBAY/D;AAED,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,uBAAuB,OAC7C,OAAO,CAAC,YAAY,CAAC,UAAU,UAAU,QAAQ,kBAAkB;;;;;;;;;;;;;mEAWrF;AAED,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,OAC3C,OAAO,CAAC,YAAY,CAAC,SAAS,aAAa;;;;;;;;;;;;;;mEAY7D"}