@fuzdev/fuz_app 0.53.0 → 0.55.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 (144) hide show
  1. package/dist/actions/CLAUDE.md +68 -13
  2. package/dist/actions/action_codegen.d.ts +13 -0
  3. package/dist/actions/action_codegen.d.ts.map +1 -1
  4. package/dist/actions/action_codegen.js +15 -1
  5. package/dist/actions/action_rpc.d.ts +60 -7
  6. package/dist/actions/action_rpc.d.ts.map +1 -1
  7. package/dist/actions/action_rpc.js +158 -44
  8. package/dist/actions/register_action_ws.d.ts +4 -4
  9. package/dist/actions/register_action_ws.js +6 -6
  10. package/dist/actions/register_ws_endpoint.d.ts +20 -7
  11. package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
  12. package/dist/actions/register_ws_endpoint.js +30 -5
  13. package/dist/actions/transports.d.ts.map +1 -1
  14. package/dist/actions/transports.js +0 -4
  15. package/dist/auth/CLAUDE.md +230 -63
  16. package/dist/auth/account_actions.d.ts +6 -6
  17. package/dist/auth/account_actions.d.ts.map +1 -1
  18. package/dist/auth/account_actions.js +8 -11
  19. package/dist/auth/account_queries.d.ts +6 -3
  20. package/dist/auth/account_queries.d.ts.map +1 -1
  21. package/dist/auth/account_queries.js +14 -5
  22. package/dist/auth/account_routes.d.ts +7 -10
  23. package/dist/auth/account_routes.d.ts.map +1 -1
  24. package/dist/auth/account_routes.js +70 -23
  25. package/dist/auth/account_schema.d.ts +19 -0
  26. package/dist/auth/account_schema.d.ts.map +1 -1
  27. package/dist/auth/account_schema.js +20 -0
  28. package/dist/auth/admin_action_specs.d.ts +45 -11
  29. package/dist/auth/admin_action_specs.d.ts.map +1 -1
  30. package/dist/auth/admin_action_specs.js +23 -8
  31. package/dist/auth/admin_actions.d.ts +8 -7
  32. package/dist/auth/admin_actions.d.ts.map +1 -1
  33. package/dist/auth/admin_actions.js +11 -18
  34. package/dist/auth/audit_log_queries.d.ts +53 -14
  35. package/dist/auth/audit_log_queries.d.ts.map +1 -1
  36. package/dist/auth/audit_log_queries.js +45 -2
  37. package/dist/auth/audit_log_schema.d.ts +55 -1
  38. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  39. package/dist/auth/audit_log_schema.js +19 -3
  40. package/dist/auth/bearer_auth.d.ts +9 -7
  41. package/dist/auth/bearer_auth.d.ts.map +1 -1
  42. package/dist/auth/bearer_auth.js +13 -21
  43. package/dist/auth/cleanup.d.ts.map +1 -1
  44. package/dist/auth/cleanup.js +5 -0
  45. package/dist/auth/daemon_token_middleware.d.ts +23 -11
  46. package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
  47. package/dist/auth/daemon_token_middleware.js +26 -20
  48. package/dist/auth/deps.d.ts +14 -0
  49. package/dist/auth/deps.d.ts.map +1 -1
  50. package/dist/auth/middleware.d.ts.map +1 -1
  51. package/dist/auth/middleware.js +4 -2
  52. package/dist/auth/migrations.d.ts +15 -7
  53. package/dist/auth/migrations.d.ts.map +1 -1
  54. package/dist/auth/migrations.js +15 -7
  55. package/dist/auth/permit_offer_action_specs.d.ts +45 -6
  56. package/dist/auth/permit_offer_action_specs.d.ts.map +1 -1
  57. package/dist/auth/permit_offer_action_specs.js +38 -7
  58. package/dist/auth/permit_offer_actions.d.ts +2 -2
  59. package/dist/auth/permit_offer_actions.d.ts.map +1 -1
  60. package/dist/auth/permit_offer_actions.js +106 -95
  61. package/dist/auth/permit_offer_notifications.d.ts +10 -0
  62. package/dist/auth/permit_offer_notifications.d.ts.map +1 -1
  63. package/dist/auth/permit_offer_queries.d.ts +68 -9
  64. package/dist/auth/permit_offer_queries.d.ts.map +1 -1
  65. package/dist/auth/permit_offer_queries.js +147 -35
  66. package/dist/auth/permit_offer_schema.d.ts +23 -1
  67. package/dist/auth/permit_offer_schema.d.ts.map +1 -1
  68. package/dist/auth/permit_offer_schema.js +5 -0
  69. package/dist/auth/permit_queries.d.ts +17 -5
  70. package/dist/auth/permit_queries.d.ts.map +1 -1
  71. package/dist/auth/permit_queries.js +19 -8
  72. package/dist/auth/request_context.d.ts +360 -32
  73. package/dist/auth/request_context.d.ts.map +1 -1
  74. package/dist/auth/request_context.js +442 -60
  75. package/dist/auth/route_guards.d.ts +10 -4
  76. package/dist/auth/route_guards.d.ts.map +1 -1
  77. package/dist/auth/route_guards.js +14 -8
  78. package/dist/auth/self_service_role_action_specs.d.ts +2 -0
  79. package/dist/auth/self_service_role_action_specs.d.ts.map +1 -1
  80. package/dist/auth/self_service_role_action_specs.js +2 -0
  81. package/dist/auth/self_service_role_actions.d.ts +6 -5
  82. package/dist/auth/self_service_role_actions.d.ts.map +1 -1
  83. package/dist/auth/self_service_role_actions.js +32 -19
  84. package/dist/db/migrate.d.ts +11 -7
  85. package/dist/db/migrate.d.ts.map +1 -1
  86. package/dist/db/migrate.js +9 -6
  87. package/dist/dev/setup.d.ts.map +1 -1
  88. package/dist/dev/setup.js +5 -3
  89. package/dist/hono_context.d.ts +77 -0
  90. package/dist/hono_context.d.ts.map +1 -1
  91. package/dist/hono_context.js +50 -0
  92. package/dist/http/CLAUDE.md +80 -17
  93. package/dist/http/error_schemas.d.ts +92 -1
  94. package/dist/http/error_schemas.d.ts.map +1 -1
  95. package/dist/http/error_schemas.js +73 -16
  96. package/dist/http/jsonrpc_errors.d.ts +27 -2
  97. package/dist/http/jsonrpc_errors.d.ts.map +1 -1
  98. package/dist/http/jsonrpc_errors.js +26 -2
  99. package/dist/http/route_spec.d.ts +62 -4
  100. package/dist/http/route_spec.d.ts.map +1 -1
  101. package/dist/http/route_spec.js +117 -21
  102. package/dist/http/schema_helpers.d.ts +13 -1
  103. package/dist/http/schema_helpers.d.ts.map +1 -1
  104. package/dist/http/schema_helpers.js +21 -2
  105. package/dist/http/surface.d.ts +10 -1
  106. package/dist/http/surface.d.ts.map +1 -1
  107. package/dist/http/surface.js +2 -2
  108. package/dist/server/app_server.d.ts.map +1 -1
  109. package/dist/server/app_server.js +11 -1
  110. package/dist/testing/CLAUDE.md +23 -17
  111. package/dist/testing/admin_integration.d.ts.map +1 -1
  112. package/dist/testing/admin_integration.js +15 -13
  113. package/dist/testing/adversarial_headers.js +1 -1
  114. package/dist/testing/app_server.js +2 -2
  115. package/dist/testing/audit_completeness.d.ts.map +1 -1
  116. package/dist/testing/audit_completeness.js +21 -7
  117. package/dist/testing/auth_apps.d.ts.map +1 -1
  118. package/dist/testing/auth_apps.js +6 -3
  119. package/dist/testing/entities.d.ts +2 -1
  120. package/dist/testing/entities.d.ts.map +1 -1
  121. package/dist/testing/entities.js +1 -0
  122. package/dist/testing/integration_helpers.d.ts +4 -2
  123. package/dist/testing/integration_helpers.d.ts.map +1 -1
  124. package/dist/testing/integration_helpers.js +9 -5
  125. package/dist/testing/middleware.d.ts +12 -8
  126. package/dist/testing/middleware.d.ts.map +1 -1
  127. package/dist/testing/middleware.js +67 -25
  128. package/dist/testing/rpc_helpers.d.ts.map +1 -1
  129. package/dist/testing/rpc_helpers.js +3 -1
  130. package/dist/testing/schema_generators.d.ts.map +1 -1
  131. package/dist/testing/schema_generators.js +12 -0
  132. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  133. package/dist/testing/ws_round_trip.js +5 -1
  134. package/dist/ui/CLAUDE.md +16 -10
  135. package/dist/ui/PermitOfferForm.svelte +14 -0
  136. package/dist/ui/PermitOfferForm.svelte.d.ts +6 -0
  137. package/dist/ui/PermitOfferForm.svelte.d.ts.map +1 -1
  138. package/dist/ui/admin_accounts_state.svelte.d.ts +8 -1
  139. package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
  140. package/dist/ui/admin_accounts_state.svelte.js +14 -3
  141. package/dist/ui/permit_offers_state.svelte.d.ts +9 -1
  142. package/dist/ui/permit_offers_state.svelte.d.ts.map +1 -1
  143. package/dist/ui/permit_offers_state.svelte.js +7 -1
  144. package/package.json +1 -1
@@ -14,7 +14,8 @@
14
14
  */
15
15
  import { DEV } from 'esm-env';
16
16
  import { ERROR_INVALID_JSON_BODY, ERROR_INVALID_REQUEST_BODY, ERROR_INVALID_ROUTE_PARAMS, ERROR_INVALID_QUERY_PARAMS, } from './error_schemas.js';
17
- import { ThrownJsonrpcError, JSONRPC_ERROR_CODES, jsonrpc_error_code_to_http_status, } from './jsonrpc_errors.js';
17
+ import { ThrownJsonrpcError, jsonrpc_error_code_to_http_status, jsonrpc_error_code_to_name, } from './jsonrpc_errors.js';
18
+ import { CACHED_REQUEST_BODY_KEY } from '../hono_context.js';
18
19
  import { is_null_schema, merge_error_schemas } from './schema_helpers.js';
19
20
  /**
20
21
  * Get validated input from the Hono context.
@@ -63,12 +64,30 @@ const create_input_validation = (input_schema, method) => {
63
64
  if (is_null_schema(input_schema))
64
65
  return [];
65
66
  const validate = async (c, next) => {
67
+ // Prefer the cached parse result written by `read_raw_acting`
68
+ // (the dispatcher's `acting` extractor). The cache decouples
69
+ // us from Hono's internal `bodyCache` — Hono keeps the body
70
+ // text alive across multiple `c.req.json()` calls but still
71
+ // re-runs `JSON.parse` each time, so caching the parsed value
72
+ // saves work and pins behavior to fuz_app code rather than to
73
+ // undocumented Hono internals.
74
+ // Hono's `c.get()` types this as the variable-map entry, but at
75
+ // runtime it returns `undefined` when no setter has run for this
76
+ // request. Narrow defensively.
77
+ const cached = c.get(CACHED_REQUEST_BODY_KEY);
66
78
  let body;
67
- try {
68
- body = await c.req.json();
79
+ if (cached !== undefined) {
80
+ if (!cached.ok)
81
+ return c.json({ error: ERROR_INVALID_JSON_BODY }, 400);
82
+ body = cached.body;
69
83
  }
70
- catch {
71
- return c.json({ error: ERROR_INVALID_JSON_BODY }, 400);
84
+ else {
85
+ try {
86
+ body = await c.req.json();
87
+ }
88
+ catch {
89
+ return c.json({ error: ERROR_INVALID_JSON_BODY }, 400);
90
+ }
72
91
  }
73
92
  if (typeof body !== 'object' || body === null || Array.isArray(body)) {
74
93
  return c.json({ error: ERROR_INVALID_JSON_BODY }, 400);
@@ -192,10 +211,29 @@ export const apply_middleware_specs = (app, specs) => {
192
211
  /**
193
212
  * Wrap a handler with error catch logic.
194
213
  *
195
- * Catches `ThrownJsonrpcError` and maps to HTTP status + JSON-RPC error response.
196
- * Catches generic `Error` and maps to `internal_error` (-32603 500), including
197
- * the error message in DEV only. Existing handlers that return `c.json()` directly
198
- * are unaffected the catch layer only activates when something is thrown.
214
+ * Catches `ThrownJsonrpcError` and maps it to a flat REST `ApiError` body
215
+ * (`{error: <reason>, message?, ...rest_data}`) at the matching HTTP status.
216
+ * Catches generic `Error` and maps to `{error: 'internal_error', message?}`
217
+ * at 500 (`message` populated only in DEV). Existing handlers that return
218
+ * `c.json()` directly are unaffected — the catch layer only activates when
219
+ * something is thrown.
220
+ *
221
+ * The flat shape matches what middleware and direct handler emissions
222
+ * produce (e.g. `c.json({error: ERROR_FOO}, status)`,
223
+ * `c.json(failure.body, status)` from the dispatcher's authorization phase),
224
+ * so REST callers see one error envelope across every emit site. The
225
+ * `<reason>` string comes from `err.data.reason` when set (consumer-supplied
226
+ * canonical reason code) or from `jsonrpc_error_code_to_name(err.code)`
227
+ * (the JSON-RPC error name — `'not_found'`, `'forbidden'`, etc.). Other
228
+ * `data` fields flatten alongside `error` so diagnostic data is visible
229
+ * to clients without descending an envelope.
230
+ *
231
+ * The JSON-RPC code is intentionally **not** carried on the REST body —
232
+ * REST callers key on HTTP status + `error` reason, and the JSON-RPC code
233
+ * is recoverable via `http_status_to_jsonrpc_error_code(status)` on the
234
+ * rare consumer that wants it. Keeping the shape transport-shaped (REST
235
+ * emits ApiError; JSON-RPC dispatcher emits the JSON-RPC envelope) avoids
236
+ * a hybrid envelope that has to be normalized on the way out.
199
237
  */
200
238
  const wrap_error_catch = (handler, log) => {
201
239
  return async (c, next) => {
@@ -205,25 +243,72 @@ const wrap_error_catch = (handler, log) => {
205
243
  catch (err) {
206
244
  if (err instanceof ThrownJsonrpcError) {
207
245
  const status = jsonrpc_error_code_to_http_status(err.code);
208
- const error = { code: err.code, message: err.message };
209
- if (err.data !== undefined)
210
- error.data = err.data;
211
- return c.json({ error }, status);
246
+ return c.json(build_rest_error_body(err), status);
212
247
  }
213
248
  // generic error — internal_error
214
249
  log.error('Unhandled handler error', err);
215
- const message = DEV && err instanceof Error ? err.message : 'internal server error';
216
- return c.json({ error: { code: JSONRPC_ERROR_CODES.internal_error, message } }, 500);
250
+ const body = { error: 'internal_error' };
251
+ if (DEV && err instanceof Error)
252
+ body.message = err.message;
253
+ return c.json(body, 500);
217
254
  }
218
255
  };
219
256
  };
257
+ /**
258
+ * Build the REST body for a thrown `ThrownJsonrpcError`. Splits out
259
+ * for unit-test directness and keeps the catch handler readable.
260
+ *
261
+ * Reason resolution order:
262
+ * 1. `err.data.reason` (consumer-supplied canonical reason — overrides code-derived name)
263
+ * 2. `jsonrpc_error_code_to_name(err.code)` (e.g. -32003 → `'not_found'`)
264
+ *
265
+ * Remaining `err.data` fields (everything except `reason`) flatten under
266
+ * the body. Non-object `data` is dropped — we don't want a primitive
267
+ * `data` to overwrite the structured shape.
268
+ */
269
+ const build_rest_error_body = (err) => {
270
+ let reason;
271
+ const rest = {};
272
+ if (err.data !== null &&
273
+ typeof err.data === 'object' &&
274
+ !Array.isArray(err.data) &&
275
+ typeof err.data.reason === 'string') {
276
+ const { reason: data_reason, ...other } = err.data;
277
+ reason = data_reason;
278
+ Object.assign(rest, other);
279
+ }
280
+ else {
281
+ reason = jsonrpc_error_code_to_name(err.code);
282
+ if (err.data !== null && typeof err.data === 'object' && !Array.isArray(err.data)) {
283
+ Object.assign(rest, err.data);
284
+ }
285
+ }
286
+ const body = { error: reason, ...rest };
287
+ if (err.message && err.message !== reason)
288
+ body.message = err.message;
289
+ return body;
290
+ };
220
291
  /**
221
292
  * Apply route specs to a Hono app.
222
293
  *
223
294
  * For each spec: resolves auth to guards via the provided resolver,
224
295
  * adds input validation middleware (for routes with non-null input schemas),
225
- * wraps handler with DEV-only output and error validation, wraps with error
226
- * catch layer (catches `ThrownJsonrpcError` and generic errors), and registers the route.
296
+ * runs the optional authorization phase to resolve the acting actor + build
297
+ * the request context, wraps handler with DEV-only output and error
298
+ * validation, wraps with error catch layer (catches `ThrownJsonrpcError`
299
+ * and generic errors), and registers the route.
300
+ *
301
+ * Per-route middleware order: params → query → pre-validation auth
302
+ * guards (401) → authorization phase → post-authorization auth guards
303
+ * (403) → input validation → handler. The 401 check runs before any
304
+ * body parsing so unauthenticated callers never see route-shape
305
+ * information from parse failures. The authorization phase runs before
306
+ * input validation (matches the RPC dispatcher's order) so role /
307
+ * keeper denials surface 403 before 400 invalid_params; it extracts
308
+ * `acting` from raw query (GET) or pre-parsed JSON body (POST/PUT/...)
309
+ * — Hono caches the parsed body internally so the subsequent input-
310
+ * validation step does not re-parse. The role / keeper guards consume
311
+ * the `RequestContext` populated by the authorization phase.
227
312
  *
228
313
  * Each handler receives a `RouteContext` with:
229
314
  * - `db`: transaction-scoped (for non-GET) or pool-level (for GET)
@@ -231,11 +316,12 @@ const wrap_error_catch = (handler, log) => {
231
316
  * - `pending_effects`: fire-and-forget effect queue
232
317
  *
233
318
  * @param resolve_auth_guards - maps `RouteAuth` to middleware — use `fuz_auth_guard_resolver` from `auth/route_guards.ts`
319
+ * @param authorize - optional authorization phase; runs between guards and input validation
234
320
  * @param db - used for transaction wrapping and `RouteContext`
235
321
  * @mutates `app`
236
322
  * @throws Error if two specs share the same `method` + `path` (each combination must be unique)
237
323
  */
238
- export const apply_route_specs = (app, specs, resolve_auth_guards, log, db) => {
324
+ export const apply_route_specs = (app, specs, resolve_auth_guards, log, db, authorize, is_acting_aware) => {
239
325
  const registered = new Set();
240
326
  for (const spec of specs) {
241
327
  const route_key = `${spec.method} ${spec.path}`;
@@ -243,11 +329,21 @@ export const apply_route_specs = (app, specs, resolve_auth_guards, log, db) => {
243
329
  throw new Error(`Duplicate route: ${route_key} — each method+path combination must be unique`);
244
330
  }
245
331
  registered.add(route_key);
246
- const guards = resolve_auth_guards(spec.auth);
332
+ const { pre_validation: pre_validation_guards, post_authorization: post_authorization_guards } = resolve_auth_guards(spec.auth);
247
333
  const params_validation = create_params_validation(spec.params);
248
334
  const query_validation = create_query_validation(spec.query);
249
335
  const input_validation = create_input_validation(spec.input, spec.method);
250
- const merged_errors = merge_error_schemas(spec);
336
+ const merged_errors = merge_error_schemas(spec, null, is_acting_aware?.(spec) ?? false);
337
+ const authorization = authorize
338
+ ? [
339
+ async (c, next) => {
340
+ const response = await authorize(c, spec);
341
+ if (response)
342
+ return response;
343
+ await next();
344
+ },
345
+ ]
346
+ : [];
251
347
  // Step 1: adapt RouteHandler → Handler (construct RouteContext, call spec.handler)
252
348
  const use_transaction = spec.transaction ?? spec.method !== 'GET';
253
349
  const inner = spec.handler;
@@ -258,7 +354,7 @@ export const apply_route_specs = (app, specs, resolve_auth_guards, log, db) => {
258
354
  handler = wrap_output_validation(handler, spec.output, merged_errors, log);
259
355
  // Step 3: error catch layer
260
356
  handler = wrap_error_catch(handler, log);
261
- app.on(spec.method, [spec.path], ...guards, ...params_validation, ...query_validation, ...input_validation, handler);
357
+ app.on(spec.method, [spec.path], ...params_validation, ...query_validation, ...pre_validation_guards, ...authorization, ...post_authorization_guards, ...input_validation, handler);
262
358
  }
263
359
  };
264
360
  /**
@@ -55,7 +55,19 @@ export declare const middleware_applies: (mw_path: string, route_path: string) =
55
55
  * Merge order: derived -> middleware -> explicit route errors.
56
56
  * Later layers override earlier ones for the same status code.
57
57
  *
58
+ * `acting_aware` flows through to `derive_error_schemas` so routes whose
59
+ * input declares `acting?: ActingActor` (or whose auth requires permits)
60
+ * pick up the actor-failure error shapes the dispatcher's authorization
61
+ * phase may emit. The flag is computed at the call site rather than here
62
+ * because the `acting`-detection helper lives in `auth/` (it depends on
63
+ * the canonical `ActingActor` schema for reference equality, and `http/`
64
+ * stays auth-agnostic). See `http/CLAUDE.md` § Three-layer error-schema
65
+ * merge.
66
+ *
58
67
  * @param spec - the route spec (needs `auth`, `input`, `params`, `rate_limit`, `errors`)
68
+ * @param middleware_errors - errors contributed by middleware whose path matches the route
69
+ * @param acting_aware - whether the dispatcher's authorization phase may emit
70
+ * actor-failure errors on this route
59
71
  * @returns merged error schemas, or `null` if empty
60
72
  */
61
73
  export declare const merge_error_schemas: (spec: {
@@ -65,5 +77,5 @@ export declare const merge_error_schemas: (spec: {
65
77
  query?: z.ZodObject;
66
78
  rate_limit?: RateLimitKey;
67
79
  errors?: RouteErrorSchemas;
68
- }, middleware_errors?: RouteErrorSchemas | null) => RouteErrorSchemas | null;
80
+ }, middleware_errors?: RouteErrorSchemas | null, acting_aware?: boolean) => RouteErrorSchemas | null;
69
81
  //# sourceMappingURL=schema_helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/schema_helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAuB,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAC,MAAM,oBAAoB,CAAC;AAEnG;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAAsC,CAAC;AAE1F;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAAsC,CAAC;AAE1F;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OACe,CAAC;AAE5E;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAQrD,CAAC;AAoBF;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,EAAE,YAAY,MAAM,KAAG,OAQxE,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAC/B,MAAM;IACL,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC3B,EACD,oBAAoB,iBAAiB,GAAG,IAAI,KAC1C,iBAAiB,GAAG,IAUtB,CAAC"}
1
+ {"version":3,"file":"schema_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/schema_helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAuB,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAC,MAAM,oBAAoB,CAAC;AAEnG;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAAsC,CAAC;AAE1F;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAAsC,CAAC;AAE1F;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OACe,CAAC;AAE5E;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAQrD,CAAC;AAoBF;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,EAAE,YAAY,MAAM,KAAG,OAQxE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,mBAAmB,GAC/B,MAAM;IACL,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC3B,EACD,oBAAoB,iBAAiB,GAAG,IAAI,EAC5C,sBAAoB,KAClB,iBAAiB,GAAG,IAWtB,CAAC"}
@@ -94,11 +94,30 @@ export const middleware_applies = (mw_path, route_path) => {
94
94
  * Merge order: derived -> middleware -> explicit route errors.
95
95
  * Later layers override earlier ones for the same status code.
96
96
  *
97
+ * `acting_aware` flows through to `derive_error_schemas` so routes whose
98
+ * input declares `acting?: ActingActor` (or whose auth requires permits)
99
+ * pick up the actor-failure error shapes the dispatcher's authorization
100
+ * phase may emit. The flag is computed at the call site rather than here
101
+ * because the `acting`-detection helper lives in `auth/` (it depends on
102
+ * the canonical `ActingActor` schema for reference equality, and `http/`
103
+ * stays auth-agnostic). See `http/CLAUDE.md` § Three-layer error-schema
104
+ * merge.
105
+ *
97
106
  * @param spec - the route spec (needs `auth`, `input`, `params`, `rate_limit`, `errors`)
107
+ * @param middleware_errors - errors contributed by middleware whose path matches the route
108
+ * @param acting_aware - whether the dispatcher's authorization phase may emit
109
+ * actor-failure errors on this route
98
110
  * @returns merged error schemas, or `null` if empty
99
111
  */
100
- export const merge_error_schemas = (spec, middleware_errors) => {
101
- const derived = derive_error_schemas(spec.auth, !is_null_schema(spec.input), !!spec.params, !!spec.query, spec.rate_limit);
112
+ export const merge_error_schemas = (spec, middleware_errors, acting_aware = false) => {
113
+ const derived = derive_error_schemas({
114
+ auth: spec.auth,
115
+ has_input: !is_null_schema(spec.input),
116
+ has_params: !!spec.params,
117
+ has_query: !!spec.query,
118
+ rate_limit: spec.rate_limit,
119
+ acting_aware,
120
+ });
102
121
  const merged = { ...derived, ...middleware_errors, ...spec.errors };
103
122
  return Object.keys(merged).length > 0 ? merged : null;
104
123
  };
@@ -9,7 +9,7 @@
9
9
  import { z } from 'zod';
10
10
  import type { EventSpec } from '../realtime/sse.js';
11
11
  import type { MiddlewareSpec } from './middleware_spec.js';
12
- import type { RouteAuth, RouteSpec } from './route_spec.js';
12
+ import type { IsActingAware, RouteAuth, RouteSpec } from './route_spec.js';
13
13
  import type { RateLimitKey, RouteErrorSchemas } from './error_schemas.js';
14
14
  import type { RpcAction } from '../actions/action_rpc.js';
15
15
  import type { Sensitivity } from '../sensitivity.js';
@@ -118,6 +118,15 @@ export interface GenerateAppSurfaceOptions {
118
118
  env_schema?: z.ZodObject;
119
119
  event_specs?: Array<EventSpec>;
120
120
  rpc_endpoints?: Array<RpcEndpointSpec>;
121
+ /**
122
+ * Per-route predicate that decides whether the dispatcher's authorization
123
+ * phase may emit `actor_required` / `actor_not_on_account` (400) or
124
+ * `no_actors_on_account` / `account_vanished` (500) on this spec. Mirrors
125
+ * the parameter on `apply_route_specs` so the surface exposes the same
126
+ * error shapes the live framework would emit. See `http/CLAUDE.md` §
127
+ * Three-layer error-schema merge.
128
+ */
129
+ is_acting_aware?: IsActingAware;
121
130
  }
122
131
  /**
123
132
  * Collect error schemas from all middleware that applies to a route path.
@@ -1 +1 @@
1
- {"version":3,"file":"surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/surface.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/surface.ts"],"names":[],"mappings":"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"}
@@ -61,7 +61,7 @@ export const events_to_surface = (event_specs) => {
61
61
  * and optional env/event metadata.
62
62
  */
63
63
  export const generate_app_surface = (options) => {
64
- const { route_specs, middleware_specs, env_schema, event_specs, rpc_endpoints } = options;
64
+ const { route_specs, middleware_specs, env_schema, event_specs, rpc_endpoints, is_acting_aware } = options;
65
65
  const diagnostics = [];
66
66
  // Spec-level diagnostics: check for non-strict input schemas
67
67
  for (const r of route_specs) {
@@ -98,7 +98,7 @@ export const generate_app_surface = (options) => {
98
98
  .map((m) => m.name);
99
99
  // Merge auto-derived + middleware + explicit error schemas
100
100
  const mw_errors = collect_middleware_errors(middleware_specs, r.path);
101
- const merged_errors = merge_error_schemas(r, mw_errors);
101
+ const merged_errors = merge_error_schemas(r, mw_errors, is_acting_aware?.(r) ?? false);
102
102
  let error_schemas = null;
103
103
  if (merged_errors) {
104
104
  const schemas = {};
@@ -1 +1 @@
1
- {"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"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"}
@@ -27,6 +27,7 @@ import { check_bootstrap_status, create_bootstrap_route_specs, } from '../auth/b
27
27
  import { create_surface_route_spec } from '../http/common_routes.js';
28
28
  import { create_auth_middleware_specs } from '../auth/middleware.js';
29
29
  import { fuz_auth_guard_resolver } from '../auth/route_guards.js';
30
+ import { create_fuz_authorization_handler, input_schema_declares_acting, is_actor_implying_auth, } from '../auth/request_context.js';
30
31
  import { ERROR_PAYLOAD_TOO_LARGE } from '../http/error_schemas.js';
31
32
  import { create_rpc_endpoint } from '../actions/action_rpc.js';
32
33
  /** Default maximum request body size: 1 MiB. */
@@ -160,12 +161,20 @@ export const create_app_server = async (options) => {
160
161
  ...(options.event_specs ?? []),
161
162
  ...(audit_sse ? AUDIT_LOG_EVENT_SPECS : []),
162
163
  ];
164
+ // Per-route flag for the dispatcher's authorization-phase error shapes.
165
+ // Mirrors the runtime check in `apply_authorization_phase`: a route emits
166
+ // actor-failure errors when its input declares `acting?: ActingActor` or
167
+ // its auth requires permits. Routes that fail this check stay on the
168
+ // narrow derived schema (validation + auth shapes) so non-fuz_app HTTP
169
+ // frameworks aren't forced to surface fuz_app-specific error codes.
170
+ const fuz_is_acting_aware = (spec) => is_actor_implying_auth(spec.auth) || input_schema_declares_acting(spec.input);
163
171
  const surface_spec = create_app_surface_spec({
164
172
  middleware_specs: surface_middleware,
165
173
  route_specs,
166
174
  env_schema: options.env_schema,
167
175
  event_specs: all_event_specs,
168
176
  rpc_endpoints: resolved_rpc_endpoints,
177
+ is_acting_aware: fuz_is_acting_aware,
169
178
  });
170
179
  // Config-level diagnostics (concatenated after spec-level from generate_app_surface)
171
180
  const config_diagnostics = [];
@@ -257,7 +266,8 @@ export const create_app_server = async (options) => {
257
266
  }));
258
267
  }
259
268
  apply_middleware_specs(app, middleware_specs);
260
- apply_route_specs(app, route_specs, fuz_auth_guard_resolver, log, deps.db);
269
+ const authorize = create_fuz_authorization_handler({ db: deps.db });
270
+ apply_route_specs(app, route_specs, fuz_auth_guard_resolver, log, deps.db, authorize, fuz_is_acting_aware);
261
271
  // Post-route middleware (before static serving)
262
272
  if (options.post_route_middleware) {
263
273
  apply_middleware_specs(app, options.post_route_middleware);
@@ -157,9 +157,13 @@ bind to the server's deps (db, keyring). Hono assembly is cheap
157
157
 
158
158
  Pre-built Hono apps at each auth level (public / authed / keeper / per-role)
159
159
  for attack-surface testing. No middleware stack — a single `/*` middleware
160
- injects the `REQUEST_CONTEXT_KEY` + `CREDENTIAL_TYPE_KEY` (default
161
- `'session'`) and hands off to `apply_route_specs` with
162
- `fuz_auth_guard_resolver`.
160
+ injects `ACCOUNT_ID_KEY` + `REQUEST_CONTEXT_KEY` + `CREDENTIAL_TYPE_KEY`
161
+ (default `'session'`) plus the `TEST_CONTEXT_PRESET_KEY` flag (so the
162
+ dispatcher's authorization phase trusts the pre-baked context and skips
163
+ its DB-backed actor resolution), then hands off to `apply_route_specs`
164
+ with `fuz_auth_guard_resolver` + `create_fuz_authorization_handler`.
165
+ Production middleware never sets `TEST_CONTEXT_PRESET_KEY`, so the escape
166
+ hatch is test-only by construction.
163
167
 
164
168
  | Helper | Role |
165
169
  | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -389,22 +393,24 @@ test file that imports from `middleware.ts` gets these mocks globally.
389
393
  Pair with `vi.restoreAllMocks()` in `afterEach` when mixing into
390
394
  `.db.test.ts` files (see DB test caveat below).
391
395
 
392
- | Helper | Role |
393
- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
394
- | `BearerAuthTestOptions`, `BearerAuthTestCase` | Test-case table shape for the bearer auth runner. |
395
- | `create_bearer_auth_mocks(tc)` | Configures the module-level mocks per test case; returns spy references. |
396
- | `TEST_CLIENT_IP = '127.0.0.1'` | IP set by the proxy stub in `create_bearer_auth_test_app`. |
397
- | `create_bearer_auth_test_app(tc, ip_rate_limiter?)` | Hono app with bearer middleware + echo route at `/api/test` returning `{ok, has_context, credential_type, account_id, actor_id, permit_count, api_token_id}`. |
398
- | `describe_bearer_auth_cases(suite_name, cases, ip_rate_limiter?)` | Table-driven runner — one `test()` per case; asserts status, error, body fields, `api_token_id`, context preservation. |
399
- | `TEST_MIDDLEWARE_PATH = '/api/test'` | Path used by the echo route in the stack factory. |
400
- | `create_test_middleware_stack_app(options?)` | Real proxy + origin + bearer middleware for integration-shape testing. Echo route returns `{ok, client_ip, has_context}`. |
396
+ | Helper | Role |
397
+ | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
398
+ | `BearerAuthTestOptions`, `BearerAuthTestCase` | Test-case table shape for the bearer auth runner. |
399
+ | `create_bearer_auth_mocks(tc)` | Configures the module-level mocks per test case; returns spy references. |
400
+ | `TEST_CLIENT_IP = '127.0.0.1'` | IP set by the proxy stub in `create_bearer_auth_test_app`. |
401
+ | `create_bearer_auth_test_app(tc, ip_rate_limiter?)` | Hono app with bearer middleware + echo route at `/api/test` returning `{ok, account_id, credential_type, api_token_id, request_context_set}` — the account-grain identity bearer auth writes, plus a flag for tests that pre-populate `REQUEST_CONTEXT_KEY` via `pre_context`. |
402
+ | `describe_bearer_auth_cases(suite_name, cases, ip_rate_limiter?)` | Table-driven runner — one `test()` per case; asserts status, error, body fields, `api_token_id`, context preservation. |
403
+ | `TEST_MIDDLEWARE_PATH = '/api/test'` | Path used by the echo route in the stack factory. |
404
+ | `create_test_middleware_stack_app(options?)` | Real proxy + origin + bearer middleware for integration-shape testing. Echo route returns `{ok, client_ip, has_context}`. |
401
405
 
402
406
  The echo route under `create_bearer_auth_test_app` deliberately surfaces
403
- every middleware-written context variable (`REQUEST_CONTEXT_KEY`,
404
- `CREDENTIAL_TYPE_KEY`, `AUTH_API_TOKEN_ID_KEY`). When public auth surface
405
- gains a new context variable, header, or field, update this echo
406
- alongside the assertions in `src/test/auth/*.test.ts` the two move
407
- together.
407
+ every middleware-written context variable (`ACCOUNT_ID_KEY`,
408
+ `CREDENTIAL_TYPE_KEY`, `AUTH_API_TOKEN_ID_KEY`) bearer middleware
409
+ writes account-grain identity only; the dispatcher's authorization phase
410
+ owns `REQUEST_CONTEXT_KEY`. The `request_context_set` flag covers the
411
+ test-only `pre_context` injection path. When public auth surface gains a
412
+ new context variable, header, or field, update this echo alongside the
413
+ assertions in `src/test/auth/*.test.ts` — the two move together.
408
414
 
409
415
  ## Round-trip suites
410
416
 
@@ -1 +1 @@
1
- {"version":3,"file":"admin_integration.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/admin_integration.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAgC7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAA0B,KAAK,gBAAgB,EAAC,MAAM,wBAAwB,CAAC;AAEtF,OAAO,EAA6C,KAAK,eAAe,EAAC,MAAM,iBAAiB,CAAC;AACjG,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,SAAS,CAAC;AASjB,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAqB1B;;GAEG;AACH,MAAM,WAAW,mCAAmC;IACnD,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,4GAA4G;IAC5G,KAAK,EAAE,gBAAgB,CAAC;IACxB;;;;;;;;;;;;OAYG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B;;;OAGG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAChC;AAgCD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,yCAAyC,GACrD,SAAS,mCAAmC,KAC1C,IAu1BF,CAAC"}
1
+ {"version":3,"file":"admin_integration.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/admin_integration.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAgC7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAA0B,KAAK,gBAAgB,EAAC,MAAM,wBAAwB,CAAC;AAEtF,OAAO,EAA6C,KAAK,eAAe,EAAC,MAAM,iBAAiB,CAAC;AACjG,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,SAAS,CAAC;AASjB,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAoB1B;;GAEG;AACH,MAAM,WAAW,mCAAmC;IACnD,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,4GAA4G;IAC5G,KAAK,EAAE,gBAAgB,CAAC;IACxB;;;;;;;;;;;;OAYG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B;;;OAGG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAChC;AAgCD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,yCAAyC,GACrD,SAAS,mCAAmC,KAC1C,IA21BF,CAAC"}
@@ -38,7 +38,6 @@ import { permit_offer_create_action_spec, permit_revoke_action_spec, } from '../
38
38
  import { admin_account_list_action_spec, admin_session_list_action_spec, admin_session_revoke_all_action_spec, admin_token_revoke_all_action_spec, audit_log_list_action_spec, audit_log_permit_history_action_spec, } from '../auth/admin_action_specs.js';
39
39
  import { account_token_create_action_spec, account_verify_action_spec, } from '../auth/account_action_specs.js';
40
40
  import { query_grant_permit } from '../auth/permit_queries.js';
41
- import { query_actor_by_account } from '../auth/account_queries.js';
42
41
  import { query_accept_offer } from '../auth/permit_offer_queries.js';
43
42
  /**
44
43
  * Pick a web-grantable role for testing, preferring a non-admin app-defined role.
@@ -153,7 +152,12 @@ export const describe_standard_admin_integration_tests = (options) => {
153
152
  });
154
153
  assert.ok(res.ok, `permit_offer_create failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
155
154
  const { offer } = res.result;
156
- const accept_result = await get_db().transaction(async (tx) => query_accept_offer({ db: tx }, { offer_id: offer.id, to_account_id: args.to_account_id, ip: null }));
155
+ const accept_result = await get_db().transaction(async (tx) => query_accept_offer({ db: tx }, {
156
+ offer_id: offer.id,
157
+ to_account_id: args.to_account_id,
158
+ actor_id: args.to_actor_id,
159
+ ip: null,
160
+ }));
157
161
  return { offer_id: offer.id, permit_id: accept_result.permit.id };
158
162
  };
159
163
  // --- 1. Admin account listing (RPC) ---
@@ -165,7 +169,7 @@ export const describe_standard_admin_integration_tests = (options) => {
165
169
  app: test_app.app,
166
170
  path: rpc_path,
167
171
  spec: admin_account_list_action_spec,
168
- params: undefined,
172
+ params: {},
169
173
  headers: test_app.create_session_headers(),
170
174
  });
171
175
  assert.ok(res.ok, `admin_account_list failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
@@ -183,7 +187,7 @@ export const describe_standard_admin_integration_tests = (options) => {
183
187
  app: test_app.app,
184
188
  path: rpc_path,
185
189
  spec: admin_account_list_action_spec,
186
- params: undefined,
190
+ params: {},
187
191
  headers: test_app.create_session_headers(),
188
192
  });
189
193
  assert.ok(!res.ok, 'Expected admin_account_list to fail for non-admin');
@@ -207,7 +211,7 @@ export const describe_standard_admin_integration_tests = (options) => {
207
211
  app: test_app.app,
208
212
  path: rpc_path,
209
213
  spec: admin_session_list_action_spec,
210
- params: undefined,
214
+ params: {},
211
215
  headers: test_app.create_session_headers(),
212
216
  });
213
217
  assert.ok(res.ok, `admin_session_list failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
@@ -358,6 +362,7 @@ export const describe_standard_admin_integration_tests = (options) => {
358
362
  app: test_app.app,
359
363
  admin_headers: test_app.create_session_headers(),
360
364
  to_account_id: user_two.account.id,
365
+ to_actor_id: user_two.actor.id,
361
366
  role: grantable_role,
362
367
  });
363
368
  const res = await rpc_call_for_spec({
@@ -376,10 +381,8 @@ export const describe_standard_admin_integration_tests = (options) => {
376
381
  test('permit revoke creates audit event', async () => {
377
382
  const test_app = await create_test_app(build_admin_test_app_options(options, get_db()));
378
383
  const user_two = await test_app.create_account({ username: 'user_two' });
379
- const target_actor = await query_actor_by_account({ db: get_db() }, user_two.account.id);
380
- assert.ok(target_actor);
381
384
  const permit = await query_grant_permit({ db: get_db() }, {
382
- actor_id: target_actor.id,
385
+ actor_id: user_two.actor.id,
383
386
  role: grantable_role,
384
387
  granted_by: test_app.backend.actor.id,
385
388
  });
@@ -388,7 +391,7 @@ export const describe_standard_admin_integration_tests = (options) => {
388
391
  app: test_app.app,
389
392
  path: rpc_path,
390
393
  spec: permit_revoke_action_spec,
391
- params: { actor_id: target_actor.id, permit_id: permit.id },
394
+ params: { actor_id: user_two.actor.id, permit_id: permit.id },
392
395
  headers: test_app.create_session_headers(),
393
396
  });
394
397
  assert.ok(revoke_res.ok, `permit_revoke failed: ${revoke_res.ok ? '' : JSON.stringify(revoke_res.error)}`);
@@ -560,16 +563,15 @@ export const describe_standard_admin_integration_tests = (options) => {
560
563
  app: test_app.app,
561
564
  admin_headers: test_app.create_session_headers(),
562
565
  to_account_id: user_two.account.id,
566
+ to_actor_id: user_two.actor.id,
563
567
  role: grantable_role,
564
568
  });
565
569
  // 4. revoke permit (RPC)
566
- const target_actor = await query_actor_by_account({ db: get_db() }, user_two.account.id);
567
- assert.ok(target_actor);
568
570
  const revoke_res = await rpc_call_for_spec({
569
571
  app: test_app.app,
570
572
  path: rpc_path,
571
573
  spec: permit_revoke_action_spec,
572
- params: { actor_id: target_actor.id, permit_id },
574
+ params: { actor_id: user_two.actor.id, permit_id },
573
575
  headers: test_app.create_session_headers(),
574
576
  });
575
577
  assert.ok(revoke_res.ok, `permit_revoke failed: ${revoke_res.ok ? '' : JSON.stringify(revoke_res.error)}`);
@@ -727,7 +729,7 @@ export const describe_standard_admin_integration_tests = (options) => {
727
729
  app: test_app.app,
728
730
  path: rpc_path,
729
731
  spec: admin_account_list_action_spec,
730
- params: undefined,
732
+ params: {},
731
733
  headers: create_headers(regular_user.session_cookie),
732
734
  });
733
735
  assert.ok(!res.ok, 'Expected admin_account_list to fail for non-admin');
@@ -112,7 +112,7 @@ export const describe_standard_adversarial_headers = (suite_name, options, allow
112
112
  }
113
113
  if (tc.expected_status === 200) {
114
114
  assert.strictEqual(body.ok, true, 'expected ok to be true for 200 response');
115
- assert.strictEqual(body.has_context, false, 'expected has_context to be false (no auth)');
115
+ assert.strictEqual(body.account_id, null, 'expected account_id to be null (no auth)');
116
116
  }
117
117
  if (tc.validate_expectation === 'not_called') {
118
118
  assert.strictEqual(mock_validate.mock.calls.length, 0, 'validate should not have been called — middleware should short-circuit');
@@ -56,10 +56,10 @@ export const bootstrap_test_account = async (options) => {
56
56
  for (const role of roles) {
57
57
  await query_grant_permit(deps, { actor_id: actor.id, role, granted_by: null });
58
58
  }
59
- // Create API token
59
+ // Create API token (account-scoped — acting actor is per-request)
60
60
  const { token: api_token, id: token_id, token_hash } = generate_api_token();
61
61
  await query_create_api_token(deps, token_id, account.id, 'test-cli', token_hash);
62
- // Create session + cookie
62
+ // Create session (account-scoped — acting actor is per-request)
63
63
  const session_token = generate_session_token();
64
64
  const session_hash = hash_session_token(session_token);
65
65
  const expires_at = new Date(Date.now() + AUTH_SESSION_LIFETIME_MS);
@@ -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,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAIrD,OAAO,EAGN,KAAK,eAAe,EAEpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,SAAS,CAAC;AAKjB,OAAO,EAIN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAsB1B;;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;;;;;;;;;;;OAWG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC,iDAAiD;IACjD,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,qEAAqE;IACrE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAChC;AAoDD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IAyezF,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,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAIrD,OAAO,EAGN,KAAK,eAAe,EAEpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,SAAS,CAAC;AAKjB,OAAO,EAIN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAqB1B;;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;;;;;;;;;;;OAWG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC,iDAAiD;IACjD,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,qEAAqE;IACrE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAChC;AAoDD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IA8fzF,CAAC"}