@pithy-sh/core 0.13.0 → 0.14.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 +433 -3
  35. package/dist/cli/commandCatalog.generated.d.ts.map +1 -1
  36. package/dist/cli/commandCatalog.generated.js +449 -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 +482 -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
@@ -0,0 +1,343 @@
1
+ // SPDX-FileCopyrightText: 2026 Pithy
2
+ // SPDX-License-Identifier: MIT
3
+ import { MIGRATIONS_READ_SCOPE } from "../scope/scope.js";
4
+ import { standingOf } from "../discovery/healthSummary.js";
5
+ import { AUDIT_PACKAGE, EVIDENCE_CONTROLS, SECRETS_CAPABILITY, SECRETS_PACKAGE } from "./mapping.js";
6
+ //#region src/controlPlane/evidence/derive.ts
7
+ /** Settle a control's state from what was missing and what could not be read. See the module doc. */
8
+ function settle(gaps, unknowns, facts) {
9
+ return {
10
+ state: gaps.length > 0 ? "gap" : unknowns.length > 0 ? "not-derivable" : "evidenced",
11
+ reasons: [...gaps, ...unknowns],
12
+ facts
13
+ };
14
+ }
15
+ /** One entry of the pack: the mapping table's row for the control, beside what was derived. */
16
+ function evidenceEntry(control, derived) {
17
+ const row = EVIDENCE_CONTROLS[control];
18
+ return {
19
+ control,
20
+ title: row.title,
21
+ soc2: [...row.soc2],
22
+ iso27001: [...row.iso27001],
23
+ state: derived.state,
24
+ reasons: derived.reasons,
25
+ facts: derived.facts
26
+ };
27
+ }
28
+ /** How many undescribed routes a reason names before it counts the rest. */
29
+ const NAMED_ROUTE_LIMIT = 10;
30
+ /**
31
+ * Audit logging in force: a composed recorder, the binding it writes through, and this request's emit seam
32
+ * actually recording. The seam's guard emits on every control-plane call, allowed or denied, so a recorder on
33
+ * the seam is a record of every management call — including the one reading this pack.
34
+ */
35
+ function deriveAuditLogging(input) {
36
+ const facts = [{
37
+ fact: "auditRecorder",
38
+ value: input.recorder?.capability ?? null,
39
+ source: "composition"
40
+ }];
41
+ const gaps = [];
42
+ if (!input.recorder) {
43
+ gaps.push(`No composed capability records audit events, so control-plane calls and every other audited action go unrecorded. Compose ${AUDIT_PACKAGE}.`);
44
+ return settle(gaps, [], facts);
45
+ }
46
+ for (const { binding, present } of input.recorder.bindings) {
47
+ facts.push({
48
+ fact: "bindingPresent",
49
+ subject: binding,
50
+ value: present,
51
+ source: "bindings"
52
+ });
53
+ if (!present) gaps.push(`The \`${binding}\` binding \`${input.recorder.capability}\` records through is missing from this Worker, so its events are dropped.`);
54
+ }
55
+ facts.push({
56
+ fact: "controlPlaneCallsRecorded",
57
+ value: input.emitting,
58
+ source: "auditRecorder"
59
+ });
60
+ if (!input.emitting) gaps.push(`This request's emit seam is not \`${input.recorder.capability}\`'s recorder, so control-plane calls go unrecorded.`);
61
+ return settle(gaps, [], facts);
62
+ }
63
+ /** The strategies a spec names, as a list. */
64
+ function strategiesOf(operation) {
65
+ const verification = operation.spec?.verification;
66
+ if (verification === void 0) return [];
67
+ return typeof verification === "string" ? [verification] : verification;
68
+ }
69
+ /** Why a control reading the route table cannot: an app `createBackend` did not assemble has no recorded groups. */
70
+ const NO_ROUTE_TABLE = "This Worker's route table was not assembled by `createBackend`, so it cannot be read.";
71
+ /**
72
+ * Every route declares how a caller is verified: `describeRoute`'s `verification`, read off the composed route
73
+ * table. A route with no `describeRoute` declares nothing, and is named.
74
+ */
75
+ function deriveRouteVerification(operations) {
76
+ if (operations === void 0) return settle([], [NO_ROUTE_TABLE], []);
77
+ const undescribed = operations.filter((operation) => operation.spec === void 0);
78
+ const counts = /* @__PURE__ */ new Map();
79
+ for (const operation of operations) for (const strategy of strategiesOf(operation)) counts.set(strategy, (counts.get(strategy) ?? 0) + 1);
80
+ const facts = [
81
+ {
82
+ fact: "routes",
83
+ value: operations.length,
84
+ source: "routeSpec"
85
+ },
86
+ {
87
+ fact: "undescribedRoutes",
88
+ value: undescribed.length,
89
+ source: "routeSpec"
90
+ },
91
+ ...[...counts].map(([strategy, count]) => ({
92
+ fact: "routesVerifiedBy",
93
+ subject: strategy,
94
+ value: count,
95
+ source: "routeSpec"
96
+ }))
97
+ ];
98
+ const gaps = undescribed.slice(0, NAMED_ROUTE_LIMIT).map((operation) => `\`${operation.method} ${operation.path}\` declares no verification.`);
99
+ const rest = undescribed.length - NAMED_ROUTE_LIMIT;
100
+ if (rest > 0) gaps.push(`${rest} more ${rest === 1 ? "route declares" : "routes declare"} no verification.`);
101
+ return settle(gaps, [], facts);
102
+ }
103
+ /** Why a control reading the connections table cannot. */
104
+ const CONNECTIONS_UNREADABLE = "The control-plane connections table could not be read on this call.";
105
+ /**
106
+ * Management access attributed and scoped: every connection in this environment, with the principal who
107
+ * authorized it, its actor kind, its purpose (#721) and its granted scopes. The principal is the email the
108
+ * connection records and nothing else; no audit row's actor is copied in.
109
+ */
110
+ function deriveManagementAccess(connections) {
111
+ if (connections === null) return settle([], [CONNECTIONS_UNREADABLE], []);
112
+ const facts = [{
113
+ fact: "connections",
114
+ value: connections.length,
115
+ source: "connection"
116
+ }];
117
+ const gaps = [];
118
+ for (const connection of connections) {
119
+ facts.push({
120
+ fact: "authorizedBy",
121
+ subject: connection.id,
122
+ value: connection.authorizedBy,
123
+ source: "connection"
124
+ }, {
125
+ fact: "actorKind",
126
+ subject: connection.id,
127
+ value: connection.actorKind,
128
+ source: "connection"
129
+ }, {
130
+ fact: "purpose",
131
+ subject: connection.id,
132
+ value: connection.purpose,
133
+ source: "connection"
134
+ }, {
135
+ fact: "scopes",
136
+ subject: connection.id,
137
+ value: [...connection.scopes],
138
+ source: "connection"
139
+ });
140
+ const missing = [
141
+ connection.authorizedBy === null ? "principal" : null,
142
+ connection.actorKind === null ? "actor kind" : null,
143
+ connection.purpose === null ? "purpose" : null
144
+ ].filter((name) => name !== null);
145
+ if (missing.length > 0) gaps.push(`Connection ${connection.id} records no ${missing.join(", ")}: it was registered before they were recorded. Registering it again records them.`);
146
+ }
147
+ return settle(gaps, [], facts);
148
+ }
149
+ /** A day, in ms. Key ages are reported in whole days, which is how a rotation cadence is reasoned about. */
150
+ const MS_PER_DAY = 864e5;
151
+ /** Whether a key verifies right now: started, not past its window, not revoked. */
152
+ function isLive(key, now) {
153
+ if (key.revokedAt !== null) return false;
154
+ if (key.validFrom.getTime() > now.getTime()) return false;
155
+ return key.validUntil === null || key.validUntil.getTime() > now.getTime();
156
+ }
157
+ /** Secrets' facts, gaps and unknowns from its declared health — by standing for a state, never by its word. */
158
+ function secretsRotation(input, facts, gaps, unknowns) {
159
+ if (input.state === "not-composed") {
160
+ unknowns.push(`${SECRETS_PACKAGE} is not composed, so no secret's rotation cadence is declared here.`);
161
+ return;
162
+ }
163
+ const report = input.report;
164
+ if (report.state === "withheld") {
165
+ const scopes = [...new Set(input.keys.map((key) => `\`${key.scope}\``))].join(", ");
166
+ unknowns.push(`This connection is not granted ${scopes}, the scope \`${SECRETS_CAPABILITY}\` reports rotation status under.`);
167
+ return;
168
+ }
169
+ if (report.state === "unavailable") {
170
+ unknowns.push(`\`${SECRETS_CAPABILITY}\` could not report its rotation status on this call.`);
171
+ return;
172
+ }
173
+ if (report.state === "undeclared") {
174
+ unknowns.push(`\`${SECRETS_CAPABILITY}\` reports no rotation status: it predates the health summary.`);
175
+ return;
176
+ }
177
+ for (const key of input.keys) {
178
+ const value = report.values[key.key];
179
+ if (value === void 0) continue;
180
+ const standing = standingOf(key, value);
181
+ facts.push({
182
+ fact: key.key,
183
+ subject: SECRETS_CAPABILITY,
184
+ value: key.kind === "count" ? value : standing,
185
+ source: "capabilityHealth"
186
+ });
187
+ if (standing === "attention") gaps.push(`\`${SECRETS_CAPABILITY}\` reports \`${key.key}\` wanting attention. Its status route says which and why.`);
188
+ }
189
+ }
190
+ /**
191
+ * Keys and secrets rotate: each connection's live keys and the age of the oldest, secrets' declared cadence and
192
+ * what is past it, and the field key's retirement guard (#390). Ages are facts and never graded here: the seam
193
+ * declares no cadence for control-plane keys, and inventing one would be the pack's opinion.
194
+ */
195
+ function deriveKeyRotation(input) {
196
+ const facts = [];
197
+ const gaps = [];
198
+ const unknowns = [];
199
+ if (input.connections === null) unknowns.push(`${CONNECTIONS_UNREADABLE} Key ages are unknown.`);
200
+ else for (const connection of input.connections) {
201
+ const live = connection.keys.filter((key) => isLive(key, input.now));
202
+ const oldest = live.reduce((min, key) => min === null ? key.validFrom.getTime() : Math.min(min, key.validFrom.getTime()), null);
203
+ facts.push({
204
+ fact: "liveKeys",
205
+ subject: connection.id,
206
+ value: live.length,
207
+ source: "connectionKeys"
208
+ }, {
209
+ fact: "oldestLiveKeyAgeDays",
210
+ subject: connection.id,
211
+ value: oldest === null ? null : Math.floor((input.now.getTime() - oldest) / MS_PER_DAY),
212
+ source: "connectionKeys"
213
+ });
214
+ }
215
+ secretsRotation(input.secrets, facts, gaps, unknowns);
216
+ facts.push({
217
+ fact: "fieldKeyRetirementBlocked",
218
+ value: input.retirement.blocked,
219
+ source: "fieldKeyRetirement"
220
+ }, {
221
+ fact: "fieldKeyRetirementReasons",
222
+ value: [...input.retirement.reasons],
223
+ source: "fieldKeyRetirement"
224
+ }, {
225
+ fact: "liveFieldKeyVersions",
226
+ value: input.retirement.liveVersions,
227
+ source: "fieldKeyRetirement"
228
+ });
229
+ if (input.retirement.blocked) gaps.push(`The field key's oldest live version is held from retirement: ${input.retirement.reasons.join(", ")}.`);
230
+ else if (input.retirement.reasons.includes("incomplete-coverage")) unknowns.push("The field key's retirement guard could not read every source on this call.");
231
+ return settle(gaps, unknowns, facts);
232
+ }
233
+ /**
234
+ * Change history per environment: each bound database's applied migrations, the last group and when it applied
235
+ * (#729). Behind `migrations:read`, as the same numbers are on the manifest, so the pack never widens it.
236
+ */
237
+ function deriveChangeHistory(input) {
238
+ if (!input.granted) return settle([], [`This connection is not granted \`${MIGRATIONS_READ_SCOPE}\`, the scope migration state is read under.`], []);
239
+ const facts = [];
240
+ const gaps = [];
241
+ const unknowns = [];
242
+ for (const database of input.databases) {
243
+ if (database.state === "unreadable") {
244
+ unknowns.push(`The migration ledger of database \`${database.database}\` could not be read on this call.`);
245
+ continue;
246
+ }
247
+ const subject = database.database;
248
+ facts.push({
249
+ fact: "applied",
250
+ subject,
251
+ value: database.applied.length,
252
+ source: "migrationLedger"
253
+ }, {
254
+ fact: "pending",
255
+ subject,
256
+ value: database.pending.length,
257
+ source: "migrationLedger"
258
+ }, {
259
+ fact: "undeclared",
260
+ subject,
261
+ value: database.undeclared.length,
262
+ source: "migrationLedger"
263
+ }, {
264
+ fact: "lastGroup",
265
+ subject,
266
+ value: database.lastGroup?.group ?? null,
267
+ source: "migrationLedger"
268
+ }, {
269
+ fact: "lastGroupAppliedAt",
270
+ subject,
271
+ value: database.lastGroup?.appliedAt ?? null,
272
+ source: "migrationLedger"
273
+ });
274
+ if (database.pending.length > 0) gaps.push(`Database \`${subject}\` has ${database.pending.length} declared ${database.pending.length === 1 ? "migration" : "migrations"} this build ships and has not applied.`);
275
+ }
276
+ return settle(gaps, unknowns, facts);
277
+ }
278
+ /**
279
+ * Data classified and encrypted at rest: the classes the stored schemas hold (#132) beside the classes the
280
+ * project declared, and the fields marked `encrypted()` (#390). Classes and counts only, never a field's name:
281
+ * those are `GET /privacy`'s, behind their own scope.
282
+ */
283
+ function deriveDataProtection(input) {
284
+ if (input.inventory === null) return settle([], ["This Worker's stored schemas could not be walked for privacy markers on this call."], []);
285
+ const held = input.inventory;
286
+ const facts = [
287
+ {
288
+ fact: "classesHeld",
289
+ value: [...held.observed],
290
+ source: "privacyMarkers"
291
+ },
292
+ ...held.observed.map((name) => ({
293
+ fact: "markedFields",
294
+ subject: name,
295
+ value: held.counts[name] ?? 0,
296
+ source: "privacyMarkers"
297
+ })),
298
+ {
299
+ fact: "schemasWalked",
300
+ value: held.schemasWalked,
301
+ source: "privacyMarkers"
302
+ },
303
+ {
304
+ fact: "encryptedFields",
305
+ value: held.encryptedFields,
306
+ source: "encryptedMarkers"
307
+ },
308
+ {
309
+ fact: "unrotatableEncryptedFields",
310
+ value: held.unrotatable,
311
+ source: "encryptedMarkers"
312
+ },
313
+ {
314
+ fact: "classesDeclared",
315
+ value: input.attestation === null ? null : [...input.attestation.classes],
316
+ source: "privacyAttestation"
317
+ }
318
+ ];
319
+ if (input.attestation === null) return settle([], ["No privacy attestation reached this Worker, so what the project declares cannot be read. A deploy through the CLI writes one."], facts);
320
+ const declared = new Set(input.attestation.classes);
321
+ const observed = new Set(held.observed);
322
+ return settle([...held.observed.filter((name) => !declared.has(name)).map((name) => `Stored schemas hold \`${name}\` fields the project never declared.`), ...input.attestation.classes.filter((name) => !observed.has(name)).map((name) => `The project declares \`${name}\` and no stored field is marked with it.`)], [], facts);
323
+ }
324
+ /**
325
+ * CSRF wherever cookie mode is: the routes that accept a session cookie, and whether a composed capability
326
+ * publishes the same-origin policy `requireSameOrigin()` enforces. No route taking a cookie needs none.
327
+ */
328
+ function deriveCsrf(input) {
329
+ if (input.operations === void 0) return settle([], [NO_ROUTE_TABLE], []);
330
+ const sessionRoutes = input.operations.filter((operation) => strategiesOf(operation).includes("session")).length;
331
+ const facts = [{
332
+ fact: "sessionRoutes",
333
+ value: sessionRoutes,
334
+ source: "routeSpec"
335
+ }, {
336
+ fact: "sameOriginPolicy",
337
+ value: input.sameOriginPublished,
338
+ source: "sameOriginGate"
339
+ }];
340
+ return settle(sessionRoutes > 0 && !input.sameOriginPublished ? [`${sessionRoutes} ${sessionRoutes === 1 ? "route accepts" : "routes accept"} a session cookie and no composed capability publishes a same-origin policy. Compose \`@pithy-sh/auth\`.`] : [], [], facts);
341
+ }
342
+ //#endregion
343
+ export { deriveAuditLogging, deriveChangeHistory, deriveCsrf, deriveDataProtection, deriveKeyRotation, deriveManagementAccess, deriveRouteVerification, evidenceEntry, settle };
@@ -0,0 +1,85 @@
1
+ // SPDX-FileCopyrightText: 2026 Pithy
2
+ // SPDX-License-Identifier: MIT
3
+ import { z } from "zod";
4
+ /**
5
+ * The evidence pack's mapping table (#821): which controls the Worker derives evidence for, and the SOC 2 and
6
+ * ISO/IEC 27001:2022 references each one answers.
7
+ *
8
+ * **Evidence, never a verdict.** Each control the pack reports carries the facts it read, where each came from,
9
+ * and one of three states. None of them is a judgment about a framework: an auditor decides that, and nothing
10
+ * here or on the wire uses the vocabulary of one.
11
+ *
12
+ * **Versioned, and the version travels with every pack.** A reference moving, a control joining or leaving, or
13
+ * a rule for a state changing is a new {@link EVIDENCE_MAPPING_VERSION}, so a pack read last quarter and one read
14
+ * today can be told apart by the number on them. A control needing a declaration the kit does not hold is a
15
+ * later version of this table, never a guess in this one.
16
+ *
17
+ * Zod-only, so a management client can import the table and the vocabulary without the Worker's runtime.
18
+ */
19
+ /** The version of this table. Bumped whenever a control, a reference, or a state rule changes. */
20
+ export declare const EVIDENCE_MAPPING_VERSION = 1;
21
+ /** The seven controls, by id. Stable: a client keys on these, and the title may be reworded. */
22
+ export declare const EVIDENCE_CONTROL_IDS: readonly ["audit-logging", "route-verification", "management-access", "key-rotation", "change-history", "data-protection", "csrf"];
23
+ /** One control's id. */
24
+ export declare const EvidenceControlId: z.ZodEnum<{
25
+ "audit-logging": "audit-logging";
26
+ "change-history": "change-history";
27
+ csrf: "csrf";
28
+ "data-protection": "data-protection";
29
+ "key-rotation": "key-rotation";
30
+ "management-access": "management-access";
31
+ "route-verification": "route-verification";
32
+ }>;
33
+ export type EvidenceControlId = z.infer<typeof EvidenceControlId>;
34
+ /**
35
+ * What the Worker could say about a control. Three states, and none of them a verdict.
36
+ *
37
+ * - `evidenced`: the facts are present.
38
+ * - `gap`: something is missing, and the entry names what.
39
+ * - `not-derivable`: the Worker cannot say, and the entry names why.
40
+ */
41
+ export declare const EvidenceState: z.ZodEnum<{
42
+ evidenced: "evidenced";
43
+ gap: "gap";
44
+ "not-derivable": "not-derivable";
45
+ }>;
46
+ export type EvidenceState = z.infer<typeof EvidenceState>;
47
+ /**
48
+ * Where a fact came from: the declaration or record the Worker read it off. Every fact names one, so an auditor
49
+ * can follow each back to the code or the row it is about.
50
+ */
51
+ export declare const EvidenceSource: z.ZodEnum<{
52
+ auditRecorder: "auditRecorder";
53
+ bindings: "bindings";
54
+ capabilityHealth: "capabilityHealth";
55
+ composition: "composition";
56
+ connection: "connection";
57
+ connectionKeys: "connectionKeys";
58
+ encryptedMarkers: "encryptedMarkers";
59
+ fieldKeyRetirement: "fieldKeyRetirement";
60
+ migrationLedger: "migrationLedger";
61
+ privacyAttestation: "privacyAttestation";
62
+ privacyMarkers: "privacyMarkers";
63
+ routeSpec: "routeSpec";
64
+ sameOriginGate: "sameOriginGate";
65
+ }>;
66
+ export type EvidenceSource = z.infer<typeof EvidenceSource>;
67
+ /** One row of the table: what a control is called and the framework references it answers. */
68
+ export interface EvidenceControl {
69
+ readonly title: string;
70
+ readonly soc2: readonly string[];
71
+ readonly iso27001: readonly string[];
72
+ }
73
+ /**
74
+ * The table itself, keyed by control so every id has exactly one row by construction. The pack reports them in
75
+ * {@link EVIDENCE_CONTROL_IDS} order. SOC 2 references are the AICPA Trust Services Criteria; ISO references are
76
+ * ISO/IEC 27001:2022 Annex A.
77
+ */
78
+ export declare const EVIDENCE_CONTROLS: Readonly<Record<EvidenceControlId, EvidenceControl>>;
79
+ /** The capability whose composition is the audit-logging control's subject. Named in the gap it reports. */
80
+ export declare const AUDIT_PACKAGE = "@pithy-sh/audit";
81
+ /** The capability whose declared health carries secrets' rotation cadence and what is overdue. */
82
+ export declare const SECRETS_CAPABILITY = "secrets";
83
+ /** Its package, named in a reason. */
84
+ export declare const SECRETS_PACKAGE = "@pithy-sh/secrets";
85
+ //# sourceMappingURL=mapping.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapping.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/evidence/mapping.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;GAcG;AAEH,kGAAkG;AAClG,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,gGAAgG;AAChG,eAAO,MAAM,oBAAoB,YAC/B,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,MAAM,CACE,CAAC;AAEX,wBAAwB;AACxB,eAAO,MAAM,iBAAiB;;;;;;;;EAEmF,CAAC;AAClH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE;;;;;;GAMG;AACH,eAAO,MAAM,aAAa;;;;EAIvB,CAAC;AACJ,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;EAkBxB,CAAC;AACJ,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,8FAA8F;AAC9F,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,iBAAiB,EAAE,eAAe,CAAC,CAgBlF,CAAC;AAEF,4GAA4G;AAC5G,eAAO,MAAM,aAAa,oBAAoB,CAAC;AAE/C,kGAAkG;AAClG,eAAO,MAAM,kBAAkB,YAAY,CAAC;AAE5C,sCAAsC;AACtC,eAAO,MAAM,eAAe,sBAAsB,CAAC"}
@@ -0,0 +1,114 @@
1
+ // SPDX-FileCopyrightText: 2026 Pithy
2
+ // SPDX-License-Identifier: MIT
3
+ import { z } from "zod";
4
+ //#region src/controlPlane/evidence/mapping.ts
5
+ /**
6
+ * The evidence pack's mapping table (#821): which controls the Worker derives evidence for, and the SOC 2 and
7
+ * ISO/IEC 27001:2022 references each one answers.
8
+ *
9
+ * **Evidence, never a verdict.** Each control the pack reports carries the facts it read, where each came from,
10
+ * and one of three states. None of them is a judgment about a framework: an auditor decides that, and nothing
11
+ * here or on the wire uses the vocabulary of one.
12
+ *
13
+ * **Versioned, and the version travels with every pack.** A reference moving, a control joining or leaving, or
14
+ * a rule for a state changing is a new {@link EVIDENCE_MAPPING_VERSION}, so a pack read last quarter and one read
15
+ * today can be told apart by the number on them. A control needing a declaration the kit does not hold is a
16
+ * later version of this table, never a guess in this one.
17
+ *
18
+ * Zod-only, so a management client can import the table and the vocabulary without the Worker's runtime.
19
+ */
20
+ /** The version of this table. Bumped whenever a control, a reference, or a state rule changes. */
21
+ const EVIDENCE_MAPPING_VERSION = 1;
22
+ /** The seven controls, by id. Stable: a client keys on these, and the title may be reworded. */
23
+ const EVIDENCE_CONTROL_IDS = [
24
+ "audit-logging",
25
+ "route-verification",
26
+ "management-access",
27
+ "key-rotation",
28
+ "change-history",
29
+ "data-protection",
30
+ "csrf"
31
+ ];
32
+ /** One control's id. */
33
+ const EvidenceControlId = z.enum(EVIDENCE_CONTROL_IDS).describe("The control this entry reports on, as a stable id. Keyed on by clients; the title may be reworded.");
34
+ /**
35
+ * What the Worker could say about a control. Three states, and none of them a verdict.
36
+ *
37
+ * - `evidenced`: the facts are present.
38
+ * - `gap`: something is missing, and the entry names what.
39
+ * - `not-derivable`: the Worker cannot say, and the entry names why.
40
+ */
41
+ const EvidenceState = z.enum([
42
+ "evidenced",
43
+ "gap",
44
+ "not-derivable"
45
+ ]).describe("What the Worker could say about this control: `evidenced` when the facts are present, `gap` when something is missing and the entry names what, `not-derivable` when the Worker cannot say and the entry names why. Evidence for an auditor, never a judgment.");
46
+ /**
47
+ * Where a fact came from: the declaration or record the Worker read it off. Every fact names one, so an auditor
48
+ * can follow each back to the code or the row it is about.
49
+ */
50
+ const EvidenceSource = z.enum([
51
+ "composition",
52
+ "auditRecorder",
53
+ "bindings",
54
+ "routeSpec",
55
+ "connection",
56
+ "connectionKeys",
57
+ "capabilityHealth",
58
+ "fieldKeyRetirement",
59
+ "migrationLedger",
60
+ "privacyMarkers",
61
+ "encryptedMarkers",
62
+ "privacyAttestation",
63
+ "sameOriginGate"
64
+ ]).describe("The declaration a fact was read from: `composition` (the capabilities this Worker composes), `auditRecorder` (the audit emit seam), `bindings` (this Worker's bindings), `routeSpec` (each route's `describeRoute`), `connection` (a control-plane connection's row), `connectionKeys` (its registered keys), `capabilityHealth` (a capability's declared health summary), `fieldKeyRetirement` (the field key's retirement guard), `migrationLedger` (a database's migration ledger), `privacyMarkers` (privacy-class markers on stored schemas), `encryptedMarkers` (`encrypted()` markers), `privacyAttestation` (the classes the last `pithy deploy` declared), or `sameOriginGate` (the same-origin policy a capability publishes).");
65
+ /**
66
+ * The table itself, keyed by control so every id has exactly one row by construction. The pack reports them in
67
+ * {@link EVIDENCE_CONTROL_IDS} order. SOC 2 references are the AICPA Trust Services Criteria; ISO references are
68
+ * ISO/IEC 27001:2022 Annex A.
69
+ */
70
+ const EVIDENCE_CONTROLS = {
71
+ "audit-logging": {
72
+ title: "Audit logging in force",
73
+ soc2: ["CC7.2"],
74
+ iso27001: ["A.8.15"]
75
+ },
76
+ "route-verification": {
77
+ title: "Every route declares how a caller is verified",
78
+ soc2: ["CC6.1"],
79
+ iso27001: ["A.5.15"]
80
+ },
81
+ "management-access": {
82
+ title: "Management access attributed and scoped",
83
+ soc2: ["CC6.2", "CC6.3"],
84
+ iso27001: ["A.5.18"]
85
+ },
86
+ "key-rotation": {
87
+ title: "Keys and secrets rotate",
88
+ soc2: ["CC6.1"],
89
+ iso27001: ["A.8.24"]
90
+ },
91
+ "change-history": {
92
+ title: "Change history per environment",
93
+ soc2: ["CC8.1"],
94
+ iso27001: ["A.8.32"]
95
+ },
96
+ "data-protection": {
97
+ title: "Data classified and encrypted at rest",
98
+ soc2: ["C1.1"],
99
+ iso27001: ["A.8.24"]
100
+ },
101
+ csrf: {
102
+ title: "CSRF wherever cookie mode is",
103
+ soc2: ["CC6.6"],
104
+ iso27001: ["A.8.26"]
105
+ }
106
+ };
107
+ /** The capability whose composition is the audit-logging control's subject. Named in the gap it reports. */
108
+ const AUDIT_PACKAGE = "@pithy-sh/audit";
109
+ /** The capability whose declared health carries secrets' rotation cadence and what is overdue. */
110
+ const SECRETS_CAPABILITY = "secrets";
111
+ /** Its package, named in a reason. */
112
+ const SECRETS_PACKAGE = "@pithy-sh/secrets";
113
+ //#endregion
114
+ export { AUDIT_PACKAGE, EVIDENCE_CONTROLS, EVIDENCE_CONTROL_IDS, EVIDENCE_MAPPING_VERSION, EvidenceControlId, EvidenceSource, EvidenceState, SECRETS_CAPABILITY, SECRETS_PACKAGE };
@@ -0,0 +1,46 @@
1
+ // SPDX-FileCopyrightText: 2026 Pithy
2
+ // SPDX-License-Identifier: MIT
3
+ import type { Context } from "hono";
4
+ import type { Capability, PithyHonoEnv } from "../../capability/capability";
5
+ import type { RouteGroup } from "../../openapi/document";
6
+ import type { PrivacyInventory } from "../../privacy/report";
7
+ import type { RetirementVerdict } from "../../privacy/retirement";
8
+ import type { ControlPlaneContext } from "../context";
9
+ import { type ControlPlaneDatabase } from "../data/tables";
10
+ import { type CapabilityHealthSource } from "../discovery/health";
11
+ import type { ControlPlaneEvidenceResponse } from "../http/responses";
12
+ import { type ComposedMigrationDatabase } from "../migrationState/migrationState";
13
+ /**
14
+ * The reads behind the evidence pack (#821): what the Worker composes, records and declares, gathered once per
15
+ * call and handed to the pure derivations in `./derive.ts`.
16
+ *
17
+ * **Every read reports what it found or that it could not look, and never throws past here.** One unreadable
18
+ * table costs the controls that read it their answer — `not-derivable`, with the reason — and the pack still
19
+ * answers. A cause goes to the Worker's log, for the operator, and never into the pack: a D1 error can name a
20
+ * table or a query.
21
+ *
22
+ * **The pack never widens a scope.** Migration state is read only when the connection holds `migrations:read`,
23
+ * and a capability's health only for the keys this connection is granted, exactly as the manifest reads them.
24
+ */
25
+ /** What the pack reads from the composed seam. Thunks, because the `compose` hook fills them after routes mount. */
26
+ export interface EvidenceSources {
27
+ /** Every capability this Worker composed. */
28
+ composition: () => readonly Capability[];
29
+ /** Every capability's health contribution, by name. */
30
+ composedHealth: () => ReadonlyMap<string, CapabilityHealthSource>;
31
+ /** Every database this Worker binds, with what this build declares for it. */
32
+ composedMigrations: () => readonly ComposedMigrationDatabase[];
33
+ /** The privacy inventory, walked once per isolate. */
34
+ privacyInventory: () => PrivacyInventory;
35
+ /** The field key's retirement guard. Never throws. */
36
+ retirement: (env: Record<string, unknown>) => Promise<RetirementVerdict>;
37
+ /** The route table `createBackend` recorded, or `undefined` for an app it did not assemble. */
38
+ routeGroups: () => readonly RouteGroup[] | undefined;
39
+ /** The app database for this request. */
40
+ database: (c: Context<PithyHonoEnv>) => ControlPlaneDatabase;
41
+ /** The clock. */
42
+ now: () => Date;
43
+ }
44
+ /** Read everything the pack derives from, and derive it. */
45
+ export declare function readEvidencePack(c: Context<PithyHonoEnv>, sources: EvidenceSources, context: ControlPlaneContext): Promise<ControlPlaneEvidenceResponse>;
46
+ //# sourceMappingURL=pack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack.d.ts","sourceRoot":"","sources":["../../../src/controlPlane/evidence/pack.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,OAAO,EAAmC,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC5F,OAAO,EAAE,KAAK,sBAAsB,EAAwB,MAAM,qBAAqB,CAAC;AACxF,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,KAAK,yBAAyB,EAA0B,MAAM,kCAAkC,CAAC;AAiB1G;;;;;;;;;;;GAWG;AAEH,oHAAoH;AACpH,MAAM,WAAW,eAAe;IAC9B,6CAA6C;IAC7C,WAAW,EAAE,MAAM,SAAS,UAAU,EAAE,CAAC;IACzC,uDAAuD;IACvD,cAAc,EAAE,MAAM,WAAW,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IAClE,8EAA8E;IAC9E,kBAAkB,EAAE,MAAM,SAAS,yBAAyB,EAAE,CAAC;IAC/D,sDAAsD;IACtD,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;IACzC,sDAAsD;IACtD,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACzE,+FAA+F;IAC/F,WAAW,EAAE,MAAM,SAAS,UAAU,EAAE,GAAG,SAAS,CAAC;IACrD,yCAAyC;IACzC,QAAQ,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,oBAAoB,CAAC;IAC7D,iBAAiB;IACjB,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AAqED,4DAA4D;AAC5D,wBAAsB,gBAAgB,CACpC,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,EACxB,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,4BAA4B,CAAC,CAiDvC"}