@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
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prefix-based actor search.
|
|
3
|
+
*
|
|
4
|
+
* Sibling to `actor_lookup_queries.ts` — that resolves a batch of ids to
|
|
5
|
+
* labels; this resolves a partial name to candidate actors. Same row
|
|
6
|
+
* shape (`ActorLookupRow`) so the labels arc on the consumer side stays
|
|
7
|
+
* uniform.
|
|
8
|
+
*
|
|
9
|
+
* Case-insensitive LIKE-prefix on `actor.name` backed by the
|
|
10
|
+
* `idx_actor_name_lower` functional index. LIKE wildcards (`%`, `_`,
|
|
11
|
+
* `\`) in the query string are escaped at the JS layer so the
|
|
12
|
+
* prefix-only contract is enforceable — an unescaped `%xyz` would
|
|
13
|
+
* widen the surface to full-LIKE and defeat the per-call cap as a
|
|
14
|
+
* binding bound.
|
|
15
|
+
*
|
|
16
|
+
* ## Auth filtering — `scope_ids`
|
|
17
|
+
*
|
|
18
|
+
* When `scope_ids` is non-empty, the result is filtered to actors
|
|
19
|
+
* holding an **active** role_grant on one of the supplied scopes. Active
|
|
20
|
+
* means `revoked_at IS NULL AND (expires_at IS NULL OR expires_at > NOW())`.
|
|
21
|
+
* Stale (revoked / expired) role_grants do **not** confer membership for
|
|
22
|
+
* search-visibility purposes — otherwise a removed student would
|
|
23
|
+
* remain visible to teachers indefinitely.
|
|
24
|
+
*
|
|
25
|
+
* The `DISTINCT` on `actor.id` collapses the case where an actor holds
|
|
26
|
+
* multiple matching role_grants in the supplied scope set into one row.
|
|
27
|
+
*
|
|
28
|
+
* When `scope_ids` is omitted (admin-only global path; the handler
|
|
29
|
+
* gates), no role_grant join — every actor with a matching prefix is
|
|
30
|
+
* returned.
|
|
31
|
+
*
|
|
32
|
+
* ## Info-leak posture (see `actor_search_action_specs.ts` § audit)
|
|
33
|
+
*
|
|
34
|
+
* - Row shape **omits** `account_id` — the join is control-plane, not
|
|
35
|
+
* wire-visible. Identical to `actor_lookup_queries.ts`.
|
|
36
|
+
* - Hard-deleted actors (cascade-orphaned via `actor.account_id` FK)
|
|
37
|
+
* drop out silently.
|
|
38
|
+
* - No `created_at` / `updated_at` projected (timing-oracle avoidance).
|
|
39
|
+
* - Scope-membership uses ANY on the supplied `scope_ids` array but
|
|
40
|
+
* never surfaces "which scope matched" — the result row carries only
|
|
41
|
+
* the actor's wire shape. An attacker passing a random scope_id
|
|
42
|
+
* learns at most "this scope has at least one member matching X"
|
|
43
|
+
* if a match exists, indistinguishable from a no-match search; the
|
|
44
|
+
* caller-passes-scope_ids design (handler trusts the array as a
|
|
45
|
+
* filter, not as authority) means the attacker had to obtain the
|
|
46
|
+
* scope_id from somewhere else first.
|
|
47
|
+
*
|
|
48
|
+
* Caller bounds `limit` (the action-spec layer enforces
|
|
49
|
+
* `ACTOR_SEARCH_LIMIT_MAX`); SQL clamps to that cap on the call site
|
|
50
|
+
* before reaching this query.
|
|
51
|
+
*
|
|
52
|
+
* @module
|
|
53
|
+
*/
|
|
54
|
+
import type { Uuid } from '@fuzdev/fuz_util/id.js';
|
|
55
|
+
import type { QueryDeps } from '../db/query_deps.js';
|
|
56
|
+
import type { ActorLookupRow } from './actor_lookup_queries.js';
|
|
57
|
+
/** Inputs for `query_actor_search`. */
|
|
58
|
+
export interface ActorSearchQueryInput {
|
|
59
|
+
/** Case-insensitive prefix string. Must be non-empty (action layer enforces `min(1)`). */
|
|
60
|
+
query: string;
|
|
61
|
+
/**
|
|
62
|
+
* When non-empty, restrict to actors holding an active role_grant on one
|
|
63
|
+
* of these scope ids. When empty / omitted, no scope filter is applied —
|
|
64
|
+
* the handler is responsible for the admin gate.
|
|
65
|
+
*/
|
|
66
|
+
scope_ids?: ReadonlyArray<Uuid>;
|
|
67
|
+
/** Maximum rows to return. The handler clamps to `ACTOR_SEARCH_LIMIT_MAX`. */
|
|
68
|
+
limit: number;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Search actors by case-insensitive prefix on `actor.name`, optionally
|
|
72
|
+
* filtered to those holding an active role_grant on one of `scope_ids`.
|
|
73
|
+
*/
|
|
74
|
+
export declare const query_actor_search: (deps: QueryDeps, input: ActorSearchQueryInput) => Promise<Array<ActorLookupRow>>;
|
|
75
|
+
//# sourceMappingURL=actor_search_queries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actor_search_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/actor_search_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AAEH,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAEjD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAE9D,uCAAuC;AACvC,MAAM,WAAW,qBAAqB;IACrC,0FAA0F;IAC1F,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,SAAS,CAAC,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;IAChC,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC;CACd;AASD;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAC9B,MAAM,SAAS,EACf,OAAO,qBAAqB,KAC1B,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAmC/B,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prefix-based actor search.
|
|
3
|
+
*
|
|
4
|
+
* Sibling to `actor_lookup_queries.ts` — that resolves a batch of ids to
|
|
5
|
+
* labels; this resolves a partial name to candidate actors. Same row
|
|
6
|
+
* shape (`ActorLookupRow`) so the labels arc on the consumer side stays
|
|
7
|
+
* uniform.
|
|
8
|
+
*
|
|
9
|
+
* Case-insensitive LIKE-prefix on `actor.name` backed by the
|
|
10
|
+
* `idx_actor_name_lower` functional index. LIKE wildcards (`%`, `_`,
|
|
11
|
+
* `\`) in the query string are escaped at the JS layer so the
|
|
12
|
+
* prefix-only contract is enforceable — an unescaped `%xyz` would
|
|
13
|
+
* widen the surface to full-LIKE and defeat the per-call cap as a
|
|
14
|
+
* binding bound.
|
|
15
|
+
*
|
|
16
|
+
* ## Auth filtering — `scope_ids`
|
|
17
|
+
*
|
|
18
|
+
* When `scope_ids` is non-empty, the result is filtered to actors
|
|
19
|
+
* holding an **active** role_grant on one of the supplied scopes. Active
|
|
20
|
+
* means `revoked_at IS NULL AND (expires_at IS NULL OR expires_at > NOW())`.
|
|
21
|
+
* Stale (revoked / expired) role_grants do **not** confer membership for
|
|
22
|
+
* search-visibility purposes — otherwise a removed student would
|
|
23
|
+
* remain visible to teachers indefinitely.
|
|
24
|
+
*
|
|
25
|
+
* The `DISTINCT` on `actor.id` collapses the case where an actor holds
|
|
26
|
+
* multiple matching role_grants in the supplied scope set into one row.
|
|
27
|
+
*
|
|
28
|
+
* When `scope_ids` is omitted (admin-only global path; the handler
|
|
29
|
+
* gates), no role_grant join — every actor with a matching prefix is
|
|
30
|
+
* returned.
|
|
31
|
+
*
|
|
32
|
+
* ## Info-leak posture (see `actor_search_action_specs.ts` § audit)
|
|
33
|
+
*
|
|
34
|
+
* - Row shape **omits** `account_id` — the join is control-plane, not
|
|
35
|
+
* wire-visible. Identical to `actor_lookup_queries.ts`.
|
|
36
|
+
* - Hard-deleted actors (cascade-orphaned via `actor.account_id` FK)
|
|
37
|
+
* drop out silently.
|
|
38
|
+
* - No `created_at` / `updated_at` projected (timing-oracle avoidance).
|
|
39
|
+
* - Scope-membership uses ANY on the supplied `scope_ids` array but
|
|
40
|
+
* never surfaces "which scope matched" — the result row carries only
|
|
41
|
+
* the actor's wire shape. An attacker passing a random scope_id
|
|
42
|
+
* learns at most "this scope has at least one member matching X"
|
|
43
|
+
* if a match exists, indistinguishable from a no-match search; the
|
|
44
|
+
* caller-passes-scope_ids design (handler trusts the array as a
|
|
45
|
+
* filter, not as authority) means the attacker had to obtain the
|
|
46
|
+
* scope_id from somewhere else first.
|
|
47
|
+
*
|
|
48
|
+
* Caller bounds `limit` (the action-spec layer enforces
|
|
49
|
+
* `ACTOR_SEARCH_LIMIT_MAX`); SQL clamps to that cap on the call site
|
|
50
|
+
* before reaching this query.
|
|
51
|
+
*
|
|
52
|
+
* @module
|
|
53
|
+
*/
|
|
54
|
+
/**
|
|
55
|
+
* Escape LIKE wildcards in a user-supplied query string so the SQL
|
|
56
|
+
* prefix-match cannot be widened by user input. The `\` escape char is
|
|
57
|
+
* declared on the LIKE expression via `ESCAPE '\'`.
|
|
58
|
+
*/
|
|
59
|
+
const escape_like_pattern = (s) => s.replace(/[\\%_]/g, '\\$&');
|
|
60
|
+
/**
|
|
61
|
+
* Search actors by case-insensitive prefix on `actor.name`, optionally
|
|
62
|
+
* filtered to those holding an active role_grant on one of `scope_ids`.
|
|
63
|
+
*/
|
|
64
|
+
export const query_actor_search = async (deps, input) => {
|
|
65
|
+
const escaped_prefix = escape_like_pattern(input.query.toLowerCase());
|
|
66
|
+
const scope_ids = input.scope_ids ?? [];
|
|
67
|
+
if (scope_ids.length === 0) {
|
|
68
|
+
// Admin-global path — no role_grant join. Handler enforces admin.
|
|
69
|
+
return deps.db.query(`SELECT act.id, a.username, act.name AS display_name
|
|
70
|
+
FROM actor act
|
|
71
|
+
JOIN account a ON a.id = act.account_id
|
|
72
|
+
WHERE LOWER(act.name) LIKE $1 || '%' ESCAPE '\\'
|
|
73
|
+
ORDER BY display_name, id
|
|
74
|
+
LIMIT $2`, [escaped_prefix, input.limit]);
|
|
75
|
+
}
|
|
76
|
+
// Scoped path — filter to actors with an active role_grant on any of
|
|
77
|
+
// `scope_ids`. DISTINCT collapses actors holding multiple matching
|
|
78
|
+
// role_grants in the supplied scope set into one row. ORDER BY must
|
|
79
|
+
// reference SELECT-listed columns under DISTINCT, so we sort by the
|
|
80
|
+
// `display_name` alias rather than `LOWER(act.name)`.
|
|
81
|
+
return deps.db.query(`SELECT DISTINCT act.id, a.username, act.name AS display_name
|
|
82
|
+
FROM actor act
|
|
83
|
+
JOIN account a ON a.id = act.account_id
|
|
84
|
+
JOIN role_grant rg ON rg.actor_id = act.id
|
|
85
|
+
AND rg.scope_id = ANY($2)
|
|
86
|
+
AND rg.revoked_at IS NULL
|
|
87
|
+
AND (rg.expires_at IS NULL OR rg.expires_at > NOW())
|
|
88
|
+
WHERE LOWER(act.name) LIKE $1 || '%' ESCAPE '\\'
|
|
89
|
+
ORDER BY display_name, id
|
|
90
|
+
LIMIT $3`, [escaped_prefix, scope_ids, input.limit]);
|
|
91
|
+
};
|
|
@@ -264,6 +264,12 @@ export declare const AppSettingsUpdateOutput: z.ZodObject<{
|
|
|
264
264
|
}, z.core.$strict>;
|
|
265
265
|
}, z.core.$strict>;
|
|
266
266
|
export type AppSettingsUpdateOutput = z.infer<typeof AppSettingsUpdateOutput>;
|
|
267
|
+
/**
|
|
268
|
+
* `rate_limit: 'account'` bounds admin-side scraping of the account table
|
|
269
|
+
* via `(limit, offset)` walking — admin trust is not a substitute for a
|
|
270
|
+
* read-rate cap when the listing is paginated and cross-account (yields
|
|
271
|
+
* every account + actor + active role_grant in the system).
|
|
272
|
+
*/
|
|
267
273
|
export declare const admin_account_list_action_spec: {
|
|
268
274
|
method: string;
|
|
269
275
|
kind: "request_response";
|
|
@@ -318,7 +324,13 @@ export declare const admin_account_list_action_spec: {
|
|
|
318
324
|
}, z.core.$strict>;
|
|
319
325
|
async: true;
|
|
320
326
|
description: string;
|
|
327
|
+
rate_limit: "account";
|
|
321
328
|
};
|
|
329
|
+
/**
|
|
330
|
+
* `rate_limit: 'account'` bounds cross-account scraping of every active
|
|
331
|
+
* `auth_session` row — no pagination, but the read is unbounded across
|
|
332
|
+
* accounts and reveals one row per live cookie globally.
|
|
333
|
+
*/
|
|
322
334
|
export declare const admin_session_list_action_spec: {
|
|
323
335
|
method: string;
|
|
324
336
|
kind: "request_response";
|
|
@@ -344,6 +356,7 @@ export declare const admin_session_list_action_spec: {
|
|
|
344
356
|
}, z.core.$strict>;
|
|
345
357
|
async: true;
|
|
346
358
|
description: string;
|
|
359
|
+
rate_limit: "account";
|
|
347
360
|
};
|
|
348
361
|
export declare const admin_session_revoke_all_action_spec: {
|
|
349
362
|
method: string;
|
|
@@ -389,6 +402,14 @@ export declare const admin_token_revoke_all_action_spec: {
|
|
|
389
402
|
description: string;
|
|
390
403
|
rate_limit: "account";
|
|
391
404
|
};
|
|
405
|
+
/**
|
|
406
|
+
* `rate_limit: 'account'` bounds admin-side enumeration of the entire
|
|
407
|
+
* audit log via `(limit, offset)` walking — same shape as
|
|
408
|
+
* `admin_account_list_action_spec`. The listing carries cross-account
|
|
409
|
+
* forensic detail (target ids, IPs, metadata), so the read-rate cap is
|
|
410
|
+
* the only check that distinguishes a human reviewer from a scraping
|
|
411
|
+
* script.
|
|
412
|
+
*/
|
|
392
413
|
export declare const audit_log_list_action_spec: {
|
|
393
414
|
method: string;
|
|
394
415
|
kind: "request_response";
|
|
@@ -433,7 +454,13 @@ export declare const audit_log_list_action_spec: {
|
|
|
433
454
|
}, z.core.$strict>;
|
|
434
455
|
async: true;
|
|
435
456
|
description: string;
|
|
457
|
+
rate_limit: "account";
|
|
436
458
|
};
|
|
459
|
+
/**
|
|
460
|
+
* `rate_limit: 'account'` bounds admin-side enumeration of the role_grant
|
|
461
|
+
* history via `(limit, offset)` walking — same shape as `audit_log_list`,
|
|
462
|
+
* narrower projection but identical scraping vector.
|
|
463
|
+
*/
|
|
437
464
|
export declare const audit_log_role_grant_history_action_spec: {
|
|
438
465
|
method: string;
|
|
439
466
|
kind: "request_response";
|
|
@@ -471,6 +498,7 @@ export declare const audit_log_role_grant_history_action_spec: {
|
|
|
471
498
|
}, z.core.$strict>;
|
|
472
499
|
async: true;
|
|
473
500
|
description: string;
|
|
501
|
+
rate_limit: "account";
|
|
474
502
|
};
|
|
475
503
|
export declare const invite_create_action_spec: {
|
|
476
504
|
method: string;
|
|
@@ -503,6 +531,12 @@ export declare const invite_create_action_spec: {
|
|
|
503
531
|
description: string;
|
|
504
532
|
rate_limit: "account";
|
|
505
533
|
};
|
|
534
|
+
/**
|
|
535
|
+
* `rate_limit: 'account'` bounds admin-side scraping of the invite table —
|
|
536
|
+
* bounded by table size, but every row carries email + username +
|
|
537
|
+
* creator/claimer identifiers worth defense-in-depth against an admin
|
|
538
|
+
* mutation oracle running scripted reads alongside `invite_create`.
|
|
539
|
+
*/
|
|
506
540
|
export declare const invite_list_action_spec: {
|
|
507
541
|
method: string;
|
|
508
542
|
kind: "request_response";
|
|
@@ -531,6 +565,7 @@ export declare const invite_list_action_spec: {
|
|
|
531
565
|
}, z.core.$strict>;
|
|
532
566
|
async: true;
|
|
533
567
|
description: string;
|
|
568
|
+
rate_limit: "account";
|
|
534
569
|
};
|
|
535
570
|
export declare const invite_delete_action_spec: {
|
|
536
571
|
method: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/admin_action_specs.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"admin_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/admin_action_specs.ts"],"names":[],"mappings":"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"}
|
|
@@ -193,6 +193,12 @@ export const AppSettingsUpdateOutput = z.strictObject({
|
|
|
193
193
|
settings: AppSettingsWithUsernameJson,
|
|
194
194
|
});
|
|
195
195
|
// -- Action specs -----------------------------------------------------------
|
|
196
|
+
/**
|
|
197
|
+
* `rate_limit: 'account'` bounds admin-side scraping of the account table
|
|
198
|
+
* via `(limit, offset)` walking — admin trust is not a substitute for a
|
|
199
|
+
* read-rate cap when the listing is paginated and cross-account (yields
|
|
200
|
+
* every account + actor + active role_grant in the system).
|
|
201
|
+
*/
|
|
196
202
|
export const admin_account_list_action_spec = {
|
|
197
203
|
method: 'admin_account_list',
|
|
198
204
|
kind: 'request_response',
|
|
@@ -203,7 +209,13 @@ export const admin_account_list_action_spec = {
|
|
|
203
209
|
output: AdminAccountListOutput,
|
|
204
210
|
async: true,
|
|
205
211
|
description: 'List all accounts with their actors, role_grants, and pending offers. Admin-only.',
|
|
212
|
+
rate_limit: 'account',
|
|
206
213
|
};
|
|
214
|
+
/**
|
|
215
|
+
* `rate_limit: 'account'` bounds cross-account scraping of every active
|
|
216
|
+
* `auth_session` row — no pagination, but the read is unbounded across
|
|
217
|
+
* accounts and reveals one row per live cookie globally.
|
|
218
|
+
*/
|
|
207
219
|
export const admin_session_list_action_spec = {
|
|
208
220
|
method: 'admin_session_list',
|
|
209
221
|
kind: 'request_response',
|
|
@@ -214,6 +226,7 @@ export const admin_session_list_action_spec = {
|
|
|
214
226
|
output: AdminSessionListOutput,
|
|
215
227
|
async: true,
|
|
216
228
|
description: 'List every active auth session across all accounts. Admin-only.',
|
|
229
|
+
rate_limit: 'account',
|
|
217
230
|
};
|
|
218
231
|
export const admin_session_revoke_all_action_spec = {
|
|
219
232
|
method: 'admin_session_revoke_all',
|
|
@@ -239,6 +252,14 @@ export const admin_token_revoke_all_action_spec = {
|
|
|
239
252
|
description: 'Revoke all API tokens for an account. Admin-only.',
|
|
240
253
|
rate_limit: 'account',
|
|
241
254
|
};
|
|
255
|
+
/**
|
|
256
|
+
* `rate_limit: 'account'` bounds admin-side enumeration of the entire
|
|
257
|
+
* audit log via `(limit, offset)` walking — same shape as
|
|
258
|
+
* `admin_account_list_action_spec`. The listing carries cross-account
|
|
259
|
+
* forensic detail (target ids, IPs, metadata), so the read-rate cap is
|
|
260
|
+
* the only check that distinguishes a human reviewer from a scraping
|
|
261
|
+
* script.
|
|
262
|
+
*/
|
|
242
263
|
export const audit_log_list_action_spec = {
|
|
243
264
|
method: 'audit_log_list',
|
|
244
265
|
kind: 'request_response',
|
|
@@ -249,7 +270,13 @@ export const audit_log_list_action_spec = {
|
|
|
249
270
|
output: AuditLogListOutput,
|
|
250
271
|
async: true,
|
|
251
272
|
description: 'List audit log events with optional filters. Admin-only.',
|
|
273
|
+
rate_limit: 'account',
|
|
252
274
|
};
|
|
275
|
+
/**
|
|
276
|
+
* `rate_limit: 'account'` bounds admin-side enumeration of the role_grant
|
|
277
|
+
* history via `(limit, offset)` walking — same shape as `audit_log_list`,
|
|
278
|
+
* narrower projection but identical scraping vector.
|
|
279
|
+
*/
|
|
253
280
|
export const audit_log_role_grant_history_action_spec = {
|
|
254
281
|
method: 'audit_log_role_grant_history',
|
|
255
282
|
kind: 'request_response',
|
|
@@ -260,6 +287,7 @@ export const audit_log_role_grant_history_action_spec = {
|
|
|
260
287
|
output: AuditLogRoleGrantHistoryOutput,
|
|
261
288
|
async: true,
|
|
262
289
|
description: 'List role_grant grant and revoke events with usernames. Admin-only.',
|
|
290
|
+
rate_limit: 'account',
|
|
263
291
|
};
|
|
264
292
|
export const invite_create_action_spec = {
|
|
265
293
|
method: 'invite_create',
|
|
@@ -273,6 +301,12 @@ export const invite_create_action_spec = {
|
|
|
273
301
|
description: 'Create an invite addressed to an email, username, or both. Admin-only.',
|
|
274
302
|
rate_limit: 'account',
|
|
275
303
|
};
|
|
304
|
+
/**
|
|
305
|
+
* `rate_limit: 'account'` bounds admin-side scraping of the invite table —
|
|
306
|
+
* bounded by table size, but every row carries email + username +
|
|
307
|
+
* creator/claimer identifiers worth defense-in-depth against an admin
|
|
308
|
+
* mutation oracle running scripted reads alongside `invite_create`.
|
|
309
|
+
*/
|
|
276
310
|
export const invite_list_action_spec = {
|
|
277
311
|
method: 'invite_list',
|
|
278
312
|
kind: 'request_response',
|
|
@@ -283,6 +317,7 @@ export const invite_list_action_spec = {
|
|
|
283
317
|
output: InviteListOutput,
|
|
284
318
|
async: true,
|
|
285
319
|
description: 'List all invites with creator and claimer usernames. Admin-only.',
|
|
320
|
+
rate_limit: 'account',
|
|
286
321
|
};
|
|
287
322
|
export const invite_delete_action_spec = {
|
|
288
323
|
method: 'invite_delete',
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
*/
|
|
30
30
|
import { rpc_action } from '../actions/action_rpc.js';
|
|
31
31
|
import { jsonrpc_errors } from '../http/jsonrpc_errors.js';
|
|
32
|
-
import {
|
|
32
|
+
import { builtin_role_specs_by_name, list_roles_with_grant_path, } from './role_schema.js';
|
|
33
33
|
import { GRANT_PATH_ADMIN } from './grant_path_schema.js';
|
|
34
34
|
import { query_account_by_email, query_account_by_id, query_account_by_username, query_admin_account_list, } from './account_queries.js';
|
|
35
35
|
import { query_session_list_all_active, query_session_revoke_all_for_account, } from './session_queries.js';
|
|
@@ -54,7 +54,7 @@ import { admin_account_list_action_spec, admin_session_list_action_spec, admin_s
|
|
|
54
54
|
* @mutates `options.app_settings` ref - `app_settings_update` writes `open_signup`, `updated_at`, and `updated_by` so signup middleware reads without a DB round trip
|
|
55
55
|
*/
|
|
56
56
|
export const create_admin_actions = (deps, options = {}) => {
|
|
57
|
-
const role_specs = options.roles?.role_specs ??
|
|
57
|
+
const role_specs = options.roles?.role_specs ?? builtin_role_specs_by_name;
|
|
58
58
|
const grantable_roles = list_roles_with_grant_path(role_specs, GRANT_PATH_ADMIN);
|
|
59
59
|
const account_list_handler = async (input, ctx) => {
|
|
60
60
|
const accounts = await query_admin_account_list(ctx, {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical list of every fuz_auth action-spec registry — **for cross-cutting
|
|
3
|
+
* walkers and codegen only**. Not a mounting surface; consumers continue to
|
|
4
|
+
* import individual `all_*_action_specs` bundles (and `create_*_actions`
|
|
5
|
+
* factories) per registration site.
|
|
6
|
+
*
|
|
7
|
+
* The "one main bundle" alternative is an antipattern for mounting:
|
|
8
|
+
* `create_standard_rpc_actions` (admin + role_grant_offer + account) is the
|
|
9
|
+
* canonical surface, and opt-in registries (`self_service_role`,
|
|
10
|
+
* `actor_lookup`) are deliberately opt-in because their eligibility
|
|
11
|
+
* (`eligible_roles`) or coverage (byline labels) is app-specific. Spreading
|
|
12
|
+
* everything into a single mount would silently widen the dispatch surface
|
|
13
|
+
* the moment a new opt-in landed — the exact failure mode this module is
|
|
14
|
+
* built to detect, not propagate. See `./CLAUDE.md` §RPC actions
|
|
15
|
+
* (`standard_rpc_actions.ts`).
|
|
16
|
+
*
|
|
17
|
+
* Use cases for this registry:
|
|
18
|
+
*
|
|
19
|
+
* - Cross-registry walker tests (input-invariants, auth-shape
|
|
20
|
+
* biconditional) — iterate the spec arrays once, fail when a new
|
|
21
|
+
* registry slips by without an entry here.
|
|
22
|
+
* - Codegen that needs to see every fuz_auth surface at once
|
|
23
|
+
* (typed-client filters, attack-surface reports). For typed-client
|
|
24
|
+
* wiring of the standard surface, prefer `all_standard_action_specs`
|
|
25
|
+
* in `./standard_action_specs.ts` — it mirrors the
|
|
26
|
+
* `create_standard_rpc_actions` mount and stays narrower than this
|
|
27
|
+
* registry-of-registries (no opt-in bundles).
|
|
28
|
+
*
|
|
29
|
+
* `protocol_action_specs` (heartbeat / cancel) is **not** included —
|
|
30
|
+
* those are transport-level wire-protocol concerns shipped by fuz_app
|
|
31
|
+
* and spread by every consumer at registration via `protocol_actions`
|
|
32
|
+
* from `../actions/protocol.ts`. Walker tests that need protocol
|
|
33
|
+
* coverage spread `protocol_action_specs` separately.
|
|
34
|
+
*
|
|
35
|
+
* @module
|
|
36
|
+
*/
|
|
37
|
+
import type { RequestResponseActionSpec } from '../actions/action_spec.js';
|
|
38
|
+
/** One named entry in the registry-of-registries. */
|
|
39
|
+
export interface FuzAuthActionSpecRegistry {
|
|
40
|
+
/** Stable identifier matching the source bundle name (`'admin'`, `'role_grant_offer'`, etc.). */
|
|
41
|
+
name: string;
|
|
42
|
+
/** The bundle's spec array — kept readonly here even when the source declares it mutable. */
|
|
43
|
+
specs: ReadonlyArray<RequestResponseActionSpec>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Every fuz_auth action-spec registry, in dependency-stable order.
|
|
47
|
+
*
|
|
48
|
+
* Update this list when a new fuz_auth registry lands. The walker tests
|
|
49
|
+
* (`action_spec_input_invariants.test.ts`,
|
|
50
|
+
* `all_action_spec_registries.acting_biconditional.test.ts`) iterate
|
|
51
|
+
* over it — a missing entry silently skips coverage, which is the
|
|
52
|
+
* failure mode the registry-of-registries shape exists to prevent.
|
|
53
|
+
*/
|
|
54
|
+
export declare const all_fuz_auth_action_spec_registries: ReadonlyArray<FuzAuthActionSpecRegistry>;
|
|
55
|
+
//# sourceMappingURL=all_action_spec_registries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"all_action_spec_registries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/all_action_spec_registries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,2BAA2B,CAAC;AAQzE,qDAAqD;AACrD,MAAM,WAAW,yBAAyB;IACzC,iGAAiG;IACjG,IAAI,EAAE,MAAM,CAAC;IACb,6FAA6F;IAC7F,KAAK,EAAE,aAAa,CAAC,yBAAyB,CAAC,CAAC;CAChD;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,mCAAmC,EAAE,aAAa,CAAC,yBAAyB,CAOxF,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical list of every fuz_auth action-spec registry — **for cross-cutting
|
|
3
|
+
* walkers and codegen only**. Not a mounting surface; consumers continue to
|
|
4
|
+
* import individual `all_*_action_specs` bundles (and `create_*_actions`
|
|
5
|
+
* factories) per registration site.
|
|
6
|
+
*
|
|
7
|
+
* The "one main bundle" alternative is an antipattern for mounting:
|
|
8
|
+
* `create_standard_rpc_actions` (admin + role_grant_offer + account) is the
|
|
9
|
+
* canonical surface, and opt-in registries (`self_service_role`,
|
|
10
|
+
* `actor_lookup`) are deliberately opt-in because their eligibility
|
|
11
|
+
* (`eligible_roles`) or coverage (byline labels) is app-specific. Spreading
|
|
12
|
+
* everything into a single mount would silently widen the dispatch surface
|
|
13
|
+
* the moment a new opt-in landed — the exact failure mode this module is
|
|
14
|
+
* built to detect, not propagate. See `./CLAUDE.md` §RPC actions
|
|
15
|
+
* (`standard_rpc_actions.ts`).
|
|
16
|
+
*
|
|
17
|
+
* Use cases for this registry:
|
|
18
|
+
*
|
|
19
|
+
* - Cross-registry walker tests (input-invariants, auth-shape
|
|
20
|
+
* biconditional) — iterate the spec arrays once, fail when a new
|
|
21
|
+
* registry slips by without an entry here.
|
|
22
|
+
* - Codegen that needs to see every fuz_auth surface at once
|
|
23
|
+
* (typed-client filters, attack-surface reports). For typed-client
|
|
24
|
+
* wiring of the standard surface, prefer `all_standard_action_specs`
|
|
25
|
+
* in `./standard_action_specs.ts` — it mirrors the
|
|
26
|
+
* `create_standard_rpc_actions` mount and stays narrower than this
|
|
27
|
+
* registry-of-registries (no opt-in bundles).
|
|
28
|
+
*
|
|
29
|
+
* `protocol_action_specs` (heartbeat / cancel) is **not** included —
|
|
30
|
+
* those are transport-level wire-protocol concerns shipped by fuz_app
|
|
31
|
+
* and spread by every consumer at registration via `protocol_actions`
|
|
32
|
+
* from `../actions/protocol.ts`. Walker tests that need protocol
|
|
33
|
+
* coverage spread `protocol_action_specs` separately.
|
|
34
|
+
*
|
|
35
|
+
* @module
|
|
36
|
+
*/
|
|
37
|
+
import { all_admin_action_specs } from './admin_action_specs.js';
|
|
38
|
+
import { all_role_grant_offer_action_specs } from './role_grant_offer_action_specs.js';
|
|
39
|
+
import { all_account_action_specs } from './account_action_specs.js';
|
|
40
|
+
import { all_self_service_role_action_specs } from './self_service_role_action_specs.js';
|
|
41
|
+
import { all_actor_lookup_action_specs } from './actor_lookup_action_specs.js';
|
|
42
|
+
import { all_actor_search_action_specs } from './actor_search_action_specs.js';
|
|
43
|
+
/**
|
|
44
|
+
* Every fuz_auth action-spec registry, in dependency-stable order.
|
|
45
|
+
*
|
|
46
|
+
* Update this list when a new fuz_auth registry lands. The walker tests
|
|
47
|
+
* (`action_spec_input_invariants.test.ts`,
|
|
48
|
+
* `all_action_spec_registries.acting_biconditional.test.ts`) iterate
|
|
49
|
+
* over it — a missing entry silently skips coverage, which is the
|
|
50
|
+
* failure mode the registry-of-registries shape exists to prevent.
|
|
51
|
+
*/
|
|
52
|
+
export const all_fuz_auth_action_spec_registries = [
|
|
53
|
+
{ name: 'admin', specs: all_admin_action_specs },
|
|
54
|
+
{ name: 'role_grant_offer', specs: all_role_grant_offer_action_specs },
|
|
55
|
+
{ name: 'account', specs: all_account_action_specs },
|
|
56
|
+
{ name: 'self_service_role', specs: all_self_service_role_action_specs },
|
|
57
|
+
{ name: 'actor_lookup', specs: all_actor_lookup_action_specs },
|
|
58
|
+
{ name: 'actor_search', specs: all_actor_search_action_specs },
|
|
59
|
+
];
|
|
@@ -144,7 +144,7 @@ export interface CreateAuditEmitterOptions {
|
|
|
144
144
|
*/
|
|
145
145
|
on_audit_event?: ((event: AuditLogEvent) => void) | null;
|
|
146
146
|
/**
|
|
147
|
-
* Audit-log config. Defaults to `
|
|
147
|
+
* Audit-log config. Defaults to `builtin_audit_log_config`. Consumer-
|
|
148
148
|
* extended configs from `create_audit_log_config({extra_events})` get
|
|
149
149
|
* registered here once at backend assembly.
|
|
150
150
|
*/
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
* @module
|
|
34
34
|
*/
|
|
35
35
|
import { query_audit_log } from './audit_log_queries.js';
|
|
36
|
-
import {
|
|
36
|
+
import { builtin_audit_log_config, } from './audit_log_schema.js';
|
|
37
37
|
/**
|
|
38
38
|
* Build a bound `AuditEmitter`. Called once at `create_app_backend` time.
|
|
39
39
|
*
|
|
@@ -41,7 +41,7 @@ import { BUILTIN_AUDIT_LOG_CONFIG, } from './audit_log_schema.js';
|
|
|
41
41
|
* @returns the bound emitter; closes over the pool + config + listener chain
|
|
42
42
|
*/
|
|
43
43
|
export const create_audit_emitter = (options) => {
|
|
44
|
-
const { db, log, audit_log_config =
|
|
44
|
+
const { db, log, audit_log_config = builtin_audit_log_config } = options;
|
|
45
45
|
const on_event_chain = [];
|
|
46
46
|
if (options.on_audit_event)
|
|
47
47
|
on_event_chain.push(options.on_audit_event);
|
|
@@ -38,7 +38,7 @@ export declare const reset_audit_unknown_event_type_failures: () => void;
|
|
|
38
38
|
*
|
|
39
39
|
* @param deps - query dependencies
|
|
40
40
|
* @param input - the audit event to record
|
|
41
|
-
* @param config - audit-log config. Defaults to `
|
|
41
|
+
* @param config - audit-log config. Defaults to `builtin_audit_log_config`.
|
|
42
42
|
* @returns the inserted audit log row
|
|
43
43
|
* @mutates `audit_log` table - inserts the new row
|
|
44
44
|
* @mutates drift counters - bumps `audit_unknown_event_type_failures` and/or `audit_metadata_validation_failures` on mismatch
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* @module
|
|
13
13
|
*/
|
|
14
14
|
import { assert_row } from '../db/assert_row.js';
|
|
15
|
-
import { AUDIT_LOG_DEFAULT_LIMIT,
|
|
15
|
+
import { AUDIT_LOG_DEFAULT_LIMIT, builtin_audit_log_config, } from './audit_log_schema.js';
|
|
16
16
|
/**
|
|
17
17
|
* Process-wide counter for audit metadata validation failures. `query_audit_log`
|
|
18
18
|
* increments on `safeParse` mismatch and writes the row anyway (fail-open —
|
|
@@ -61,12 +61,12 @@ export const reset_audit_unknown_event_type_failures = () => {
|
|
|
61
61
|
*
|
|
62
62
|
* @param deps - query dependencies
|
|
63
63
|
* @param input - the audit event to record
|
|
64
|
-
* @param config - audit-log config. Defaults to `
|
|
64
|
+
* @param config - audit-log config. Defaults to `builtin_audit_log_config`.
|
|
65
65
|
* @returns the inserted audit log row
|
|
66
66
|
* @mutates `audit_log` table - inserts the new row
|
|
67
67
|
* @mutates drift counters - bumps `audit_unknown_event_type_failures` and/or `audit_metadata_validation_failures` on mismatch
|
|
68
68
|
*/
|
|
69
|
-
export const query_audit_log = async (deps, input, config =
|
|
69
|
+
export const query_audit_log = async (deps, input, config = builtin_audit_log_config) => {
|
|
70
70
|
if (!config.event_types.includes(input.event_type)) {
|
|
71
71
|
audit_unknown_event_type_failures++;
|
|
72
72
|
console.error(`[audit_log] unknown event_type '${input.event_type}' — register via create_audit_log_config({extra_events})`);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* `admin_session_list` on the same file. What remains here is the optional
|
|
7
7
|
* `GET /audit/stream` SSE route — streams aren't an action-kind, so they
|
|
8
8
|
* stay on REST. The event payload broadcast on the stream surfaces via
|
|
9
|
-
* `
|
|
9
|
+
* `audit_log_event_specs` (one `EventSpec` per audit event type) declared
|
|
10
10
|
* alongside the broadcaster in `../realtime/sse_auth_guard.ts`.
|
|
11
11
|
*
|
|
12
12
|
* @module
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* `admin_session_list` on the same file. What remains here is the optional
|
|
7
7
|
* `GET /audit/stream` SSE route — streams aren't an action-kind, so they
|
|
8
8
|
* stay on REST. The event payload broadcast on the stream surfaces via
|
|
9
|
-
* `
|
|
9
|
+
* `audit_log_event_specs` (one `EventSpec` per audit event type) declared
|
|
10
10
|
* alongside the broadcaster in `../realtime/sse_auth_guard.ts`.
|
|
11
11
|
*
|
|
12
12
|
* @module
|
|
@@ -64,7 +64,7 @@ export type AuditOutcome = z.infer<typeof AuditOutcome>;
|
|
|
64
64
|
* stub schema); the Zod schemas themselves are reachable and mutable —
|
|
65
65
|
* freeze isn't a security boundary.
|
|
66
66
|
*/
|
|
67
|
-
export declare const
|
|
67
|
+
export declare const audit_metadata_schemas: Readonly<{
|
|
68
68
|
login: z.ZodNullable<z.ZodObject<{
|
|
69
69
|
username: z.ZodString;
|
|
70
70
|
}, z.core.$loose>>;
|
|
@@ -175,7 +175,7 @@ export declare const AUDIT_METADATA_SCHEMAS: Readonly<{
|
|
|
175
175
|
}>;
|
|
176
176
|
/** Mapped type of metadata shapes per event type, derived from Zod schemas. */
|
|
177
177
|
export type AuditMetadataMap = {
|
|
178
|
-
[K in AuditEventType]: z.infer<(typeof
|
|
178
|
+
[K in AuditEventType]: z.infer<(typeof audit_metadata_schemas)[K]>;
|
|
179
179
|
};
|
|
180
180
|
/** Audit log row from the database. See `AuditLogEventJson` for `event_type` widening rationale. */
|
|
181
181
|
export interface AuditLogEvent {
|
|
@@ -270,7 +270,7 @@ export interface AuditLogInput<T extends string = AuditEventType> {
|
|
|
270
270
|
* Lets consumers extend the closed `AUDIT_EVENT_TYPES` enum with their own
|
|
271
271
|
* event strings (and metadata Zod schemas) without forking. Pass to
|
|
272
272
|
* `create_audit_emitter` (or `query_audit_log` for in-tx call sites) as the
|
|
273
|
-
* optional `config` argument; both default to `
|
|
273
|
+
* optional `config` argument; both default to `builtin_audit_log_config`.
|
|
274
274
|
*
|
|
275
275
|
* The DB column is `TEXT NOT NULL` and never enforced an enum, so consumer
|
|
276
276
|
* event types round-trip through `query_audit_log_list` and SSE identically
|
|
@@ -291,7 +291,7 @@ export interface AuditLogConfig {
|
|
|
291
291
|
readonly metadata_schemas: Readonly<Record<string, z.ZodType>>;
|
|
292
292
|
}
|
|
293
293
|
/** Builtin fuz_app audit-log config — every existing event type and its metadata schema. */
|
|
294
|
-
export declare const
|
|
294
|
+
export declare const builtin_audit_log_config: AuditLogConfig;
|
|
295
295
|
/** Options for `create_audit_log_config`. */
|
|
296
296
|
export interface CreateAuditLogConfigOptions {
|
|
297
297
|
/**
|
|
@@ -313,7 +313,7 @@ export interface CreateAuditLogConfigOptions {
|
|
|
313
313
|
*
|
|
314
314
|
* Call once at startup; pass the result to `create_app_backend` (which
|
|
315
315
|
* threads it into `AppDeps.audit`). Builtin handlers omit the
|
|
316
|
-
* `audit_log_config` slot and pick up `
|
|
316
|
+
* `audit_log_config` slot and pick up `builtin_audit_log_config`.
|
|
317
317
|
*
|
|
318
318
|
* @throws Error when an `extra_events` key collides with a builtin event type or fails `AuditEventTypeName` format validation
|
|
319
319
|
*/
|