@fuzdev/fuz_app 0.30.0 → 0.32.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 (222) hide show
  1. package/dist/actions/CLAUDE.md +630 -0
  2. package/dist/actions/action_rpc.d.ts +29 -0
  3. package/dist/actions/action_rpc.d.ts.map +1 -1
  4. package/dist/actions/action_rpc.js +42 -6
  5. package/dist/actions/action_types.d.ts +2 -2
  6. package/dist/actions/cancel.d.ts +12 -13
  7. package/dist/actions/cancel.d.ts.map +1 -1
  8. package/dist/actions/cancel.js +10 -13
  9. package/dist/actions/heartbeat.d.ts +8 -13
  10. package/dist/actions/heartbeat.d.ts.map +1 -1
  11. package/dist/actions/heartbeat.js +5 -8
  12. package/dist/actions/register_action_ws.d.ts +3 -3
  13. package/dist/actions/register_action_ws.js +2 -2
  14. package/dist/actions/register_ws_endpoint.d.ts +4 -4
  15. package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
  16. package/dist/actions/register_ws_endpoint.js +3 -3
  17. package/dist/actions/rpc_client.d.ts +29 -0
  18. package/dist/actions/rpc_client.d.ts.map +1 -1
  19. package/dist/actions/rpc_client.js +31 -0
  20. package/dist/actions/socket.svelte.d.ts +16 -16
  21. package/dist/actions/socket.svelte.d.ts.map +1 -1
  22. package/dist/actions/socket.svelte.js +15 -15
  23. package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
  24. package/dist/auth/CLAUDE.md +945 -0
  25. package/dist/auth/account_action_specs.d.ts +216 -0
  26. package/dist/auth/account_action_specs.d.ts.map +1 -0
  27. package/dist/auth/account_action_specs.js +159 -0
  28. package/dist/auth/account_actions.d.ts +51 -0
  29. package/dist/auth/account_actions.d.ts.map +1 -0
  30. package/dist/auth/account_actions.js +119 -0
  31. package/dist/auth/account_queries.d.ts +6 -2
  32. package/dist/auth/account_queries.d.ts.map +1 -1
  33. package/dist/auth/account_queries.js +40 -4
  34. package/dist/auth/account_routes.d.ts +94 -16
  35. package/dist/auth/account_routes.d.ts.map +1 -1
  36. package/dist/auth/account_routes.js +108 -180
  37. package/dist/auth/account_schema.d.ts +85 -30
  38. package/dist/auth/account_schema.d.ts.map +1 -1
  39. package/dist/auth/account_schema.js +40 -8
  40. package/dist/auth/admin_action_specs.d.ts +674 -0
  41. package/dist/auth/admin_action_specs.d.ts.map +1 -0
  42. package/dist/auth/admin_action_specs.js +287 -0
  43. package/dist/auth/admin_actions.d.ts +69 -0
  44. package/dist/auth/admin_actions.d.ts.map +1 -0
  45. package/dist/auth/admin_actions.js +256 -0
  46. package/dist/auth/admin_rpc_actions.d.ts +49 -0
  47. package/dist/auth/admin_rpc_actions.d.ts.map +1 -0
  48. package/dist/auth/admin_rpc_actions.js +32 -0
  49. package/dist/auth/api_token.d.ts +10 -0
  50. package/dist/auth/api_token.d.ts.map +1 -1
  51. package/dist/auth/api_token.js +9 -0
  52. package/dist/auth/api_token_queries.d.ts +3 -3
  53. package/dist/auth/api_token_queries.js +3 -3
  54. package/dist/auth/app_settings_schema.d.ts +4 -3
  55. package/dist/auth/app_settings_schema.d.ts.map +1 -1
  56. package/dist/auth/app_settings_schema.js +2 -1
  57. package/dist/auth/audit_log_routes.d.ts +14 -6
  58. package/dist/auth/audit_log_routes.d.ts.map +1 -1
  59. package/dist/auth/audit_log_routes.js +22 -79
  60. package/dist/auth/audit_log_schema.d.ts +100 -29
  61. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  62. package/dist/auth/audit_log_schema.js +83 -11
  63. package/dist/auth/bootstrap_routes.d.ts +14 -0
  64. package/dist/auth/bootstrap_routes.d.ts.map +1 -1
  65. package/dist/auth/bootstrap_routes.js +10 -3
  66. package/dist/auth/cleanup.d.ts +63 -0
  67. package/dist/auth/cleanup.d.ts.map +1 -0
  68. package/dist/auth/cleanup.js +80 -0
  69. package/dist/auth/invite_schema.d.ts +11 -10
  70. package/dist/auth/invite_schema.d.ts.map +1 -1
  71. package/dist/auth/invite_schema.js +4 -3
  72. package/dist/auth/migrations.d.ts +6 -0
  73. package/dist/auth/migrations.d.ts.map +1 -1
  74. package/dist/auth/migrations.js +28 -0
  75. package/dist/auth/permit_offer_action_specs.d.ts +364 -0
  76. package/dist/auth/permit_offer_action_specs.d.ts.map +1 -0
  77. package/dist/auth/permit_offer_action_specs.js +216 -0
  78. package/dist/auth/permit_offer_actions.d.ts +96 -0
  79. package/dist/auth/permit_offer_actions.d.ts.map +1 -0
  80. package/dist/auth/permit_offer_actions.js +428 -0
  81. package/dist/auth/permit_offer_notifications.d.ts +361 -0
  82. package/dist/auth/permit_offer_notifications.d.ts.map +1 -0
  83. package/dist/auth/permit_offer_notifications.js +179 -0
  84. package/dist/auth/permit_offer_queries.d.ts +165 -0
  85. package/dist/auth/permit_offer_queries.d.ts.map +1 -0
  86. package/dist/auth/permit_offer_queries.js +390 -0
  87. package/dist/auth/permit_offer_schema.d.ts +103 -0
  88. package/dist/auth/permit_offer_schema.d.ts.map +1 -0
  89. package/dist/auth/permit_offer_schema.js +142 -0
  90. package/dist/auth/permit_queries.d.ts +77 -14
  91. package/dist/auth/permit_queries.d.ts.map +1 -1
  92. package/dist/auth/permit_queries.js +119 -24
  93. package/dist/auth/session_queries.d.ts +4 -2
  94. package/dist/auth/session_queries.d.ts.map +1 -1
  95. package/dist/auth/session_queries.js +4 -2
  96. package/dist/auth/signup_routes.d.ts +13 -0
  97. package/dist/auth/signup_routes.d.ts.map +1 -1
  98. package/dist/auth/signup_routes.js +14 -7
  99. package/dist/http/CLAUDE.md +584 -0
  100. package/dist/http/pending_effects.d.ts +29 -0
  101. package/dist/http/pending_effects.d.ts.map +1 -0
  102. package/dist/http/pending_effects.js +31 -0
  103. package/dist/http/route_spec.d.ts.map +1 -1
  104. package/dist/http/route_spec.js +4 -3
  105. package/dist/rate_limiter.d.ts +30 -0
  106. package/dist/rate_limiter.d.ts.map +1 -1
  107. package/dist/rate_limiter.js +25 -2
  108. package/dist/realtime/sse_auth_guard.d.ts +2 -0
  109. package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
  110. package/dist/realtime/sse_auth_guard.js +5 -3
  111. package/dist/server/app_server.d.ts +13 -2
  112. package/dist/server/app_server.d.ts.map +1 -1
  113. package/dist/server/app_server.js +12 -1
  114. package/dist/testing/CLAUDE.md +668 -1
  115. package/dist/testing/admin_integration.d.ts +10 -7
  116. package/dist/testing/admin_integration.d.ts.map +1 -1
  117. package/dist/testing/admin_integration.js +382 -482
  118. package/dist/testing/app_server.d.ts +7 -6
  119. package/dist/testing/app_server.d.ts.map +1 -1
  120. package/dist/testing/attack_surface.d.ts +9 -3
  121. package/dist/testing/attack_surface.d.ts.map +1 -1
  122. package/dist/testing/attack_surface.js +4 -4
  123. package/dist/testing/audit_completeness.d.ts +11 -0
  124. package/dist/testing/audit_completeness.d.ts.map +1 -1
  125. package/dist/testing/audit_completeness.js +169 -134
  126. package/dist/testing/auth_apps.d.ts.map +1 -1
  127. package/dist/testing/auth_apps.js +4 -33
  128. package/dist/testing/db.d.ts +1 -1
  129. package/dist/testing/db.d.ts.map +1 -1
  130. package/dist/testing/db.js +2 -0
  131. package/dist/testing/entities.d.ts +35 -13
  132. package/dist/testing/entities.d.ts.map +1 -1
  133. package/dist/testing/entities.js +17 -0
  134. package/dist/testing/integration.d.ts +10 -0
  135. package/dist/testing/integration.d.ts.map +1 -1
  136. package/dist/testing/integration.js +352 -340
  137. package/dist/testing/integration_helpers.d.ts +16 -5
  138. package/dist/testing/integration_helpers.d.ts.map +1 -1
  139. package/dist/testing/integration_helpers.js +24 -4
  140. package/dist/testing/rate_limiting.d.ts +7 -0
  141. package/dist/testing/rate_limiting.d.ts.map +1 -1
  142. package/dist/testing/rate_limiting.js +41 -10
  143. package/dist/testing/rpc_helpers.d.ts +153 -1
  144. package/dist/testing/rpc_helpers.d.ts.map +1 -1
  145. package/dist/testing/rpc_helpers.js +184 -8
  146. package/dist/testing/sse_round_trip.d.ts +8 -0
  147. package/dist/testing/sse_round_trip.d.ts.map +1 -1
  148. package/dist/testing/sse_round_trip.js +10 -3
  149. package/dist/testing/standard.d.ts +9 -1
  150. package/dist/testing/standard.d.ts.map +1 -1
  151. package/dist/testing/standard.js +6 -2
  152. package/dist/testing/stubs.d.ts +10 -2
  153. package/dist/testing/stubs.d.ts.map +1 -1
  154. package/dist/testing/stubs.js +17 -2
  155. package/dist/testing/surface_invariants.d.ts +7 -3
  156. package/dist/testing/surface_invariants.d.ts.map +1 -1
  157. package/dist/testing/surface_invariants.js +5 -4
  158. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  159. package/dist/testing/ws_round_trip.js +9 -38
  160. package/dist/ui/AccountSessions.svelte +8 -4
  161. package/dist/ui/AccountSessions.svelte.d.ts.map +1 -1
  162. package/dist/ui/AdminAccounts.svelte +61 -33
  163. package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
  164. package/dist/ui/AdminAuditLog.svelte +3 -2
  165. package/dist/ui/AdminAuditLog.svelte.d.ts.map +1 -1
  166. package/dist/ui/AdminInvites.svelte +3 -2
  167. package/dist/ui/AdminInvites.svelte.d.ts.map +1 -1
  168. package/dist/ui/AdminOverview.svelte +14 -9
  169. package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
  170. package/dist/ui/AdminPermitHistory.svelte +3 -2
  171. package/dist/ui/AdminPermitHistory.svelte.d.ts.map +1 -1
  172. package/dist/ui/AdminSessions.svelte +29 -25
  173. package/dist/ui/AdminSessions.svelte.d.ts.map +1 -1
  174. package/dist/ui/CLAUDE.md +363 -0
  175. package/dist/ui/OpenSignupToggle.svelte +6 -3
  176. package/dist/ui/OpenSignupToggle.svelte.d.ts.map +1 -1
  177. package/dist/ui/PermitOfferForm.svelte +141 -0
  178. package/dist/ui/PermitOfferForm.svelte.d.ts +14 -0
  179. package/dist/ui/PermitOfferForm.svelte.d.ts.map +1 -0
  180. package/dist/ui/PermitOfferHistory.svelte +109 -0
  181. package/dist/ui/PermitOfferHistory.svelte.d.ts +11 -0
  182. package/dist/ui/PermitOfferHistory.svelte.d.ts.map +1 -0
  183. package/dist/ui/PermitOfferInbox.svelte +121 -0
  184. package/dist/ui/PermitOfferInbox.svelte.d.ts +12 -0
  185. package/dist/ui/PermitOfferInbox.svelte.d.ts.map +1 -0
  186. package/dist/ui/account_sessions_state.svelte.d.ts +53 -3
  187. package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
  188. package/dist/ui/account_sessions_state.svelte.js +39 -16
  189. package/dist/ui/admin_accounts_state.svelte.d.ts +118 -2
  190. package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
  191. package/dist/ui/admin_accounts_state.svelte.js +99 -23
  192. package/dist/ui/admin_invites_state.svelte.d.ts +47 -1
  193. package/dist/ui/admin_invites_state.svelte.d.ts.map +1 -1
  194. package/dist/ui/admin_invites_state.svelte.js +38 -26
  195. package/dist/ui/admin_rpc_adapters.d.ts +94 -0
  196. package/dist/ui/admin_rpc_adapters.d.ts.map +1 -0
  197. package/dist/ui/admin_rpc_adapters.js +100 -0
  198. package/dist/ui/admin_sessions_state.svelte.d.ts +26 -0
  199. package/dist/ui/admin_sessions_state.svelte.d.ts.map +1 -1
  200. package/dist/ui/admin_sessions_state.svelte.js +35 -21
  201. package/dist/ui/app_settings_state.svelte.d.ts +39 -0
  202. package/dist/ui/app_settings_state.svelte.d.ts.map +1 -1
  203. package/dist/ui/app_settings_state.svelte.js +34 -18
  204. package/dist/ui/audit_log_state.svelte.d.ts +40 -3
  205. package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
  206. package/dist/ui/audit_log_state.svelte.js +36 -42
  207. package/dist/ui/auth_state.svelte.d.ts +4 -3
  208. package/dist/ui/auth_state.svelte.d.ts.map +1 -1
  209. package/dist/ui/auth_state.svelte.js +4 -1
  210. package/dist/ui/permit_offers_state.svelte.d.ts +125 -0
  211. package/dist/ui/permit_offers_state.svelte.d.ts.map +1 -0
  212. package/dist/ui/permit_offers_state.svelte.js +197 -0
  213. package/package.json +3 -3
  214. package/dist/auth/admin_routes.d.ts +0 -29
  215. package/dist/auth/admin_routes.d.ts.map +0 -1
  216. package/dist/auth/admin_routes.js +0 -226
  217. package/dist/auth/app_settings_routes.d.ts +0 -27
  218. package/dist/auth/app_settings_routes.d.ts.map +0 -1
  219. package/dist/auth/app_settings_routes.js +0 -66
  220. package/dist/auth/invite_routes.d.ts +0 -18
  221. package/dist/auth/invite_routes.d.ts.map +0 -1
  222. package/dist/auth/invite_routes.js +0 -129
@@ -18,6 +18,7 @@ import type { Db, DbType } from '../db/db.js';
18
18
  import type { PasswordHashDeps } from '../auth/password.js';
19
19
  import { type SessionOptions } from '../auth/session_cookie.js';
20
20
  import type { AuditLogEvent } from '../auth/audit_log_schema.js';
21
+ import type { Uuid } from '../uuid.js';
21
22
  import type { AppBackend } from '../server/app_backend.js';
22
23
  import { type AppServerOptions, type AppServerContext } from '../server/app_server.js';
23
24
  import type { AppSurface, AppSurfaceSpec } from '../http/surface.js';
@@ -52,11 +53,11 @@ export interface BootstrapTestAccountOptions {
52
53
  */
53
54
  export declare const bootstrap_test_account: (options: BootstrapTestAccountOptions) => Promise<{
54
55
  account: {
55
- id: string;
56
+ id: Uuid;
56
57
  username: string;
57
58
  };
58
59
  actor: {
59
- id: string;
60
+ id: Uuid;
60
61
  };
61
62
  api_token: string;
62
63
  session_cookie: string;
@@ -67,12 +68,12 @@ export declare const bootstrap_test_account: (options: BootstrapTestAccountOptio
67
68
  export interface TestAppServer extends AppBackend {
68
69
  /** The bootstrapped account. */
69
70
  account: {
70
- id: string;
71
+ id: Uuid;
71
72
  username: string;
72
73
  };
73
74
  /** The actor linked to the account. */
74
75
  actor: {
75
- id: string;
76
+ id: Uuid;
76
77
  };
77
78
  /** Raw API token for Bearer auth. */
78
79
  api_token: string;
@@ -125,11 +126,11 @@ export interface CreateTestAppOptions extends TestAppServerOptions {
125
126
  */
126
127
  export interface TestAccount {
127
128
  account: {
128
- id: string;
129
+ id: Uuid;
129
130
  username: string;
130
131
  };
131
132
  actor: {
132
- id: string;
133
+ id: Uuid;
133
134
  };
134
135
  /** Signed session cookie value. */
135
136
  session_cookie: string;
@@ -1 +1 @@
1
- {"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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"}
@@ -36,8 +36,14 @@ export interface StandardAttackSurfaceOptions {
36
36
  api_path_prefix?: string;
37
37
  /** Security policy configuration. Omit for sensible defaults. */
38
38
  security_policy?: SurfaceSecurityPolicyOptions;
39
- /** Error schema tightness assertion. Omit for informational-only behavior. */
40
- error_schema_tightness?: ErrorSchemaTightnessOptions;
39
+ /**
40
+ * Error schema tightness assertion config. Defaults to
41
+ * `DEFAULT_ERROR_SCHEMA_TIGHTNESS` (ignores 401/403/429,
42
+ * `min_specificity: 'enum'`). Pass a narrower config to extend the
43
+ * allowlist or tighten the threshold; pass `null` to skip the assertion
44
+ * and keep the audit log informational-only.
45
+ */
46
+ error_schema_tightness?: ErrorSchemaTightnessOptions | null;
41
47
  }
42
48
  /**
43
49
  * Run the standard attack surface test suite.
@@ -49,7 +55,7 @@ export interface StandardAttackSurfaceOptions {
49
55
  * 4. Middleware stack — every API route has the full middleware chain
50
56
  * 5. Surface invariants — structural assertions (error schemas, descriptions, duplicates, consistency)
51
57
  * 6. Security policy — rate limiting on sensitive routes, no unexpected public mutations, method conventions
52
- * 7. Error schema tightness audit — informational log of generic vs specific error schemas
58
+ * 7. Error schema tightness — informational log of generic vs specific error schemas, plus assertion against `DEFAULT_ERROR_SCHEMA_TIGHTNESS` by default (opt out with `error_schema_tightness: null`)
53
59
  * 8. Adversarial auth — unauthenticated/wrong-role/correct-auth enforcement
54
60
  * 9. Adversarial input — input body and params validation
55
61
  * 10. Adversarial 404 — stub 404 handlers, validate response bodies against declared schemas
@@ -1 +1 @@
1
- {"version":3,"file":"attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAoB7B,OAAO,EAKN,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,MAAM,yBAAyB,CAAC;AAoBjC,OAAO,EAA4B,KAAK,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAsClF,oFAAoF;AACpF,MAAM,WAAW,sBAAsB;IACtC,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACrB;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,sBAAsB,KAAG,IAkH3E,CAAC;AAIF,0DAA0D;AAC1D,MAAM,WAAW,4BAA4B;IAC5C,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,sBAAsB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACtC,gHAAgH;IAChH,uBAAuB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACvC,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB,qEAAqE;IACrE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,eAAe,CAAC,EAAE,4BAA4B,CAAC;IAC/C,8EAA8E;IAC9E,sBAAsB,CAAC,EAAE,2BAA2B,CAAC;CACrD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,sCAAsC,GAClD,SAAS,4BAA4B,KACnC,IAoEF,CAAC"}
1
+ {"version":3,"file":"attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAoB7B,OAAO,EAMN,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,MAAM,yBAAyB,CAAC;AAoBjC,OAAO,EAA4B,KAAK,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAsClF,oFAAoF;AACpF,MAAM,WAAW,sBAAsB;IACtC,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACrB;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,sBAAsB,KAAG,IAkH3E,CAAC;AAIF,0DAA0D;AAC1D,MAAM,WAAW,4BAA4B;IAC5C,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,sBAAsB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACtC,gHAAgH;IAChH,uBAAuB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACvC,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB,qEAAqE;IACrE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,eAAe,CAAC,EAAE,4BAA4B,CAAC;IAC/C;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;CAC5D;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,sCAAsC,GAClD,SAAS,4BAA4B,KACnC,IAoEF,CAAC"}
@@ -15,7 +15,7 @@ import './assert_dev_env.js';
15
15
  * @module
16
16
  */
17
17
  import { test, assert, describe } from 'vitest';
18
- import { assert_surface_invariants, assert_surface_security_policy, audit_error_schema_tightness, assert_error_schema_tightness, } from './surface_invariants.js';
18
+ import { assert_surface_invariants, assert_surface_security_policy, audit_error_schema_tightness, assert_error_schema_tightness, DEFAULT_ERROR_SCHEMA_TIGHTNESS, } from './surface_invariants.js';
19
19
  import { describe_adversarial_input } from './adversarial_input.js';
20
20
  import { describe_adversarial_404 } from './adversarial_404.js';
21
21
  import { create_test_app_from_specs, create_test_request_context, create_auth_test_apps, select_auth_app, resolve_test_path, } from './auth_apps.js';
@@ -167,7 +167,7 @@ export const describe_adversarial_auth = (options) => {
167
167
  * 4. Middleware stack — every API route has the full middleware chain
168
168
  * 5. Surface invariants — structural assertions (error schemas, descriptions, duplicates, consistency)
169
169
  * 6. Security policy — rate limiting on sensitive routes, no unexpected public mutations, method conventions
170
- * 7. Error schema tightness audit — informational log of generic vs specific error schemas
170
+ * 7. Error schema tightness — informational log of generic vs specific error schemas, plus assertion against `DEFAULT_ERROR_SCHEMA_TIGHTNESS` by default (opt out with `error_schema_tightness: null`)
171
171
  * 8. Adversarial auth — unauthenticated/wrong-role/correct-auth enforcement
172
172
  * 9. Adversarial input — input body and params validation
173
173
  * 10. Adversarial 404 — stub 404 handlers, validate response bodies against declared schemas
@@ -178,7 +178,7 @@ export const describe_adversarial_auth = (options) => {
178
178
  * @param options - the test configuration
179
179
  */
180
180
  export const describe_standard_attack_surface_tests = (options) => {
181
- const { build, snapshot_path, expected_public_routes, expected_api_middleware, roles, api_path_prefix = '/api/', security_policy, error_schema_tightness, } = options;
181
+ const { build, snapshot_path, expected_public_routes, expected_api_middleware, roles, api_path_prefix = '/api/', security_policy, error_schema_tightness = DEFAULT_ERROR_SCHEMA_TIGHTNESS, } = options;
182
182
  const built = build();
183
183
  const { surface } = built;
184
184
  describe('attack surface snapshot', () => {
@@ -202,7 +202,7 @@ export const describe_standard_attack_surface_tests = (options) => {
202
202
  test('security policy', () => {
203
203
  assert_surface_security_policy(surface, security_policy);
204
204
  });
205
- test('error schema tightness audit', () => {
205
+ test('error schema tightness', () => {
206
206
  const entries = audit_error_schema_tightness(surface);
207
207
  const generic = entries.filter((e) => e.specificity === 'generic');
208
208
  const literal = entries.filter((e) => e.specificity === 'literal');
@@ -3,6 +3,7 @@ import type { SessionOptions } from '../auth/session_cookie.js';
3
3
  import type { AppServerContext, AppServerOptions } from '../server/app_server.js';
4
4
  import type { RouteSpec } from '../http/route_spec.js';
5
5
  import { type DbFactory } from './db.js';
6
+ import type { RpcEndpointSpec } from '../http/surface.js';
6
7
  /**
7
8
  * Configuration for `describe_audit_completeness_tests`.
8
9
  */
@@ -11,6 +12,16 @@ export interface AuditCompletenessTestOptions {
11
12
  session_options: SessionOptions<string>;
12
13
  /** Route spec factory — same one used in production. */
13
14
  create_route_specs: (ctx: AppServerContext) => Array<RouteSpec>;
15
+ /**
16
+ * RPC endpoint specs — the source `RpcAction` arrays. Required; the
17
+ * admin permit flow is RPC-only and the suite hard-fails without it.
18
+ *
19
+ * Accepts either an array (eager) or a factory
20
+ * `(ctx: AppServerContext) => Array<RpcEndpointSpec>` — the factory form
21
+ * is required when action handlers must close over the per-test
22
+ * `ctx.app_settings` / `ctx.deps` (e.g. exercising `app_settings_update`).
23
+ */
24
+ rpc_endpoints: Array<RpcEndpointSpec> | ((ctx: AppServerContext) => Array<RpcEndpointSpec>);
14
25
  /** Optional overrides for `AppServerOptions`. */
15
26
  app_options?: Partial<Omit<AppServerOptions, 'backend' | 'session_options' | 'create_route_specs'>>;
16
27
  /** Database factories to run tests against. Default: pglite only. */
@@ -1 +1 @@
1
- {"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAkB7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAKrD,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,SAAS,CAAC;AAKjB;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C,4CAA4C;IAC5C,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,wDAAwD;IACxD,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE,iDAAiD;IACjD,WAAW,CAAC,EAAE,OAAO,CACpB,IAAI,CAAC,gBAAgB,EAAE,SAAS,GAAG,iBAAiB,GAAG,oBAAoB,CAAC,CAC5E,CAAC;IACF,qEAAqE;IACrE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAChC;AAkFD;;;;;;;;GAQG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IA0azF,CAAC"}
1
+ {"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAkB7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAMrD,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,SAAS,CAAC;AAOjB,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,oBAAoB,CAAC;AAsBxD;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C,4CAA4C;IAC5C,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,wDAAwD;IACxD,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE;;;;;;;;OAQG;IACH,aAAa,EAAE,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;IAC5F,iDAAiD;IACjD,WAAW,CAAC,EAAE,OAAO,CACpB,IAAI,CAAC,gBAAgB,EAAE,SAAS,GAAG,iBAAiB,GAAG,oBAAoB,CAAC,CAC5E,CAAC;IACF,qEAAqE;IACrE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAChC;AAoDD;;;;;;;;GAQG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IA2ezF,CAAC"}
@@ -20,11 +20,13 @@ import { create_test_app } from './app_server.js';
20
20
  import { create_pglite_factory, create_describe_db, AUTH_INTEGRATION_TRUNCATE_TABLES, } from './db.js';
21
21
  import { find_auth_route } from './integration_helpers.js';
22
22
  import { run_migrations } from '../db/migrate.js';
23
- /** Find an admin route by suffix and method. */
24
- const find_admin_route = (specs, suffix, method) => specs.find((s) => s.method === method &&
25
- s.path.endsWith(suffix) &&
26
- s.auth.type === 'role' &&
27
- s.auth.role === 'admin');
23
+ import { query_accept_offer } from '../auth/permit_offer_queries.js';
24
+ import { rpc_call, require_rpc_endpoint_path } from './rpc_helpers.js';
25
+ import { create_stub_app_server_context } from './stubs.js';
26
+ import { permit_offer_create_action_spec, permit_revoke_action_spec, } from '../auth/permit_offer_action_specs.js';
27
+ import { admin_session_revoke_all_action_spec, admin_token_revoke_all_action_spec, app_settings_update_action_spec, invite_create_action_spec, invite_delete_action_spec, } from '../auth/admin_action_specs.js';
28
+ import { account_session_list_action_spec, account_session_revoke_action_spec, account_session_revoke_all_action_spec, account_token_create_action_spec, account_token_list_action_spec, account_token_revoke_action_spec, } from '../auth/account_action_specs.js';
29
+ import { query_actor_by_account } from '../auth/account_queries.js';
28
30
  /** Query audit log events from the database. */
29
31
  const query_audit_events = async (db) => {
30
32
  return db.query('SELECT event_type, seq FROM audit_log ORDER BY seq');
@@ -39,7 +41,10 @@ const build_options = (options, db) => ({
39
41
  create_route_specs: options.create_route_specs,
40
42
  db,
41
43
  roles: [ROLE_KEEPER, ROLE_ADMIN],
42
- app_options: options.app_options,
44
+ app_options: {
45
+ rpc_endpoints: options.rpc_endpoints,
46
+ ...options.app_options,
47
+ },
43
48
  });
44
49
  /** Headers for unauthenticated JSON requests (login, signup). */
45
50
  const UNAUTHENTICATED_JSON_HEADERS = {
@@ -52,15 +57,6 @@ const json_session_headers = (test_app, extra) => test_app.create_session_header
52
57
  'content-type': 'application/json',
53
58
  ...extra,
54
59
  });
55
- /**
56
- * Find an account-scoped parameterized route (e.g. `/tokens/:id/revoke`).
57
- *
58
- * Matches routes with a `:id` or `:param` segment that are NOT admin role-gated.
59
- */
60
- const find_account_parameterized_route = (specs, segment, suffix, method) => specs.find((s) => s.method === method &&
61
- s.path.includes(`/${segment}/`) &&
62
- s.path.endsWith(suffix) &&
63
- s.auth.type !== 'role');
64
60
  /**
65
61
  * Composable audit log completeness test suite.
66
62
  *
@@ -71,6 +67,19 @@ const find_account_parameterized_route = (specs, segment, suffix, method) => spe
71
67
  * @param options - session config, route factory, and optional overrides
72
68
  */
73
69
  export const describe_audit_completeness_tests = (options) => {
70
+ // Hard-fail early so consumers see a clear setup error instead of a
71
+ // confusing test failure when `rpc_endpoints` is missing. For the
72
+ // factory form we invoke the factory once here with a stub ctx purely
73
+ // to extract the endpoint path (a stable string like `/api/rpc`); the
74
+ // resulting actions array is discarded. `create_app_server` invokes
75
+ // the factory a second time inside each test with its real ctx, and
76
+ // those are the handlers that actually serve requests. Safe as long
77
+ // as the factory is pure — the stock helpers (e.g.
78
+ // `create_admin_rpc_actions`) are.
79
+ const rpc_endpoints_for_setup = typeof options.rpc_endpoints === 'function'
80
+ ? options.rpc_endpoints(create_stub_app_server_context(options.session_options))
81
+ : options.rpc_endpoints;
82
+ const rpc_path = require_rpc_endpoint_path(rpc_endpoints_for_setup);
74
83
  const init_schema = async (db) => {
75
84
  await run_migrations(db, [AUTH_MIGRATION_NS]);
76
85
  };
@@ -125,37 +134,39 @@ export const describe_audit_completeness_tests = (options) => {
125
134
  });
126
135
  test('token create produces token_create event', async () => {
127
136
  const test_app = await create_test_app(build_options(options, get_db()));
128
- const route = find_auth_route(test_app.route_specs, '/tokens/create', 'POST');
129
- assert.ok(route, 'Expected POST /tokens/create route');
130
- const res = await test_app.app.request(route.path, {
131
- method: 'POST',
132
- headers: json_session_headers(test_app),
133
- body: JSON.stringify({ name: 'audit-test' }),
137
+ const res = await rpc_call({
138
+ app: test_app.app,
139
+ path: rpc_path,
140
+ method: account_token_create_action_spec.method,
141
+ params: { name: 'audit-test' },
142
+ headers: test_app.create_session_headers(),
134
143
  });
135
- assert.strictEqual(res.status, 200);
144
+ assert.ok(res.ok, `account_token_create failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
136
145
  const events = await query_audit_events(test_app.backend.deps.db);
137
- assert_has_event(events, 'token_create', 'POST /tokens/create');
146
+ assert_has_event(events, 'token_create', 'account_token_create RPC');
138
147
  });
139
148
  test('token revoke produces token_revoke event', async () => {
140
149
  const test_app = await create_test_app(build_options(options, get_db()));
141
150
  // get a token ID to revoke
142
- const tokens_route = find_auth_route(test_app.route_specs, '/tokens', 'GET');
143
- assert.ok(tokens_route, 'Expected GET /tokens route');
144
- const list_res = await test_app.app.request(tokens_route.path, {
151
+ const list_res = await rpc_call({
152
+ app: test_app.app,
153
+ path: rpc_path,
154
+ method: account_token_list_action_spec.method,
145
155
  headers: test_app.create_session_headers(),
146
156
  });
147
- const { tokens } = (await list_res.json());
157
+ assert.ok(list_res.ok, 'account_token_list should succeed');
158
+ const { tokens } = list_res.result;
148
159
  assert.ok(tokens.length > 0, 'Expected at least one token');
149
- const route = find_account_parameterized_route(test_app.route_specs, 'tokens', '/revoke', 'POST');
150
- assert.ok(route, 'Expected POST /tokens/:id/revoke route');
151
- const path = route.path.replace(':id', tokens[0].id);
152
- const res = await test_app.app.request(path, {
153
- method: 'POST',
160
+ const res = await rpc_call({
161
+ app: test_app.app,
162
+ path: rpc_path,
163
+ method: account_token_revoke_action_spec.method,
164
+ params: { token_id: tokens[0].id },
154
165
  headers: test_app.create_session_headers(),
155
166
  });
156
- assert.strictEqual(res.status, 200);
167
+ assert.ok(res.ok, `account_token_revoke failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
157
168
  const events = await query_audit_events(test_app.backend.deps.db);
158
- assert_has_event(events, 'token_revoke', 'POST /tokens/:id/revoke');
169
+ assert_has_event(events, 'token_revoke', 'account_token_revoke RPC');
159
170
  });
160
171
  test('session revoke produces session_revoke event', async () => {
161
172
  const test_app = await create_test_app(build_options(options, get_db()));
@@ -171,36 +182,38 @@ export const describe_audit_completeness_tests = (options) => {
171
182
  }),
172
183
  });
173
184
  // get session IDs (newest first)
174
- const sessions_route = find_auth_route(test_app.route_specs, '/sessions', 'GET');
175
- assert.ok(sessions_route, 'Expected GET /sessions route');
176
- const list_res = await test_app.app.request(sessions_route.path, {
185
+ const list_res = await rpc_call({
186
+ app: test_app.app,
187
+ path: rpc_path,
188
+ method: account_session_list_action_spec.method,
177
189
  headers: test_app.create_session_headers(),
178
190
  });
179
- const { sessions } = (await list_res.json());
191
+ assert.ok(list_res.ok, 'account_session_list should succeed');
192
+ const { sessions } = list_res.result;
180
193
  assert.ok(sessions.length >= 2, 'Expected at least 2 sessions');
181
- const route = find_account_parameterized_route(test_app.route_specs, 'sessions', '/revoke', 'POST');
182
- assert.ok(route, 'Expected POST /sessions/:id/revoke route');
183
194
  // revoke the second session (not the one used for auth)
184
- const path = route.path.replace(':id', sessions[1].id);
185
- const res = await test_app.app.request(path, {
186
- method: 'POST',
195
+ const res = await rpc_call({
196
+ app: test_app.app,
197
+ path: rpc_path,
198
+ method: account_session_revoke_action_spec.method,
199
+ params: { session_id: sessions[1].id },
187
200
  headers: test_app.create_session_headers(),
188
201
  });
189
- assert.strictEqual(res.status, 200);
202
+ assert.ok(res.ok, `account_session_revoke failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
190
203
  const events = await query_audit_events(test_app.backend.deps.db);
191
- assert_has_event(events, 'session_revoke', 'POST /sessions/:id/revoke');
204
+ assert_has_event(events, 'session_revoke', 'account_session_revoke RPC');
192
205
  });
193
206
  test('session revoke-all produces session_revoke_all event', async () => {
194
207
  const test_app = await create_test_app(build_options(options, get_db()));
195
- const route = find_auth_route(test_app.route_specs, '/sessions/revoke-all', 'POST');
196
- assert.ok(route, 'Expected POST /sessions/revoke-all route');
197
- const res = await test_app.app.request(route.path, {
198
- method: 'POST',
208
+ const res = await rpc_call({
209
+ app: test_app.app,
210
+ path: rpc_path,
211
+ method: account_session_revoke_all_action_spec.method,
199
212
  headers: test_app.create_session_headers(),
200
213
  });
201
- assert.strictEqual(res.status, 200);
214
+ assert.ok(res.ok, `account_session_revoke_all failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
202
215
  const events = await query_audit_events(test_app.backend.deps.db);
203
- assert_has_event(events, 'session_revoke_all', 'POST /sessions/revoke-all');
216
+ assert_has_event(events, 'session_revoke_all', 'account_session_revoke_all RPC');
204
217
  });
205
218
  test('password change produces password_change event', async () => {
206
219
  const test_app = await create_test_app(build_options(options, get_db()));
@@ -221,137 +234,144 @@ export const describe_audit_completeness_tests = (options) => {
221
234
  });
222
235
  // --- Admin routes ---
223
236
  describe('admin mutation audit events', () => {
224
- test('permit grant produces permit_grant event', async () => {
237
+ test('admin offer (RPC) + accept produces permit_offer_create and permit_grant events', async () => {
225
238
  const test_app = await create_test_app(build_options(options, get_db()));
226
- const route = find_admin_route(test_app.route_specs, '/permits/grant', 'POST');
227
- assert.ok(route, 'Expected admin POST /permits/grant route');
228
239
  const target = await test_app.create_account({ username: 'audit_target' });
229
- const path = route.path.replace(':account_id', target.account.id);
230
- const res = await test_app.app.request(path, {
231
- method: 'POST',
232
- headers: json_session_headers(test_app),
233
- body: JSON.stringify({ role: ROLE_ADMIN }),
240
+ const offer_res = await rpc_call({
241
+ app: test_app.app,
242
+ path: rpc_path,
243
+ method: permit_offer_create_action_spec.method,
244
+ params: { to_account_id: target.account.id, role: ROLE_ADMIN },
245
+ headers: test_app.create_session_headers(),
234
246
  });
235
- assert.strictEqual(res.status, 200);
236
- const events = await query_audit_events(test_app.backend.deps.db);
237
- assert_has_event(events, 'permit_grant', 'POST /permits/grant');
247
+ assert.ok(offer_res.ok, `permit_offer_create failed: ${offer_res.ok ? '' : JSON.stringify(offer_res.error)}`);
248
+ const { offer } = offer_res.result;
249
+ // Admin offer emits `permit_offer_create` only — the permit doesn't
250
+ // exist yet. Drive the accept to confirm `permit_grant` fires on the
251
+ // downstream consent transition.
252
+ const events_after_offer = await query_audit_events(test_app.backend.deps.db);
253
+ assert_has_event(events_after_offer, 'permit_offer_create', 'permit_offer_create RPC');
254
+ await get_db().transaction(async (tx) => {
255
+ await query_accept_offer({ db: tx }, { offer_id: offer.id, to_account_id: target.account.id, ip: null });
256
+ });
257
+ const events_after_accept = await query_audit_events(test_app.backend.deps.db);
258
+ assert_has_event(events_after_accept, 'permit_grant', 'offer accept');
238
259
  });
239
- test('permit revoke produces permit_revoke event', async () => {
260
+ test('permit revoke (RPC) produces permit_revoke event', async () => {
240
261
  const test_app = await create_test_app(build_options(options, get_db()));
241
- const grant_route = find_admin_route(test_app.route_specs, '/permits/grant', 'POST');
242
- const revoke_route = test_app.route_specs.find((s) => s.method === 'POST' &&
243
- s.path.includes('/permits/') &&
244
- s.path.endsWith('/revoke') &&
245
- s.auth.type === 'role');
246
- assert.ok(grant_route, 'Expected admin POST /permits/grant route');
247
- assert.ok(revoke_route, 'Expected admin POST /permits/:permit_id/revoke route');
248
262
  const target = await test_app.create_account({ username: 'audit_revoke_target' });
249
- // grant a permit first
250
- const grant_path = grant_route.path.replace(':account_id', target.account.id);
251
- const grant_res = await test_app.app.request(grant_path, {
252
- method: 'POST',
253
- headers: json_session_headers(test_app),
254
- body: JSON.stringify({ role: ROLE_ADMIN }),
263
+ const target_actor = await query_actor_by_account({ db: get_db() }, target.account.id);
264
+ assert.ok(target_actor);
265
+ // Offer + accept to materialize a permit we can revoke.
266
+ const offer_res = await rpc_call({
267
+ app: test_app.app,
268
+ path: rpc_path,
269
+ method: permit_offer_create_action_spec.method,
270
+ params: { to_account_id: target.account.id, role: ROLE_ADMIN },
271
+ headers: test_app.create_session_headers(),
255
272
  });
256
- const grant_body = (await grant_res.json());
257
- // revoke it
258
- const revoke_path = revoke_route.path
259
- .replace(':account_id', target.account.id)
260
- .replace(':permit_id', grant_body.permit.id);
261
- const res = await test_app.app.request(revoke_path, {
262
- method: 'POST',
273
+ assert.ok(offer_res.ok, `permit_offer_create failed: ${offer_res.ok ? '' : JSON.stringify(offer_res.error)}`);
274
+ const { offer } = offer_res.result;
275
+ const accept_result = await get_db().transaction(async (tx) => {
276
+ return query_accept_offer({ db: tx }, { offer_id: offer.id, to_account_id: target.account.id, ip: null });
277
+ });
278
+ // Revoke via RPC.
279
+ const revoke_res = await rpc_call({
280
+ app: test_app.app,
281
+ path: rpc_path,
282
+ method: permit_revoke_action_spec.method,
283
+ params: { actor_id: target_actor.id, permit_id: accept_result.permit.id },
263
284
  headers: test_app.create_session_headers(),
264
285
  });
265
- assert.strictEqual(res.status, 200);
286
+ assert.ok(revoke_res.ok, `permit_revoke failed: ${revoke_res.ok ? '' : JSON.stringify(revoke_res.error)}`);
266
287
  const events = await query_audit_events(test_app.backend.deps.db);
267
- assert_has_event(events, 'permit_revoke', 'POST /permits/:permit_id/revoke');
288
+ assert_has_event(events, 'permit_revoke', 'permit_revoke RPC');
268
289
  });
269
290
  test('admin session revoke-all produces session_revoke_all event', async () => {
270
291
  const test_app = await create_test_app(build_options(options, get_db()));
271
- const route = find_admin_route(test_app.route_specs, '/sessions/revoke-all', 'POST');
272
- assert.ok(route, 'Expected admin POST /sessions/revoke-all route');
273
292
  const target = await test_app.create_account({ username: 'audit_sessions_target' });
274
- const path = route.path.replace(':account_id', target.account.id);
275
- const res = await test_app.app.request(path, {
276
- method: 'POST',
293
+ const res = await rpc_call({
294
+ app: test_app.app,
295
+ path: rpc_path,
296
+ method: admin_session_revoke_all_action_spec.method,
297
+ params: { account_id: target.account.id },
277
298
  headers: test_app.create_session_headers(),
278
299
  });
279
- assert.strictEqual(res.status, 200);
300
+ assert.ok(res.ok, `admin_session_revoke_all failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
280
301
  const events = await query_audit_events(test_app.backend.deps.db);
281
302
  // admin session revoke-all also produces session_revoke_all
282
- assert_has_event(events, 'session_revoke_all', 'admin POST /sessions/revoke-all');
303
+ assert_has_event(events, 'session_revoke_all', 'admin_session_revoke_all RPC');
283
304
  });
284
305
  test('admin token revoke-all produces token_revoke_all event', async () => {
285
306
  const test_app = await create_test_app(build_options(options, get_db()));
286
- const route = find_admin_route(test_app.route_specs, '/tokens/revoke-all', 'POST');
287
- assert.ok(route, 'Expected admin POST /tokens/revoke-all route');
288
307
  const target = await test_app.create_account({ username: 'audit_tokens_target' });
289
- const path = route.path.replace(':account_id', target.account.id);
290
- const res = await test_app.app.request(path, {
291
- method: 'POST',
308
+ const res = await rpc_call({
309
+ app: test_app.app,
310
+ path: rpc_path,
311
+ method: admin_token_revoke_all_action_spec.method,
312
+ params: { account_id: target.account.id },
292
313
  headers: test_app.create_session_headers(),
293
314
  });
294
- assert.strictEqual(res.status, 200);
315
+ assert.ok(res.ok, `admin_token_revoke_all failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
295
316
  const events = await query_audit_events(test_app.backend.deps.db);
296
- assert_has_event(events, 'token_revoke_all', 'admin POST /tokens/revoke-all');
317
+ assert_has_event(events, 'token_revoke_all', 'admin_token_revoke_all RPC');
297
318
  });
298
319
  });
299
- // --- Invite routes ---
320
+ // --- Invite RPC actions ---
300
321
  describe('invite mutation audit events', () => {
301
322
  test('invite create and delete produce audit events', async () => {
302
323
  const test_app = await create_test_app(build_options(options, get_db()));
303
- const create_route = find_admin_route(test_app.route_specs, '/invites', 'POST');
304
- const delete_route = test_app.route_specs.find((s) => s.method === 'DELETE' && s.path.includes('/invites/') && s.auth.type === 'role');
305
- assert.ok(create_route, 'Expected admin POST /invites route');
306
- assert.ok(delete_route, 'Expected admin DELETE /invites/:id route');
307
- // create invite
308
- const create_res = await test_app.app.request(create_route.path, {
309
- method: 'POST',
310
- headers: json_session_headers(test_app),
311
- body: JSON.stringify({ username: 'invited_user' }),
324
+ const create_res = await rpc_call({
325
+ app: test_app.app,
326
+ path: rpc_path,
327
+ method: invite_create_action_spec.method,
328
+ params: { username: 'invited_user' },
329
+ headers: test_app.create_session_headers(),
312
330
  });
313
- assert.strictEqual(create_res.status, 200);
314
- const { invite } = (await create_res.json());
315
- // delete invite
316
- const delete_path = delete_route.path.replace(':id', invite.id);
317
- const delete_res = await test_app.app.request(delete_path, {
318
- method: 'DELETE',
331
+ assert.ok(create_res.ok, `invite_create failed: ${create_res.ok ? '' : JSON.stringify(create_res.error)}`);
332
+ const { invite } = create_res.result;
333
+ const delete_res = await rpc_call({
334
+ app: test_app.app,
335
+ path: rpc_path,
336
+ method: invite_delete_action_spec.method,
337
+ params: { invite_id: invite.id },
319
338
  headers: test_app.create_session_headers(),
320
339
  });
321
- assert.strictEqual(delete_res.status, 200);
340
+ assert.ok(delete_res.ok, `invite_delete failed: ${delete_res.ok ? '' : JSON.stringify(delete_res.error)}`);
322
341
  const events = await query_audit_events(test_app.backend.deps.db);
323
- assert_has_event(events, 'invite_create', 'POST /invites');
324
- assert_has_event(events, 'invite_delete', 'DELETE /invites/:id');
342
+ assert_has_event(events, 'invite_create', 'invite_create RPC');
343
+ assert_has_event(events, 'invite_delete', 'invite_delete RPC');
325
344
  });
326
345
  });
327
- // --- App settings routes ---
346
+ // --- App settings RPC action ---
328
347
  describe('app settings mutation audit events', () => {
329
348
  test('settings update produces app_settings_update event', async () => {
330
349
  const test_app = await create_test_app(build_options(options, get_db()));
331
- const route = find_admin_route(test_app.route_specs, '/settings', 'PATCH');
332
- assert.ok(route, 'Expected admin PATCH /settings route');
333
- const res = await test_app.app.request(route.path, {
334
- method: 'PATCH',
335
- headers: json_session_headers(test_app),
336
- body: JSON.stringify({ open_signup: true }),
350
+ const res = await rpc_call({
351
+ app: test_app.app,
352
+ path: rpc_path,
353
+ method: app_settings_update_action_spec.method,
354
+ params: { open_signup: true },
355
+ headers: test_app.create_session_headers(),
337
356
  });
338
- assert.strictEqual(res.status, 200);
357
+ assert.ok(res.ok, `app_settings_update failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
339
358
  const events = await query_audit_events(test_app.backend.deps.db);
340
- assert_has_event(events, 'app_settings_update', 'PATCH /settings');
359
+ assert_has_event(events, 'app_settings_update', 'app_settings_update RPC');
341
360
  });
342
361
  });
343
362
  // --- Signup route ---
344
363
  describe('signup audit events', () => {
345
364
  test('signup produces signup event', async () => {
346
365
  const test_app = await create_test_app(build_options(options, get_db()));
347
- // enable open signup
348
- const settings_route = find_admin_route(test_app.route_specs, '/settings', 'PATCH');
349
- assert.ok(settings_route, 'Expected admin PATCH /settings route');
350
- await test_app.app.request(settings_route.path, {
351
- method: 'PATCH',
352
- headers: json_session_headers(test_app),
353
- body: JSON.stringify({ open_signup: true }),
366
+ // enable open signup via RPC
367
+ const settings_res = await rpc_call({
368
+ app: test_app.app,
369
+ path: rpc_path,
370
+ method: app_settings_update_action_spec.method,
371
+ params: { open_signup: true },
372
+ headers: test_app.create_session_headers(),
354
373
  });
374
+ assert.ok(settings_res.ok, `app_settings_update failed: ${settings_res.ok ? '' : JSON.stringify(settings_res.error)}`);
355
375
  // signup
356
376
  const signup_route = find_auth_route(test_app.route_specs, '/signup', 'POST');
357
377
  assert.ok(signup_route, 'Expected POST /signup route');
@@ -384,6 +404,7 @@ export const describe_audit_completeness_tests = (options) => {
384
404
  'token_create',
385
405
  'token_revoke',
386
406
  'token_revoke_all',
407
+ 'permit_offer_create',
387
408
  'permit_grant',
388
409
  'permit_revoke',
389
410
  'invite_create',
@@ -393,6 +414,20 @@ export const describe_audit_completeness_tests = (options) => {
393
414
  /** Event types excluded with justification. */
394
415
  const EXCLUDED_EVENT_TYPES = new Set([
395
416
  'bootstrap', // requires filesystem token — tested in bootstrap_account.db.test.ts
417
+ // The remaining `permit_offer_*` events fire only via the RPC
418
+ // endpoint or via downstream effects of `permit_revoke`. Direct
419
+ // coverage lives in `permit_offer_queries.db.test.ts`,
420
+ // `permit_offer_actions.db.test.ts`,
421
+ // `permit_offer_actions.notifications.db.test.ts`, and
422
+ // `permit_offer_actions.notifications.revoke.db.test.ts`.
423
+ // `permit_offer_expire` fires from the cleanup sweep
424
+ // (`cleanup_expired_permit_offers` in `auth/cleanup.ts`) —
425
+ // covered in `cleanup.db.test.ts`.
426
+ 'permit_offer_accept',
427
+ 'permit_offer_decline',
428
+ 'permit_offer_retract',
429
+ 'permit_offer_expire',
430
+ 'permit_offer_supersede',
396
431
  ]);
397
432
  test('all audit event types are covered or explicitly excluded', () => {
398
433
  const all_covered = new Set([...COVERED_EVENT_TYPES, ...EXCLUDED_EVENT_TYPES]);