@fuzdev/fuz_app 0.110.1 → 0.112.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 (210) hide show
  1. package/dist/actions/CLAUDE.md +15 -11
  2. package/dist/actions/action_rpc.d.ts +3 -3
  3. package/dist/actions/action_rpc.d.ts.map +1 -1
  4. package/dist/actions/action_rpc.js +4 -6
  5. package/dist/actions/peer_ping.d.ts +8 -0
  6. package/dist/actions/peer_ping.d.ts.map +1 -1
  7. package/dist/actions/peer_ping.js +8 -0
  8. package/dist/actions/peer_request.d.ts +1 -1
  9. package/dist/actions/peer_request.js +1 -1
  10. package/dist/actions/perform_action.d.ts +16 -9
  11. package/dist/actions/perform_action.d.ts.map +1 -1
  12. package/dist/actions/perform_action.js +30 -21
  13. package/dist/actions/register_action_ws.d.ts +1 -1
  14. package/dist/actions/register_action_ws.d.ts.map +1 -1
  15. package/dist/actions/register_action_ws.js +88 -21
  16. package/dist/auth/CLAUDE.md +59 -10
  17. package/dist/auth/account_action_specs.d.ts +0 -2
  18. package/dist/auth/account_action_specs.d.ts.map +1 -1
  19. package/dist/auth/account_actions.d.ts.map +1 -1
  20. package/dist/auth/account_actions.js +10 -1
  21. package/dist/auth/account_queries.d.ts +22 -0
  22. package/dist/auth/account_queries.d.ts.map +1 -1
  23. package/dist/auth/account_queries.js +70 -16
  24. package/dist/auth/account_schema.d.ts +27 -9
  25. package/dist/auth/account_schema.d.ts.map +1 -1
  26. package/dist/auth/account_schema.js +23 -3
  27. package/dist/auth/actor_search_actions.d.ts +3 -3
  28. package/dist/auth/actor_search_actions.d.ts.map +1 -1
  29. package/dist/auth/actor_search_actions.js +10 -6
  30. package/dist/auth/admin_action_specs.d.ts +0 -2
  31. package/dist/auth/admin_action_specs.d.ts.map +1 -1
  32. package/dist/auth/api_token_queries.d.ts +9 -4
  33. package/dist/auth/api_token_queries.d.ts.map +1 -1
  34. package/dist/auth/api_token_queries.js +36 -8
  35. package/dist/auth/app_settings_queries.d.ts +8 -0
  36. package/dist/auth/app_settings_queries.d.ts.map +1 -1
  37. package/dist/auth/app_settings_queries.js +16 -3
  38. package/dist/auth/audit_log_queries.d.ts +21 -3
  39. package/dist/auth/audit_log_queries.d.ts.map +1 -1
  40. package/dist/auth/audit_log_queries.js +38 -7
  41. package/dist/auth/audit_log_schema.d.ts +0 -1
  42. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  43. package/dist/auth/auth_ddl.d.ts +6 -0
  44. package/dist/auth/auth_ddl.d.ts.map +1 -1
  45. package/dist/auth/auth_ddl.js +9 -0
  46. package/dist/auth/cell_actions.d.ts.map +1 -1
  47. package/dist/auth/cell_actions.js +3 -8
  48. package/dist/auth/cell_audit_actions.d.ts.map +1 -1
  49. package/dist/auth/cell_audit_actions.js +1 -1
  50. package/dist/auth/cell_field_actions.js +1 -1
  51. package/dist/auth/cell_grant_actions.js +1 -1
  52. package/dist/auth/cell_item_actions.js +1 -1
  53. package/dist/auth/invite_queries.d.ts +10 -0
  54. package/dist/auth/invite_queries.d.ts.map +1 -1
  55. package/dist/auth/invite_queries.js +28 -7
  56. package/dist/auth/migrations.d.ts.map +1 -1
  57. package/dist/auth/migrations.js +10 -0
  58. package/dist/auth/role_grant_offer_ddl.d.ts +34 -2
  59. package/dist/auth/role_grant_offer_ddl.d.ts.map +1 -1
  60. package/dist/auth/role_grant_offer_ddl.js +61 -2
  61. package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
  62. package/dist/auth/role_grant_offer_queries.js +48 -53
  63. package/dist/auth/role_grant_queries.d.ts +18 -0
  64. package/dist/auth/role_grant_queries.d.ts.map +1 -1
  65. package/dist/auth/role_grant_queries.js +58 -18
  66. package/dist/auth/session_queries.d.ts +12 -3
  67. package/dist/auth/session_queries.d.ts.map +1 -1
  68. package/dist/auth/session_queries.js +26 -6
  69. package/dist/db/CLAUDE.md +34 -0
  70. package/dist/db/cell_audit_queries.d.ts.map +1 -1
  71. package/dist/db/cell_audit_queries.js +3 -1
  72. package/dist/db/cell_field_queries.d.ts +12 -3
  73. package/dist/db/cell_field_queries.d.ts.map +1 -1
  74. package/dist/db/cell_field_queries.js +25 -7
  75. package/dist/db/cell_grant_queries.d.ts +10 -2
  76. package/dist/db/cell_grant_queries.d.ts.map +1 -1
  77. package/dist/db/cell_grant_queries.js +29 -11
  78. package/dist/db/cell_item_queries.d.ts +13 -2
  79. package/dist/db/cell_item_queries.d.ts.map +1 -1
  80. package/dist/db/cell_item_queries.js +27 -7
  81. package/dist/db/cell_queries.d.ts +32 -3
  82. package/dist/db/cell_queries.d.ts.map +1 -1
  83. package/dist/db/cell_queries.js +57 -22
  84. package/dist/db/fact_queries.d.ts +7 -0
  85. package/dist/db/fact_queries.d.ts.map +1 -1
  86. package/dist/db/fact_queries.js +27 -4
  87. package/dist/db/schema_ready.d.ts +6 -1
  88. package/dist/db/schema_ready.d.ts.map +1 -1
  89. package/dist/db/schema_ready.js +6 -2
  90. package/dist/db/sql_columns.d.ts +144 -0
  91. package/dist/db/sql_columns.d.ts.map +1 -0
  92. package/dist/db/sql_columns.js +164 -0
  93. package/dist/db/sql_identifier.d.ts +6 -5
  94. package/dist/db/sql_identifier.d.ts.map +1 -1
  95. package/dist/db/sql_identifier.js +6 -5
  96. package/dist/http/CLAUDE.md +4 -2
  97. package/dist/http/jsonrpc_helpers.d.ts +13 -0
  98. package/dist/http/jsonrpc_helpers.d.ts.map +1 -1
  99. package/dist/http/jsonrpc_helpers.js +13 -0
  100. package/dist/http/route_spec.d.ts +2 -2
  101. package/dist/server/app_server.d.ts.map +1 -1
  102. package/dist/server/app_server.js +1 -2
  103. package/dist/server/serve_fact_route.d.ts.map +1 -1
  104. package/dist/server/serve_fact_route.js +7 -2
  105. package/dist/testing/CLAUDE.md +55 -28
  106. package/dist/testing/admin_integration.d.ts +0 -3
  107. package/dist/testing/admin_integration.d.ts.map +1 -1
  108. package/dist/testing/admin_integration.js +0 -1
  109. package/dist/testing/adversarial_404.d.ts.map +1 -1
  110. package/dist/testing/adversarial_404.js +4 -1
  111. package/dist/testing/adversarial_input.d.ts.map +1 -1
  112. package/dist/testing/adversarial_input.js +5 -4
  113. package/dist/testing/app_server.d.ts +4 -3
  114. package/dist/testing/app_server.d.ts.map +1 -1
  115. package/dist/testing/app_server.js +11 -5
  116. package/dist/testing/attack_surface.d.ts +16 -0
  117. package/dist/testing/attack_surface.d.ts.map +1 -1
  118. package/dist/testing/attack_surface.js +36 -13
  119. package/dist/testing/audit_completeness.d.ts +0 -3
  120. package/dist/testing/audit_completeness.d.ts.map +1 -1
  121. package/dist/testing/audit_completeness.js +0 -1
  122. package/dist/testing/auth_apps.d.ts +9 -2
  123. package/dist/testing/auth_apps.d.ts.map +1 -1
  124. package/dist/testing/auth_apps.js +9 -2
  125. package/dist/testing/cross_backend/account_lifecycle.d.ts +5 -4
  126. package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
  127. package/dist/testing/cross_backend/account_lifecycle.js +7 -0
  128. package/dist/testing/cross_backend/action_manifest.d.ts +19 -3
  129. package/dist/testing/cross_backend/action_manifest.d.ts.map +1 -1
  130. package/dist/testing/cross_backend/action_manifest.js +10 -4
  131. package/dist/testing/cross_backend/action_manifest_parity.d.ts +9 -3
  132. package/dist/testing/cross_backend/action_manifest_parity.d.ts.map +1 -1
  133. package/dist/testing/cross_backend/action_manifest_parity.js +6 -0
  134. package/dist/testing/cross_backend/actor_lookup.d.ts +3 -2
  135. package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -1
  136. package/dist/testing/cross_backend/body_size.d.ts +4 -2
  137. package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
  138. package/dist/testing/cross_backend/body_size.js +1 -1
  139. package/dist/testing/cross_backend/cell_crud.d.ts +2 -2
  140. package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
  141. package/dist/testing/cross_backend/cell_crud.js +10 -0
  142. package/dist/testing/cross_backend/cell_gated_create.d.ts +3 -3
  143. package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
  144. package/dist/testing/cross_backend/cell_grant_role.d.ts +2 -2
  145. package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -1
  146. package/dist/testing/cross_backend/cell_relations.d.ts +2 -2
  147. package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
  148. package/dist/testing/cross_backend/expected_schema.json +1 -1
  149. package/dist/testing/cross_backend/fact_serving.d.ts +2 -2
  150. package/dist/testing/cross_backend/fact_serving.d.ts.map +1 -1
  151. package/dist/testing/cross_backend/make_cross_backend_project.d.ts +17 -2
  152. package/dist/testing/cross_backend/make_cross_backend_project.d.ts.map +1 -1
  153. package/dist/testing/cross_backend/make_cross_backend_project.js +23 -3
  154. package/dist/testing/cross_backend/origin.d.ts +4 -2
  155. package/dist/testing/cross_backend/origin.d.ts.map +1 -1
  156. package/dist/testing/cross_backend/origin.js +1 -1
  157. package/dist/testing/cross_backend/peer_ping_ws.d.ts.map +1 -1
  158. package/dist/testing/cross_backend/peer_ping_ws.js +11 -7
  159. package/dist/testing/cross_backend/query_shape.d.ts +6 -5
  160. package/dist/testing/cross_backend/query_shape.d.ts.map +1 -1
  161. package/dist/testing/cross_backend/ready.d.ts +2 -2
  162. package/dist/testing/cross_backend/ready.d.ts.map +1 -1
  163. package/dist/testing/cross_backend/role_grant_offer_enumeration.d.ts.map +1 -1
  164. package/dist/testing/cross_backend/role_grant_offer_enumeration.js +7 -1
  165. package/dist/testing/cross_backend/setup.d.ts +26 -6
  166. package/dist/testing/cross_backend/setup.d.ts.map +1 -1
  167. package/dist/testing/cross_backend/standard.d.ts +5 -1
  168. package/dist/testing/cross_backend/standard.d.ts.map +1 -1
  169. package/dist/testing/cross_backend/standard.js +1 -5
  170. package/dist/testing/cross_backend/testing_backdoor.js +1 -1
  171. package/dist/testing/cross_backend/testing_reset_actions.d.ts +5 -0
  172. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
  173. package/dist/testing/cross_backend/token_lifetime.d.ts.map +1 -1
  174. package/dist/testing/cross_backend/token_lifetime.js +6 -0
  175. package/dist/testing/cross_backend/token_scope_surface.d.ts +2 -2
  176. package/dist/testing/cross_backend/token_scope_surface.d.ts.map +1 -1
  177. package/dist/testing/cross_backend/wire_shapes.d.ts +22 -0
  178. package/dist/testing/cross_backend/wire_shapes.d.ts.map +1 -0
  179. package/dist/testing/cross_backend/wire_shapes.js +41 -0
  180. package/dist/testing/data_exposure.d.ts +0 -3
  181. package/dist/testing/data_exposure.d.ts.map +1 -1
  182. package/dist/testing/data_exposure.js +0 -1
  183. package/dist/testing/db.d.ts +62 -7
  184. package/dist/testing/db.d.ts.map +1 -1
  185. package/dist/testing/db.js +86 -28
  186. package/dist/testing/integration.d.ts.map +1 -1
  187. package/dist/testing/integration.js +15 -2
  188. package/dist/testing/round_trip.d.ts +0 -3
  189. package/dist/testing/round_trip.d.ts.map +1 -1
  190. package/dist/testing/round_trip.js +0 -4
  191. package/dist/testing/rpc_attack_surface.js +2 -2
  192. package/dist/testing/rpc_round_trip.d.ts +0 -3
  193. package/dist/testing/rpc_round_trip.d.ts.map +1 -1
  194. package/dist/testing/rpc_round_trip.js +0 -1
  195. package/dist/testing/schema_generators.d.ts +15 -0
  196. package/dist/testing/schema_generators.d.ts.map +1 -1
  197. package/dist/testing/schema_generators.js +45 -11
  198. package/dist/testing/standard.d.ts +5 -1
  199. package/dist/testing/standard.d.ts.map +1 -1
  200. package/dist/testing/standard.js +2 -7
  201. package/dist/timestamp.d.ts +58 -0
  202. package/dist/timestamp.d.ts.map +1 -0
  203. package/dist/timestamp.js +57 -0
  204. package/dist/ui/AdminOverview.svelte +5 -10
  205. package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
  206. package/dist/ui/RoleGrantOfferHistory.svelte +3 -1
  207. package/dist/ui/RoleGrantOfferHistory.svelte.d.ts.map +1 -1
  208. package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -1
  209. package/dist/ui/role_grant_offers_state.svelte.js +6 -2
  210. package/package.json +1 -1
@@ -69,7 +69,7 @@ Convention — `*_schema.ts` is Zod-only; `*_ddl.ts` holds DDL strings.
69
69
  - `auth/invite_schema.ts` — `Invite`, `CreateInviteInput`.
70
70
  - `auth/app_settings_schema.ts` — `AppSettings`, `UpdateAppSettingsInput` (single-row via `CHECK (id = 1)`).
71
71
  - `auth/role_grant_offer_schema.ts` — `RoleGrantOffer`, `RoleGrantOfferJson`, `to_role_grant_offer_json`, scope-sentinel constants.
72
- - `auth/role_grant_offer_ddl.ts` — `role_grant_offer` table + indexes + `ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID` / `_GLOBAL_TOKEN`.
72
+ - `auth/role_grant_offer_ddl.ts` — `role_grant_offer` table + indexes + `ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID` / `_GLOBAL_TOKEN` + `ROLE_GRANT_OFFER_COLUMNS` and the shared `ROLE_GRANT_OFFER_WITH_GRANTOR_SELECT` CTE tail (homed here per the placement rule in `db/sql_columns.ts`).
73
73
  - `auth/role_grant_offer_notifications.ts` — six WS notification specs for the consentful-grant lifecycle.
74
74
 
75
75
  ### Queries
@@ -81,12 +81,35 @@ All take `deps: QueryDeps = {db}` first; `query_validate_api_token` adds `log`.
81
81
  - `auth/actor_search_queries.ts` — case-insensitive prefix search on `actor.name`, scope-filtered when not admin.
82
82
  - `auth/role_grant_queries.ts` — idempotent create, IDOR-guarded revoke (with in-tx supersede), scope-aware lookup, role/account predicates, `query_role_grant_revoke_for_scope` parent-scope cascade.
83
83
  - `auth/role_grant_offer_queries.ts` — offer create/decline/retract/list/history/sweep, atomic `query_accept_offer` with sibling supersede; error classes `RoleGrantOfferSelfTargetError` / `_AlreadyTerminalError` / `_ExpiredError` / `_NotFoundError` / `_ActorAccountMismatchError` / `_ActorMismatchError`.
84
- - `auth/session_queries.ts` — server-side sessions (blake3-hashed), `query_session_revoke_by_hash_unscoped` (logout only), `query_session_enforce_limit` (transaction-required). No touch/renewal query — `expires_at` is an absolute cap set at mint (`AUTH_SESSION_LIFETIME_MS`), and `last_seen_at` is decorative pending its own removal migration.
84
+ - `auth/session_queries.ts` — server-side sessions (blake3-hashed), `query_session_revoke_by_hash_unscoped` (logout only), `query_session_enforce_limit` (transaction-required). No touch/renewal query — `expires_at` is an absolute cap set at mint (`AUTH_SESSION_LIFETIME_MS`).
85
85
  - `auth/api_token_queries.ts` — token validation with fire-and-forget usage tracking, IDOR-guarded revoke, `query_api_token_enforce_limit` (transaction-required).
86
86
  - `auth/invite_queries.ts` — invite create/find/claim/list/delete; `query_invite_claim_unscoped` (scoping enforced upstream by `_find_unclaimed_match_for_update`, which runs inside the signup tx with `FOR UPDATE` so find + claim are atomic).
87
87
  - `auth/app_settings_queries.ts` — load/update for the single-row settings table.
88
88
  - `auth/audit_log_queries.ts` — `query_audit_log` (in-tx insert), `_list` / `_list_with_usernames` / `_list_role_grant_history` / `_cleanup_before`, drift counters (`get_audit_metadata_validation_failures` / `get_audit_unknown_event_type_failures`).
89
89
 
90
+ **Named projections** (rationale + helpers: ../../../docs/architecture.md
91
+ §Query Modules). The consts on this side: `ACCOUNT_COLUMNS` + `ACTOR_COLUMNS`
92
+ (`account_queries.ts` owns both tables; its admin listing derives an
93
+ active-grant subset from `ROLE_GRANT_COLUMNS`), `AUTH_SESSION_COLUMNS`,
94
+ `INVITE_COLUMNS`, `AUDIT_LOG_COLUMNS` (shared with `db/cell_audit_queries.ts`),
95
+ `API_TOKEN_COLUMNS` (the client listing derives `minus token_hash`),
96
+ `ROLE_GRANT_COLUMNS`, `ROLE_GRANT_OFFER_COLUMNS` (in `role_grant_offer_ddl.ts`),
97
+ `APP_SETTINGS_COLUMNS` (reads omit the constant singleton `id`; the one const
98
+ without a `satisfies keyof Row` clause, since `AppSettings` deliberately
99
+ doesn't carry `id`). Module direction: `role_grant_offer_queries.ts` imports
100
+ `role_grant_queries.ts` (the accept path writes through
101
+ `query_create_role_grant` / `query_role_grant_by_id`) and not the reverse —
102
+ the revoke cascades in `role_grant_queries.ts` still _write_ offer rows, which
103
+ is exactly why the offer const and the shared CTE tail live in the DDL module.
104
+ Every const is pinned to the live schema by
105
+ `src/test/db/column_projections.db.test.ts`. The Rust `fuz_auth` twin carries
106
+ the same nine consts (names and order), projected through
107
+ `fuz_db::qualify_columns` / `omit_columns`, decoded with compile-time
108
+ name→index (`fuz_db::col!`), and pinned by its own
109
+ `tests/columns.rs` registry; its narrow purpose rows (`AccountRow` is an
110
+ `id, username` pair) keep private per-shape lists checked as subsets of the
111
+ table const.
112
+
90
113
  `_unscoped` suffix on `query_session_revoke_by_hash_unscoped` and
91
114
  `query_invite_claim_unscoped` is the safety signal: SQL only checks row state,
92
115
  caller is responsible for scoping. Production scoping for invites is enforced
@@ -277,7 +300,7 @@ Account-only — never loads actor or role_grants, never populates
277
300
  `REQUEST_CONTEXT_KEY`.
278
301
 
279
302
  **Authorization runs before input validation**, matching the dispatcher's
280
- 401 → authz → 403 → 400 phase order (see `http/CLAUDE.md` §Validation
303
+ 401 → authz → 403 → 400 phase order — the 429 is dispatcher-only (see `http/CLAUDE.md` §Validation
281
304
  pipeline). When the route's input declares `acting?: ActingActor` or its auth
282
305
  requires role_grants, the authorization phase calls `resolve_acting_actor` over
283
306
  the `acting` selector — the validated query value on GETs, read off the raw
@@ -302,6 +325,31 @@ path populates `REQUEST_CONTEXT_KEY`; it sets only `ACCOUNT_ID_KEY`,
302
325
  `TEST_CONTEXT_PRESET_KEY` to bypass DB-backed actor resolution; production
303
326
  code that reads `REQUEST_CONTEXT_KEY` is reading test escape-hatch state.
304
327
 
328
+ ### Wire timestamp shape
329
+
330
+ Every timestamp on the wire is **second-precision UTC** —
331
+ `YYYY-MM-DDTHH:MM:SSZ`, exactly 20 characters, the shape both spines emit.
332
+ Timestamps read from a row get it from the SQL projection: each query module
333
+ builds an `iso8601_timestamp_expr` override over its table const and passes
334
+ it to `columns_sql` / `qualify_columns` (see `db/CLAUDE.md`), covering `account`, `actor`,
335
+ `role_grant`, `role_grant_offer`, `auth_session`, `api_token`, `invite`,
336
+ `app_settings`, `audit_log`, and the cell tables. Stamps minted in TS rather
337
+ than read back — the `expires_at` on `account_token_create`'s output — use
338
+ `to_iso8601_seconds` (`timestamp.ts`), so the mint and the later
339
+ `account_token_list` read agree byte for byte.
340
+
341
+ The `*Json` schemas type these as `z.string()`, which a millisecond stamp
342
+ also satisfies — so the shape is pinned behaviorally by
343
+ `assert_iso8601_seconds` (`testing/cross_backend/wire_shapes.ts`) inside the
344
+ shared suites, on both backends.
345
+
346
+ The alias costs one rule: **an `ORDER BY` in a projected SELECT qualifies
347
+ its column** (`ORDER BY auth_session.created_at`) — bare, Postgres resolves
348
+ it to the formatted output column, which ties rows written inside the same
349
+ second and drops the btree index. Qualified, ordering stays exact and
350
+ index-backed, matching the Rust twin (which aliases nothing, so its bare
351
+ `ORDER BY created_at` already means the column).
352
+
305
353
  ### Open-registry composition
306
354
 
307
355
  Four open string registries — `RoleName`, `ScopeKindName`,
@@ -414,9 +462,10 @@ in the HTTP RPC dispatcher (`actions/action_rpc.ts`), the WS dispatcher
414
462
 
415
463
  ### Migrations
416
464
 
417
- Schema migrations live in `auth/migrations.ts` — two migrations today (`full_auth_schema`,
418
- `role_grant_offer_and_scoped_role_grants`) under the single reserved namespace
419
- `AUTH_MIGRATION_NAMESPACE = 'fuz_auth'`. Consumer namespaces must avoid
465
+ Schema migrations live in `auth/migrations.ts` — a frozen, append-only chain
466
+ (`full_auth_schema` first, every later change its own appended entry) under
467
+ the single reserved namespace `AUTH_MIGRATION_NAMESPACE = 'fuz_auth'`, each
468
+ entry twinned byte-for-byte by name on the Rust spine. Consumer namespaces must avoid
420
469
  `reserved_migration_namespaces`. Runner contract, error vocabulary, and
421
470
  operator recipes (rename, mark applied, reset, baseline) are in
422
471
  ../../../docs/migrations.md.
@@ -492,15 +541,15 @@ authenticate.
492
541
  - `account_delete_action_spec` — mutation (soft delete); self-or-admin (`{account, actor}`, handler elevates to admin when `account_id` ≠ self); input `{account_id?}`; output `{ok, deleted}`. Tombstones account + actor(s), revokes sessions/tokens, emits `account_delete` + per-actor `actor_delete`. Refuses a keeper-role target (`ERROR_CANNOT_DELETE_KEEPER`) or the sole active admin (`ERROR_CANNOT_DELETE_LAST_ADMIN`) — both fail-loud with a failure-audit row.
493
542
  - `account_purge_action_spec` — mutation (hard purge); keeper-only (`credential_types: ['daemon_token']` + `roles: ['keeper']`) + `confirm: true` + WARN; input `{account_id, confirm?}`; output `{ok, purged}`. Cascading delete; emits `account_purge` + per-actor `actor_purge` (identity snapshot survives in metadata since `audit_log` ids carry no FK). Refuses a keeper-role target (`ERROR_CANNOT_DELETE_KEEPER`) or the sole active admin (`ERROR_CANNOT_DELETE_LAST_ADMIN`).
494
543
  - `account_undelete_action_spec` — mutation (reactivation, inverse of soft delete); admin-only (`roles: ['admin']` — no self path, a tombstoned account can't authenticate); input `{account_id}`; output `{ok, undeleted}`. Clears the `deleted_at` tombstone on the account + its actor(s), emits `account_undelete` + per-actor `actor_undelete`. Does not restore revoked sessions/tokens.
495
- - `admin_session_list_action_spec` — read; input `z.void()`; output `{sessions}`.
544
+ - `admin_session_list_action_spec` — read; input `{acting?}` (`.default({})`); output `{sessions}`.
496
545
  - `admin_session_revoke_all_action_spec` — mutation; input `{account_id}`; output `{ok, count}`.
497
546
  - `admin_token_revoke_all_action_spec` — mutation; input `{account_id}`; output `{ok, count}`.
498
- - `audit_log_list_action_spec` — read; input `{event_type?, account_id?, limit?, offset?, since_seq?}`; output `{events}`.
547
+ - `audit_log_list_action_spec` — read; input `{event_type?, outcome?, account_id?, limit?, offset?, since_seq?}`; output `{events}`.
499
548
  - `audit_log_role_grant_history_action_spec` — read; input `{limit?, offset?}`; output `{events}`.
500
549
  - `invite_create_action_spec` — mutation; input `{email?, username?}`; output `{ok, invite}`.
501
- - `invite_list_action_spec` — read; input `z.void()`; output `{invites}`.
550
+ - `invite_list_action_spec` — read; input `{acting?}` (`.default({})`); output `{invites}`.
502
551
  - `invite_delete_action_spec` — mutation; input `{invite_id}`; output `{ok}`.
503
- - `app_settings_get_action_spec` — read; input `z.void()`; output `{settings}`.
552
+ - `app_settings_get_action_spec` — read; input `{acting?}` (`.default({})`); output `{settings}`.
504
553
  - `app_settings_update_action_spec` — mutation; input `{open_signup}`; output `{ok, settings}`.
505
554
 
506
555
  Constants: `AUDIT_LOG_LIST_LIMIT_MAX = 200`, `ADMIN_ACCOUNT_LIST_DEFAULT_LIMIT = 50`,
@@ -22,7 +22,6 @@ export declare const SessionListOutput: z.ZodObject<{
22
22
  account_id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
23
23
  created_at: z.ZodString;
24
24
  expires_at: z.ZodString;
25
- last_seen_at: z.ZodString;
26
25
  }, z.core.$strict>>;
27
26
  }, z.core.$strict>;
28
27
  export type SessionListOutput = z.infer<typeof SessionListOutput>;
@@ -149,7 +148,6 @@ export declare const account_session_list_action_spec: {
149
148
  account_id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
150
149
  created_at: z.ZodString;
151
150
  expires_at: z.ZodString;
152
- last_seen_at: z.ZodString;
153
151
  }, z.core.$strict>>;
154
152
  }, z.core.$strict>;
155
153
  async: true;
@@ -1 +1 @@
1
- {"version":3,"file":"account_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_action_specs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"account_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_action_specs.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"account_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC9F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAqBxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAwBnD,4CAA4C;AAC5C,MAAM,WAAW,oBAAoB;IACpC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAC5C;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,iBAAiB,EACvB,UAAS,oBAAyB,KAChC,KAAK,CAAC,SAAS,CAgKjB,CAAC"}
1
+ {"version":3,"file":"account_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC9F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAqBxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAyBnD,4CAA4C;AAC5C,MAAM,WAAW,oBAAoB;IACpC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAC5C;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,iBAAiB,EACvB,UAAS,oBAAyB,KAChC,KAAK,CAAC,SAAS,CAwKjB,CAAC"}
@@ -30,6 +30,7 @@ import { parse_stored_token_scope, token_scope_from_input, token_scope_label } f
30
30
  import { token_lifetime_to_expires_at } from './token_lifetime.js';
31
31
  import { generate_api_token } from './api_token.js';
32
32
  import { DEFAULT_MAX_TOKENS } from './account_route_schema.js';
33
+ import { to_iso8601_seconds } from '../timestamp.js';
33
34
  import { account_verify_action_spec, account_session_list_action_spec, account_session_revoke_action_spec, account_session_revoke_all_action_spec, account_token_create_action_spec, account_token_list_action_spec, account_token_revoke_action_spec } from './account_action_specs.js';
34
35
  /**
35
36
  * Create the self-service account RPC actions.
@@ -120,7 +121,15 @@ export const create_account_actions = (deps, options = {}) => {
120
121
  credential_type: ctx.credential_type ?? undefined
121
122
  }
122
123
  });
123
- return { ok: true, token, id, name: input.name, expires_at: expires_at?.toISOString() ?? null };
124
+ return {
125
+ ok: true,
126
+ token,
127
+ id,
128
+ name: input.name,
129
+ // the canonical wire shape, matching what `token_list` reads back
130
+ // through `api_token`'s ISO-8601 projection
131
+ expires_at: expires_at === null ? null : to_iso8601_seconds(expires_at)
132
+ };
124
133
  };
125
134
  const token_list_handler = async (_input, ctx) => {
126
135
  const rows = await query_api_token_list_for_account(ctx, ctx.auth.account.id);
@@ -8,6 +8,28 @@
8
8
  */
9
9
  import type { QueryDeps } from '../db/query_deps.js';
10
10
  import { type Account, type Actor, type CreateAccountInput, type AdminAccountEntryJson } from './account_schema.js';
11
+ /**
12
+ * The full `account` column set, named explicitly so a row read fails loud
13
+ * on schema drift.
14
+ *
15
+ * `SELECT *` silently omits a dropped column, which the login lookups then
16
+ * misread: `query_account_by_username_or_email` filters its result with
17
+ * `account.deleted_at === null`, so a missing `deleted_at` column reads back
18
+ * as `undefined`, `undefined === null` is `false`, and *every* login resolves
19
+ * to "not found" (401) — a silent, total auth outage instead of an error.
20
+ * Selecting named columns turns that drift into a hard Postgres
21
+ * `column "..." does not exist`. Mirrors the Rust side
22
+ * (`fuz_auth/src/account_queries.rs`), which selects named columns and
23
+ * decodes them positionally. Keep in sync with `Account` and the `account`
24
+ * DDL in `auth/auth_ddl.ts`.
25
+ */
26
+ export declare const ACCOUNT_COLUMNS: readonly ["id", "username", "email", "email_verified", "password_hash", "created_at", "created_by", "updated_at", "updated_by", "deleted_at", "deleted_by"];
27
+ /**
28
+ * The full `actor` column set — the same fail-loud discipline as
29
+ * `ACCOUNT_COLUMNS` (this module owns both tables). Keep in sync with
30
+ * `Actor` and the `actor` DDL in `auth/auth_ddl.ts`.
31
+ */
32
+ export declare const ACTOR_COLUMNS: readonly ["id", "account_id", "name", "created_at", "updated_at", "updated_by", "deleted_at", "deleted_by"];
11
33
  /**
12
34
  * Create a new account.
13
35
  *
@@ -1 +1 @@
1
- {"version":3,"file":"account_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_queries.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"account_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_queries.ts"],"names":[],"mappings":"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"}
@@ -7,8 +7,10 @@
7
7
  * @module
8
8
  */
9
9
  import { assert_row } from '../db/assert_row.js';
10
+ import { columns_sql, iso8601_timestamp_column, iso8601_timestamp_expr, omit_columns } from '../db/sql_columns.js';
10
11
  import { to_admin_account } from './account_schema.js';
11
12
  import { ADMIN_ACCOUNT_LIST_DEFAULT_LIMIT } from './admin_action_specs.js';
13
+ import { ROLE_GRANT_COLUMNS } from './role_grant_queries.js';
12
14
  /**
13
15
  * The full `account` column set, named explicitly so a row read fails loud
14
16
  * on schema drift.
@@ -24,7 +26,46 @@ import { ADMIN_ACCOUNT_LIST_DEFAULT_LIMIT } from './admin_action_specs.js';
24
26
  * decodes them positionally. Keep in sync with `Account` and the `account`
25
27
  * DDL in `auth/auth_ddl.ts`.
26
28
  */
27
- const ACCOUNT_COLUMNS = 'id, username, email, email_verified, password_hash, created_at, created_by, updated_at, updated_by, deleted_at, deleted_by';
29
+ export const ACCOUNT_COLUMNS = [
30
+ 'id',
31
+ 'username',
32
+ 'email',
33
+ 'email_verified',
34
+ 'password_hash',
35
+ 'created_at',
36
+ 'created_by',
37
+ 'updated_at',
38
+ 'updated_by',
39
+ 'deleted_at',
40
+ 'deleted_by'
41
+ ];
42
+ /**
43
+ * The full `actor` column set — the same fail-loud discipline as
44
+ * `ACCOUNT_COLUMNS` (this module owns both tables). Keep in sync with
45
+ * `Actor` and the `actor` DDL in `auth/auth_ddl.ts`.
46
+ */
47
+ export const ACTOR_COLUMNS = [
48
+ 'id',
49
+ 'account_id',
50
+ 'name',
51
+ 'created_at',
52
+ 'updated_at',
53
+ 'updated_by',
54
+ 'deleted_at',
55
+ 'deleted_by'
56
+ ];
57
+ /**
58
+ * The timestamp override both tables this module owns share (`ACTOR_COLUMNS`
59
+ * carries the same triple); every read here is on the bare table. Actor
60
+ * timestamps never reach the wire (the summaries carry `{id, name}` only —
61
+ * see `actor_lookup_action_specs.ts` on timing-oracle avoidance), but `Actor`
62
+ * declares them as strings, so the projection makes the row honest there too.
63
+ */
64
+ const account_actor_expr = iso8601_timestamp_expr(ACCOUNT_COLUMNS, [
65
+ 'created_at',
66
+ 'updated_at',
67
+ 'deleted_at'
68
+ ])('');
28
69
  /**
29
70
  * Create a new account.
30
71
  *
@@ -36,7 +77,7 @@ const ACCOUNT_COLUMNS = 'id, username, email, email_verified, password_hash, cre
36
77
  export const query_create_account = async (deps, input) => {
37
78
  const row = await deps.db.query_one(`INSERT INTO account (username, password_hash, email)
38
79
  VALUES ($1, $2, $3)
39
- RETURNING *`, [input.username, input.password_hash, input.email ?? null]);
80
+ RETURNING ${columns_sql(ACCOUNT_COLUMNS, account_actor_expr)}`, [input.username, input.password_hash, input.email ?? null]);
40
81
  return assert_row(row, 'INSERT INTO account');
41
82
  };
42
83
  /**
@@ -49,19 +90,19 @@ export const query_create_account = async (deps, input) => {
49
90
  * soft-deleted rows too, uses `query_purge_account` directly.
50
91
  */
51
92
  export const query_account_by_id = async (deps, id) => {
52
- return deps.db.query_one(`SELECT ${ACCOUNT_COLUMNS} FROM account WHERE id = $1 AND deleted_at IS NULL`, [id]);
93
+ return deps.db.query_one(`SELECT ${columns_sql(ACCOUNT_COLUMNS, account_actor_expr)} FROM account WHERE id = $1 AND deleted_at IS NULL`, [id]);
53
94
  };
54
95
  /**
55
96
  * Find an account by username (case-insensitive).
56
97
  */
57
98
  export const query_account_by_username = async (deps, username) => {
58
- return deps.db.query_one(`SELECT ${ACCOUNT_COLUMNS} FROM account WHERE LOWER(username) = LOWER($1)`, [username]);
99
+ return deps.db.query_one(`SELECT ${columns_sql(ACCOUNT_COLUMNS, account_actor_expr)} FROM account WHERE LOWER(username) = LOWER($1)`, [username]);
59
100
  };
60
101
  /**
61
102
  * Find an account by email (case-insensitive).
62
103
  */
63
104
  export const query_account_by_email = async (deps, email) => {
64
- return deps.db.query_one(`SELECT ${ACCOUNT_COLUMNS} FROM account WHERE LOWER(email) = LOWER($1)`, [email]);
105
+ return deps.db.query_one(`SELECT ${columns_sql(ACCOUNT_COLUMNS, account_actor_expr)} FROM account WHERE LOWER(email) = LOWER($1)`, [email]);
65
106
  };
66
107
  /**
67
108
  * Find an account by username or email.
@@ -214,7 +255,7 @@ export const query_account_has_any = async (deps) => {
214
255
  * @mutates `actor` table - inserts the new row
215
256
  */
216
257
  export const query_create_actor = async (deps, account_id, name) => {
217
- const row = await deps.db.query_one(`INSERT INTO actor (account_id, name) VALUES ($1, $2) RETURNING *`, [account_id, name]);
258
+ const row = await deps.db.query_one(`INSERT INTO actor (account_id, name) VALUES ($1, $2) RETURNING ${columns_sql(ACTOR_COLUMNS, account_actor_expr)}`, [account_id, name]);
218
259
  return assert_row(row, 'INSERT INTO actor');
219
260
  };
220
261
  /**
@@ -229,7 +270,7 @@ export const query_create_actor = async (deps, account_id, name) => {
229
270
  * `query_actor_by_id` instead.
230
271
  */
231
272
  export const query_actors_by_account = async (deps, account_id) => {
232
- return deps.db.query(`SELECT * FROM actor WHERE account_id = $1 ORDER BY created_at ASC, id ASC`, [account_id]);
273
+ return deps.db.query(`SELECT ${columns_sql(ACTOR_COLUMNS, account_actor_expr)} FROM actor WHERE account_id = $1 ORDER BY actor.created_at ASC, actor.id ASC`, [account_id]);
233
274
  };
234
275
  /**
235
276
  * List **active** (non-tombstoned) actors on an account, ordered by
@@ -243,13 +284,13 @@ export const query_actors_by_account = async (deps, account_id) => {
243
284
  * tombstoned rows stay on the unfiltered `query_actors_by_account`.
244
285
  */
245
286
  export const query_active_actors_by_account = async (deps, account_id) => {
246
- return deps.db.query(`SELECT * FROM actor WHERE account_id = $1 AND deleted_at IS NULL ORDER BY created_at ASC, id ASC`, [account_id]);
287
+ return deps.db.query(`SELECT ${columns_sql(ACTOR_COLUMNS, account_actor_expr)} FROM actor WHERE account_id = $1 AND deleted_at IS NULL ORDER BY actor.created_at ASC, actor.id ASC`, [account_id]);
247
288
  };
248
289
  /**
249
290
  * Find an actor by id.
250
291
  */
251
292
  export const query_actor_by_id = async (deps, id) => {
252
- return deps.db.query_one(`SELECT * FROM actor WHERE id = $1`, [id]);
293
+ return deps.db.query_one(`SELECT ${columns_sql(ACTOR_COLUMNS, account_actor_expr)} FROM actor WHERE id = $1`, [id]);
253
294
  };
254
295
  /**
255
296
  * Create an account and its actor in a single operation.
@@ -266,6 +307,19 @@ export const query_create_account_with_actor = async (deps, input) => {
266
307
  const actor = await query_create_actor(deps, account.id, input.username);
267
308
  return { account, actor };
268
309
  };
310
+ /**
311
+ * `ROLE_GRANT_COLUMNS` minus the revocation + provenance columns — the
312
+ * active-grant summary the admin listing projects (`RoleGrantWithActorId`).
313
+ */
314
+ const ADMIN_ROLE_GRANT_COLUMNS = omit_columns(ROLE_GRANT_COLUMNS, 'revoked_at', 'revoked_by', 'revoked_reason', 'source_offer_id');
315
+ /**
316
+ * The admin listing's active-grant summary timestamps — twin of the spine's
317
+ * `role_grant_summary_columns`. The read is on the bare `role_grant` table.
318
+ */
319
+ const admin_role_grant_expr = iso8601_timestamp_expr(ADMIN_ROLE_GRANT_COLUMNS, [
320
+ 'created_at',
321
+ 'expires_at'
322
+ ])('');
269
323
  /**
270
324
  * List accounts with their actors, active role_grants, and pending inbound
271
325
  * role_grant offers for admin display.
@@ -291,23 +345,23 @@ export const query_admin_account_list = async (deps, options) => {
291
345
  // `include_deleted` to surface tombstoned rows for reactivation.
292
346
  const where = options?.include_deleted ? '' : 'WHERE deleted_at IS NULL';
293
347
  const account_query = limit == null
294
- ? deps.db.query(`SELECT * FROM account ${where} ORDER BY created_at OFFSET $1`, [
295
- offset
296
- ])
297
- : deps.db.query(`SELECT * FROM account ${where} ORDER BY created_at LIMIT $1 OFFSET $2`, [limit, offset]);
348
+ ? deps.db.query(`SELECT ${columns_sql(ACCOUNT_COLUMNS, account_actor_expr)} FROM account ${where} ORDER BY account.created_at OFFSET $1`, [offset])
349
+ : deps.db.query(`SELECT ${columns_sql(ACCOUNT_COLUMNS, account_actor_expr)} FROM account ${where} ORDER BY account.created_at LIMIT $1 OFFSET $2`, [limit, offset]);
298
350
  const accounts = await account_query;
299
351
  if (accounts.length === 0)
300
352
  return [];
301
353
  const account_ids = accounts.map((a) => a.id);
302
354
  const [actors, role_grants, pending_offers] = await Promise.all([
303
- deps.db.query(`SELECT * FROM actor WHERE account_id = ANY($1::uuid[])`, [account_ids]),
304
- deps.db.query(`SELECT id, actor_id, role, scope_kind, scope_id, created_at, expires_at, granted_by
355
+ deps.db.query(`SELECT ${columns_sql(ACTOR_COLUMNS, account_actor_expr)} FROM actor WHERE account_id = ANY($1::uuid[])`, [account_ids]),
356
+ deps.db.query(`SELECT ${columns_sql(ADMIN_ROLE_GRANT_COLUMNS, admin_role_grant_expr)}
305
357
  FROM role_grant
306
358
  WHERE actor_id IN (SELECT id FROM actor WHERE account_id = ANY($1::uuid[]))
307
359
  AND revoked_at IS NULL
308
360
  AND (expires_at IS NULL OR expires_at > NOW())`, [account_ids]),
309
361
  deps.db.query(`SELECT po.id, po.to_account_id, po.from_actor_id, po.role, po.scope_kind, po.scope_id,
310
- po.created_at, po.expires_at, a.username AS from_username
362
+ ${iso8601_timestamp_column('po', 'created_at')} AS created_at,
363
+ ${iso8601_timestamp_column('po', 'expires_at')} AS expires_at,
364
+ a.username AS from_username
311
365
  FROM role_grant_offer po
312
366
  JOIN actor act ON act.id = po.from_actor_id
313
367
  JOIN account a ON a.id = act.account_id
@@ -97,6 +97,26 @@ export interface RoleGrant {
97
97
  /** Offer that produced this role_grant (set by `query_accept_offer`). `null` for direct grants. */
98
98
  source_offer_id: Uuid | null;
99
99
  }
100
+ /**
101
+ * Whether a role grant is live, from its wire fields — not revoked, and not
102
+ * past `expires_at`.
103
+ *
104
+ * Server-side this is belt-and-suspenders: every `role_grant` a request
105
+ * context holds came through `query_role_grant_find_active_for_actor`, whose
106
+ * `revoked_at IS NULL AND (expires_at IS NULL OR expires_at > NOW())` is the
107
+ * authoritative gate on both spines (the Rust twin's in-memory
108
+ * `RoleGrantRow` carries no timestamps at all and re-checks nothing).
109
+ *
110
+ * `expires_at` arrives second-truncated, so the expiry check is
111
+ * `is_iso8601_seconds_live` (`timestamp.ts`): it admits through the end of
112
+ * the second the stamp names, so the predicate can never report a grant
113
+ * expired *before* the SQL gate would — a bare `expires_at > now` would deny
114
+ * an admitted grant for up to a second.
115
+ *
116
+ * @param p - the grant's revocation + expiry fields
117
+ * @param now - current time (defaults to `new Date()`, pass for testability and hot-path efficiency)
118
+ * @returns `true` when the grant is neither revoked nor expired
119
+ */
100
120
  export declare const is_role_grant_active: (p: {
101
121
  revoked_at?: string | null;
102
122
  expires_at: string | null;
@@ -112,14 +132,13 @@ export declare const is_role_grant_active: (p: {
112
132
  */
113
133
  export declare const SessionId: z.core.$ZodBranded<z.ZodString, "SessionId", "out">;
114
134
  export type SessionId = z.infer<typeof SessionId>;
115
- /** Server-side auth session, keyed by blake3 hash of session token. */
116
- export interface AuthSession {
117
- id: SessionId;
118
- account_id: Uuid;
119
- created_at: string;
120
- expires_at: string;
121
- last_seen_at: string;
122
- }
135
+ /**
136
+ * Server-side auth session row, keyed by blake3 hash of the session token.
137
+ * Structurally identical to the wire shape `AuthSessionJson` — kept as the
138
+ * DB-row name so query signatures keep saying which side of the boundary
139
+ * they read.
140
+ */
141
+ export type AuthSession = AuthSessionJson;
123
142
  /** API token for CLI/programmatic access. */
124
143
  export interface ApiToken {
125
144
  id: string;
@@ -152,7 +171,6 @@ export declare const AuthSessionJson: z.ZodObject<{
152
171
  account_id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
153
172
  created_at: z.ZodString;
154
173
  expires_at: z.ZodString;
155
- last_seen_at: z.ZodString;
156
174
  }, z.core.$strict>;
157
175
  export type AuthSessionJson = z.infer<typeof AuthSessionJson>;
158
176
  /** Zod schema for client-safe API token listing (excludes `token_hash`). */
@@ -1 +1 @@
1
- {"version":3,"file":"account_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"account_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_schema.ts"],"names":[],"mappings":"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"}
@@ -20,6 +20,7 @@ import { z } from 'zod';
20
20
  import { Uuid } from '@fuzdev/fuz_util/id.ts';
21
21
  import { Blake3Hash } from '@fuzdev/fuz_util/hash_schemas.ts';
22
22
  import { Username, Email } from '../primitive_schemas.js';
23
+ import { is_iso8601_seconds_live } from '../timestamp.js';
23
24
  /**
24
25
  * Maximum length of the optional free-form `revoked_reason` attached to a
25
26
  * revoked role_grant. Bounds the value at the schema layer so both the admin
@@ -27,7 +28,27 @@ import { Username, Email } from '../primitive_schemas.js';
27
28
  * `role_grant_revoke` WS notification validate against the same ceiling.
28
29
  */
29
30
  export const ROLE_GRANT_REVOKED_REASON_LENGTH_MAX = 500;
30
- export const is_role_grant_active = (p, now = new Date()) => !p.revoked_at && (!p.expires_at || new Date(p.expires_at) > now);
31
+ /**
32
+ * Whether a role grant is live, from its wire fields — not revoked, and not
33
+ * past `expires_at`.
34
+ *
35
+ * Server-side this is belt-and-suspenders: every `role_grant` a request
36
+ * context holds came through `query_role_grant_find_active_for_actor`, whose
37
+ * `revoked_at IS NULL AND (expires_at IS NULL OR expires_at > NOW())` is the
38
+ * authoritative gate on both spines (the Rust twin's in-memory
39
+ * `RoleGrantRow` carries no timestamps at all and re-checks nothing).
40
+ *
41
+ * `expires_at` arrives second-truncated, so the expiry check is
42
+ * `is_iso8601_seconds_live` (`timestamp.ts`): it admits through the end of
43
+ * the second the stamp names, so the predicate can never report a grant
44
+ * expired *before* the SQL gate would — a bare `expires_at > now` would deny
45
+ * an admitted grant for up to a second.
46
+ *
47
+ * @param p - the grant's revocation + expiry fields
48
+ * @param now - current time (defaults to `new Date()`, pass for testability and hot-path efficiency)
49
+ * @returns `true` when the grant is neither revoked nor expired
50
+ */
51
+ export const is_role_grant_active = (p, now = new Date()) => !p.revoked_at && (!p.expires_at || is_iso8601_seconds_live(p.expires_at, now.getTime()));
31
52
  /**
32
53
  * A session's storage key — the blake3 hash of its raw token, branded so a
33
54
  * bare `string` can't stand in for one. `Blake3Hash` (`hash_schemas.ts`) is
@@ -52,8 +73,7 @@ export const AuthSessionJson = z.strictObject({
52
73
  id: SessionId,
53
74
  account_id: Uuid,
54
75
  created_at: z.string(),
55
- expires_at: z.string(),
56
- last_seen_at: z.string()
76
+ expires_at: z.string()
57
77
  });
58
78
  /** Zod schema for client-safe API token listing (excludes `token_hash`). */
59
79
  export const ClientApiTokenJson = z.strictObject({
@@ -11,9 +11,9 @@
11
11
  * - **Admin gate on empty `scope_ids`** — unbounded global search is
12
12
  * admin-only. Non-admin callers without a `scope_ids` filter are
13
13
  * rejected with `invalid_params` carrying `actor_search_scope_required`.
14
- * The admin check is account-grain (any actor on the caller's account
15
- * holds a global `admin` role_grant) since the `actor: 'none'` posture
16
- * doesn't load `auth.role_grants` for an in-memory check.
14
+ * The admin check is account-grain (any **active** actor on the caller's
15
+ * account holds a global `admin` role_grant) since the `actor: 'none'`
16
+ * posture doesn't load `auth.role_grants` for an in-memory check.
17
17
  * - **Limit clamp** — input is bounded by `ACTOR_SEARCH_LIMIT_MAX` at
18
18
  * the schema; the handler picks the default when omitted.
19
19
  *
@@ -1 +1 @@
1
- {"version":3,"file":"actor_search_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/actor_search_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAGtD,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAc9F,sDAAsD;AACtD,MAAM,WAAW,qBAAqB;IACrC,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,eAAO,MAAM,2BAA2B,GAAI,OAAO,qBAAqB,KAAG,KAAK,CAAC,SAAS,CAiCzF,CAAC"}
1
+ {"version":3,"file":"actor_search_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/actor_search_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAGtD,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAc9F,sDAAsD;AACtD,MAAM,WAAW,qBAAqB;IACrC,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,eAAO,MAAM,2BAA2B,GAAI,OAAO,qBAAqB,KAAG,KAAK,CAAC,SAAS,CAyCzF,CAAC"}
@@ -11,9 +11,9 @@
11
11
  * - **Admin gate on empty `scope_ids`** — unbounded global search is
12
12
  * admin-only. Non-admin callers without a `scope_ids` filter are
13
13
  * rejected with `invalid_params` carrying `actor_search_scope_required`.
14
- * The admin check is account-grain (any actor on the caller's account
15
- * holds a global `admin` role_grant) since the `actor: 'none'` posture
16
- * doesn't load `auth.role_grants` for an in-memory check.
14
+ * The admin check is account-grain (any **active** actor on the caller's
15
+ * account holds a global `admin` role_grant) since the `actor: 'none'`
16
+ * posture doesn't load `auth.role_grants` for an in-memory check.
17
17
  * - **Limit clamp** — input is bounded by `ACTOR_SEARCH_LIMIT_MAX` at
18
18
  * the schema; the handler picks the default when omitted.
19
19
  *
@@ -26,15 +26,19 @@
26
26
  import { jsonrpc_errors } from '../http/jsonrpc_errors.js';
27
27
  import { rpc_action } from '../actions/action_rpc.js';
28
28
  import { query_actor_search } from './actor_search_queries.js';
29
- import { query_account_has_global_role } from './role_grant_queries.js';
29
+ import { query_account_has_active_global_role } from './role_grant_queries.js';
30
30
  import { ROLE_ADMIN } from './role_schema.js';
31
31
  import { ACTOR_SEARCH_LIMIT_DEFAULT, ERROR_ACTOR_SEARCH_SCOPE_REQUIRED, actor_search_action_spec } from './actor_search_action_specs.js';
32
32
  export const create_actor_search_actions = (_deps) => {
33
33
  const handler = async (input, ctx) => {
34
34
  if (!input.scope_ids || input.scope_ids.length === 0) {
35
35
  // Unbounded global search is admin-only. Account-grain admin
36
- // check — any actor on the caller's account holds the role.
37
- const is_admin = await query_account_has_global_role(ctx, ctx.auth.account.id, ROLE_ADMIN);
36
+ // check — any *active* actor on the caller's account holds the
37
+ // role. The tombstone-aware query is what every admission gate
38
+ // reads: soft-delete leaves `role_grant` rows intact (undelete
39
+ // restores them), so the blind sibling would admit an account
40
+ // whose only admin grant hangs off a tombstoned actor.
41
+ const is_admin = await query_account_has_active_global_role(ctx, ctx.auth.account.id, ROLE_ADMIN);
38
42
  if (!is_admin) {
39
43
  throw jsonrpc_errors.invalid_params('scope_ids required for non-admin callers', {
40
44
  reason: ERROR_ACTOR_SEARCH_SCOPE_REQUIRED
@@ -83,7 +83,6 @@ export declare const AdminSessionListOutput: z.ZodObject<{
83
83
  account_id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
84
84
  created_at: z.ZodString;
85
85
  expires_at: z.ZodString;
86
- last_seen_at: z.ZodString;
87
86
  username: z.ZodString;
88
87
  }, z.core.$strict>>;
89
88
  }, z.core.$strict>;
@@ -429,7 +428,6 @@ export declare const admin_session_list_action_spec: {
429
428
  account_id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
430
429
  created_at: z.ZodString;
431
430
  expires_at: z.ZodString;
432
- last_seen_at: z.ZodString;
433
431
  username: z.ZodString;
434
432
  }, z.core.$strict>>;
435
433
  }, z.core.$strict>;