@fuzdev/fuz_app 0.58.0 → 0.60.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.
- package/dist/actions/CLAUDE.md +13 -8
- package/dist/actions/action_codegen.d.ts +1 -1
- package/dist/actions/action_codegen.js +2 -2
- package/dist/actions/action_event_helpers.d.ts +3 -3
- package/dist/actions/action_event_helpers.js +8 -8
- package/dist/actions/action_event_types.d.ts +3 -3
- package/dist/actions/action_event_types.js +3 -3
- package/dist/actions/transports_ws_auth_guard.d.ts +2 -2
- package/dist/actions/transports_ws_auth_guard.js +3 -3
- package/dist/auth/CLAUDE.md +215 -45
- package/dist/auth/account_action_specs.d.ts +9 -0
- package/dist/auth/account_action_specs.d.ts.map +1 -1
- package/dist/auth/account_action_specs.js +9 -0
- package/dist/auth/actor_lookup_action_specs.d.ts +127 -0
- package/dist/auth/actor_lookup_action_specs.d.ts.map +1 -0
- package/dist/auth/actor_lookup_action_specs.js +93 -0
- package/dist/auth/actor_lookup_actions.d.ts +19 -0
- package/dist/auth/actor_lookup_actions.d.ts.map +1 -0
- package/dist/auth/actor_lookup_actions.js +32 -0
- package/dist/auth/actor_lookup_queries.d.ts +44 -0
- package/dist/auth/actor_lookup_queries.d.ts.map +1 -0
- package/dist/auth/actor_lookup_queries.js +42 -0
- package/dist/auth/actor_search_action_specs.d.ts +166 -0
- package/dist/auth/actor_search_action_specs.d.ts.map +1 -0
- package/dist/auth/actor_search_action_specs.js +139 -0
- package/dist/auth/actor_search_actions.d.ts +31 -0
- package/dist/auth/actor_search_actions.d.ts.map +1 -0
- package/dist/auth/actor_search_actions.js +61 -0
- package/dist/auth/actor_search_queries.d.ts +75 -0
- package/dist/auth/actor_search_queries.d.ts.map +1 -0
- package/dist/auth/actor_search_queries.js +91 -0
- package/dist/auth/admin_action_specs.d.ts +35 -0
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +35 -0
- package/dist/auth/admin_actions.js +2 -2
- package/dist/auth/all_action_spec_registries.d.ts +55 -0
- package/dist/auth/all_action_spec_registries.d.ts.map +1 -0
- package/dist/auth/all_action_spec_registries.js +59 -0
- package/dist/auth/audit_emitter.d.ts +1 -1
- package/dist/auth/audit_emitter.js +2 -2
- package/dist/auth/audit_log_queries.d.ts +1 -1
- package/dist/auth/audit_log_queries.js +3 -3
- package/dist/auth/audit_log_routes.d.ts +1 -1
- package/dist/auth/audit_log_routes.js +1 -1
- package/dist/auth/audit_log_schema.d.ts +5 -5
- package/dist/auth/audit_log_schema.js +7 -7
- package/dist/auth/auth_ddl.d.ts +7 -0
- package/dist/auth/auth_ddl.d.ts.map +1 -1
- package/dist/auth/auth_ddl.js +8 -0
- package/dist/auth/credential_type_schema.d.ts +1 -1
- package/dist/auth/credential_type_schema.js +3 -3
- package/dist/auth/grant_path_schema.d.ts +1 -1
- package/dist/auth/grant_path_schema.js +3 -3
- package/dist/auth/migrations.d.ts +4 -4
- package/dist/auth/migrations.d.ts.map +1 -1
- package/dist/auth/migrations.js +7 -6
- package/dist/auth/role_grant_offer_action_specs.d.ts +17 -0
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_action_specs.js +17 -0
- package/dist/auth/role_grant_offer_actions.js +2 -2
- package/dist/auth/role_grant_offer_notifications.d.ts +2 -2
- package/dist/auth/role_grant_offer_notifications.js +2 -2
- package/dist/auth/role_grant_queries.d.ts +21 -0
- package/dist/auth/role_grant_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_queries.js +31 -0
- package/dist/auth/role_schema.d.ts +2 -2
- package/dist/auth/role_schema.js +3 -3
- package/dist/auth/self_service_role_action_specs.d.ts +8 -0
- package/dist/auth/self_service_role_action_specs.d.ts.map +1 -1
- package/dist/auth/self_service_role_action_specs.js +8 -0
- package/dist/auth/self_service_role_actions.d.ts +1 -1
- package/dist/auth/self_service_role_actions.js +2 -2
- package/dist/auth/session_cookie.d.ts +1 -1
- package/dist/auth/session_cookie.js +1 -1
- package/dist/auth/session_middleware.d.ts +1 -1
- package/dist/auth/session_middleware.js +5 -5
- package/dist/rate_limiter.d.ts +5 -5
- package/dist/rate_limiter.js +6 -6
- package/dist/realtime/sse_auth_guard.d.ts +3 -3
- package/dist/realtime/sse_auth_guard.js +4 -4
- package/dist/server/app_backend.d.ts +3 -3
- package/dist/server/app_backend.js +4 -4
- package/dist/server/app_server.d.ts +1 -1
- package/dist/server/app_server.js +10 -10
- package/dist/testing/CLAUDE.md +22 -12
- package/dist/testing/admin_integration.js +4 -4
- package/dist/testing/app_server.d.ts +1 -1
- package/dist/testing/app_server.js +2 -2
- package/dist/testing/attack_surface.d.ts +4 -4
- package/dist/testing/attack_surface.js +6 -6
- package/dist/testing/audit_completeness.js +4 -4
- package/dist/testing/data_exposure.d.ts +2 -2
- package/dist/testing/data_exposure.js +7 -7
- package/dist/testing/db.d.ts +8 -8
- package/dist/testing/db.js +11 -11
- package/dist/testing/integration.js +4 -4
- package/dist/testing/integration_helpers.d.ts +6 -6
- package/dist/testing/integration_helpers.js +7 -7
- package/dist/testing/rate_limiting.js +4 -4
- package/dist/testing/round_trip.js +2 -2
- package/dist/testing/rpc_round_trip.js +2 -2
- package/dist/testing/schema_generators.d.ts.map +1 -1
- package/dist/testing/schema_generators.js +23 -2
- package/dist/testing/sse_round_trip.js +2 -2
- package/dist/testing/surface_invariants.d.ts +4 -4
- package/dist/testing/surface_invariants.js +5 -5
- package/package.json +1 -1
package/dist/auth/CLAUDE.md
CHANGED
|
@@ -21,7 +21,7 @@ sections.
|
|
|
21
21
|
| Module | Exports |
|
|
22
22
|
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
23
23
|
| `keyring.ts` | `Keyring`, `create_keyring`, `validate_keyring`, `create_validated_keyring`, `ValidatedKeyringResult` |
|
|
24
|
-
| `session_cookie.ts` | `SessionOptions<T>`, `SessionCookieOptions`, `
|
|
24
|
+
| `session_cookie.ts` | `SessionOptions<T>`, `SessionCookieOptions`, `session_cookie_options`, `SESSION_AGE_MAX`, `SESSION_REFRESH_THRESHOLD_S`, `ParsedSession`, `ProcessSessionResult`, `parse_session`, `create_session_cookie_value`, `process_session_cookie`, `create_session_config`, `fuz_session_config` |
|
|
25
25
|
| `password.ts` | `Password`, `PasswordProvided`, `PasswordHashDeps`, `PASSWORD_LENGTH_MIN` (12, OWASP), `PASSWORD_LENGTH_MAX` (300) |
|
|
26
26
|
| `password_argon2.ts` | `hash_password`, `verify_password`, `verify_dummy`, `argon2_password_deps` |
|
|
27
27
|
| `api_token.ts` | `API_TOKEN_PREFIX` (`secret_fuz_token_`), `hash_api_token`, `generate_api_token` |
|
|
@@ -179,7 +179,7 @@ those three. Mirrors the open-registry pattern used for `RoleName` /
|
|
|
179
179
|
constant is named `_API_TOKEN` (not `_BEARER`) so wire literal and
|
|
180
180
|
the `api_token` storage table stay in lockstep.
|
|
181
181
|
- `BUILTIN_CREDENTIAL_TYPES` const tuple, `BuiltinCredentialType` Zod
|
|
182
|
-
enum, `
|
|
182
|
+
enum, `builtin_credential_type_meta` admin-UI-facing descriptions.
|
|
183
183
|
- `create_credential_type_schema(consumer_types?)`
|
|
184
184
|
→ `{CredentialType, credential_types: ReadonlyMap}`. Builtins always
|
|
185
185
|
present; consumer collisions / regex failures / duplicates throw at
|
|
@@ -197,7 +197,7 @@ granted. Four builtins (`admin`, `self_service`, `system`, `bootstrap`).
|
|
|
197
197
|
- `GRANT_PATH_ADMIN` / `_SELF_SERVICE` / `_SYSTEM` / `_BOOTSTRAP` —
|
|
198
198
|
builtin literal constants.
|
|
199
199
|
- `BUILTIN_GRANT_PATHS` const tuple, `BuiltinGrantPath` Zod enum,
|
|
200
|
-
`
|
|
200
|
+
`builtin_grant_path_meta` descriptions.
|
|
201
201
|
- `create_grant_path_schema(consumer_paths?)`
|
|
202
202
|
→ `{GrantPath, grant_paths: ReadonlyMap}`. Same construction-time
|
|
203
203
|
guards as the credential-type schema. Pass the result into
|
|
@@ -224,7 +224,7 @@ against the corresponding open registries at construction time.
|
|
|
224
224
|
- `ROLE_KEEPER = 'keeper'` — bootstrap-only via daemon token; `grant_paths: ['bootstrap']`,
|
|
225
225
|
`required_credential_types: ['daemon_token']`.
|
|
226
226
|
- `ROLE_ADMIN = 'admin'` — admin-grantable; `grant_paths: ['admin']`.
|
|
227
|
-
- `BUILTIN_ROLES`, `BuiltinRole` (Zod enum), `
|
|
227
|
+
- `BUILTIN_ROLES`, `BuiltinRole` (Zod enum), `builtin_role_specs_by_name`
|
|
228
228
|
(`ReadonlyMap<string, RoleSpec>`) — not overridable by consumers.
|
|
229
229
|
- `RoleSpec`: `{name, description?, required_credential_types?, applicable_scope_kinds?, grant_paths?}`
|
|
230
230
|
— every cross-axis field is an open-registry string array. Empty
|
|
@@ -302,7 +302,7 @@ Zod enum; `AuditOutcome` is `'success' | 'failure'`.
|
|
|
302
302
|
|
|
303
303
|
#### Metadata schemas
|
|
304
304
|
|
|
305
|
-
- `
|
|
305
|
+
- `audit_metadata_schemas` — per-type `z.looseObject`. Notable shapes:
|
|
306
306
|
- `role_grant_create` — `scope_id`, optional `role_grant_id` (failed grants
|
|
307
307
|
omit — admin-grant-path denial never produces a row), optional
|
|
308
308
|
`source_offer_id`, optional `self_service` (set by
|
|
@@ -376,7 +376,7 @@ Zod enum; `AuditOutcome` is `'success' | 'failure'`.
|
|
|
376
376
|
without validation). Pass the result to `create_app_backend({audit_log_config})`
|
|
377
377
|
— it gets captured inside the bound `AppDeps.audit` emitter, and every
|
|
378
378
|
call to `audit.emit` validates against it (defaults to
|
|
379
|
-
`
|
|
379
|
+
`builtin_audit_log_config` when absent). `query_audit_log` still accepts
|
|
380
380
|
the trailing `config` positional arg for in-transaction emit sites that
|
|
381
381
|
hold a transaction-scoped DB only. Builtin collisions and
|
|
382
382
|
`AuditEventTypeName` format failures throw at construction. The DB
|
|
@@ -396,7 +396,7 @@ Zod enum; `AuditOutcome` is `'success' | 'failure'`.
|
|
|
396
396
|
factory they track the same config, but a hand-rolled `AuditLogConfig`
|
|
397
397
|
(or a cast escape) can fire both on a single emission. Sample via
|
|
398
398
|
`get_*` getters; `reset_*` are test-only. `AUDIT_EVENT_TYPES`,
|
|
399
|
-
`
|
|
399
|
+
`audit_metadata_schemas`, `builtin_audit_log_config`, and the configs
|
|
400
400
|
returned by `create_audit_log_config` are `Object.freeze`'d to convert
|
|
401
401
|
accidental mutation (bugs, test cross-contamination, cast escapes)
|
|
402
402
|
into loud TypeErrors — not a security boundary.
|
|
@@ -470,7 +470,7 @@ exports: `RoleGrantOfferReceivedParams`, `_RetractedParams`, `_AcceptedParams`,
|
|
|
470
470
|
`_DeclinedParams`, `_SupersedeParams`, `RoleGrantRevokeParams`. Notification
|
|
471
471
|
builders: `build_role_grant_offer_received_notification(params)` etc.
|
|
472
472
|
|
|
473
|
-
`
|
|
473
|
+
`role_grant_offer_notification_specs: Array<EventSpec>` — pass to
|
|
474
474
|
`create_app_server`'s `event_specs` so the attack surface reflects them
|
|
475
475
|
and DEV-mode `create_validated_broadcaster` catches payload drift.
|
|
476
476
|
|
|
@@ -521,6 +521,31 @@ account_id = ANY(...))` so `actor.id`s never round-trip back to the
|
|
|
521
521
|
visibility). Returns `Array<AdminAccountEntryJson>`, sorted by
|
|
522
522
|
`created_at`.
|
|
523
523
|
|
|
524
|
+
### `actor_lookup_queries.ts`
|
|
525
|
+
|
|
526
|
+
- `query_actors_by_ids(deps, ids) → Array<ActorLookupRow>` — batched
|
|
527
|
+
`actor` ⨝ `account` INNER JOIN, returns
|
|
528
|
+
`{id, username, display_name}` per resolved actor. Empty input
|
|
529
|
+
fast-paths to `[]`; hard-deleted (or cascade-orphaned) rows silently
|
|
530
|
+
drop. Row shape omits `account_id` — the join is control-plane, not
|
|
531
|
+
wire-visible. Caller bounds `ids.length` (the action spec enforces
|
|
532
|
+
`ACTOR_LOOKUP_IDS_MAX`); SQL does not.
|
|
533
|
+
|
|
534
|
+
### `actor_search_queries.ts`
|
|
535
|
+
|
|
536
|
+
- `query_actor_search(deps, {query, scope_ids?, limit}) → Array<ActorLookupRow>` —
|
|
537
|
+
case-insensitive LIKE-prefix on `actor.name`, backed by the
|
|
538
|
+
`idx_actor_name_lower` functional index in `auth_ddl.ts`. Returns the
|
|
539
|
+
same `{id, username, display_name}` row shape as `query_actors_by_ids`
|
|
540
|
+
so the labels arc stays uniform. LIKE wildcards (`%`, `_`, `\`) in
|
|
541
|
+
the user-supplied `query` are escaped before substitution so the
|
|
542
|
+
prefix-only contract is enforceable. When `scope_ids` is non-empty,
|
|
543
|
+
the result is filtered to actors holding an **active** role_grant
|
|
544
|
+
(`revoked_at IS NULL AND (expires_at IS NULL OR expires_at > NOW())`)
|
|
545
|
+
on one of the supplied scopes; `DISTINCT` collapses multi-grant
|
|
546
|
+
duplicates. When `scope_ids` is empty, no role_grant join — the handler
|
|
547
|
+
enforces admin for that path.
|
|
548
|
+
|
|
524
549
|
### `role_grant_queries.ts`
|
|
525
550
|
|
|
526
551
|
- `query_create_role_grant` — idempotent; `ON CONFLICT` target and fallback
|
|
@@ -551,6 +576,12 @@ account_id = ANY(...))` so `actor.id`s never round-trip back to the
|
|
|
551
576
|
that isn't the request actor (e.g., post-mutation verification, scripts,
|
|
552
577
|
audit-time checks). For the request actor, prefer `has_scoped_role` /
|
|
553
578
|
`has_any_scoped_role` on the in-memory `auth.role_grants` snapshot.
|
|
579
|
+
- `query_account_has_global_role(deps, account_id, role)` — account-grain
|
|
580
|
+
sibling: does any actor on `account_id` hold an active **global**
|
|
581
|
+
(`scope_id IS NULL`) role_grant for `role`? For surfaces with
|
|
582
|
+
`auth: actor: 'none'` that don't load `auth.role_grants` and can't use
|
|
583
|
+
`has_scoped_role`. EXISTS over the `idx_role_grant_actor`-backed
|
|
584
|
+
subquery, stops at the first match.
|
|
554
585
|
- `query_role_grant_find_account_id_for_role(deps, role)` — joins
|
|
555
586
|
role_grant → actor → account, returns first match. Used by daemon token
|
|
556
587
|
middleware to resolve the keeper account.
|
|
@@ -701,7 +732,7 @@ run'` if the seed somehow missed (defensive — migrations always seed).
|
|
|
701
732
|
### `audit_log_queries.ts`
|
|
702
733
|
|
|
703
734
|
- `query_audit_log<T>(deps, input, config?)` — `config` defaults to
|
|
704
|
-
`
|
|
735
|
+
`builtin_audit_log_config`. Membership check runs against
|
|
705
736
|
`config.event_types`; metadata validation runs independently against
|
|
706
737
|
`config.metadata_schemas[event_type]` when present. Mismatches and
|
|
707
738
|
unknown types log + bump their counters (see schema section);
|
|
@@ -769,8 +800,8 @@ without rebuilding `AppDeps`.
|
|
|
769
800
|
|
|
770
801
|
### `migrations.ts`
|
|
771
802
|
|
|
772
|
-
- `AUTH_MIGRATION_NAMESPACE = 'fuz_auth'`, `
|
|
773
|
-
- `
|
|
803
|
+
- `AUTH_MIGRATION_NAMESPACE = 'fuz_auth'`, `auth_migration_ns` (pre-composed), `reserved_migration_namespaces: ReadonlyArray<string>` (membership list `create_app_backend` rejects on; consumer-discoverable instead of probing the runtime throw).
|
|
804
|
+
- `auth_migrations`:
|
|
774
805
|
- **v0 `full_auth_schema`** — every table + index + seed for the v1
|
|
775
806
|
identity system (account, actor, role_grant, auth_session, api_token,
|
|
776
807
|
audit_log, bootstrap_lock, invite, app_settings). All
|
|
@@ -1232,26 +1263,30 @@ acting?: ActingActor` biconditional).
|
|
|
1232
1263
|
|
|
1233
1264
|
| Spec | Side effects | Rate limit | Input | Output |
|
|
1234
1265
|
| ------------------------------------------ | ------------ | ----------- | --------------------------------------------------------- | ----------------------------- |
|
|
1235
|
-
| `admin_account_list_action_spec` | false |
|
|
1236
|
-
| `admin_session_list_action_spec` | false |
|
|
1266
|
+
| `admin_account_list_action_spec` | false | `'account'` | `{limit?, offset?}` | `{accounts, grantable_roles}` |
|
|
1267
|
+
| `admin_session_list_action_spec` | false | `'account'` | `z.void()` | `{sessions}` |
|
|
1237
1268
|
| `admin_session_revoke_all_action_spec` | true | `'account'` | `{account_id}` | `{ok, count}` |
|
|
1238
1269
|
| `admin_token_revoke_all_action_spec` | true | `'account'` | `{account_id}` | `{ok, count}` |
|
|
1239
|
-
| `audit_log_list_action_spec` | false |
|
|
1240
|
-
| `audit_log_role_grant_history_action_spec` | false |
|
|
1270
|
+
| `audit_log_list_action_spec` | false | `'account'` | `{event_type?, account_id?, limit?, offset?, since_seq?}` | `{events}` |
|
|
1271
|
+
| `audit_log_role_grant_history_action_spec` | false | `'account'` | `{limit?, offset?}` | `{events}` |
|
|
1241
1272
|
| `invite_create_action_spec` | true | `'account'` | `{email?, username?}` | `{ok, invite}` |
|
|
1242
|
-
| `invite_list_action_spec` | false |
|
|
1273
|
+
| `invite_list_action_spec` | false | `'account'` | `z.void()` | `{invites}` |
|
|
1243
1274
|
| `invite_delete_action_spec` | true | `'account'` | `{invite_id}` | `{ok}` |
|
|
1244
1275
|
| `app_settings_get_action_spec` | false | | `z.void()` | `{settings}` |
|
|
1245
1276
|
| `app_settings_update_action_spec` | true | `'account'` | `{open_signup}` | `{ok, settings}` |
|
|
1246
1277
|
|
|
1247
|
-
|
|
1248
|
-
admin's `request_context.actor.id`.
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1278
|
+
Every admin spec declares `rate_limit: 'account'` — keyed on the
|
|
1279
|
+
admin's `request_context.actor.id`. Mutations cap the
|
|
1280
|
+
`invite_create`-style account-existence oracle (`LOWER()` lookup in
|
|
1281
|
+
`query_account_by_username/_by_email`); reads cap admin-side scraping
|
|
1282
|
+
of paginated cross-account listings (`admin_account_list`,
|
|
1283
|
+
`audit_log_list`, `audit_log_role_grant_history`) and unbounded
|
|
1284
|
+
cross-account reads (`admin_session_list`, `invite_list`). The
|
|
1285
|
+
dispatcher's per-action hook (shared by HTTP RPC + WS) records every
|
|
1286
|
+
invocation regardless of outcome so successful probes consume budget.
|
|
1287
|
+
Default `default_action_account_rate_limit` is 1200/15min per actor —
|
|
1288
|
+
permissive enough for any human admin workflow, slow enough that
|
|
1289
|
+
scripted oracles surface in audit. Tighten downstream via
|
|
1255
1290
|
`AppServerOptions.action_account_rate_limiter`.
|
|
1256
1291
|
|
|
1257
1292
|
`AUDIT_LOG_LIST_LIMIT_MAX = 200` — page size clamp. `ADMIN_ACCOUNT_LIST_DEFAULT_LIMIT = 50` / `ADMIN_ACCOUNT_LIST_LIMIT_MAX = 200` — same shape on `admin_account_list`.
|
|
@@ -1282,7 +1317,7 @@ self-service `account_actions.ts` surface):
|
|
|
1282
1317
|
|
|
1283
1318
|
Closure state:
|
|
1284
1319
|
|
|
1285
|
-
- `grantable_roles` is derived once from `options.roles?.role_specs ??
|
|
1320
|
+
- `grantable_roles` is derived once from `options.roles?.role_specs ?? builtin_role_specs_by_name`
|
|
1286
1321
|
via `list_roles_with_grant_path(_, GRANT_PATH_ADMIN)` and closed over
|
|
1287
1322
|
by the `admin_account_list` handler.
|
|
1288
1323
|
- `options.app_settings` — when provided, captured by the
|
|
@@ -1344,15 +1379,25 @@ Every input row below also carries the shared `acting?: ActingActor`
|
|
|
1344
1379
|
field that the dispatcher's authorization phase reads off the raw
|
|
1345
1380
|
params (omitted from the table for brevity).
|
|
1346
1381
|
|
|
1347
|
-
| Spec | Input | Output |
|
|
1348
|
-
| -------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------- |
|
|
1349
|
-
| `role_grant_offer_create_action_spec` | `{to_account_id, to_actor_id?, role, scope_id?, message?}` | `{offer}` |
|
|
1350
|
-
| `role_grant_offer_accept_action_spec` | `{offer_id}` | `{role_grant_id, offer, superseded_offer_ids}` |
|
|
1351
|
-
| `role_grant_offer_decline_action_spec` | `{offer_id, reason?}` | `{ok}` |
|
|
1352
|
-
| `role_grant_offer_retract_action_spec` | `{offer_id}` | `{ok}` |
|
|
1353
|
-
| `role_grant_offer_list_action_spec` | `{account_id?}` | `{offers}` |
|
|
1354
|
-
| `role_grant_offer_history_action_spec` | `{account_id?, limit?, offset?}` | `{offers}` |
|
|
1355
|
-
| `role_grant_revoke_action_spec` | `{actor_id, role_grant_id, reason?}` | `{ok, revoked}` |
|
|
1382
|
+
| Spec | Rate limit | Input | Output |
|
|
1383
|
+
| -------------------------------------- | ----------- | ---------------------------------------------------------- | ---------------------------------------------- |
|
|
1384
|
+
| `role_grant_offer_create_action_spec` | `'account'` | `{to_account_id, to_actor_id?, role, scope_id?, message?}` | `{offer}` |
|
|
1385
|
+
| `role_grant_offer_accept_action_spec` | | `{offer_id}` | `{role_grant_id, offer, superseded_offer_ids}` |
|
|
1386
|
+
| `role_grant_offer_decline_action_spec` | | `{offer_id, reason?}` | `{ok}` |
|
|
1387
|
+
| `role_grant_offer_retract_action_spec` | | `{offer_id}` | `{ok}` |
|
|
1388
|
+
| `role_grant_offer_list_action_spec` | | `{account_id?}` | `{offers}` |
|
|
1389
|
+
| `role_grant_offer_history_action_spec` | | `{account_id?, limit?, offset?}` | `{offers}` |
|
|
1390
|
+
| `role_grant_revoke_action_spec` | `'account'` | `{actor_id, role_grant_id, reason?}` | `{ok, revoked}` |
|
|
1391
|
+
|
|
1392
|
+
`role_grant_offer_create` carries the same shape as `invite_create` —
|
|
1393
|
+
hostile authed callers can iterate `to_account_id` to spam offers and
|
|
1394
|
+
probe `ERROR_ACCOUNT_NOT_FOUND` /
|
|
1395
|
+
`ERROR_ROLE_GRANT_OFFER_ACTOR_ACCOUNT_MISMATCH` as account-existence
|
|
1396
|
+
oracles, so the rate cap fires on the same threat model the admin
|
|
1397
|
+
`invite_create` spec addresses upstream. `role_grant_revoke` keeps its
|
|
1398
|
+
cap because it's an admin mutation. The accept / decline / retract /
|
|
1399
|
+
list / history specs are recipient-side or caller-own-data — no
|
|
1400
|
+
enumeration vector, no rate cap.
|
|
1356
1401
|
|
|
1357
1402
|
Error reason constants (exported as `as const` literals):
|
|
1358
1403
|
|
|
@@ -1416,7 +1461,7 @@ Options:
|
|
|
1416
1461
|
|
|
1417
1462
|
- `roles?: RoleSchemaResult` — drives the admin-grant-path lookup
|
|
1418
1463
|
(`role_has_grant_path(_, role, GRANT_PATH_ADMIN)`); defaults to
|
|
1419
|
-
`
|
|
1464
|
+
`builtin_role_specs_by_name`.
|
|
1420
1465
|
- `default_ttl_ms?: number` — applied to new offers (defaults to
|
|
1421
1466
|
`ROLE_GRANT_OFFER_DEFAULT_TTL_MS`).
|
|
1422
1467
|
- `authorize?: RoleGrantOfferCreateAuthorize` — custom policy for
|
|
@@ -1481,6 +1526,14 @@ and `create_frontend_rpc_client({specs})` wiring. Self-service role
|
|
|
1481
1526
|
specs are not included (opt-in, app-specific `eligible_roles`) —
|
|
1482
1527
|
spread `all_self_service_role_action_specs` separately when needed.
|
|
1483
1528
|
|
|
1529
|
+
### `all_action_spec_registries.ts` — walker-only registry-of-registries
|
|
1530
|
+
|
|
1531
|
+
`all_fuz_auth_action_spec_registries` — walker/codegen entry for every
|
|
1532
|
+
fuz-auth action-spec bundle (`admin`, `role_grant_offer`, `account`,
|
|
1533
|
+
`self_service_role`, `actor_lookup`). Not a mounting surface; protocol
|
|
1534
|
+
specs are excluded. Iterated by `../../test/auth/action_spec_input_invariants.test.ts`
|
|
1535
|
+
and `../../test/auth/all_action_spec_registries.acting_biconditional.test.ts`.
|
|
1536
|
+
|
|
1484
1537
|
### `account_action_specs.ts` + `account_actions.ts` — seven self-service RPC actions
|
|
1485
1538
|
|
|
1486
1539
|
Counterpart to `account_routes.ts`. Cookie-lifecycle flows (`login`,
|
|
@@ -1500,15 +1553,23 @@ operations are account-scoped via `query_session_revoke_for_account` /
|
|
|
1500
1553
|
or token id returns `revoked: false` rather than revealing whether the id
|
|
1501
1554
|
exists.
|
|
1502
1555
|
|
|
1503
|
-
| Spec | Side effects | Input | Output |
|
|
1504
|
-
| ---------------------------------------- | ------------ | -------------- | ----------------------- |
|
|
1505
|
-
| `account_verify_action_spec` | false | `z.void()` | `SessionAccountJson` |
|
|
1506
|
-
| `account_session_list_action_spec` | false | `z.void()` | `{sessions}` |
|
|
1507
|
-
| `account_session_revoke_action_spec` | true | `{session_id}` | `{ok, revoked}` |
|
|
1508
|
-
| `account_session_revoke_all_action_spec` | true | `z.void()` | `{ok, count}` |
|
|
1509
|
-
| `account_token_create_action_spec` | true | `{name?}` | `{ok, token, id, name}` |
|
|
1510
|
-
| `account_token_list_action_spec` | false | `z.void()` | `{tokens}` |
|
|
1511
|
-
| `account_token_revoke_action_spec` | true | `{token_id}` | `{ok, revoked}` |
|
|
1556
|
+
| Spec | Side effects | Rate limit | Input | Output |
|
|
1557
|
+
| ---------------------------------------- | ------------ | ----------- | -------------- | ----------------------- |
|
|
1558
|
+
| `account_verify_action_spec` | false | | `z.void()` | `SessionAccountJson` |
|
|
1559
|
+
| `account_session_list_action_spec` | false | | `z.void()` | `{sessions}` |
|
|
1560
|
+
| `account_session_revoke_action_spec` | true | | `{session_id}` | `{ok, revoked}` |
|
|
1561
|
+
| `account_session_revoke_all_action_spec` | true | | `z.void()` | `{ok, count}` |
|
|
1562
|
+
| `account_token_create_action_spec` | true | `'account'` | `{name?}` | `{ok, token, id, name}` |
|
|
1563
|
+
| `account_token_list_action_spec` | false | | `z.void()` | `{tokens}` |
|
|
1564
|
+
| `account_token_revoke_action_spec` | true | | `{token_id}` | `{ok, revoked}` |
|
|
1565
|
+
|
|
1566
|
+
`account_token_create` declares `rate_limit: 'account'` to bound the
|
|
1567
|
+
_rate_ of token churn. The outstanding-token count is already capped by
|
|
1568
|
+
`max_tokens` via `query_api_token_enforce_limit`, but the per-account
|
|
1569
|
+
burn rate is not — without this cap a caller could rotate tokens in a
|
|
1570
|
+
tight loop to amplify `token_create` audit churn. The other six specs
|
|
1571
|
+
are IDOR-guarded reads/revokes of caller-own state with no enumeration
|
|
1572
|
+
vector, so rate caps are symmetry-only and skipped.
|
|
1512
1573
|
|
|
1513
1574
|
`session_id` validates as `Blake3Hash`; `token_id` validates as
|
|
1514
1575
|
`ApiTokenId` (`tok_[A-Za-z0-9_-]{12}`).
|
|
@@ -1546,6 +1607,12 @@ distinguish self-toggled role_grants from admin grants/offers. The
|
|
|
1546
1607
|
part of the documented surface rather than riding on `z.looseObject`
|
|
1547
1608
|
permissiveness.
|
|
1548
1609
|
|
|
1610
|
+
Declares `rate_limit: 'account'` — every call writes a
|
|
1611
|
+
`role_grant_create` / `role_grant_revoke` audit row regardless of
|
|
1612
|
+
`changed`, so a flapping loop could inflate the log and obscure
|
|
1613
|
+
unrelated activity. The toggle's idempotency doesn't bound the burn
|
|
1614
|
+
rate; the dispatcher's per-action hook does.
|
|
1615
|
+
|
|
1549
1616
|
Method name is static — `role` lives in the input, not the method
|
|
1550
1617
|
name. Mirrors the `role_grant_offer_create({role})` precedent. Per-role
|
|
1551
1618
|
parameterized methods would break the `satisfies RequestResponseActionSpec`
|
|
@@ -1555,7 +1622,7 @@ codegen invariant and grow the surface linearly per role.
|
|
|
1555
1622
|
|
|
1556
1623
|
- `eligible_roles?: ReadonlyArray<string>` — optional override
|
|
1557
1624
|
allowlist. When omitted, eligibility is derived from
|
|
1558
|
-
`roles.role_specs` (or `
|
|
1625
|
+
`roles.role_specs` (or `builtin_role_specs_by_name` when `roles` is
|
|
1559
1626
|
also omitted) by selecting every role whose `RoleSpec.grant_paths`
|
|
1560
1627
|
includes `'self_service'` (`GRANT_PATH_SELF_SERVICE`). Roles outside
|
|
1561
1628
|
the eligible set are rejected with `forbidden` + reason
|
|
@@ -1582,6 +1649,109 @@ Deps: `Pick<RouteFactoryDeps, 'log' | 'audit'>`.
|
|
|
1582
1649
|
`all_self_service_role_action_specs: ReadonlyArray<RequestResponseActionSpec>` —
|
|
1583
1650
|
codegen-ready registry of the single unified spec.
|
|
1584
1651
|
|
|
1652
|
+
### `actor_lookup_action_specs.ts` + `actor_lookup_actions.ts` — opt-in batched actor → label resolver
|
|
1653
|
+
|
|
1654
|
+
One static `request_response` action — `actor_lookup({ids}) → {actors:
|
|
1655
|
+
[{id, username, display_name?}]}` — powers the labels arc for surfaces
|
|
1656
|
+
that stamp an actor id (bylines, owner columns, grantor labels, audit
|
|
1657
|
+
"by" cells). One round trip resolves a batch to display strings;
|
|
1658
|
+
`ACTOR_LOOKUP_IDS_MAX = 50` cap per call.
|
|
1659
|
+
|
|
1660
|
+
**Auth + rate-limit posture.** `{account: 'required', actor: 'none'}` +
|
|
1661
|
+
`rate_limit: 'account'`. Account-grain — the caller need only be signed
|
|
1662
|
+
in; resolution skips the actor phase. The auth gate + per-account rate
|
|
1663
|
+
limit + per-call cap bound the batched username-enumeration surface that
|
|
1664
|
+
a `cell_list` ↔ `actor_lookup` pair would otherwise present. Don't loosen
|
|
1665
|
+
to public — a public-surface byline should resolve via SSR-stamped labels
|
|
1666
|
+
or per-cell embedded actor labels, not by widening this gate.
|
|
1667
|
+
|
|
1668
|
+
**Wire shape — info-leak audit.** Deliberately omitted from
|
|
1669
|
+
`ActorLookupEntryJson`:
|
|
1670
|
+
|
|
1671
|
+
- `account_id` — the actor↔account join is a control-plane detail.
|
|
1672
|
+
- `email`, password/credential fields — never queried.
|
|
1673
|
+
- `created_at` / `updated_at` — timing-oracle avoidance.
|
|
1674
|
+
- role / role_grants / session state — separation of concern.
|
|
1675
|
+
|
|
1676
|
+
`display_name` is omitted (not `null`) when `actor.name` is blank, so
|
|
1677
|
+
clients see `undefined` rather than a sentinel string. Unknown ids are
|
|
1678
|
+
silently absent — by construction this is an existence-oracle (caller
|
|
1679
|
+
diffs response ids against request ids), bounded by rate-limit, the
|
|
1680
|
+
50-id cap, actor-uuid intractability (122-bit random), and the
|
|
1681
|
+
hard-delete-cascade indistinguishability from never-existed (no
|
|
1682
|
+
tombstone oracle). Response order is unspecified — callers index by
|
|
1683
|
+
`id` when needed.
|
|
1684
|
+
|
|
1685
|
+
`create_actor_lookup_actions(deps)` — `deps:
|
|
1686
|
+
Pick<RouteFactoryDeps, 'log'>`. Pure read; no audit, no side effects.
|
|
1687
|
+
Backed by `query_actors_by_ids` (see Queries §
|
|
1688
|
+
[`actor_lookup_queries.ts`](#actor_lookup_queriests)).
|
|
1689
|
+
|
|
1690
|
+
Bundle is **not** included in `create_standard_rpc_actions` — consumers
|
|
1691
|
+
without a byline surface can skip it. Spread
|
|
1692
|
+
`all_actor_lookup_action_specs` alongside the standard bundle when the
|
|
1693
|
+
labels arc is needed.
|
|
1694
|
+
|
|
1695
|
+
### `actor_search_action_specs.ts` + `actor_search_actions.ts` — opt-in prefix-search picker
|
|
1696
|
+
|
|
1697
|
+
One static `request_response` action — `actor_search({query, scope_ids?, limit?}) → {actors: [{id, username, display_name?}]}` —
|
|
1698
|
+
powers person-target pickers. Sibling to `actor_lookup`: that resolves a
|
|
1699
|
+
batch of known ids to labels; this resolves a partial name to candidate
|
|
1700
|
+
actors. Reuses `ActorLookupEntryJson` from
|
|
1701
|
+
`./actor_lookup_action_specs.ts` so the labels arc on the consumer side
|
|
1702
|
+
stays uniform. Default limit `ACTOR_SEARCH_LIMIT_DEFAULT = 20`, hard cap
|
|
1703
|
+
`ACTOR_SEARCH_LIMIT_MAX = 50`. Query string capped at
|
|
1704
|
+
`ACTOR_SEARCH_QUERY_LENGTH_MAX = 50`.
|
|
1705
|
+
|
|
1706
|
+
**Auth + rate-limit posture.** `{account: 'required', actor: 'none'}` +
|
|
1707
|
+
`rate_limit: 'account'`. Same shape as `actor_lookup`. The handler
|
|
1708
|
+
additionally requires the caller to be admin when `scope_ids` is empty —
|
|
1709
|
+
unbounded global search is the admin-only arm; non-admin callers must
|
|
1710
|
+
always pass at least one scope_id and get filtered to actors with active
|
|
1711
|
+
role_grants on those scopes. The admin check is **account-grain** (any
|
|
1712
|
+
actor on the caller's account holds a global `admin` role_grant) via
|
|
1713
|
+
`query_account_has_global_role` — the `actor: 'none'` posture means
|
|
1714
|
+
`auth.role_grants` is empty and the in-memory `has_scoped_role` helper
|
|
1715
|
+
doesn't apply.
|
|
1716
|
+
|
|
1717
|
+
**Caller-passes-scope_ids design.** `scope_ids` is trusted as a filter,
|
|
1718
|
+
not as an authority claim — the SQL filters to actors with role_grants on
|
|
1719
|
+
those scopes regardless of whether the caller has authority over them.
|
|
1720
|
+
Consumers (e.g. visiones' `CellGrantsEditor.svelte`) pre-filter
|
|
1721
|
+
`scope_ids` against their own authority (the teacher-predicate stays in
|
|
1722
|
+
the consumer layer, not in fuz_app). This does **not** widen the
|
|
1723
|
+
scope-existence oracle: an attacker passing a random scope_id never
|
|
1724
|
+
learns the scope existed if no member happens to match the query, and
|
|
1725
|
+
even on a match the result row only carries the matching actor — never
|
|
1726
|
+
"which scope matched".
|
|
1727
|
+
|
|
1728
|
+
**Wire shape — additional info-leak posture beyond `actor_lookup`'s.**
|
|
1729
|
+
|
|
1730
|
+
- Prefix match (`LOWER(name) LIKE LOWER(query) || '%' ESCAPE '\\'`),
|
|
1731
|
+
**not** full `%query%`. LIKE wildcards in the user-supplied query are
|
|
1732
|
+
escaped at the JS layer so a `%xyz` input can't widen to full LIKE
|
|
1733
|
+
and defeat the per-call cap.
|
|
1734
|
+
- Empty result set on no-match — fail-soft like `cell_list`. No "no
|
|
1735
|
+
actor matches" error that would leak an existence boundary on the
|
|
1736
|
+
search-term axis.
|
|
1737
|
+
- Hard-deleted actors silently drop (same `account_id` cascade as
|
|
1738
|
+
`actor_lookup`).
|
|
1739
|
+
|
|
1740
|
+
Reason constant exported for failed-arm matching: `ERROR_ACTOR_SEARCH_SCOPE_REQUIRED`
|
|
1741
|
+
(`'actor_search_scope_required'`) — fired with `invalid_params` when a
|
|
1742
|
+
non-admin caller omits `scope_ids` or passes `[]`. Surfaced on
|
|
1743
|
+
`spec.error_reasons` so codegen + form-state matching can read it
|
|
1744
|
+
declaratively.
|
|
1745
|
+
|
|
1746
|
+
`create_actor_search_actions(deps)` — `deps:
|
|
1747
|
+
Pick<RouteFactoryDeps, 'log'>`. Pure read; no audit, no side effects.
|
|
1748
|
+
Backed by `query_actor_search` (see Queries §`actor_search_queries.ts`).
|
|
1749
|
+
|
|
1750
|
+
Bundle is **not** included in `create_standard_rpc_actions` — consumers
|
|
1751
|
+
without a person-target picker can skip it. Spread
|
|
1752
|
+
`all_actor_search_action_specs` alongside the standard bundle when the
|
|
1753
|
+
picker is needed.
|
|
1754
|
+
|
|
1585
1755
|
## Cleanup
|
|
1586
1756
|
|
|
1587
1757
|
`cleanup.ts` — periodic auth maintenance:
|
|
@@ -164,6 +164,14 @@ export declare const account_session_revoke_all_action_spec: {
|
|
|
164
164
|
async: true;
|
|
165
165
|
description: string;
|
|
166
166
|
};
|
|
167
|
+
/**
|
|
168
|
+
* `rate_limit: 'account'` bounds the burn rate of API-token creates. The
|
|
169
|
+
* outstanding-token count is already capped by `max_tokens` (via
|
|
170
|
+
* `query_api_token_enforce_limit`), but the per-account *rate* of churn
|
|
171
|
+
* is not — without this cap, a caller could rotate tokens in a tight
|
|
172
|
+
* loop to amplify `token_create` audit churn or attempt to provoke
|
|
173
|
+
* downstream rate-limit hot spots.
|
|
174
|
+
*/
|
|
167
175
|
export declare const account_token_create_action_spec: {
|
|
168
176
|
method: string;
|
|
169
177
|
kind: "request_response";
|
|
@@ -184,6 +192,7 @@ export declare const account_token_create_action_spec: {
|
|
|
184
192
|
}, z.core.$strict>;
|
|
185
193
|
async: true;
|
|
186
194
|
description: string;
|
|
195
|
+
rate_limit: "account";
|
|
187
196
|
};
|
|
188
197
|
export declare const account_token_list_action_spec: {
|
|
189
198
|
method: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_action_specs.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAGtB,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,2BAA2B,CAAC;AAMzE,6EAA6E;AAC7E,eAAO,MAAM,WAAW,WAAW,CAAC;AACpC,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD,uDAAuD;AACvD,eAAO,MAAM,gBAAgB,WAAW,CAAC;AACzC,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE,yCAAyC;AACzC,eAAO,MAAM,iBAAiB;;;;;;;;kBAE5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,2EAA2E;AAC3E,eAAO,MAAM,kBAAkB;;kBAE7B,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,iFAAiF;AACjF,eAAO,MAAM,mBAAmB;;;kBAG9B,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEtE,6DAA6D;AAC7D,eAAO,MAAM,qBAAqB,WAAW,CAAC;AAC9C,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE1E,+CAA+C;AAC/C,eAAO,MAAM,sBAAsB;;;kBAGjC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE5E,wCAAwC;AACxC,eAAO,MAAM,gBAAgB;;mBAOf,CAAC;AACf,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE,2EAA2E;AAC3E,eAAO,MAAM,iBAAiB;;;;;kBAK5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,qDAAqD;AACrD,eAAO,MAAM,cAAc,WAAW,CAAC;AACvC,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,4DAA4D;AAC5D,eAAO,MAAM,eAAe;;;;;;;;;;kBAE1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,wCAAwC;AACxC,eAAO,MAAM,gBAAgB;;kBAE3B,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE,+EAA+E;AAC/E,eAAO,MAAM,iBAAiB;;;kBAG5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAIlE,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;CAUF,CAAC;AAEtC,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;;;;;;;;CAUR,CAAC;AAEtC,eAAO,MAAM,kCAAkC;;;;;;;;;;;;;;;;;;CAUV,CAAC;AAEtC,eAAO,MAAM,sCAAsC;;;;;;;;;;;;;;;;CAUd,CAAC;AAEtC,eAAO,MAAM,gCAAgC
|
|
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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"}
|
|
@@ -112,6 +112,14 @@ export const account_session_revoke_all_action_spec = {
|
|
|
112
112
|
async: true,
|
|
113
113
|
description: 'Revoke every auth session for the current account.',
|
|
114
114
|
};
|
|
115
|
+
/**
|
|
116
|
+
* `rate_limit: 'account'` bounds the burn rate of API-token creates. The
|
|
117
|
+
* outstanding-token count is already capped by `max_tokens` (via
|
|
118
|
+
* `query_api_token_enforce_limit`), but the per-account *rate* of churn
|
|
119
|
+
* is not — without this cap, a caller could rotate tokens in a tight
|
|
120
|
+
* loop to amplify `token_create` audit churn or attempt to provoke
|
|
121
|
+
* downstream rate-limit hot spots.
|
|
122
|
+
*/
|
|
115
123
|
export const account_token_create_action_spec = {
|
|
116
124
|
method: 'account_token_create',
|
|
117
125
|
kind: 'request_response',
|
|
@@ -122,6 +130,7 @@ export const account_token_create_action_spec = {
|
|
|
122
130
|
output: TokenCreateOutput,
|
|
123
131
|
async: true,
|
|
124
132
|
description: 'Create an API token for the current account. Raw token is returned once.',
|
|
133
|
+
rate_limit: 'account',
|
|
125
134
|
};
|
|
126
135
|
export const account_token_list_action_spec = {
|
|
127
136
|
method: 'account_token_list',
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `actor_lookup` RPC spec — authenticated batched id → username/display_name
|
|
3
|
+
* resolver, keyed by actor id.
|
|
4
|
+
*
|
|
5
|
+
* Powers the labels arc for surfaces that stamp an actor id (bylines,
|
|
6
|
+
* owner columns, grantor labels, audit-log "by" cells). One round trip
|
|
7
|
+
* resolves an array of ids to display strings.
|
|
8
|
+
*
|
|
9
|
+
* ## Auth + rate-limit posture
|
|
10
|
+
*
|
|
11
|
+
* `{account: 'required', actor: 'none'}` + `rate_limit: 'account'`.
|
|
12
|
+
* Account-grain — only that the caller is signed in matters, not which
|
|
13
|
+
* actor is calling, so resolution skips the actor phase. The auth gate
|
|
14
|
+
* + per-account rate limit (default 1200/15min) + the
|
|
15
|
+
* {@link ACTOR_LOOKUP_IDS_MAX | per-call cap} bound the batched
|
|
16
|
+
* username-enumeration surface that the `cell_list` ↔ `actor_lookup`
|
|
17
|
+
* pair would otherwise present.
|
|
18
|
+
*
|
|
19
|
+
* If a public-surface byline ever lands (e.g. an unauthenticated
|
|
20
|
+
* gallery), it should resolve via a separate public-safe mechanism
|
|
21
|
+
* (SSR-stamped labels or a per-cell embedded actor label), **not** by
|
|
22
|
+
* loosening this gate.
|
|
23
|
+
*
|
|
24
|
+
* ## Wire shape — info-leak audit
|
|
25
|
+
*
|
|
26
|
+
* Output: `{actors: [{id, username, display_name?}]}`. Deliberately
|
|
27
|
+
* omitted:
|
|
28
|
+
*
|
|
29
|
+
* - `account_id` — the actor↔account join is a control-plane detail
|
|
30
|
+
* - `email`, password/credential fields — never queried
|
|
31
|
+
* - `created_at` / `updated_at` — timing-oracle avoidance
|
|
32
|
+
* - role / role_grants / session state — separation of concern
|
|
33
|
+
*
|
|
34
|
+
* `display_name` is omitted (not `null`) when `actor.name` is blank, so
|
|
35
|
+
* clients see `undefined` rather than a sentinel string. Unknown ids are
|
|
36
|
+
* silently absent from the response — by construction this is an
|
|
37
|
+
* existence-oracle (the caller can diff response ids against request
|
|
38
|
+
* ids), bounded by:
|
|
39
|
+
*
|
|
40
|
+
* 1. rate-limit (per-account, see above),
|
|
41
|
+
* 2. {@link ACTOR_LOOKUP_IDS_MAX} cap per call,
|
|
42
|
+
* 3. actor-uuid intractability (122-bit random),
|
|
43
|
+
* 4. hard-deleted actors are indistinguishable from never-existed (no
|
|
44
|
+
* tombstone oracle — see `actor_lookup_queries.ts`).
|
|
45
|
+
*
|
|
46
|
+
* Response order is unspecified — callers index by `id` when needed.
|
|
47
|
+
*
|
|
48
|
+
* @module
|
|
49
|
+
*/
|
|
50
|
+
import { z } from 'zod';
|
|
51
|
+
/**
|
|
52
|
+
* Hard cap on the number of ids resolvable in one call. Bounds the
|
|
53
|
+
* batched username-enumeration surface.
|
|
54
|
+
*/
|
|
55
|
+
export declare const ACTOR_LOOKUP_IDS_MAX = 50;
|
|
56
|
+
/** One resolved actor row. `display_name` omitted when blank. */
|
|
57
|
+
export declare const ActorLookupEntryJson: z.ZodObject<{
|
|
58
|
+
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
59
|
+
username: z.ZodString;
|
|
60
|
+
display_name: z.ZodOptional<z.ZodString>;
|
|
61
|
+
}, z.core.$strict>;
|
|
62
|
+
export type ActorLookupEntryJson = z.infer<typeof ActorLookupEntryJson>;
|
|
63
|
+
export declare const ActorLookupInput: z.ZodObject<{
|
|
64
|
+
ids: z.ZodArray<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
|
|
65
|
+
}, z.core.$strict>;
|
|
66
|
+
export type ActorLookupInput = z.infer<typeof ActorLookupInput>;
|
|
67
|
+
export declare const ActorLookupOutput: z.ZodObject<{
|
|
68
|
+
actors: z.ZodArray<z.ZodObject<{
|
|
69
|
+
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
70
|
+
username: z.ZodString;
|
|
71
|
+
display_name: z.ZodOptional<z.ZodString>;
|
|
72
|
+
}, z.core.$strict>>;
|
|
73
|
+
}, z.core.$strict>;
|
|
74
|
+
export type ActorLookupOutput = z.infer<typeof ActorLookupOutput>;
|
|
75
|
+
export declare const actor_lookup_action_spec: {
|
|
76
|
+
method: string;
|
|
77
|
+
kind: "request_response";
|
|
78
|
+
initiator: "frontend";
|
|
79
|
+
auth: {
|
|
80
|
+
account: "required";
|
|
81
|
+
actor: "none";
|
|
82
|
+
};
|
|
83
|
+
side_effects: false;
|
|
84
|
+
input: z.ZodObject<{
|
|
85
|
+
ids: z.ZodArray<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
|
|
86
|
+
}, z.core.$strict>;
|
|
87
|
+
output: z.ZodObject<{
|
|
88
|
+
actors: z.ZodArray<z.ZodObject<{
|
|
89
|
+
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
90
|
+
username: z.ZodString;
|
|
91
|
+
display_name: z.ZodOptional<z.ZodString>;
|
|
92
|
+
}, z.core.$strict>>;
|
|
93
|
+
}, z.core.$strict>;
|
|
94
|
+
async: true;
|
|
95
|
+
rate_limit: "account";
|
|
96
|
+
description: string;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* All actor_lookup action specs — independent opt-in registry. Consumers
|
|
100
|
+
* spread alongside `all_standard_action_specs` if they want the labels
|
|
101
|
+
* arc; not folded into the standard bundle because consumers without a
|
|
102
|
+
* byline surface can skip it.
|
|
103
|
+
*/
|
|
104
|
+
export declare const all_actor_lookup_action_specs: readonly [{
|
|
105
|
+
method: string;
|
|
106
|
+
kind: "request_response";
|
|
107
|
+
initiator: "frontend";
|
|
108
|
+
auth: {
|
|
109
|
+
account: "required";
|
|
110
|
+
actor: "none";
|
|
111
|
+
};
|
|
112
|
+
side_effects: false;
|
|
113
|
+
input: z.ZodObject<{
|
|
114
|
+
ids: z.ZodArray<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
|
|
115
|
+
}, z.core.$strict>;
|
|
116
|
+
output: z.ZodObject<{
|
|
117
|
+
actors: z.ZodArray<z.ZodObject<{
|
|
118
|
+
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
119
|
+
username: z.ZodString;
|
|
120
|
+
display_name: z.ZodOptional<z.ZodString>;
|
|
121
|
+
}, z.core.$strict>>;
|
|
122
|
+
}, z.core.$strict>;
|
|
123
|
+
async: true;
|
|
124
|
+
rate_limit: "account";
|
|
125
|
+
description: string;
|
|
126
|
+
}];
|
|
127
|
+
//# sourceMappingURL=actor_lookup_action_specs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actor_lookup_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/actor_lookup_action_specs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAKtB;;;GAGG;AACH,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,iEAAiE;AACjE,eAAO,MAAM,oBAAoB;;;;kBAI/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE,eAAO,MAAM,gBAAgB;;kBAQ3B,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE,eAAO,MAAM,iBAAiB;;;;;;kBAE5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;CAWA,CAAC;AAEtC;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;EAAsC,CAAC"}
|