@fuzdev/fuz_app 0.113.0 → 0.115.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 (129) hide show
  1. package/dist/actions/CLAUDE.md +46 -20
  2. package/dist/actions/action_dispatcher.d.ts +1 -1
  3. package/dist/actions/action_dispatcher.d.ts.map +1 -1
  4. package/dist/actions/action_dispatcher.js +22 -16
  5. package/dist/actions/action_event.d.ts +18 -2
  6. package/dist/actions/action_event.d.ts.map +1 -1
  7. package/dist/actions/action_event.js +63 -19
  8. package/dist/actions/action_event_types.d.ts +22 -0
  9. package/dist/actions/action_event_types.d.ts.map +1 -1
  10. package/dist/actions/action_event_types.js +19 -0
  11. package/dist/actions/compile_action_registry.d.ts +4 -2
  12. package/dist/actions/compile_action_registry.d.ts.map +1 -1
  13. package/dist/actions/compile_action_registry.js +8 -3
  14. package/dist/actions/frontend_rpc_client.d.ts +6 -0
  15. package/dist/actions/frontend_rpc_client.d.ts.map +1 -1
  16. package/dist/actions/frontend_rpc_client.js +2 -1
  17. package/dist/actions/heartbeat.d.ts +8 -4
  18. package/dist/actions/heartbeat.d.ts.map +1 -1
  19. package/dist/actions/heartbeat.js +8 -4
  20. package/dist/actions/peer_ping.d.ts.map +1 -1
  21. package/dist/actions/peer_ping.js +3 -1
  22. package/dist/actions/perform_action.d.ts +1 -1
  23. package/dist/actions/perform_action.d.ts.map +1 -1
  24. package/dist/actions/perform_action.js +6 -4
  25. package/dist/actions/register_action_ws.d.ts +13 -0
  26. package/dist/actions/register_action_ws.d.ts.map +1 -1
  27. package/dist/actions/register_action_ws.js +23 -2
  28. package/dist/actions/rpc_client.d.ts +1 -1
  29. package/dist/actions/rpc_client.d.ts.map +1 -1
  30. package/dist/actions/rpc_client.js +2 -1
  31. package/dist/actions/socket.svelte.d.ts +53 -10
  32. package/dist/actions/socket.svelte.d.ts.map +1 -1
  33. package/dist/actions/socket.svelte.js +84 -23
  34. package/dist/actions/transports.d.ts +24 -0
  35. package/dist/actions/transports.d.ts.map +1 -1
  36. package/dist/actions/transports.js +30 -0
  37. package/dist/actions/transports_http.d.ts +11 -4
  38. package/dist/actions/transports_http.d.ts.map +1 -1
  39. package/dist/actions/transports_http.js +63 -18
  40. package/dist/actions/transports_ws.d.ts +28 -3
  41. package/dist/actions/transports_ws.d.ts.map +1 -1
  42. package/dist/actions/transports_ws.js +33 -4
  43. package/dist/actions/ws_endpoint_spec.d.ts +7 -0
  44. package/dist/actions/ws_endpoint_spec.d.ts.map +1 -1
  45. package/dist/auth/CLAUDE.md +52 -8
  46. package/dist/auth/admin_actions.d.ts.map +1 -1
  47. package/dist/auth/admin_actions.js +4 -3
  48. package/dist/auth/audit_log_schema.d.ts +3 -1
  49. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  50. package/dist/auth/audit_log_schema.js +13 -3
  51. package/dist/auth/cell_action_specs.js +1 -1
  52. package/dist/auth/cell_actions.d.ts.map +1 -1
  53. package/dist/auth/cell_actions.js +33 -4
  54. package/dist/auth/cell_authorize.d.ts +3 -0
  55. package/dist/auth/cell_authorize.d.ts.map +1 -1
  56. package/dist/auth/cell_authorize.js +7 -4
  57. package/dist/auth/request_context.d.ts +12 -3
  58. package/dist/auth/request_context.d.ts.map +1 -1
  59. package/dist/auth/request_context.js +12 -3
  60. package/dist/auth/role_grant_offer_action_specs.d.ts +30 -5
  61. package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
  62. package/dist/auth/role_grant_offer_action_specs.js +53 -17
  63. package/dist/auth/role_grant_offer_actions.d.ts +23 -12
  64. package/dist/auth/role_grant_offer_actions.d.ts.map +1 -1
  65. package/dist/auth/role_grant_offer_actions.js +132 -38
  66. package/dist/auth/role_grant_offer_queries.d.ts +36 -0
  67. package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
  68. package/dist/auth/role_grant_offer_queries.js +76 -0
  69. package/dist/auth/role_schema.d.ts +12 -0
  70. package/dist/auth/role_schema.d.ts.map +1 -1
  71. package/dist/auth/role_schema.js +12 -0
  72. package/dist/auth/self_service_role_actions.d.ts +14 -0
  73. package/dist/auth/self_service_role_actions.d.ts.map +1 -1
  74. package/dist/auth/self_service_role_actions.js +25 -1
  75. package/dist/http/CLAUDE.md +7 -3
  76. package/dist/http/jsonrpc_helpers.d.ts +0 -5
  77. package/dist/http/jsonrpc_helpers.d.ts.map +1 -1
  78. package/dist/http/jsonrpc_helpers.js +0 -13
  79. package/dist/http/schema_helpers.d.ts +56 -0
  80. package/dist/http/schema_helpers.d.ts.map +1 -1
  81. package/dist/http/schema_helpers.js +91 -0
  82. package/dist/server/app_server.d.ts.map +1 -1
  83. package/dist/server/app_server.js +1 -0
  84. package/dist/server/serve_fact_route.d.ts +3 -1
  85. package/dist/server/serve_fact_route.d.ts.map +1 -1
  86. package/dist/server/serve_fact_route.js +7 -4
  87. package/dist/testing/CLAUDE.md +29 -16
  88. package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
  89. package/dist/testing/cross_backend/cell_gated_create.js +23 -1
  90. package/dist/testing/cross_backend/peer_ping_ws.d.ts.map +1 -1
  91. package/dist/testing/cross_backend/peer_ping_ws.js +18 -0
  92. package/dist/testing/cross_backend/role_grant_participation.d.ts.map +1 -1
  93. package/dist/testing/cross_backend/role_grant_participation.js +34 -0
  94. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts +12 -1
  95. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
  96. package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +13 -1
  97. package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts.map +1 -1
  98. package/dist/testing/cross_backend/test_cell_gated_create_authorize.js +9 -7
  99. package/dist/testing/cross_backend/ws_round_trip.d.ts +11 -3
  100. package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -1
  101. package/dist/testing/cross_backend/ws_round_trip.js +74 -12
  102. package/dist/testing/entities.d.ts +6 -3
  103. package/dist/testing/entities.d.ts.map +1 -1
  104. package/dist/testing/entities.js +4 -1
  105. package/dist/testing/transports/ws_client.d.ts +6 -0
  106. package/dist/testing/transports/ws_client.d.ts.map +1 -1
  107. package/dist/testing/transports/ws_transport.d.ts.map +1 -1
  108. package/dist/testing/transports/ws_transport.js +6 -1
  109. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  110. package/dist/testing/ws_round_trip.js +5 -0
  111. package/dist/ui/BootstrapForm.svelte +14 -10
  112. package/dist/ui/BootstrapForm.svelte.d.ts.map +1 -1
  113. package/dist/ui/CLAUDE.md +19 -7
  114. package/dist/ui/LoginForm.svelte +8 -4
  115. package/dist/ui/LoginForm.svelte.d.ts.map +1 -1
  116. package/dist/ui/RoleGrantOfferForm.svelte +29 -14
  117. package/dist/ui/RoleGrantOfferForm.svelte.d.ts +2 -3
  118. package/dist/ui/RoleGrantOfferForm.svelte.d.ts.map +1 -1
  119. package/dist/ui/SignupForm.svelte +14 -13
  120. package/dist/ui/form_state.svelte.d.ts +24 -11
  121. package/dist/ui/form_state.svelte.d.ts.map +1 -1
  122. package/dist/ui/form_state.svelte.js +38 -10
  123. package/dist/ui/role_grant_offers_state.svelte.d.ts +25 -15
  124. package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -1
  125. package/dist/ui/role_grant_offers_state.svelte.js +7 -1
  126. package/package.json +2 -22
  127. package/dist/actions/request_tracker.svelte.d.ts +0 -77
  128. package/dist/actions/request_tracker.svelte.d.ts.map +0 -1
  129. package/dist/actions/request_tracker.svelte.js +0 -169
@@ -16,6 +16,7 @@ import { columns_sql, qualify_columns } from '../db/sql_columns.js';
16
16
  import { ROLE_GRANT_OFFER_COLUMNS, role_grant_offer_expr, ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN, ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID, ROLE_GRANT_OFFER_WITH_GRANTOR_SELECT } from './role_grant_offer_ddl.js';
17
17
  import { query_create_role_grant, query_role_grant_by_id } from './role_grant_queries.js';
18
18
  import { query_audit_log } from './audit_log_queries.js';
19
+ import { is_builtin_role, ROLE_ADMIN, ROLE_KEEPER } from './role_schema.js';
19
20
  /**
20
21
  * Error thrown by offer-lifecycle queries when the offer is in a non-pending
21
22
  * state (accepted / declined / retracted / superseded) and therefore not
@@ -81,6 +82,22 @@ export class RoleGrantOfferActorMismatchError extends Error {
81
82
  this.name = 'RoleGrantOfferActorMismatchError';
82
83
  }
83
84
  }
85
+ /**
86
+ * Error thrown by `query_accept_offer` when the offer confers a builtin role
87
+ * (`admin` / `keeper`) that this accept must not mint — `refusal` names the
88
+ * rule. Carries the locked (still pending, untouched) offer so the caller can
89
+ * audit the refusal with the offer's role and scope.
90
+ */
91
+ export class RoleGrantOfferBuiltinRefusedError extends Error {
92
+ offer;
93
+ refusal;
94
+ constructor(offer, refusal) {
95
+ super(`Offer ${offer.id} is refused: ${refusal}`);
96
+ this.name = 'RoleGrantOfferBuiltinRefusedError';
97
+ this.offer = offer;
98
+ this.refusal = refusal;
99
+ }
100
+ }
84
101
  /**
85
102
  * Error thrown when `query_role_grant_offer_create` is called with a
86
103
  * `to_actor_id` that does not exist or does not belong to `to_account_id`.
@@ -307,6 +324,35 @@ export const query_role_grant_offer_sweep_expired = async (deps) => {
307
324
  AND expires_at <= NOW()
308
325
  ORDER BY role_grant_offer.expires_at ASC`);
309
326
  };
327
+ /**
328
+ * Whether `grantor_actor_id` holds an active **global** `admin` on an active
329
+ * actor of an active account — the authority a builtin-role offer needs at the
330
+ * moment its grant is minted.
331
+ *
332
+ * A locking read: `FOR SHARE OF rg` takes a share lock on the grant row that
333
+ * admits, so a concurrent revoke (an `UPDATE` of that row) is strictly ordered
334
+ * against this accept — it either commits first, and the recheck of the locked
335
+ * row sees `revoked_at` set and excludes it, or it waits for this transaction
336
+ * to commit the grant it authorized. A plain `EXISTS` could read the grant as
337
+ * active while a revoke commits in between. The actor and account tombstones
338
+ * are read but not locked — ordering a delete racing an accept isn't worth
339
+ * locking two more tables. Twin of the Rust `grantor_holds_global_admin`.
340
+ */
341
+ const query_grantor_holds_global_admin = async (deps, grantor_actor_id) => {
342
+ const row = await deps.db.query_one(`SELECT 1 AS held FROM role_grant rg
343
+ JOIN actor act ON act.id = rg.actor_id
344
+ JOIN account a ON a.id = act.account_id
345
+ WHERE rg.actor_id = $1
346
+ AND rg.role = $2
347
+ AND rg.scope_id IS NULL
348
+ AND rg.revoked_at IS NULL
349
+ AND (rg.expires_at IS NULL OR rg.expires_at > NOW())
350
+ AND act.deleted_at IS NULL
351
+ AND a.deleted_at IS NULL
352
+ LIMIT 1
353
+ FOR SHARE OF rg`, [grantor_actor_id, ROLE_ADMIN]);
354
+ return row != null;
355
+ };
310
356
  /**
311
357
  * Accept an offer atomically: mark accepted, insert the role_grant, stamp
312
358
  * `resulting_role_grant_id`, supersede sibling pending offers for the same
@@ -325,6 +371,11 @@ export const query_role_grant_offer_sweep_expired = async (deps) => {
325
371
  * - `RoleGrantOfferAlreadyTerminalError` — offer is declined, retracted, or
326
372
  * superseded.
327
373
  * - `RoleGrantOfferExpiredError` — offer is pending but past `expires_at`.
374
+ * - `RoleGrantOfferActorMismatchError` — actor-targeted offer, different actor.
375
+ * - `RoleGrantOfferBuiltinRefusedError` — a builtin-role offer that must not
376
+ * mint: `keeper` at all, a builtin bound to a scope, or one whose grantor no
377
+ * longer holds an active global `admin` (read under `FOR SHARE`). The offer
378
+ * stays pending.
328
379
  *
329
380
  * Sibling supersede is what closes the "accept a pre-revoke sibling offer
330
381
  * to bypass a revoke" path: once A is accepted, B/C/... can no longer be
@@ -337,6 +388,8 @@ export const query_role_grant_offer_sweep_expired = async (deps) => {
337
388
  * @throws RoleGrantOfferNotFoundError if the offer is missing or belongs to another recipient
338
389
  * @throws RoleGrantOfferAlreadyTerminalError if the offer is declined, retracted, or superseded
339
390
  * @throws RoleGrantOfferExpiredError if the offer is pending but past `expires_at`
391
+ * @throws RoleGrantOfferActorMismatchError if the offer is actor-targeted to a different actor
392
+ * @throws RoleGrantOfferBuiltinRefusedError if a builtin-role offer must not mint (see `BuiltinOfferRefusal`)
340
393
  * @throws Error if the accepting `actor_id` does not belong to `to_account_id`, or invariant assertions fail
341
394
  */
342
395
  export const query_accept_offer = async (deps, input) => {
@@ -413,6 +466,29 @@ export const query_accept_offer = async (deps, input) => {
413
466
  if (locked.to_actor_id != null && locked.to_actor_id !== actor_id) {
414
467
  throw new RoleGrantOfferActorMismatchError(offer_id);
415
468
  }
469
+ // Builtin-role offers — checked here, the chokepoint where an offer turns
470
+ // into a `role_grant`, so a direct caller of this query can't skip them. No
471
+ // current grant path creates a refused shape, but a pending row can still
472
+ // carry one (a direct `query_role_grant_offer_create`, or an offer made under
473
+ // a since-closed path).
474
+ if (is_builtin_role(locked.role)) {
475
+ let refusal = null;
476
+ if (locked.role === ROLE_KEEPER) {
477
+ // `keeper` is bootstrap-only — never conferred through an offer, even
478
+ // from a global admin.
479
+ refusal = 'bootstrap_only';
480
+ }
481
+ else if (locked.scope_id !== null) {
482
+ // Builtin roles are global-only.
483
+ refusal = 'scoped';
484
+ }
485
+ else if (!(await query_grantor_holds_global_admin(deps, locked.from_actor_id))) {
486
+ refusal = 'grantor_not_authorized';
487
+ }
488
+ if (refusal !== null) {
489
+ throw new RoleGrantOfferBuiltinRefusedError(locked, refusal);
490
+ }
491
+ }
416
492
  // Verify the accepting actor belongs to the recipient account.
417
493
  // Defense-in-depth: the action handler passes `auth.actor.id` which is
418
494
  // already session-bound, but enforcing the invariant here protects
@@ -39,6 +39,18 @@ export declare const BuiltinRole: z.ZodEnum<{
39
39
  keeper: "keeper";
40
40
  }>;
41
41
  export type BuiltinRole = z.infer<typeof BuiltinRole>;
42
+ /**
43
+ * Whether `name` is a builtin role (`keeper` / `admin`).
44
+ *
45
+ * Builtin roles are **global-only**: no grant path binds one to a scope
46
+ * (`role_grant_assign`, `role_grant_offer_create`, and
47
+ * `role_grant_offer_accept` refuse it), and every gate that reads one checks
48
+ * the global grant, so a scoped builtin row confers nothing.
49
+ *
50
+ * @param name - the role name to check
51
+ * @returns `true` for `keeper` or `admin`
52
+ */
53
+ export declare const is_builtin_role: (name: string) => name is BuiltinRole;
42
54
  /**
43
55
  * Configuration for a role.
44
56
  *
@@ -1 +1 @@
1
- {"version":3,"file":"role_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAEN,KAAK,0BAA0B,EAC/B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAGN,KAAK,qBAAqB,EAC1B,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAEpE,0FAA0F;AAC1F,eAAO,MAAM,QAAQ,aAKnB,CAAC;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAC;AAIhD,sFAAsF;AACtF,eAAO,MAAM,WAAW,WAAW,CAAC;AAEpC,iEAAiE;AACjE,eAAO,MAAM,UAAU,UAAU,CAAC;AAElC,+CAA+C;AAC/C,eAAO,MAAM,aAAa,8BAAqC,CAAC;AAEhE,yCAAyC;AACzC,eAAO,MAAM,WAAW;;;EAAwB,CAAC;AACjD,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,QAAQ;IACxB,qFAAqF;IACrF,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,yBAAyB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAClD;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC/C;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACpC;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAuBnE,CAAC;AAEH,iGAAiG;AACjG,MAAM,WAAW,uBAAuB;IACvC,uGAAuG;IACvG,gBAAgB,CAAC,EAAE,0BAA0B,CAAC;IAC9C,+FAA+F;IAC/F,WAAW,CAAC,EAAE,qBAAqB,CAAC;IACpC,oFAAoF;IACpF,WAAW,CAAC,EAAE,qBAAqB,CAAC;CACpC;AAED,oFAAoF;AACpF,MAAM,WAAW,gBAAgB;IAChC,0GAA0G;IAC1G,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACxB,yEAAyE;IACzE,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;CAC1C;AAqBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,eAAO,MAAM,kBAAkB,GAC9B,gBAAgB,aAAa,CAAC,QAAQ,CAAC,EACvC,UAAS,uBAA4B,KACnC,gBA2CF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAC/B,YAAY,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,EACzC,MAAM,MAAM,EACZ,YAAY,MAAM,KAChB,OAGF,CAAC;AAEF,kFAAkF;AAClF,eAAO,MAAM,0BAA0B,GACtC,YAAY,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,EACzC,YAAY,MAAM,KAChB,KAAK,CAAC,MAAM,CAMd,CAAC"}
1
+ {"version":3,"file":"role_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAEN,KAAK,0BAA0B,EAC/B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAGN,KAAK,qBAAqB,EAC1B,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAEpE,0FAA0F;AAC1F,eAAO,MAAM,QAAQ,aAKnB,CAAC;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAC;AAIhD,sFAAsF;AACtF,eAAO,MAAM,WAAW,WAAW,CAAC;AAEpC,iEAAiE;AACjE,eAAO,MAAM,UAAU,UAAU,CAAC;AAElC,+CAA+C;AAC/C,eAAO,MAAM,aAAa,8BAAqC,CAAC;AAEhE,yCAAyC;AACzC,eAAO,MAAM,WAAW;;;EAAwB,CAAC;AACjD,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,KAAG,IAAI,IAAI,WACX,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,QAAQ;IACxB,qFAAqF;IACrF,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,yBAAyB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAClD;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC/C;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACpC;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAuBnE,CAAC;AAEH,iGAAiG;AACjG,MAAM,WAAW,uBAAuB;IACvC,uGAAuG;IACvG,gBAAgB,CAAC,EAAE,0BAA0B,CAAC;IAC9C,+FAA+F;IAC/F,WAAW,CAAC,EAAE,qBAAqB,CAAC;IACpC,oFAAoF;IACpF,WAAW,CAAC,EAAE,qBAAqB,CAAC;CACpC;AAED,oFAAoF;AACpF,MAAM,WAAW,gBAAgB;IAChC,0GAA0G;IAC1G,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACxB,yEAAyE;IACzE,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;CAC1C;AAqBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,eAAO,MAAM,kBAAkB,GAC9B,gBAAgB,aAAa,CAAC,QAAQ,CAAC,EACvC,UAAS,uBAA4B,KACnC,gBA2CF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAC/B,YAAY,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,EACzC,MAAM,MAAM,EACZ,YAAY,MAAM,KAChB,OAGF,CAAC;AAEF,kFAAkF;AAClF,eAAO,MAAM,0BAA0B,GACtC,YAAY,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,EACzC,YAAY,MAAM,KAChB,KAAK,CAAC,MAAM,CAMd,CAAC"}
@@ -36,6 +36,18 @@ export const ROLE_ADMIN = 'admin';
36
36
  export const BUILTIN_ROLES = [ROLE_KEEPER, ROLE_ADMIN];
37
37
  /** Zod schema for builtin roles only. */
38
38
  export const BuiltinRole = z.enum(BUILTIN_ROLES);
39
+ /**
40
+ * Whether `name` is a builtin role (`keeper` / `admin`).
41
+ *
42
+ * Builtin roles are **global-only**: no grant path binds one to a scope
43
+ * (`role_grant_assign`, `role_grant_offer_create`, and
44
+ * `role_grant_offer_accept` refuse it), and every gate that reads one checks
45
+ * the global grant, so a scoped builtin row confers nothing.
46
+ *
47
+ * @param name - the role name to check
48
+ * @returns `true` for `keeper` or `admin`
49
+ */
50
+ export const is_builtin_role = (name) => name === ROLE_KEEPER || name === ROLE_ADMIN;
39
51
  /**
40
52
  * Builtin role specs, keyed by role name. Not overridable by consumers
41
53
  * — read once at startup by `create_role_schema` and the action
@@ -39,6 +39,19 @@
39
39
  import { type RpcAction } from '../actions/action_rpc.js';
40
40
  import { type RoleSchemaResult } from './role_schema.js';
41
41
  import type { ActionFactoryDeps } from './deps.js';
42
+ /**
43
+ * Validate a deployment's self-service eligible-role set at construction.
44
+ *
45
+ * Refuses a builtin role (`admin` / `keeper`) — self-toggling one would hand
46
+ * any account instance-wide authority, so no eligible set may name it — and a
47
+ * name outside the `RoleName` grammar. The same fail-loud-at-boot shape as
48
+ * `create_role_schema`, which refuses a builtin name where only app roles
49
+ * belong. Twin of the Rust `validate_self_service_eligible_roles`.
50
+ *
51
+ * @param roles - the eligible role names
52
+ * @throws Error on a builtin role or a name outside the role grammar
53
+ */
54
+ export declare const validate_self_service_eligible_roles: (roles: Iterable<string>) => void;
42
55
  /** Options for `create_self_service_role_actions`. */
43
56
  export interface SelfServiceRoleActionsOptions {
44
57
  /**
@@ -70,6 +83,7 @@ export interface SelfServiceRoleActionsOptions {
70
83
  * @param options - optional eligible-role override plus optional role schema for default-eligibility derivation
71
84
  * @returns the `RpcAction` array to spread into a `create_rpc_endpoint` call
72
85
  * @throws Error at factory time if any `eligible_roles` entry is missing from `options.roles.role_specs`
86
+ * @throws Error at factory time if the eligible set names a builtin role or an invalid role name
73
87
  */
74
88
  export declare const create_self_service_role_actions: (deps: ActionFactoryDeps, options?: SelfServiceRoleActionsOptions) => Array<RpcAction>;
75
89
  //# sourceMappingURL=self_service_role_actions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"self_service_role_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/self_service_role_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE/F,OAAO,EAGN,KAAK,gBAAgB,EACrB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAWnD,sDAAsD;AACtD,MAAM,WAAW,6BAA6B;IAC7C;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACvC;;;;OAIG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,iBAAiB,EACvB,UAAS,6BAAkC,KACzC,KAAK,CAAC,SAAS,CA2HjB,CAAC"}
1
+ {"version":3,"file":"self_service_role_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/self_service_role_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE/F,OAAO,EAKN,KAAK,gBAAgB,EACrB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAWnD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oCAAoC,GAAI,OAAO,QAAQ,CAAC,MAAM,CAAC,KAAG,IAW9E,CAAC;AAEF,sDAAsD;AACtD,MAAM,WAAW,6BAA6B;IAC7C;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACvC;;;;OAIG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,iBAAiB,EACvB,UAAS,6BAAkC,KACzC,KAAK,CAAC,SAAS,CA6HjB,CAAC"}
@@ -38,12 +38,34 @@
38
38
  */
39
39
  import { rpc_action } from '../actions/action_rpc.js';
40
40
  import { jsonrpc_errors } from '../http/jsonrpc_errors.js';
41
- import { builtin_role_specs_by_name, list_roles_with_grant_path } from './role_schema.js';
41
+ import { builtin_role_specs_by_name, is_builtin_role, list_roles_with_grant_path, RoleName } from './role_schema.js';
42
42
  import { GRANT_PATH_SELF_SERVICE } from './grant_path_schema.js';
43
43
  import { query_create_role_grant, query_revoke_role_grant } from './role_grant_queries.js';
44
44
  import { is_role_grant_active } from './account_schema.js';
45
45
  import { has_scoped_role } from './request_context.js';
46
46
  import { ERROR_ROLE_NOT_SELF_SERVICE_ELIGIBLE, self_service_role_set_action_spec } from './self_service_role_action_specs.js';
47
+ /**
48
+ * Validate a deployment's self-service eligible-role set at construction.
49
+ *
50
+ * Refuses a builtin role (`admin` / `keeper`) — self-toggling one would hand
51
+ * any account instance-wide authority, so no eligible set may name it — and a
52
+ * name outside the `RoleName` grammar. The same fail-loud-at-boot shape as
53
+ * `create_role_schema`, which refuses a builtin name where only app roles
54
+ * belong. Twin of the Rust `validate_self_service_eligible_roles`.
55
+ *
56
+ * @param roles - the eligible role names
57
+ * @throws Error on a builtin role or a name outside the role grammar
58
+ */
59
+ export const validate_self_service_eligible_roles = (roles) => {
60
+ for (const role of roles) {
61
+ if (is_builtin_role(role)) {
62
+ throw new Error(`self-service eligible role "${role}" is a builtin role — builtin roles are never self-service`);
63
+ }
64
+ if (!RoleName.safeParse(role).success) {
65
+ throw new Error(`self-service eligible role "${role}" is not a valid role name`);
66
+ }
67
+ }
68
+ };
47
69
  /**
48
70
  * Build the unified self-service role toggle RPC action.
49
71
  *
@@ -53,12 +75,14 @@ import { ERROR_ROLE_NOT_SELF_SERVICE_ELIGIBLE, self_service_role_set_action_spec
53
75
  * @param options - optional eligible-role override plus optional role schema for default-eligibility derivation
54
76
  * @returns the `RpcAction` array to spread into a `create_rpc_endpoint` call
55
77
  * @throws Error at factory time if any `eligible_roles` entry is missing from `options.roles.role_specs`
78
+ * @throws Error at factory time if the eligible set names a builtin role or an invalid role name
56
79
  */
57
80
  export const create_self_service_role_actions = (deps, options = {}) => {
58
81
  const role_specs = options.roles?.role_specs ?? builtin_role_specs_by_name;
59
82
  const eligible = options.eligible_roles
60
83
  ? new Set(options.eligible_roles)
61
84
  : new Set(list_roles_with_grant_path(role_specs, GRANT_PATH_SELF_SERVICE));
85
+ validate_self_service_eligible_roles(eligible);
62
86
  if (options.eligible_roles && options.roles) {
63
87
  for (const r of eligible) {
64
88
  if (!role_specs.has(r)) {
@@ -17,7 +17,7 @@ effects, see ../../../docs/architecture.md.
17
17
  - `http/route_spec.ts` — `RouteSpec` + `apply_route_specs`, validation pipeline, transactions.
18
18
  - `http/auth_shape.ts` — canonical `RouteAuth` Zod schema + cross-axis invariants + predicates.
19
19
  - `http/error_schemas.ts` — `ERROR_*` constants, standard error shapes, `derive_error_schemas`.
20
- - `http/schema_helpers.ts` — shared Zod introspection (null/strict/surface/merge/middleware-applies).
20
+ - `http/schema_helpers.ts` — shared Zod introspection (null/void/strict/surface/merge/middleware-applies) + `to_void_params` / `to_input_params` / `safe_parse_dropping_unknown_keys`.
21
21
  - `http/middleware_spec.ts` — `MiddlewareSpec` interface.
22
22
  - `http/surface.ts` — `AppSurface`, `AppSurfaceSpec`, `generate_app_surface`, diagnostics.
23
23
  - `http/surface_query.ts` — pure filters/groupings over `AppSurface`.
@@ -220,7 +220,7 @@ confirmation.
220
220
  — extracted to break a circular dependency between `http/route_spec.ts`
221
221
  (input validation) and `http/surface.ts` (surface generation).
222
222
 
223
- **Import `is_null_schema`, `is_strict_object_schema`, `schema_to_surface`,
223
+ **Import `is_null_schema`, `is_void_schema`, `to_void_params`, `to_input_params`, `is_strict_object_schema`, `schema_to_surface`,
224
224
  `middleware_applies`, and `merge_error_schemas` from `http/schema_helpers.ts`,
225
225
  not from `http/surface.ts`.** The helpers were moved; `http/surface.ts`
226
226
  only imports and re-uses them for generation logic.
@@ -228,6 +228,10 @@ only imports and re-uses them for generation logic.
228
228
  Key helpers:
229
229
 
230
230
  - `is_null_schema(schema)` — `instanceof z.ZodNull` (not parse-null, to avoid false positives from `z.nullable(z.string())`)
231
+ - `is_void_schema(schema)` — `instanceof z.ZodVoid`; a `z.void()` input declares a parameterless RPC method
232
+ - `to_void_params(raw_params)` — reads absent, `null`, and `{}` params of a `z.void()` input as the no-arg call (`undefined`), passing any other shape through for `z.void()` to refuse — the twin of the Rust spine's `require_void_params`
233
+ - `to_input_params(input, raw_params)` — the one params normalizer every receiving end runs before `input.safeParse`: `to_void_params` for a `z.void()` input, else absent/`null` → `{}`; used by both the server dispatch core (`perform_action`) and the peer receive path (`ActionDispatcher`)
234
+ - `safe_parse_dropping_unknown_keys(schema, value)` — the forward-compatible read of a remote-authored payload: when every issue is an unrecognized key, strips those keys from a copy and reparses; any other issue still fails. Used for response `result`s (`ActionEvent`) and peer replies (`peer/ping`); matches serde's ignore-unknown-fields default on the Rust spine
231
235
  - `is_strict_object_schema(schema)` — detects `z.strictObject()` by checking `schema.def.catchall instanceof z.ZodNever`
232
236
  - `schema_to_surface(schema)` — Zod → JSON Schema, with `$schema` and `default` keys stripped recursively (defaults may be non-deterministic; `$schema` is snapshot noise)
233
237
  - `middleware_applies(mw_path, route_path)` — Hono pattern matching: `'*'`, exact, `'/api/*'` prefix (handles `prefix.slice(0, -1)` so `/api/*` also matches the bare `/api`)
@@ -401,7 +405,7 @@ to a JSON-RPC code.
401
405
  - Envelope schemas in `http/jsonrpc.ts`: `JsonrpcRequest`, `JsonrpcNotification`, `JsonrpcResponse`, `JsonrpcErrorResponse`, `JsonrpcResponseOrError`, `JsonrpcMessage`, `JsonrpcMessageFromClientToServer`, `JsonrpcMessageFromServerToClient`. Also `JsonrpcRequestId`, `JsonrpcMethod`, `JsonrpcProgressToken`, `JsonrpcMcpMeta`, `JsonrpcRequestParamsMeta`
402
406
  - Builders in `http/jsonrpc_helpers.ts`: `create_jsonrpc_request`, `create_jsonrpc_notification`, `create_jsonrpc_response`, `create_jsonrpc_error_response`, `create_jsonrpc_error_response_from_thrown` (preserves code/message/data on `ThrownJsonrpcError`; plain `Error` → `internal_error` with `{stack}` in DEV)
403
407
  - Type guards: `is_jsonrpc_request_id` (rejects NaN/Infinity), `is_jsonrpc_object`, `is_jsonrpc_message`, `is_jsonrpc_request` / `_notification` / `_response` / `_error_response`
404
- - Converters: `to_jsonrpc_message_id`, `to_jsonrpc_params` (normalizes primitives to `{value}`), `to_jsonrpc_result` (null/undefined → `{}`, primitives → `{value}`)
408
+ - Converters: `to_jsonrpc_message_id`, `to_jsonrpc_params` (normalizes primitives to `{value}`)
405
409
 
406
410
  Handlers can `throw jsonrpc_errors.*` — `apply_route_specs`' error-catch
407
411
  layer converts to `{error: JsonrpcErrorObject}` at the correct HTTP status.
@@ -61,9 +61,4 @@ export declare const to_jsonrpc_envelope_id: (frame: unknown) => JsonrpcRequestI
61
61
  * Returns `undefined` for null/undefined, wraps primitives in `{value}`.
62
62
  */
63
63
  export declare const to_jsonrpc_params: (input: unknown) => Record<string, any> | undefined;
64
- /**
65
- * Normalizes output to JSON-RPC result format.
66
- * Returns empty object for null/undefined, wraps primitives in `{value}`.
67
- */
68
- export declare const to_jsonrpc_result: (output: unknown) => Record<string, any>;
69
64
  //# sourceMappingURL=jsonrpc_helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"jsonrpc_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/jsonrpc_helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EACN,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,cAAc,EAEnB,eAAe,EACf,MAAM,cAAc,CAAC;AAKtB,0CAA0C;AAC1C,eAAO,MAAM,sBAAsB,GAClC,QAAQ,aAAa,EACrB,QAAQ,oBAAoB,GAAG,SAAS,EACxC,IAAI,gBAAgB,KAClB,cAUF,CAAC;AAEF,mDAAmD;AACnD,eAAO,MAAM,uBAAuB,GACnC,IAAI,gBAAgB,EACpB,QAAQ,aAAa,KACnB,eAID,CAAC;AAEH,6EAA6E;AAC7E,eAAO,MAAM,2BAA2B,GACvC,QAAQ,aAAa,EACrB,QAAQ,yBAAyB,GAAG,SAAS,KAC3C,mBASF,CAAC;AAEF,iDAAiD;AACjD,eAAO,MAAM,6BAA6B,GACzC,IAAI,oBAAoB,CAAC,IAAI,CAAC,EAC9B,OAAO,oBAAoB,CAAC,OAAO,CAAC,KAClC,oBAID,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,yCAAyC,GACrD,IAAI,gBAAgB,GAAG,IAAI,EAC3B,OAAO,OAAO,KACZ,oBAyBF,CAAC;AAIF,kFAAkF;AAClF,eAAO,MAAM,qBAAqB,GAAI,IAAI,OAAO,KAAG,EAAE,IAAI,gBAGzD,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,GAC7B,SAAS,OAAO,KACd,OAAO,IAAI;IAAE,OAAO,EAAE,OAAO,eAAe,CAAA;CAIF,CAAC;AAE9C,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,GAC9B,SAAS,OAAO,KACd,OAAO,IAAI,cAAc,GAAG,KAAK,CAAC,cAAc,CAGrB,CAAC;AAE/B,iEAAiE;AACjE,eAAO,MAAM,kBAAkB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,cACI,CAAC;AAEtE,wEAAwE;AACxE,eAAO,MAAM,uBAAuB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,mBACE,CAAC;AAEzE,0EAA0E;AAC1E,eAAO,MAAM,mBAAmB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,eACG,CAAC;AAEtE,uEAAuE;AACvE,eAAO,MAAM,yBAAyB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,oBACJ,CAAC;AAIrE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAAI,eAAe,OAAO,KAAG,gBAAgB,GAAG,IAOjF,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GAAI,OAAO,OAAO,KAAG,gBAAgB,GAAG,IACX,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAQxE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAQrE,CAAC"}
1
+ {"version":3,"file":"jsonrpc_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/jsonrpc_helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EACN,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,cAAc,EAEnB,eAAe,EACf,MAAM,cAAc,CAAC;AAKtB,0CAA0C;AAC1C,eAAO,MAAM,sBAAsB,GAClC,QAAQ,aAAa,EACrB,QAAQ,oBAAoB,GAAG,SAAS,EACxC,IAAI,gBAAgB,KAClB,cAUF,CAAC;AAEF,mDAAmD;AACnD,eAAO,MAAM,uBAAuB,GACnC,IAAI,gBAAgB,EACpB,QAAQ,aAAa,KACnB,eAID,CAAC;AAEH,6EAA6E;AAC7E,eAAO,MAAM,2BAA2B,GACvC,QAAQ,aAAa,EACrB,QAAQ,yBAAyB,GAAG,SAAS,KAC3C,mBASF,CAAC;AAEF,iDAAiD;AACjD,eAAO,MAAM,6BAA6B,GACzC,IAAI,oBAAoB,CAAC,IAAI,CAAC,EAC9B,OAAO,oBAAoB,CAAC,OAAO,CAAC,KAClC,oBAID,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,yCAAyC,GACrD,IAAI,gBAAgB,GAAG,IAAI,EAC3B,OAAO,OAAO,KACZ,oBAyBF,CAAC;AAIF,kFAAkF;AAClF,eAAO,MAAM,qBAAqB,GAAI,IAAI,OAAO,KAAG,EAAE,IAAI,gBAGzD,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,GAC7B,SAAS,OAAO,KACd,OAAO,IAAI;IAAE,OAAO,EAAE,OAAO,eAAe,CAAA;CAIF,CAAC;AAE9C,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,GAC9B,SAAS,OAAO,KACd,OAAO,IAAI,cAAc,GAAG,KAAK,CAAC,cAAc,CAGrB,CAAC;AAE/B,iEAAiE;AACjE,eAAO,MAAM,kBAAkB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,cACI,CAAC;AAEtE,wEAAwE;AACxE,eAAO,MAAM,uBAAuB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,mBACE,CAAC;AAEzE,0EAA0E;AAC1E,eAAO,MAAM,mBAAmB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,eACG,CAAC;AAEtE,uEAAuE;AACvE,eAAO,MAAM,yBAAyB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,oBACJ,CAAC;AAIrE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAAI,eAAe,OAAO,KAAG,gBAAgB,GAAG,IAOjF,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GAAI,OAAO,OAAO,KAAG,gBAAgB,GAAG,IACX,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAQxE,CAAC"}
@@ -136,16 +136,3 @@ export const to_jsonrpc_params = (input) => {
136
136
  }
137
137
  return { value: input };
138
138
  };
139
- /**
140
- * Normalizes output to JSON-RPC result format.
141
- * Returns empty object for null/undefined, wraps primitives in `{value}`.
142
- */
143
- export const to_jsonrpc_result = (output) => {
144
- if (output === null || output === undefined) {
145
- return {};
146
- }
147
- if (typeof output === 'object' && !Array.isArray(output)) {
148
- return output;
149
- }
150
- return { value: output };
151
- };
@@ -27,6 +27,62 @@ export declare const is_null_schema: (schema: z.ZodType) => boolean;
27
27
  * and the dispatcher maps absent params to `undefined` for these specs.
28
28
  */
29
29
  export declare const is_void_schema: (schema: z.ZodType) => boolean;
30
+ /**
31
+ * Normalize the raw `params` of a `z.void()` input: an absent `params`, an
32
+ * empty object, and the `null` a GET `?params=null` parses to are all the
33
+ * no-arg call (`undefined`); anything else passes through for `z.void()` to
34
+ * refuse.
35
+ *
36
+ * JSON-RPC 2.0 lets a parameterless call omit `params`, and an empty by-name
37
+ * structure carries no parameters, so refusing `{}` would only break clients
38
+ * that send it for a parameterless call. Arrays — `[]` included — are not
39
+ * normalized: RPC params are by-name only. The Rust spine's
40
+ * `require_void_params` accepts the same shapes. Receiving ends reach it
41
+ * through `to_input_params`.
42
+ *
43
+ * @param raw_params - the request's `params` as parsed off the wire
44
+ * @returns `undefined` for an empty shape, else `raw_params` unchanged
45
+ */
46
+ export declare const to_void_params: (raw_params: unknown) => unknown;
47
+ /**
48
+ * Normalize the raw `params` of an RPC call for `input` to validate: for a
49
+ * `z.void()` input, `to_void_params`; for any other input, an absent or
50
+ * `null` `params` reads as `{}`, so an all-optional object input accepts a
51
+ * call that omits them. Every receiving end — the server dispatch
52
+ * (`perform_action`) and a peer receiving a request or notification
53
+ * (`ActionDispatcher`) — reads params through this, so a peer is never
54
+ * stricter than the server.
55
+ *
56
+ * @param input - the action's input schema
57
+ * @param raw_params - the call's `params` as parsed off the wire
58
+ * @returns the value to run `input.safeParse` on
59
+ */
60
+ export declare const to_input_params: (input: z.ZodType, raw_params: unknown) => unknown;
61
+ /**
62
+ * `schema.safeParse(value)`, except that keys the schema doesn't declare are
63
+ * dropped rather than refused — the forward-compatible read of a payload a
64
+ * remote authored.
65
+ *
66
+ * Output schemas are strict so the server's DEV output validation catches a
67
+ * handler leaking fields it shouldn't. The receiving side, though, is often
68
+ * older than the remote it talks to — a deploy lands before open tabs reload
69
+ * — and a remote that adds a field is making a compatible change. So when
70
+ * every issue is an unrecognized key, those keys are stripped from a copy of
71
+ * `value` and it's parsed again; any other issue (a wrong type, a missing
72
+ * field, `null` where a value is required) still fails. `value` itself is
73
+ * never mutated. This matches serde's default on the Rust spine, which
74
+ * ignores unknown fields. Keys nested in a `z.union` branch surface as a
75
+ * union issue and aren't stripped; a `z.discriminatedUnion` reports its
76
+ * matched branch's issues directly, so those are.
77
+ *
78
+ * Used for a response's `result` (`ActionEvent`) and for a peer's reply to a
79
+ * server-initiated request (`peer/ping`).
80
+ *
81
+ * @param schema - the declared shape
82
+ * @param value - the remote-authored payload
83
+ * @returns the parse result, with undeclared keys dropped on success
84
+ */
85
+ export declare const safe_parse_dropping_unknown_keys: <T extends z.ZodType>(schema: T, value: unknown) => z.ZodSafeParseResult<z.output<T>>;
30
86
  /**
31
87
  * Check if a schema is a strict object (`z.strictObject()`).
32
88
  *
@@ -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,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAEN,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;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;;;;;;;;;GASG;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,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAEN,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;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;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,cAAc,GAAI,YAAY,OAAO,KAAG,OAUpD,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,CAAC,CAAC,OAAO,EAAE,YAAY,OAAO,KAAG,OACA,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,gCAAgC,GAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EACnE,QAAQ,CAAC,EACT,OAAO,OAAO,KACZ,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAclC,CAAC;AAWF;;;;;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;;;;;;;;;GASG;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"}
@@ -26,6 +26,97 @@ export const is_null_schema = (schema) => schema instanceof z.ZodNull;
26
26
  * and the dispatcher maps absent params to `undefined` for these specs.
27
27
  */
28
28
  export const is_void_schema = (schema) => schema instanceof z.ZodVoid;
29
+ /**
30
+ * Normalize the raw `params` of a `z.void()` input: an absent `params`, an
31
+ * empty object, and the `null` a GET `?params=null` parses to are all the
32
+ * no-arg call (`undefined`); anything else passes through for `z.void()` to
33
+ * refuse.
34
+ *
35
+ * JSON-RPC 2.0 lets a parameterless call omit `params`, and an empty by-name
36
+ * structure carries no parameters, so refusing `{}` would only break clients
37
+ * that send it for a parameterless call. Arrays — `[]` included — are not
38
+ * normalized: RPC params are by-name only. The Rust spine's
39
+ * `require_void_params` accepts the same shapes. Receiving ends reach it
40
+ * through `to_input_params`.
41
+ *
42
+ * @param raw_params - the request's `params` as parsed off the wire
43
+ * @returns `undefined` for an empty shape, else `raw_params` unchanged
44
+ */
45
+ export const to_void_params = (raw_params) => {
46
+ if (raw_params === undefined || raw_params === null)
47
+ return undefined;
48
+ if (typeof raw_params === 'object' &&
49
+ !Array.isArray(raw_params) &&
50
+ Object.keys(raw_params).length === 0) {
51
+ return undefined;
52
+ }
53
+ return raw_params;
54
+ };
55
+ /**
56
+ * Normalize the raw `params` of an RPC call for `input` to validate: for a
57
+ * `z.void()` input, `to_void_params`; for any other input, an absent or
58
+ * `null` `params` reads as `{}`, so an all-optional object input accepts a
59
+ * call that omits them. Every receiving end — the server dispatch
60
+ * (`perform_action`) and a peer receiving a request or notification
61
+ * (`ActionDispatcher`) — reads params through this, so a peer is never
62
+ * stricter than the server.
63
+ *
64
+ * @param input - the action's input schema
65
+ * @param raw_params - the call's `params` as parsed off the wire
66
+ * @returns the value to run `input.safeParse` on
67
+ */
68
+ export const to_input_params = (input, raw_params) => is_void_schema(input) ? to_void_params(raw_params) : (raw_params ?? {});
69
+ /**
70
+ * `schema.safeParse(value)`, except that keys the schema doesn't declare are
71
+ * dropped rather than refused — the forward-compatible read of a payload a
72
+ * remote authored.
73
+ *
74
+ * Output schemas are strict so the server's DEV output validation catches a
75
+ * handler leaking fields it shouldn't. The receiving side, though, is often
76
+ * older than the remote it talks to — a deploy lands before open tabs reload
77
+ * — and a remote that adds a field is making a compatible change. So when
78
+ * every issue is an unrecognized key, those keys are stripped from a copy of
79
+ * `value` and it's parsed again; any other issue (a wrong type, a missing
80
+ * field, `null` where a value is required) still fails. `value` itself is
81
+ * never mutated. This matches serde's default on the Rust spine, which
82
+ * ignores unknown fields. Keys nested in a `z.union` branch surface as a
83
+ * union issue and aren't stripped; a `z.discriminatedUnion` reports its
84
+ * matched branch's issues directly, so those are.
85
+ *
86
+ * Used for a response's `result` (`ActionEvent`) and for a peer's reply to a
87
+ * server-initiated request (`peer/ping`).
88
+ *
89
+ * @param schema - the declared shape
90
+ * @param value - the remote-authored payload
91
+ * @returns the parse result, with undeclared keys dropped on success
92
+ */
93
+ export const safe_parse_dropping_unknown_keys = (schema, value) => {
94
+ const parsed = schema.safeParse(value);
95
+ if (parsed.success)
96
+ return parsed;
97
+ const { issues } = parsed.error;
98
+ if (!issues.every((issue) => issue.code === 'unrecognized_keys'))
99
+ return parsed;
100
+ const stripped = structuredClone(value);
101
+ for (const issue of issues) {
102
+ const target = get_at_path(stripped, issue.path);
103
+ if (typeof target !== 'object' || target === null)
104
+ return parsed;
105
+ for (const key of issue.keys) {
106
+ Reflect.deleteProperty(target, key);
107
+ }
108
+ }
109
+ return schema.safeParse(stripped);
110
+ };
111
+ const get_at_path = (value, path) => {
112
+ let current = value;
113
+ for (const segment of path) {
114
+ if (typeof current !== 'object' || current === null)
115
+ return undefined;
116
+ current = current[segment];
117
+ }
118
+ return current;
119
+ };
29
120
  /**
30
121
  * Check if a schema is a strict object (`z.strictObject()`).
31
122
  *
@@ -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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"}
@@ -375,6 +375,7 @@ export const create_app_server = async (options) => {
375
375
  transport: endpoint_transport,
376
376
  heartbeat: endpoint.heartbeat,
377
377
  artificial_delay: endpoint.artificial_delay,
378
+ max_message_bytes: endpoint.max_message_bytes,
378
379
  on_socket_open: endpoint.on_socket_open,
379
380
  on_socket_close: endpoint.on_socket_close,
380
381
  log,
@@ -138,7 +138,9 @@ export declare const create_serve_cell_fact_route_spec: (options: CreateServeFac
138
138
  * Auth is `{account: 'required', actor: 'required', roles: ['admin']}` —
139
139
  * the dispatcher's authorization phase resolves the acting actor and the
140
140
  * post-authorization guard enforces the admin role before the handler runs.
141
- * The handler re-checks `has_role(_, admin)` as defense-in-depth so a future
141
+ * The handler re-checks the **global** admin grant
142
+ * (`has_scoped_role(_, admin, null)`, the auth phase's own reading) as
143
+ * defense-in-depth so a future
142
144
  * mounting/auth-shape regression fails closed rather than serving by bare
143
145
  * hash to a non-admin.
144
146
  */
@@ -1 +1 @@
1
- {"version":3,"file":"serve_fact_route.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/serve_fact_route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwFG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAetD,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAE5F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAM/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAkEjD,MAAM,WAAW,+BAA+B;IAC/C;;;;;;OAMG;IACH,IAAI,EAAE,OAAO,CAAC;IACd,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;CACZ;AAgHD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iCAAiC,GAC7C,SAAS,+BAA+B,KACtC,SAgDF,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,4BAA4B,GACxC,SAAS,+BAA+B,KACtC,SA0CF,CAAC"}
1
+ {"version":3,"file":"serve_fact_route.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/serve_fact_route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwFG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAetD,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAE5F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAM/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAkEjD,MAAM,WAAW,+BAA+B;IAC/C;;;;;;OAMG;IACH,IAAI,EAAE,OAAO,CAAC;IACd,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;CACZ;AAgHD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iCAAiC,GAC7C,SAAS,+BAA+B,KACtC,SAgDF,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,4BAA4B,GACxC,SAAS,+BAA+B,KACtC,SA2CF,CAAC"}
@@ -93,7 +93,7 @@ import { join } from 'node:path';
93
93
  import { FactHashSchema } from '@fuzdev/fuz_util/hash_schemas.ts';
94
94
  import { Uuid } from '@fuzdev/fuz_util/id.ts';
95
95
  import { z } from 'zod';
96
- import { build_request_context, get_request_context, has_role } from '../auth/request_context.js';
96
+ import { build_request_context, get_request_context, has_scoped_role } from '../auth/request_context.js';
97
97
  import { ROLE_ADMIN } from '../auth/role_schema.js';
98
98
  import { ActingActor } from '../http/auth_shape.js';
99
99
  import { ACCOUNT_ID_KEY } from '../hono_context.js';
@@ -315,7 +315,9 @@ export const create_serve_cell_fact_route_spec = (options) => {
315
315
  * Auth is `{account: 'required', actor: 'required', roles: ['admin']}` —
316
316
  * the dispatcher's authorization phase resolves the acting actor and the
317
317
  * post-authorization guard enforces the admin role before the handler runs.
318
- * The handler re-checks `has_role(_, admin)` as defense-in-depth so a future
318
+ * The handler re-checks the **global** admin grant
319
+ * (`has_scoped_role(_, admin, null)`, the auth phase's own reading) as
320
+ * defense-in-depth so a future
319
321
  * mounting/auth-shape regression fails closed rather than serving by bare
320
322
  * hash to a non-admin.
321
323
  */
@@ -351,8 +353,9 @@ export const create_serve_fact_route_spec = (options) => {
351
353
  const { hash } = get_route_params(c);
352
354
  // Defense-in-depth: the auth phase already gated this on the admin
353
355
  // role, but re-check the resolved context so a mounting/auth-shape
354
- // regression fails closed instead of serving by bare hash.
355
- if (!has_role(get_request_context(c), ROLE_ADMIN)) {
356
+ // regression fails closed instead of serving by bare hash. Global
357
+ // grant only — a scoped `admin` row must not read every fact.
358
+ if (!has_scoped_role(get_request_context(c), ROLE_ADMIN, null)) {
356
359
  return c.body(null, 404);
357
360
  }
358
361
  return serve_fact_bytes(c, route, hash, config);