@fuzdev/fuz_app 0.149.0 → 0.150.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 (141) hide show
  1. package/dist/actions/CLAUDE.md +9 -7
  2. package/dist/actions/action_bridge.d.ts +7 -0
  3. package/dist/actions/action_bridge.d.ts.map +1 -1
  4. package/dist/actions/action_bridge.js +10 -0
  5. package/dist/actions/action_event_types.d.ts +1 -1
  6. package/dist/actions/action_event_types.d.ts.map +1 -1
  7. package/dist/actions/perform_action.d.ts.map +1 -1
  8. package/dist/actions/perform_action.js +8 -5
  9. package/dist/actions/register_action_ws.d.ts +8 -2
  10. package/dist/actions/register_action_ws.d.ts.map +1 -1
  11. package/dist/actions/register_action_ws.js +24 -4
  12. package/dist/actions/transports_ws_auth_guard.d.ts +6 -4
  13. package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
  14. package/dist/actions/transports_ws_auth_guard.js +9 -6
  15. package/dist/actions/ws_endpoint_spec.d.ts +1 -1
  16. package/dist/auth/CLAUDE.md +69 -31
  17. package/dist/auth/actor_lookup_queries.d.ts +3 -2
  18. package/dist/auth/actor_lookup_queries.d.ts.map +1 -1
  19. package/dist/auth/actor_lookup_queries.js +6 -3
  20. package/dist/auth/actor_search_queries.d.ts +5 -2
  21. package/dist/auth/actor_search_queries.d.ts.map +1 -1
  22. package/dist/auth/actor_search_queries.js +11 -4
  23. package/dist/auth/admin_action_specs.d.ts +4 -4
  24. package/dist/auth/admin_action_specs.js +4 -4
  25. package/dist/auth/admin_actions.d.ts.map +1 -1
  26. package/dist/auth/admin_actions.js +35 -6
  27. package/dist/auth/all_cell_actions.d.ts +11 -2
  28. package/dist/auth/all_cell_actions.d.ts.map +1 -1
  29. package/dist/auth/all_cell_actions.js +2 -1
  30. package/dist/auth/audit_emitter.d.ts +9 -3
  31. package/dist/auth/audit_emitter.d.ts.map +1 -1
  32. package/dist/auth/audit_emitter.js +9 -3
  33. package/dist/auth/audit_log_schema.d.ts +1 -0
  34. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  35. package/dist/auth/audit_log_schema.js +9 -5
  36. package/dist/auth/bootstrap_account.d.ts +25 -1
  37. package/dist/auth/bootstrap_account.d.ts.map +1 -1
  38. package/dist/auth/bootstrap_account.js +37 -4
  39. package/dist/auth/bootstrap_routes.d.ts +3 -2
  40. package/dist/auth/bootstrap_routes.d.ts.map +1 -1
  41. package/dist/auth/bootstrap_routes.js +5 -4
  42. package/dist/auth/cell_action_specs.d.ts +215 -15
  43. package/dist/auth/cell_action_specs.d.ts.map +1 -1
  44. package/dist/auth/cell_action_specs.js +103 -16
  45. package/dist/auth/cell_actions.d.ts +40 -18
  46. package/dist/auth/cell_actions.d.ts.map +1 -1
  47. package/dist/auth/cell_actions.js +126 -19
  48. package/dist/auth/cell_audit_events.d.ts +1 -1
  49. package/dist/auth/cell_audit_events.d.ts.map +1 -1
  50. package/dist/auth/cell_audit_events.js +3 -2
  51. package/dist/auth/cell_audit_metadata.d.ts +11 -0
  52. package/dist/auth/cell_audit_metadata.d.ts.map +1 -1
  53. package/dist/auth/cell_audit_metadata.js +10 -0
  54. package/dist/auth/cell_grant_action_specs.d.ts +7 -0
  55. package/dist/auth/cell_grant_action_specs.d.ts.map +1 -1
  56. package/dist/auth/cell_grant_action_specs.js +7 -0
  57. package/dist/auth/cell_grant_actions.d.ts +36 -4
  58. package/dist/auth/cell_grant_actions.d.ts.map +1 -1
  59. package/dist/auth/cell_grant_actions.js +20 -3
  60. package/dist/auth/credential_type_schema.d.ts +3 -2
  61. package/dist/auth/credential_type_schema.d.ts.map +1 -1
  62. package/dist/auth/credential_type_schema.js +3 -2
  63. package/dist/auth/role_grant_offer_action_specs.d.ts +18 -2
  64. package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
  65. package/dist/auth/role_grant_offer_action_specs.js +25 -3
  66. package/dist/auth/role_grant_offer_actions.d.ts +9 -4
  67. package/dist/auth/role_grant_offer_actions.d.ts.map +1 -1
  68. package/dist/auth/role_grant_offer_actions.js +58 -7
  69. package/dist/auth/role_grant_offer_notifications.d.ts +3 -2
  70. package/dist/auth/role_grant_offer_notifications.d.ts.map +1 -1
  71. package/dist/auth/role_grant_offer_notifications.js +3 -2
  72. package/dist/auth/role_grant_offer_queries.d.ts +20 -0
  73. package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
  74. package/dist/auth/role_grant_offer_queries.js +35 -0
  75. package/dist/auth/role_grant_queries.d.ts +27 -9
  76. package/dist/auth/role_grant_queries.d.ts.map +1 -1
  77. package/dist/auth/role_grant_queries.js +40 -13
  78. package/dist/auth/role_schema.d.ts +14 -8
  79. package/dist/auth/role_schema.d.ts.map +1 -1
  80. package/dist/auth/role_schema.js +6 -3
  81. package/dist/auth/self_service_role_actions.d.ts.map +1 -1
  82. package/dist/auth/self_service_role_actions.js +20 -0
  83. package/dist/db/CLAUDE.md +9 -2
  84. package/dist/db/cell_ddl.d.ts +9 -1
  85. package/dist/db/cell_ddl.d.ts.map +1 -1
  86. package/dist/db/cell_ddl.js +11 -1
  87. package/dist/db/cell_queries.d.ts +53 -7
  88. package/dist/db/cell_queries.d.ts.map +1 -1
  89. package/dist/db/cell_queries.js +61 -7
  90. package/dist/http/body_limit.d.ts +3 -1
  91. package/dist/http/body_limit.d.ts.map +1 -1
  92. package/dist/http/body_limit.js +6 -1
  93. package/dist/http/jsonrpc.d.ts +9 -0
  94. package/dist/http/jsonrpc.d.ts.map +1 -1
  95. package/dist/http/jsonrpc.js +17 -0
  96. package/dist/http/jsonrpc_errors.d.ts.map +1 -1
  97. package/dist/http/jsonrpc_errors.js +29 -20
  98. package/dist/http/proxy.d.ts +4 -2
  99. package/dist/http/proxy.d.ts.map +1 -1
  100. package/dist/http/proxy.js +7 -2
  101. package/dist/http/surface.d.ts +3 -1
  102. package/dist/http/surface.d.ts.map +1 -1
  103. package/dist/http/surface.js +7 -4
  104. package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
  105. package/dist/realtime/sse_auth_guard.js +3 -2
  106. package/dist/schema_meta.d.ts +32 -1
  107. package/dist/schema_meta.d.ts.map +1 -1
  108. package/dist/schema_meta.js +58 -5
  109. package/dist/server/app_server.d.ts +13 -5
  110. package/dist/server/app_server.d.ts.map +1 -1
  111. package/dist/server/app_server.js +32 -8
  112. package/dist/testing/CLAUDE.md +34 -21
  113. package/dist/testing/app_server.d.ts +3 -1
  114. package/dist/testing/app_server.d.ts.map +1 -1
  115. package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -1
  116. package/dist/testing/cross_backend/actor_lookup.js +27 -0
  117. package/dist/testing/cross_backend/capabilities.d.ts +2 -1
  118. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  119. package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
  120. package/dist/testing/cross_backend/cell_gated_create.js +109 -13
  121. package/dist/testing/cross_backend/cell_moderate.d.ts +7 -1
  122. package/dist/testing/cross_backend/cell_moderate.d.ts.map +1 -1
  123. package/dist/testing/cross_backend/cell_moderate.js +68 -4
  124. package/dist/testing/cross_backend/cell_ref_admission.d.ts.map +1 -1
  125. package/dist/testing/cross_backend/cell_ref_admission.js +18 -9
  126. package/dist/testing/cross_backend/conformance_table.d.ts +5 -0
  127. package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
  128. package/dist/testing/cross_backend/conformance_table.js +12 -10
  129. package/dist/testing/cross_backend/differential.d.ts +7 -0
  130. package/dist/testing/cross_backend/differential.d.ts.map +1 -1
  131. package/dist/testing/cross_backend/differential.js +17 -0
  132. package/dist/testing/cross_backend/expected_schema.json +1 -0
  133. package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
  134. package/dist/testing/cross_backend/full_spine_mount.js +5 -4
  135. package/dist/testing/cross_backend/role_grant_offer_lifecycle.d.ts.map +1 -1
  136. package/dist/testing/cross_backend/role_grant_offer_lifecycle.js +74 -2
  137. package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts +18 -19
  138. package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts.map +1 -1
  139. package/dist/testing/cross_backend/test_cell_gated_create_authorize.js +66 -42
  140. package/dist/testing/cross_backend/transport_composition.js +3 -3
  141. package/package.json +1 -1
@@ -103,11 +103,12 @@ export const query_create_role_grant = async (deps, input) => {
103
103
  */
104
104
  export const query_role_grant_by_id = async (deps, id) => deps.db.query_one(`SELECT ${columns_sql(ROLE_GRANT_COLUMNS, role_grant_expr)} FROM role_grant WHERE id = $1`, [id]);
105
105
  /**
106
- * Look up the role of an active role_grant (constrained to a specific
107
- * actor) plus the actor's `account_id`.
106
+ * Look up the role and scope of an active role_grant (constrained to a
107
+ * specific actor) plus the actor's `account_id`.
108
108
  *
109
109
  * Used by admin routes to inspect the role_grant's role before acting
110
- * (e.g., enforcing the admin-grant-path gate on revoke). The actor constraint
110
+ * (e.g., enforcing the admin-grant-path gate and the global-`admin` guards on
111
+ * revoke). The actor constraint
111
112
  * mirrors `query_revoke_role_grant` so IDOR protection is consistent:
112
113
  * a caller can only see role_grants belonging to the target actor.
113
114
  *
@@ -124,10 +125,10 @@ export const query_role_grant_by_id = async (deps, id) => deps.db.query_one(`SEL
124
125
  * @param deps - query dependencies
125
126
  * @param role_grant_id - the role_grant id to look up
126
127
  * @param actor_id - the actor that must own the role_grant
127
- * @returns `{role, account_id}` on a match, or `null`
128
+ * @returns `{role, scope_id, account_id}` on a match, or `null`
128
129
  */
129
130
  export const query_role_grant_find_active_role_for_actor = async (deps, role_grant_id, actor_id) => {
130
- const row = await deps.db.query_one(`SELECT role_grant.role, actor.account_id
131
+ const row = await deps.db.query_one(`SELECT role_grant.role, role_grant.scope_id, actor.account_id
131
132
  FROM role_grant
132
133
  JOIN actor ON actor.id = role_grant.actor_id
133
134
  WHERE role_grant.id = $1 AND role_grant.actor_id = $2 AND role_grant.revoked_at IS NULL`, [role_grant_id, actor_id]);
@@ -264,7 +265,7 @@ export const query_account_has_global_role = async (deps, account_id, role) => {
264
265
  * Like `query_account_has_global_role`, but only counts the grant when the
265
266
  * **account and the granting actor are both active** (`deleted_at IS NULL`).
266
267
  * Used by the last-admin branch of the removability guard: a soft-deleted admin
267
- * can't log in and is excluded from `query_count_active_accounts_with_global_role`,
268
+ * can't log in and is excluded from `query_count_active_accounts_with_global_role_for_update`,
268
269
  * so the guard must use the same active predicate when testing whether the
269
270
  * *target* is an admin — otherwise removing an already-tombstoned admin is
270
271
  * falsely blocked as `cannot_delete_last_admin`. Soft-delete does **not** revoke
@@ -298,19 +299,39 @@ export const query_account_has_active_global_role = async (deps, account_id, rol
298
299
  /**
299
300
  * Count **active** accounts (`deleted_at IS NULL`) holding an active global
300
301
  * role_grant for `role`, counting only grants on **active actors**
301
- * (`act.deleted_at IS NULL`). Used by the last-admin guard on `account_delete` /
302
- * `account_purge`: soft-delete revokes neither a tombstoned account's nor a
302
+ * (`act.deleted_at IS NULL`) — and lock what it counted. Used by the last-admin
303
+ * guards on `account_delete` / `account_purge` and on `role_grant_revoke` of an
304
+ * `admin` grant: soft-delete revokes neither a tombstoned account's nor a
303
305
  * tombstoned actor's admin grant, so an unfiltered count would include unusable
304
306
  * admins — this joins `account` *and* filters the actor, so an account whose
305
307
  * only admin grant sits on a tombstoned actor no longer counts (otherwise the
306
- * guard could fail-open and let the last usable admin be deleted).
308
+ * guard could fail-open and let the last usable admin be removed).
309
+ *
310
+ * **Locks** every counted `role_grant`, `actor`, and `account` row
311
+ * (`FOR UPDATE`) until the caller's transaction ends, so two guards racing to
312
+ * remove the last two admins serialize: the second waits for the first to
313
+ * commit, re-reads the rows it changed, and counts one. Run it inside the
314
+ * transaction that does the removal. Twin of the Rust
315
+ * `query_count_active_accounts_with_global_role_for_update`.
316
+ *
317
+ * `exclude_role_grant_id` leaves one grant out of the count, so a revoke asks
318
+ * whether any active admin account would remain without that grant — an
319
+ * account holding the grant on two actors keeps counting through the other.
320
+ * The excluded grant is still locked, so two revokes of each other's grant
321
+ * serialize rather than deadlock.
307
322
  *
308
323
  * @param deps - query dependencies
309
324
  * @param role - the role to count (e.g. `ROLE_ADMIN`)
325
+ * @param exclude_role_grant_id - a grant to leave out of the count (the one being revoked)
310
326
  * @returns the number of distinct active accounts with an active global `role` grant
327
+ * @mutates `role_grant` / `actor` / `account` rows - row-locks the counted rows for the transaction
311
328
  */
312
- export const query_count_active_accounts_with_global_role = async (deps, role) => {
313
- const row = await deps.db.query_one(`SELECT COUNT(DISTINCT a.id) AS count
329
+ export const query_count_active_accounts_with_global_role_for_update = async (deps, role, exclude_role_grant_id = null) => {
330
+ // `FOR UPDATE` refuses an aggregate, so the rows come back and are counted
331
+ // here. The excluded grant is dropped in code, not SQL: filtering it in the
332
+ // WHERE would leave it unlocked, and two cross-revokes (each locking only the
333
+ // other's grant, then updating it) would deadlock instead of serializing
334
+ const rows = await deps.db.query(`SELECT a.id AS account_id, rg.id AS role_grant_id
314
335
  FROM account a
315
336
  JOIN actor act ON act.account_id = a.id
316
337
  JOIN role_grant rg ON rg.actor_id = act.id
@@ -319,8 +340,14 @@ export const query_count_active_accounts_with_global_role = async (deps, role) =
319
340
  AND rg.role = $1
320
341
  AND rg.scope_id IS NULL
321
342
  AND rg.revoked_at IS NULL
322
- AND (rg.expires_at IS NULL OR rg.expires_at > NOW())`, [role]);
323
- return Number(row?.count ?? 0);
343
+ AND (rg.expires_at IS NULL OR rg.expires_at > NOW())
344
+ FOR UPDATE`, [role]);
345
+ const account_ids = new Set();
346
+ for (const row of rows) {
347
+ if (row.role_grant_id !== exclude_role_grant_id)
348
+ account_ids.add(row.account_id);
349
+ }
350
+ return account_ids.size;
324
351
  };
325
352
  /**
326
353
  * List all role_grants for an actor (including revoked/expired).
@@ -64,7 +64,7 @@ export declare const is_builtin_role: (name: string) => name is BuiltinRole;
64
64
  /**
65
65
  * Configuration for a role.
66
66
  *
67
- * Each role declares the credential types its holders must use, the
67
+ * Each role declares the credential types its holders are meant to use, the
68
68
  * scope kinds it applies to, and the grant paths through which it can
69
69
  * be granted. Every cross-axis field is an open-registry string array —
70
70
  * `required_credential_types` against `create_credential_type_schema`,
@@ -93,10 +93,13 @@ export interface RoleSpec {
93
93
  /** Admin-UI-facing copy describing the role's intent. */
94
94
  description?: string;
95
95
  /**
96
- * Credential types whose holders are permitted to exercise this
97
- * role. Each entry is checked at construction time against the
98
- * `credential_types` registry passed to `create_role_schema`. Empty
99
- * array = any authenticated credential type.
96
+ * Credential types whose holders are meant to exercise this role.
97
+ * Informative only: each entry is checked at construction time against
98
+ * the `credential_types` registry passed to `create_role_schema`, but no
99
+ * middleware or dispatcher reads it per request — a route or action
100
+ * gates credentials with its own `auth.credential_types` (the keeper
101
+ * verbs declare `['daemon_token']` there). Empty array = any
102
+ * authenticated credential type.
100
103
  */
101
104
  required_credential_types?: ReadonlyArray<string>;
102
105
  /**
@@ -150,9 +153,12 @@ export interface RoleSchemaResult {
150
153
  *
151
154
  * Call once at server init. The returned `Role` schema validates role
152
155
  * strings at I/O boundaries (grant endpoint, role_grant queries). The
153
- * `role_specs` map is read by middleware for `required_credential_types`
154
- * checks and by admin / self-service factories to derive their default
155
- * eligibility filters from `RoleSpec.grant_paths`.
156
+ * `role_specs` map is read by the admin / self-service factories to derive
157
+ * their default eligibility filters from `RoleSpec.grant_paths`, and by the
158
+ * cell grant verbs to refuse an unregistered role principal.
159
+ * `RoleSpec.required_credential_types` is validated here but enforced
160
+ * nowhere — credential gating lives on each route's and action's
161
+ * `auth.credential_types`.
156
162
  *
157
163
  * Construction-time guards (all fire on misconfiguration):
158
164
  *
@@ -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;AAGxB,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;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC;;;GAGG;AACH,eAAO,MAAM,QAAQ,aAMpB,CAAC;AACF,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,0GAA0G;IAC1G,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;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;AAGxB,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;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC;;;GAGG;AACH,eAAO,MAAM,QAAQ,aAMpB,CAAC;AACF,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,0GAA0G;IAC1G,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;;OAQG;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;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"}
@@ -105,9 +105,12 @@ const validate_registry_membership = (role_name, field, values, registry) => {
105
105
  *
106
106
  * Call once at server init. The returned `Role` schema validates role
107
107
  * strings at I/O boundaries (grant endpoint, role_grant queries). The
108
- * `role_specs` map is read by middleware for `required_credential_types`
109
- * checks and by admin / self-service factories to derive their default
110
- * eligibility filters from `RoleSpec.grant_paths`.
108
+ * `role_specs` map is read by the admin / self-service factories to derive
109
+ * their default eligibility filters from `RoleSpec.grant_paths`, and by the
110
+ * cell grant verbs to refuse an unregistered role principal.
111
+ * `RoleSpec.required_credential_types` is validated here but enforced
112
+ * nowhere — credential gating lives on each route's and action's
113
+ * `auth.credential_types`.
111
114
  *
112
115
  * Construction-time guards (all fire on misconfiguration):
113
116
  *
@@ -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,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"}
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,CAiJjB,CAAC"}
@@ -176,6 +176,26 @@ export const create_self_service_role_actions = (deps, options = {}) => {
176
176
  self_service: true
177
177
  }
178
178
  });
179
+ // Supersede cascade — `query_revoke_role_grant` marks any pending offers
180
+ // for the same `(account, role, scope)` superseded as a side effect, and
181
+ // each gets its forensic row like the admin revoke's (empty in practice:
182
+ // offer creation gates on the role's `grant_paths`, so only a role that is
183
+ // both offerable and self-service-eligible has siblings). The WS supersede
184
+ // notification to each grantor is not sent (twin of the Rust path).
185
+ for (const offer of result.superseded_offers) {
186
+ deps.audit.emit_role_grant_target(ctx, auth, {
187
+ event_type: 'role_grant_offer_supersede',
188
+ target_account_id: offer.to_account_id,
189
+ target_actor_id: offer.to_actor_id,
190
+ metadata: {
191
+ offer_id: offer.id,
192
+ role: offer.role,
193
+ scope_id: offer.scope_id,
194
+ reason: 'role_grant_revoked',
195
+ cause_id: result.id
196
+ }
197
+ });
198
+ }
179
199
  return { ok: true, enabled: false, changed: true };
180
200
  };
181
201
  return [rpc_action(self_service_role_set_action_spec, handler)];
package/dist/db/CLAUDE.md CHANGED
@@ -94,7 +94,10 @@ The wire schemas + RPC handlers + authz predicates for this layer live in
94
94
  `cell.moderation text` (nullable — `pending` / `approved` / `rejected`) is the
95
95
  approval-lifecycle marker, peer to `visibility` (partial `idx_cell_moderation_pending`
96
96
  on `root_id WHERE moderation = 'pending'` for the mod queue), set by the create
97
- authorizer's verdict + the `cell_moderate` verb, never in `data`. Nullable
97
+ authorizer's verdict + the `cell_moderate` verb, never in `data`.
98
+ `cell.policy jsonb` (nullable) is a root's contribution policy, opaque to the
99
+ spine — a table CHECK holds it to roots and to JSON objects (`policy @> '{}'`,
100
+ the object test pglet runs too). Nullable
98
101
  `created_by` / `updated_by` FKs to `actor` (NULL = system origin). GIN on `data`
99
102
  / `refs`; global partial-unique on `path`.
100
103
  - **`cell_queries.ts`** — `query_cell_create / get / get_by_path / update /
@@ -109,7 +112,11 @@ delete / set_moderation`, `_list_by_kind / _list_by_creator`, the
109
112
  the in-memory relation filter the `cell_get` bundle and the clone walk
110
113
  run). `query_cell_set_moderation` is the dedicated
111
114
  `cell_moderate` write (`moderation` is absent from `CellUpdatePatch`, so an
112
- author can't self-approve via `cell_update`). `cell.refs` derived from
115
+ author can't self-approve via `cell_update`); approval moves only a
116
+ `pending` row and rejection a `pending` or `approved` one (the takedown) —
117
+ the `WHERE` carries the from-state. `query_cell_set_policy` is the dedicated
118
+ `cell_policy_set` write (`policy` is absent from `CellUpdatePatch`; its
119
+ `WHERE` matches roots only). `cell.refs` derived from
113
120
  `data` via the exported `derive_cell_refs` on create/update (exact
114
121
  whole-string hashes, sorted — byte-identical with the Rust `extract_refs`). `CellRow.grant_count` is a
115
122
  derived projection (correlated subquery on `idx_cell_grant_cell`).
@@ -78,8 +78,16 @@ export declare const CELL_VISIBILITY_TYPE = "\nDO $$ BEGIN\n\tCREATE TYPE cell_v
78
78
  * (nullable text — `pending` / `approved` / `rejected`; `NULL` = unmoderated)
79
79
  * is the approval-lifecycle marker, peer to `visibility` (a control field with
80
80
  * a non-author writer — see `auth/cell_actions.ts`), never inside `data`.
81
+ *
82
+ * `policy` (nullable JSONB) is a root's contribution policy — an object opaque
83
+ * to the spine, read by the consumer's create authorizer (`NULL` = its floor
84
+ * governs). The table CHECK holds it to roots (`parent_id IS NULL`) and to JSON
85
+ * objects (`policy @> '{}'` holds for an object and for nothing else — the
86
+ * object test every driver runs, where `jsonb_typeof` is not on pglet). A
87
+ * manage-tier control field: written at a root's create and by
88
+ * `cell_policy_set`, never through `data`, which an editor grant can write.
81
89
  */
82
- export declare const CELL_SCHEMA = "\nCREATE TABLE IF NOT EXISTS cell (\n\tid UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n\tdata JSONB NOT NULL,\n\tkind TEXT,\n\tvisibility cell_visibility NOT NULL DEFAULT 'private',\n\tpath TEXT,\n\trefs TEXT[],\n\tparent_id UUID REFERENCES cell(id) ON DELETE SET NULL,\n\troot_id UUID REFERENCES cell(id) ON DELETE SET NULL,\n\tmoderation TEXT,\n\tcreated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n\tupdated_at TIMESTAMPTZ,\n\tdeleted_at TIMESTAMPTZ,\n\tcreated_by UUID REFERENCES actor(id) ON DELETE SET NULL,\n\tupdated_by UUID REFERENCES actor(id) ON DELETE SET NULL\n)";
90
+ export declare const CELL_SCHEMA = "\nCREATE TABLE IF NOT EXISTS cell (\n\tid UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n\tdata JSONB NOT NULL,\n\tkind TEXT,\n\tvisibility cell_visibility NOT NULL DEFAULT 'private',\n\tpath TEXT,\n\trefs TEXT[],\n\tparent_id UUID REFERENCES cell(id) ON DELETE SET NULL,\n\troot_id UUID REFERENCES cell(id) ON DELETE SET NULL,\n\tmoderation TEXT,\n\tpolicy JSONB,\n\tcreated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n\tupdated_at TIMESTAMPTZ,\n\tdeleted_at TIMESTAMPTZ,\n\tcreated_by UUID REFERENCES actor(id) ON DELETE SET NULL,\n\tupdated_by UUID REFERENCES actor(id) ON DELETE SET NULL,\n\tCHECK (policy IS NULL OR (parent_id IS NULL AND policy @> '{}'::jsonb))\n)";
83
91
  /**
84
92
  * Cell indexes — all active-only, partial on `deleted_at IS NULL`.
85
93
  *
@@ -1 +1 @@
1
- {"version":3,"file":"cell_ddl.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_ddl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,oIAI1B,CAAC;AAER;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,WAAW,gkBAgBtB,CAAC;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,MAAM,CAkBtC,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,0iBAc5B,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,MAAM,CAW5C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,+RAO5B,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,MAAM,CAE5C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,qSAO3B,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,MAAM,CAG3C,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB,4DAA6D,CAAC;AAE3F,uBAAuB;AACvB,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,SAAS,CAuB5C,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,wBAAwB,aAAa,CAAC;AAEnD,wDAAwD;AACxD,eAAO,MAAM,iBAAiB,EAAE,kBAG/B,CAAC"}
1
+ {"version":3,"file":"cell_ddl.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_ddl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,oIAI1B,CAAC;AAER;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,eAAO,MAAM,WAAW,6pBAkBtB,CAAC;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,MAAM,CAkBtC,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,0iBAc5B,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,MAAM,CAW5C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,+RAO5B,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,MAAM,CAE5C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,qSAO3B,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,MAAM,CAG3C,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB,4DAA6D,CAAC;AAE3F,uBAAuB;AACvB,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,SAAS,CAuB5C,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,wBAAwB,aAAa,CAAC;AAEnD,wDAAwD;AACxD,eAAO,MAAM,iBAAiB,EAAE,kBAG/B,CAAC"}
@@ -81,6 +81,14 @@ END $$`;
81
81
  * (nullable text — `pending` / `approved` / `rejected`; `NULL` = unmoderated)
82
82
  * is the approval-lifecycle marker, peer to `visibility` (a control field with
83
83
  * a non-author writer — see `auth/cell_actions.ts`), never inside `data`.
84
+ *
85
+ * `policy` (nullable JSONB) is a root's contribution policy — an object opaque
86
+ * to the spine, read by the consumer's create authorizer (`NULL` = its floor
87
+ * governs). The table CHECK holds it to roots (`parent_id IS NULL`) and to JSON
88
+ * objects (`policy @> '{}'` holds for an object and for nothing else — the
89
+ * object test every driver runs, where `jsonb_typeof` is not on pglet). A
90
+ * manage-tier control field: written at a root's create and by
91
+ * `cell_policy_set`, never through `data`, which an editor grant can write.
84
92
  */
85
93
  export const CELL_SCHEMA = `
86
94
  CREATE TABLE IF NOT EXISTS cell (
@@ -93,11 +101,13 @@ CREATE TABLE IF NOT EXISTS cell (
93
101
  parent_id UUID REFERENCES cell(id) ON DELETE SET NULL,
94
102
  root_id UUID REFERENCES cell(id) ON DELETE SET NULL,
95
103
  moderation TEXT,
104
+ policy JSONB,
96
105
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
97
106
  updated_at TIMESTAMPTZ,
98
107
  deleted_at TIMESTAMPTZ,
99
108
  created_by UUID REFERENCES actor(id) ON DELETE SET NULL,
100
- updated_by UUID REFERENCES actor(id) ON DELETE SET NULL
109
+ updated_by UUID REFERENCES actor(id) ON DELETE SET NULL,
110
+ CHECK (policy IS NULL OR (parent_id IS NULL AND policy @> '{}'::jsonb))
101
111
  )`;
102
112
  /**
103
113
  * Cell indexes — all active-only, partial on `deleted_at IS NULL`.
@@ -25,7 +25,7 @@ import type { Json } from '@fuzdev/fuz_util/json.ts';
25
25
  import type { Uuid } from '../primitive_schemas.js';
26
26
  import { type FactHash } from '@fuzdev/fuz_util/hash_schemas.ts';
27
27
  import type { CellData } from '../auth/cell_data_schema.js';
28
- import type { CellVisibility } from '../auth/cell_action_specs.js';
28
+ import type { CellPolicy, CellVisibility } from '../auth/cell_action_specs.js';
29
29
  /**
30
30
  * Row shape returned by `cell` SELECTs. `data` is typed as `CellData` —
31
31
  * the storage layer trusts the wire validation; the row is what was
@@ -49,6 +49,7 @@ export interface CellRow {
49
49
  parent_id: Uuid | null;
50
50
  root_id: Uuid | null;
51
51
  moderation: string | null;
52
+ policy: CellPolicy | null;
52
53
  created_at: string;
53
54
  updated_at: string | null;
54
55
  deleted_at: string | null;
@@ -66,7 +67,7 @@ export interface CellRow {
66
67
  * `grant_count` is not a column — `cell_row_projection` appends it. Keep
67
68
  * in sync with `CellRow` and the `cell` DDL in `db/cell_ddl.ts`.
68
69
  */
69
- export declare const CELL_COLUMNS: readonly ["id", "data", "kind", "visibility", "path", "refs", "parent_id", "root_id", "moderation", "created_at", "updated_at", "deleted_at", "created_by", "updated_by"];
70
+ export declare const CELL_COLUMNS: readonly ["id", "data", "kind", "visibility", "path", "refs", "parent_id", "root_id", "moderation", "policy", "created_at", "updated_at", "deleted_at", "created_by", "updated_by"];
70
71
  /**
71
72
  * The full `CellRow` projection — `CELL_COLUMNS` qualified by `alias` plus
72
73
  * the derived `grant_count` (a correlated subquery against `cell_grant`,
@@ -101,7 +102,9 @@ export type CellViewRow = Pick<CellRow, (typeof CELL_VIEW_COLUMNS)[number]>;
101
102
  * `parent_id` / `root_id` / `moderation` are the directory-tree + lifecycle
102
103
  * columns the handler derives (parent containment, governing root, the
103
104
  * create-authorizer verdict) — likewise write-once / control-gated, never on
104
- * `CellUpdatePatch`.
105
+ * `CellUpdatePatch`. `policy` is a root's contribution policy (the table CHECK
106
+ * refuses one beside a `parent_id`), changed later only by
107
+ * `query_cell_set_policy`.
105
108
  */
106
109
  export interface CellCreateQueryInput {
107
110
  data: Json;
@@ -111,6 +114,7 @@ export interface CellCreateQueryInput {
111
114
  parent_id?: Uuid | null;
112
115
  root_id?: Uuid | null;
113
116
  moderation?: string | null;
117
+ policy?: CellPolicy | null;
114
118
  created_by?: Uuid | null;
115
119
  }
116
120
  /**
@@ -219,9 +223,15 @@ export declare const query_cell_delete: (deps: QueryDeps, id: Uuid, options?: {
219
223
  }) => Promise<boolean>;
220
224
  /**
221
225
  * Transition a contribution's `moderation` marker (the `cell_moderate` verb's
222
- * write). On `set_visibility_public` (an approval) it also flips `visibility`
223
- * to `'public'` so the approved contribution goes live; a rejection leaves
224
- * visibility untouched (stays private). `updated_at` / `updated_by` are stamped.
226
+ * write). The `WHERE` carries the allowed from-states: an approval moves only a
227
+ * `'pending'` row, and a rejection a `'pending'` or `'approved'` one (the
228
+ * moderator's takedown). A rejected or unmoderated cell never moves, and nothing
229
+ * already approved is approved again — without the from-state an approval would
230
+ * force any cell its caller can reach public. On `set_visibility_public` (an
231
+ * approval) it also flips `visibility` to `'public'` so the approved
232
+ * contribution goes live; a rejection leaves visibility untouched — a pending
233
+ * post stays as its author set it, and a taken-down one keeps its visibility,
234
+ * hidden by the marker alone. `updated_at` / `updated_by` are stamped.
225
235
  *
226
236
  * `moderation` is deliberately **not** writable through `query_cell_update`
227
237
  * (it's absent from `CellUpdatePatch`) — gating the transition on a dedicated
@@ -232,13 +242,49 @@ export declare const query_cell_delete: (deps: QueryDeps, id: Uuid, options?: {
232
242
  * @param id - cell id
233
243
  * @param moderation - the terminal marker to write (`'approved'` | `'rejected'`)
234
244
  * @param options - `set_visibility_public` flips visibility on approval; `updated_by` records the moderator
235
- * @returns the updated row, or `null` when no active row matched (raced delete)
245
+ * @returns the updated row, or `null` when no active row in an allowed from-state matched (a raced delete or decision, or a refused transition)
236
246
  * @mutates `cell` - sets `moderation` (+ maybe `visibility`) on one row
237
247
  */
238
248
  export declare const query_cell_set_moderation: (deps: QueryDeps, id: Uuid, moderation: string, options: {
239
249
  set_visibility_public: boolean;
240
250
  updated_by?: Uuid | null;
241
251
  }) => Promise<CellRow | null>;
252
+ /** What `query_cell_set_policy` answers: the updated root and the policy it replaced. */
253
+ export interface CellSetPolicyResult {
254
+ cell: CellRow;
255
+ /** The root's `policy` as the `UPDATE` found it, under the row lock it writes with. */
256
+ previous_policy: CellPolicy | null;
257
+ }
258
+ /**
259
+ * Replace a root's contribution `policy` (the `cell_policy_set` verb's write);
260
+ * `null` clears it.
261
+ *
262
+ * The `WHERE` carries `parent_id IS NULL`, so only a root ever takes a policy
263
+ * — the table CHECK's rule restated where the write is. `updated_at` /
264
+ * `updated_by` are stamped.
265
+ *
266
+ * The replaced policy is read by the statement that writes, from a
267
+ * `FOR UPDATE` subquery: a concurrent set waits on that row lock and then
268
+ * reads the committed value, so the audit's `previous_policy` is the value
269
+ * this write replaced, never one a racing set already overwrote.
270
+ *
271
+ * `policy` is deliberately **not** writable through `query_cell_update` (it's
272
+ * absent from `CellUpdatePatch`): `data` writes gate on edit authority, which
273
+ * an editor grant carries, and the policy decides who may contribute under the
274
+ * root, so it changes only through this query behind the handler's
275
+ * manage-tier check.
276
+ *
277
+ * @param deps - query deps
278
+ * @param id - the root's id
279
+ * @param policy - the new policy, or `null` to clear it
280
+ * @param options - `updated_by` records the manager
281
+ * @returns the updated row and the policy it replaced, or `null` when no active root
282
+ * matched (a raced delete)
283
+ * @mutates `cell` - sets `policy` on one root
284
+ */
285
+ export declare const query_cell_set_policy: (deps: QueryDeps, id: Uuid, policy: CellPolicy | null, options: {
286
+ updated_by?: Uuid | null;
287
+ }) => Promise<CellSetPolicyResult | null>;
242
288
  /**
243
289
  * List active cells with the given `kind`, newest first. Uses the
244
290
  * `idx_cell_kind` index (`cell.kind = ?`).
@@ -1 +1 @@
1
- {"version":3,"file":"cell_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_queries.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cell_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_queries.ts"],"names":[],"mappings":"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"}
@@ -23,7 +23,7 @@
23
23
  import { is_fact_hash } from '@fuzdev/fuz_util/hash_schemas.ts';
24
24
  import { assert_row } from './assert_row.js';
25
25
  import { columns_sql, iso8601_timestamp_expr, qualify_columns } from './sql_columns.js';
26
- import { cell_moderation_admits_audience_sql, cell_moderation_is_stewardable_sql } from './cell_moderation.js';
26
+ import { CELL_MODERATION_APPROVED, CELL_MODERATION_PENDING, CELL_MODERATION_REJECTED, cell_moderation_admits_audience_sql, cell_moderation_is_stewardable_sql } from './cell_moderation.js';
27
27
  import { can_view_cell_sql, caller_role_grants_cte, cell_grant_admits_caller_sql } from './cell_view_sql.js';
28
28
  /**
29
29
  * The full `cell` column set, named explicitly so a row read fails loud on
@@ -45,6 +45,7 @@ export const CELL_COLUMNS = [
45
45
  'parent_id',
46
46
  'root_id',
47
47
  'moderation',
48
+ 'policy',
48
49
  'created_at',
49
50
  'updated_at',
50
51
  'deleted_at',
@@ -100,8 +101,8 @@ export const CELL_VIEW_COLUMNS = [
100
101
  export const query_cell_create = async (deps, input) => {
101
102
  const refs = derive_cell_refs(input.data);
102
103
  const row = await deps.db.query_one(`INSERT INTO cell
103
- (data, kind, visibility, path, refs, parent_id, root_id, moderation, created_by)
104
- VALUES ($1::jsonb, $2, COALESCE($3::cell_visibility, 'private'::cell_visibility), $4, $5::text[], $6, $7, $8, $9)
104
+ (data, kind, visibility, path, refs, parent_id, root_id, moderation, policy, created_by)
105
+ VALUES ($1::jsonb, $2, COALESCE($3::cell_visibility, 'private'::cell_visibility), $4, $5::text[], $6, $7, $8, $9::jsonb, $10)
105
106
  RETURNING ${cell_row_projection('cell')}`, [
106
107
  JSON.stringify(input.data),
107
108
  input.kind ?? null,
@@ -111,6 +112,7 @@ export const query_cell_create = async (deps, input) => {
111
112
  input.parent_id ?? null,
112
113
  input.root_id ?? null,
113
114
  input.moderation ?? null,
115
+ input.policy == null ? null : JSON.stringify(input.policy),
114
116
  input.created_by ?? null
115
117
  ]);
116
118
  return assert_row(row, 'INSERT INTO cell');
@@ -242,9 +244,15 @@ export const query_cell_delete = async (deps, id, options) => {
242
244
  };
243
245
  /**
244
246
  * Transition a contribution's `moderation` marker (the `cell_moderate` verb's
245
- * write). On `set_visibility_public` (an approval) it also flips `visibility`
246
- * to `'public'` so the approved contribution goes live; a rejection leaves
247
- * visibility untouched (stays private). `updated_at` / `updated_by` are stamped.
247
+ * write). The `WHERE` carries the allowed from-states: an approval moves only a
248
+ * `'pending'` row, and a rejection a `'pending'` or `'approved'` one (the
249
+ * moderator's takedown). A rejected or unmoderated cell never moves, and nothing
250
+ * already approved is approved again — without the from-state an approval would
251
+ * force any cell its caller can reach public. On `set_visibility_public` (an
252
+ * approval) it also flips `visibility` to `'public'` so the approved
253
+ * contribution goes live; a rejection leaves visibility untouched — a pending
254
+ * post stays as its author set it, and a taken-down one keeps its visibility,
255
+ * hidden by the marker alone. `updated_at` / `updated_by` are stamped.
248
256
  *
249
257
  * `moderation` is deliberately **not** writable through `query_cell_update`
250
258
  * (it's absent from `CellUpdatePatch`) — gating the transition on a dedicated
@@ -255,7 +263,7 @@ export const query_cell_delete = async (deps, id, options) => {
255
263
  * @param id - cell id
256
264
  * @param moderation - the terminal marker to write (`'approved'` | `'rejected'`)
257
265
  * @param options - `set_visibility_public` flips visibility on approval; `updated_by` records the moderator
258
- * @returns the updated row, or `null` when no active row matched (raced delete)
266
+ * @returns the updated row, or `null` when no active row in an allowed from-state matched (a raced delete or decision, or a refused transition)
259
267
  * @mutates `cell` - sets `moderation` (+ maybe `visibility`) on one row
260
268
  */
261
269
  export const query_cell_set_moderation = async (deps, id, moderation, options) => {
@@ -265,9 +273,55 @@ export const query_cell_set_moderation = async (deps, id, moderation, options) =
265
273
  updated_by = $4,
266
274
  updated_at = NOW()
267
275
  WHERE id = $1 AND deleted_at IS NULL
276
+ AND (moderation = '${CELL_MODERATION_PENDING}'
277
+ OR ($2::text = '${CELL_MODERATION_REJECTED}' AND moderation = '${CELL_MODERATION_APPROVED}'))
268
278
  RETURNING ${cell_row_projection('cell')}`, [id, moderation, options.set_visibility_public, options.updated_by ?? null]);
269
279
  return row ?? null;
270
280
  };
281
+ /**
282
+ * Replace a root's contribution `policy` (the `cell_policy_set` verb's write);
283
+ * `null` clears it.
284
+ *
285
+ * The `WHERE` carries `parent_id IS NULL`, so only a root ever takes a policy
286
+ * — the table CHECK's rule restated where the write is. `updated_at` /
287
+ * `updated_by` are stamped.
288
+ *
289
+ * The replaced policy is read by the statement that writes, from a
290
+ * `FOR UPDATE` subquery: a concurrent set waits on that row lock and then
291
+ * reads the committed value, so the audit's `previous_policy` is the value
292
+ * this write replaced, never one a racing set already overwrote.
293
+ *
294
+ * `policy` is deliberately **not** writable through `query_cell_update` (it's
295
+ * absent from `CellUpdatePatch`): `data` writes gate on edit authority, which
296
+ * an editor grant carries, and the policy decides who may contribute under the
297
+ * root, so it changes only through this query behind the handler's
298
+ * manage-tier check.
299
+ *
300
+ * @param deps - query deps
301
+ * @param id - the root's id
302
+ * @param policy - the new policy, or `null` to clear it
303
+ * @param options - `updated_by` records the manager
304
+ * @returns the updated row and the policy it replaced, or `null` when no active root
305
+ * matched (a raced delete)
306
+ * @mutates `cell` - sets `policy` on one root
307
+ */
308
+ export const query_cell_set_policy = async (deps, id, policy, options) => {
309
+ const row = await deps.db.query_one(`UPDATE cell SET
310
+ policy = $2::jsonb,
311
+ updated_by = $3,
312
+ updated_at = NOW()
313
+ FROM (
314
+ SELECT p.id, p.policy AS previous_policy FROM cell p
315
+ WHERE p.id = $1 AND p.deleted_at IS NULL AND p.parent_id IS NULL
316
+ FOR UPDATE
317
+ ) prev
318
+ WHERE cell.id = prev.id
319
+ RETURNING ${cell_row_projection('cell')}, prev.previous_policy`, [id, policy === null ? null : JSON.stringify(policy), options.updated_by ?? null]);
320
+ if (!row)
321
+ return null;
322
+ const { previous_policy, ...cell } = row;
323
+ return { cell, previous_policy };
324
+ };
271
325
  /**
272
326
  * List active cells with the given `kind`, newest first. Uses the
273
327
  * `idx_cell_kind` index (`cell.kind = ?`).
@@ -67,8 +67,10 @@ import type { MiddlewareHandler } from 'hono';
67
67
  /**
68
68
  * Create the body size limit middleware.
69
69
  *
70
- * @param max_size - the largest accepted body, in bytes
70
+ * @param max_size - the largest accepted body, in bytes; a safe integer, `0` or more
71
71
  * @returns middleware answering `413` `{error: 'payload_too_large'}` past the cap and `400` `{error: 'invalid_content_length'}` to a `Content-Length` that is not a run of digits
72
+ * @throws Error when `max_size` is not a safe integer `0` or more — a `NaN` cap
73
+ * compares false against every length and would admit any body
72
74
  */
73
75
  export declare const create_body_limit_middleware: (max_size: number) => MiddlewareHandler;
74
76
  //# sourceMappingURL=body_limit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"body_limit.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/body_limit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAQ9C;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,GAAI,UAAU,MAAM,KAAG,iBAwB/D,CAAC"}
1
+ {"version":3,"file":"body_limit.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/body_limit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAQ9C;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B,GAAI,UAAU,MAAM,KAAG,iBA6B/D,CAAC"}
@@ -70,10 +70,15 @@ const CONTENT_LENGTH_DIGITS = /^[0-9]+$/;
70
70
  /**
71
71
  * Create the body size limit middleware.
72
72
  *
73
- * @param max_size - the largest accepted body, in bytes
73
+ * @param max_size - the largest accepted body, in bytes; a safe integer, `0` or more
74
74
  * @returns middleware answering `413` `{error: 'payload_too_large'}` past the cap and `400` `{error: 'invalid_content_length'}` to a `Content-Length` that is not a run of digits
75
+ * @throws Error when `max_size` is not a safe integer `0` or more — a `NaN` cap
76
+ * compares false against every length and would admit any body
75
77
  */
76
78
  export const create_body_limit_middleware = (max_size) => {
79
+ if (!Number.isSafeInteger(max_size) || max_size < 0) {
80
+ throw new Error(`create_body_limit_middleware: max_size must be a safe integer 0 or more, got ${max_size}`);
81
+ }
77
82
  const streaming_limit = bodyLimit({
78
83
  maxSize: max_size,
79
84
  onError: (c) => c.json({ error: ERROR_PAYLOAD_TOO_LARGE }, 413)
@@ -96,6 +96,15 @@ export type JsonrpcServerErrorCode = z.infer<typeof JsonrpcServerErrorCode>;
96
96
  */
97
97
  export declare const JsonrpcErrorCode: z.ZodUnion<readonly [z.ZodLiteral<-32700>, z.ZodLiteral<-32600>, z.ZodLiteral<-32601>, z.ZodLiteral<-32602>, z.ZodLiteral<-32603>, z.core.$ZodBranded<z.ZodNumber, "JsonrpcServerErrorCode", "out">]>;
98
98
  export type JsonrpcErrorCode = z.infer<typeof JsonrpcErrorCode>;
99
+ /**
100
+ * Whether `value` is a valid JSON-RPC error code — the codes `JsonrpcErrorCode`
101
+ * parses, but integer-only in the server range. A numeric `code` alone doesn't make an error a
102
+ * JSON-RPC one: a `DOMException`'s `code` is a small positive number.
103
+ *
104
+ * @param value - the candidate code
105
+ * @returns whether `value` is one of the standard codes or an integer in the server range
106
+ */
107
+ export declare const is_jsonrpc_error_code: (value: unknown) => value is JsonrpcErrorCode;
99
108
  /** Error object within a JSON-RPC error response. */
100
109
  export declare const JsonrpcErrorObject: z.ZodObject<{
101
110
  code: z.ZodUnion<readonly [z.ZodLiteral<-32700>, z.ZodLiteral<-32600>, z.ZodLiteral<-32601>, z.ZodLiteral<-32602>, z.ZodLiteral<-32603>, z.core.$ZodBranded<z.ZodNumber, "JsonrpcServerErrorCode", "out">]>;
@@ -1 +1 @@
1
- {"version":3,"file":"jsonrpc.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/jsonrpc.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"jsonrpc.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/jsonrpc.ts"],"names":[],"mappings":"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"}