@fuzdev/fuz_app 0.62.0 → 0.64.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 (136) hide show
  1. package/dist/actions/CLAUDE.md +139 -24
  2. package/dist/actions/action_rpc.d.ts +10 -0
  3. package/dist/actions/action_rpc.d.ts.map +1 -1
  4. package/dist/actions/action_rpc.js +1 -1
  5. package/dist/actions/action_spec.d.ts +1 -1
  6. package/dist/actions/action_spec.js +1 -1
  7. package/dist/actions/connection_closer.d.ts +68 -0
  8. package/dist/actions/connection_closer.d.ts.map +1 -0
  9. package/dist/actions/connection_closer.js +41 -0
  10. package/dist/actions/perform_action.d.ts.map +1 -1
  11. package/dist/actions/perform_action.js +1 -0
  12. package/dist/actions/register_action_ws.d.ts.map +1 -1
  13. package/dist/actions/register_action_ws.js +23 -2
  14. package/dist/actions/register_ws_endpoint.d.ts +11 -9
  15. package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
  16. package/dist/actions/register_ws_endpoint.js +5 -5
  17. package/dist/actions/transports_ws_auth_guard.d.ts +24 -8
  18. package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
  19. package/dist/actions/transports_ws_auth_guard.js +23 -7
  20. package/dist/actions/ws_endpoint_spec.d.ts +119 -0
  21. package/dist/actions/ws_endpoint_spec.d.ts.map +1 -0
  22. package/dist/actions/ws_endpoint_spec.js +13 -0
  23. package/dist/auth/CLAUDE.md +124 -39
  24. package/dist/auth/account_action_specs.d.ts +7 -1
  25. package/dist/auth/account_action_specs.d.ts.map +1 -1
  26. package/dist/auth/account_action_specs.js +11 -4
  27. package/dist/auth/account_actions.d.ts +13 -0
  28. package/dist/auth/account_actions.d.ts.map +1 -1
  29. package/dist/auth/account_actions.js +40 -5
  30. package/dist/auth/account_routes.d.ts +12 -2
  31. package/dist/auth/account_routes.d.ts.map +1 -1
  32. package/dist/auth/account_routes.js +63 -12
  33. package/dist/auth/account_schema.d.ts +5 -5
  34. package/dist/auth/account_schema.js +2 -2
  35. package/dist/auth/actor_lookup_actions.d.ts +1 -1
  36. package/dist/auth/actor_lookup_actions.js +1 -1
  37. package/dist/auth/actor_lookup_queries.d.ts +1 -1
  38. package/dist/auth/actor_lookup_queries.js +1 -1
  39. package/dist/auth/actor_search_action_specs.d.ts +1 -1
  40. package/dist/auth/actor_search_action_specs.js +1 -1
  41. package/dist/auth/actor_search_actions.d.ts +1 -1
  42. package/dist/auth/actor_search_actions.js +1 -1
  43. package/dist/auth/actor_search_queries.d.ts +1 -1
  44. package/dist/auth/actor_search_queries.js +1 -1
  45. package/dist/auth/admin_action_specs.d.ts +8 -8
  46. package/dist/auth/admin_actions.d.ts +11 -0
  47. package/dist/auth/admin_actions.d.ts.map +1 -1
  48. package/dist/auth/admin_actions.js +25 -0
  49. package/dist/auth/all_action_spec_registries.d.ts +2 -2
  50. package/dist/auth/all_action_spec_registries.js +2 -2
  51. package/dist/auth/audit_emitter.d.ts +56 -12
  52. package/dist/auth/audit_emitter.d.ts.map +1 -1
  53. package/dist/auth/audit_emitter.js +38 -12
  54. package/dist/auth/audit_log_routes.d.ts +1 -1
  55. package/dist/auth/audit_log_routes.js +1 -1
  56. package/dist/auth/audit_log_schema.d.ts +30 -3
  57. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  58. package/dist/auth/audit_log_schema.js +21 -3
  59. package/dist/auth/bootstrap_routes.d.ts +1 -1
  60. package/dist/auth/invite_schema.d.ts +2 -2
  61. package/dist/auth/request_context.d.ts +1 -1
  62. package/dist/auth/signup_routes.d.ts +1 -1
  63. package/dist/auth/standard_rpc_actions.d.ts +1 -0
  64. package/dist/auth/standard_rpc_actions.d.ts.map +1 -1
  65. package/dist/auth/standard_rpc_actions.js +1 -0
  66. package/dist/env/update_env_variable.js +1 -1
  67. package/dist/http/CLAUDE.md +42 -26
  68. package/dist/http/ip_canonical.d.ts +99 -0
  69. package/dist/http/ip_canonical.d.ts.map +1 -0
  70. package/dist/http/ip_canonical.js +191 -0
  71. package/dist/http/origin.d.ts +13 -5
  72. package/dist/http/origin.d.ts.map +1 -1
  73. package/dist/http/origin.js +13 -31
  74. package/dist/http/pending_effects.d.ts +1 -1
  75. package/dist/http/pending_effects.js +1 -1
  76. package/dist/http/proxy.d.ts +13 -5
  77. package/dist/http/proxy.d.ts.map +1 -1
  78. package/dist/http/proxy.js +15 -23
  79. package/dist/http/surface.d.ts +50 -0
  80. package/dist/http/surface.d.ts.map +1 -1
  81. package/dist/http/surface.js +27 -1
  82. package/dist/primitive_schemas.d.ts +20 -4
  83. package/dist/primitive_schemas.d.ts.map +1 -1
  84. package/dist/primitive_schemas.js +25 -4
  85. package/dist/realtime/sse_auth_guard.d.ts +16 -4
  86. package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
  87. package/dist/realtime/sse_auth_guard.js +15 -3
  88. package/dist/server/app_backend.d.ts +66 -19
  89. package/dist/server/app_backend.d.ts.map +1 -1
  90. package/dist/server/app_backend.js +57 -34
  91. package/dist/server/app_server.d.ts +60 -0
  92. package/dist/server/app_server.d.ts.map +1 -1
  93. package/dist/server/app_server.js +95 -2
  94. package/dist/server/startup.d.ts.map +1 -1
  95. package/dist/server/startup.js +12 -0
  96. package/dist/testing/CLAUDE.md +91 -71
  97. package/dist/testing/admin_integration.d.ts.map +1 -1
  98. package/dist/testing/admin_integration.js +4 -5
  99. package/dist/testing/adversarial_headers.d.ts +6 -0
  100. package/dist/testing/adversarial_headers.d.ts.map +1 -1
  101. package/dist/testing/adversarial_headers.js +13 -5
  102. package/dist/testing/app_server.d.ts +33 -32
  103. package/dist/testing/app_server.d.ts.map +1 -1
  104. package/dist/testing/app_server.js +4 -13
  105. package/dist/testing/attack_surface.d.ts +8 -7
  106. package/dist/testing/attack_surface.d.ts.map +1 -1
  107. package/dist/testing/attack_surface.js +12 -8
  108. package/dist/testing/audit_completeness.d.ts.map +1 -1
  109. package/dist/testing/audit_completeness.js +20 -6
  110. package/dist/testing/audit_drift_guard.d.ts +116 -0
  111. package/dist/testing/audit_drift_guard.d.ts.map +1 -0
  112. package/dist/testing/audit_drift_guard.js +134 -0
  113. package/dist/testing/connection_closer_helpers.d.ts +44 -0
  114. package/dist/testing/connection_closer_helpers.d.ts.map +1 -0
  115. package/dist/testing/connection_closer_helpers.js +48 -0
  116. package/dist/testing/integration.d.ts.map +1 -1
  117. package/dist/testing/integration.js +7 -9
  118. package/dist/testing/rate_limiting.js +4 -4
  119. package/dist/testing/rpc_helpers.d.ts +2 -1
  120. package/dist/testing/rpc_helpers.d.ts.map +1 -1
  121. package/dist/testing/rpc_round_trip.d.ts.map +1 -1
  122. package/dist/testing/rpc_round_trip.js +6 -8
  123. package/dist/testing/sse_round_trip.d.ts.map +1 -1
  124. package/dist/testing/sse_round_trip.js +12 -6
  125. package/dist/testing/stubs.d.ts +11 -0
  126. package/dist/testing/stubs.d.ts.map +1 -1
  127. package/dist/testing/stubs.js +4 -0
  128. package/dist/testing/surface_invariants.d.ts +66 -1
  129. package/dist/testing/surface_invariants.d.ts.map +1 -1
  130. package/dist/testing/surface_invariants.js +103 -1
  131. package/dist/ui/CLAUDE.md +13 -18
  132. package/dist/ui/SurfaceExplorer.svelte +161 -2
  133. package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
  134. package/dist/ui/keyed_async_slot.svelte.d.ts +1 -1
  135. package/dist/ui/keyed_async_slot.svelte.js +1 -1
  136. package/package.json +1 -1
@@ -28,6 +28,7 @@
28
28
  * @module
29
29
  */
30
30
  import { type RpcAction } from '../actions/action_rpc.js';
31
+ import type { ConnectionCloser } from '../actions/connection_closer.js';
31
32
  import { type RoleSchemaResult } from './role_schema.js';
32
33
  import { type AppSettings } from './app_settings_schema.js';
33
34
  import type { RouteFactoryDeps } from './deps.js';
@@ -49,6 +50,16 @@ export interface AdminActionOptions {
49
50
  * handler and RPC dispatch returns `method_not_found`.
50
51
  */
51
52
  app_settings?: AppSettings;
53
+ /**
54
+ * Live-connection closer — when set, `admin_session_revoke_all` and
55
+ * `admin_token_revoke_all` handlers eagerly close affected WebSocket
56
+ * sockets for the target account BEFORE emitting the corresponding
57
+ * audit event. Mirrors the self-service surface (see
58
+ * `AccountActionOptions.connection_closer`). `BackendWebsocketTransport`
59
+ * satisfies this interface structurally. When absent, only the
60
+ * listener-based close (`transports_ws_auth_guard`) runs.
61
+ */
62
+ connection_closer?: ConnectionCloser | null;
52
63
  }
53
64
  /**
54
65
  * Create the admin-only RPC actions.
@@ -1 +1 @@
1
- {"version":3,"file":"admin_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/admin_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAsC,KAAK,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAE7F,OAAO,EAGN,KAAK,gBAAgB,EACrB,MAAM,kBAAkB,CAAC;AAuB1B,OAAO,EAAC,KAAK,WAAW,EAAC,MAAM,0BAA0B,CAAC;AAK1D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AA6ChD,0CAA0C;AAC1C,MAAM,WAAW,kBAAkB;IAClC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,GAChC,MAAM,IAAI,CAAC,gBAAgB,EAAE,KAAK,GAAG,OAAO,CAAC,EAC7C,UAAS,kBAAuB,KAC9B,KAAK,CAAC,SAAS,CA0PjB,CAAC"}
1
+ {"version":3,"file":"admin_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/admin_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAsC,KAAK,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAC7F,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,iCAAiC,CAAC;AAEtE,OAAO,EAGN,KAAK,gBAAgB,EACrB,MAAM,kBAAkB,CAAC;AAuB1B,OAAO,EAAC,KAAK,WAAW,EAAC,MAAM,0BAA0B,CAAC;AAK1D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AA6ChD,0CAA0C;AAC1C,MAAM,WAAW,kBAAkB;IAClC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B;;;;;;;;OAQG;IACH,iBAAiB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAC5C;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,GAChC,MAAM,IAAI,CAAC,gBAAgB,EAAE,KAAK,GAAG,OAAO,CAAC,EAC7C,UAAS,kBAAuB,KAC9B,KAAK,CAAC,SAAS,CAmRjB,CAAC"}
@@ -56,6 +56,7 @@ import { admin_account_list_action_spec, admin_session_list_action_spec, admin_s
56
56
  export const create_admin_actions = (deps, options = {}) => {
57
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
+ const connection_closer = options.connection_closer ?? null;
59
60
  const account_list_handler = async (input, ctx) => {
60
61
  const accounts = await query_admin_account_list(ctx, {
61
62
  limit: input.limit,
@@ -88,6 +89,23 @@ export const create_admin_actions = (deps, options = {}) => {
88
89
  throw jsonrpc_errors.not_found('account', { reason: ERROR_ACCOUNT_NOT_FOUND });
89
90
  }
90
91
  const count = await query_session_revoke_all_for_account(ctx, input.account_id);
92
+ // Handler-side belt+suspenders — close the target account's live WS
93
+ // sockets BEFORE the audit emit so revocation lands even if the audit
94
+ // INSERT fails. Listener-based close (`transports_ws_auth_guard` on
95
+ // `audit.on_event_chain`) stays as a fail-safe for out-of-band emit
96
+ // sites. Idempotent — see `account_actions.ts::session_revoke_handler`.
97
+ if (connection_closer) {
98
+ connection_closer.close_sockets_for_account(input.account_id);
99
+ }
100
+ // TOCTOU window — admin B hard-deletes `input.account_id` between the
101
+ // pre-check above and this emit; the FK rejects the row, the audit
102
+ // emitter logs + swallows, and the operation goes unaudited. Bounded
103
+ // by the audit emitter's failure logging (operator-visible) and by
104
+ // the rarity of concurrent admin hard-deletes. Not switching to the
105
+ // failure-shape (`target_account_id: null + metadata.attempted_account_id`)
106
+ // because the FK linkage powers the username-join in
107
+ // `audit_log_list_with_usernames`; losing it on every success row
108
+ // to harden a corner case isn't worth the query-shape change.
91
109
  deps.audit.emit(ctx, {
92
110
  event_type: 'session_revoke_all',
93
111
  account_id: auth.account.id,
@@ -117,6 +135,13 @@ export const create_admin_actions = (deps, options = {}) => {
117
135
  throw jsonrpc_errors.not_found('account', { reason: ERROR_ACCOUNT_NOT_FOUND });
118
136
  }
119
137
  const count = await query_revoke_all_api_tokens_for_account(ctx, input.account_id);
138
+ // Handler-side belt+suspenders — see `session_revoke_all_handler`.
139
+ if (connection_closer) {
140
+ connection_closer.close_sockets_for_account(input.account_id);
141
+ }
142
+ // TOCTOU window — see `session_revoke_all_handler` for the rationale on
143
+ // keeping `target_account_id` populated rather than switching to the
144
+ // failure-shape.
120
145
  deps.audit.emit(ctx, {
121
146
  event_type: 'token_revoke_all',
122
147
  account_id: auth.account.id,
@@ -22,14 +22,14 @@
22
22
  * - Codegen that needs to see every fuz_auth surface at once
23
23
  * (typed-client filters, attack-surface reports). For typed-client
24
24
  * wiring of the standard surface, prefer `all_standard_action_specs`
25
- * in `./standard_action_specs.ts` — it mirrors the
25
+ * in `auth/standard_action_specs.ts` — it mirrors the
26
26
  * `create_standard_rpc_actions` mount and stays narrower than this
27
27
  * registry-of-registries (no opt-in bundles).
28
28
  *
29
29
  * `protocol_action_specs` (heartbeat / cancel) is **not** included —
30
30
  * those are transport-level wire-protocol concerns shipped by fuz_app
31
31
  * and spread by every consumer at registration via `protocol_actions`
32
- * from `../actions/protocol.ts`. Walker tests that need protocol
32
+ * from `actions/protocol.ts`. Walker tests that need protocol
33
33
  * coverage spread `protocol_action_specs` separately.
34
34
  *
35
35
  * @module
@@ -22,14 +22,14 @@
22
22
  * - Codegen that needs to see every fuz_auth surface at once
23
23
  * (typed-client filters, attack-surface reports). For typed-client
24
24
  * wiring of the standard surface, prefer `all_standard_action_specs`
25
- * in `./standard_action_specs.ts` — it mirrors the
25
+ * in `auth/standard_action_specs.ts` — it mirrors the
26
26
  * `create_standard_rpc_actions` mount and stays narrower than this
27
27
  * registry-of-registries (no opt-in bundles).
28
28
  *
29
29
  * `protocol_action_specs` (heartbeat / cancel) is **not** included —
30
30
  * those are transport-level wire-protocol concerns shipped by fuz_app
31
31
  * and spread by every consumer at registration via `protocol_actions`
32
- * from `../actions/protocol.ts`. Walker tests that need protocol
32
+ * from `actions/protocol.ts`. Walker tests that need protocol
33
33
  * coverage spread `protocol_action_specs` separately.
34
34
  *
35
35
  * @module
@@ -2,11 +2,14 @@
2
2
  * Bound audit-emit capability.
3
3
  *
4
4
  * `AuditEmitter` closes over the pool-level `Db`, the `on_audit_event`
5
- * subscriber chain, and the optional `AuditLogConfig` at backend-assembly
6
- * time. Consumers reach for `deps.audit.emit(ctx, input)` and never see the
7
- * pool handlers cannot accidentally emit an audit event against the
8
- * request's transactional `db` (which would be rolled back with the parent
9
- * on a handler throw).
5
+ * subscriber chain, and the optional `AuditLogConfig`. Built by the
6
+ * consumer's `audit_factory` callback on `CreateAppBackendOptions`
7
+ * `create_app_backend` invokes the factory once with its constructed
8
+ * `{db, log}` and lands the result on `AppDeps.audit`. Consumers reach
9
+ * for `deps.audit.emit(ctx, input)` and never see the pool — handlers
10
+ * cannot accidentally emit an audit event against the request's
11
+ * transactional `db` (which would be rolled back with the parent on a
12
+ * handler throw).
10
13
  *
11
14
  * Four methods cover every fan-out shape the auth domain needs:
12
15
  *
@@ -26,9 +29,13 @@
26
29
  * the query layer). Runs every listener on the chain; per-listener throws
27
30
  * are isolated.
28
31
  *
29
- * The chain is mutable so server assembly can append additional listeners
30
- * (e.g. the audit-log SSE registry composed by `create_app_server`) after
31
- * the backend is built but before the first request runs.
32
+ * The chain is a documented mutable seam `create_app_server` appends
33
+ * additional listeners after the backend is built (the factory-managed
34
+ * audit-log SSE, per-endpoint WS auth guards and logout closers, any
35
+ * `extra_audit_handlers` on a `WsEndpointSpec`) before the first request
36
+ * runs. Consumers can also append listeners directly on the emitter
37
+ * they return from `audit_factory` for setups that don't pass through
38
+ * `create_app_server`.
32
39
  *
33
40
  * @module
34
41
  */
@@ -126,12 +133,36 @@ export interface AuditEmitter {
126
133
  */
127
134
  notify(event: AuditLogEvent): void;
128
135
  /**
129
- * Mutable subscriber chain. Append at server assembly to compose the
130
- * factory-managed audit-log SSE on top of the consumer's
131
- * `on_audit_event` callback without shallow-copying `AppDeps`.
136
+ * Mutable subscriber chain. `create_app_server` appends the
137
+ * factory-managed audit-log SSE listener and per-endpoint WS auth
138
+ * guards / logout closers here so SSE + WS fan-out compose on top of
139
+ * the consumer's `on_audit_event` callback without shallow-copying
140
+ * `AppDeps`. Consumers can also append listeners directly for setups
141
+ * that don't run through `create_app_server`.
132
142
  */
133
143
  readonly on_event_chain: Array<(event: AuditLogEvent) => void>;
134
144
  }
145
+ /**
146
+ * Signature of `AuditEmitter.emit` — captured by the inner closure so
147
+ * `emit_role_grant_target` reaches the decorated function rather than
148
+ * a `this.emit` lookup. Exposed as a type so `EmitDecorator` can name
149
+ * the inner / outer slot.
150
+ */
151
+ export type AuditEmitFn = <T extends string>(ctx: AuditEmitterContext, input: AuditLogInput<T>) => void;
152
+ /**
153
+ * Wrap the bound `emit` before it gets captured by `emit_role_grant_target`'s
154
+ * closure and exposed on the returned `AuditEmitter`. Test instrumentation
155
+ * uses this to record `emit` invocation ordering against external markers
156
+ * (e.g. eager `ConnectionCloser` calls in `connection_closer.db.test.ts`)
157
+ * without paying the freeze-breaking footgun the pre-decorator
158
+ * `patch_audit_emit_capture` hot-patcher had.
159
+ *
160
+ * Because the inner closure captures the decorated function (not the
161
+ * outer slot reference), `emit_role_grant_target` also routes through
162
+ * the wrap — the close-vs-emit ordering helper sees role-grant-shape
163
+ * emissions, not just bare `emit` calls. Production never sets this.
164
+ */
165
+ export type EmitDecorator = (inner: AuditEmitFn) => AuditEmitFn;
135
166
  /** Options for `create_audit_emitter`. */
136
167
  export interface CreateAuditEmitterOptions {
137
168
  /** Pool-level `Db`. Captured by every emit call. */
@@ -149,9 +180,22 @@ export interface CreateAuditEmitterOptions {
149
180
  * registered here once at backend assembly.
150
181
  */
151
182
  audit_log_config?: AuditLogConfig;
183
+ /**
184
+ * Test-only hook to wrap `emit` at construction time. The decorated
185
+ * function is captured by `emit_role_grant_target`'s closure and is
186
+ * the function exposed on the returned `AuditEmitter`, so both call
187
+ * shapes route through it — see `EmitDecorator` for the rationale.
188
+ *
189
+ * Leave unset in production. The intended caller is
190
+ * `create_emit_ordering_audit_factory` in `testing/audit_drift_guard.ts`.
191
+ */
192
+ emit_decorator?: EmitDecorator;
152
193
  }
153
194
  /**
154
- * Build a bound `AuditEmitter`. Called once at `create_app_backend` time.
195
+ * Build a bound `AuditEmitter`. Typical caller is the consumer's
196
+ * `audit_factory` callback on `CreateAppBackendOptions` —
197
+ * `create_app_backend` invokes that callback with its constructed
198
+ * `{db, log}` and lands the result on `AppDeps.audit`.
155
199
  *
156
200
  * @param options - pool, logger, optional initial subscriber, optional config
157
201
  * @returns the bound emitter; closes over the pool + config + listener chain
@@ -1 +1 @@
1
- {"version":3,"file":"audit_emitter.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_emitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AACpD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAEjD,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,sBAAsB,CAAC;AAE9D,OAAO,EAEN,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,MAAM,uBAAuB,CAAC;AAE/B;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,mBAAmB;IACnC,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;CACtC;AAED;;;;GAIG;AACH,MAAM,WAAW,yBAA0B,SAAQ,mBAAmB;IACrE,0FAA0F;IAC1F,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC5B;;;;;;;;;;;;;;;;OAgBG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,mBAAmB,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAChF;;;;;;;OAOG;IACH,sBAAsB,CAAC,CAAC,SAAS,MAAM,EACtC,GAAG,EAAE,yBAAyB,EAC9B,IAAI,EAAE,mBAAmB,EACzB,KAAK,EAAE;QACN,UAAU,EAAE,CAAC,CAAC;QACd,iBAAiB,EAAE,IAAI,GAAG,IAAI,CAAC;QAC/B,eAAe,EAAE,IAAI,GAAG,IAAI,CAAC;QAC7B,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;QACvC,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;KAChC,GACC,IAAI,CAAC;IACR;;;;;;;;;;OAUG;IACH,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE;;;;;;;OAOG;IACH,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC,CAAC;CAC/D;AAED,0CAA0C;AAC1C,MAAM,WAAW,yBAAyB;IACzC,oDAAoD;IACpD,EAAE,EAAE,EAAE,CAAC;IACP,qDAAqD;IACrD,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACzD;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,cAAc,CAAC;CAClC;AAED;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,yBAAyB,KAAG,YAoDzE,CAAC"}
1
+ {"version":3,"file":"audit_emitter.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_emitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AACpD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAEjD,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,sBAAsB,CAAC;AAE9D,OAAO,EAEN,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,MAAM,uBAAuB,CAAC;AAE/B;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,mBAAmB;IACnC,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;CACtC;AAED;;;;GAIG;AACH,MAAM,WAAW,yBAA0B,SAAQ,mBAAmB;IACrE,0FAA0F;IAC1F,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC5B;;;;;;;;;;;;;;;;OAgBG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,mBAAmB,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAChF;;;;;;;OAOG;IACH,sBAAsB,CAAC,CAAC,SAAS,MAAM,EACtC,GAAG,EAAE,yBAAyB,EAC9B,IAAI,EAAE,mBAAmB,EACzB,KAAK,EAAE;QACN,UAAU,EAAE,CAAC,CAAC;QACd,iBAAiB,EAAE,IAAI,GAAG,IAAI,CAAC;QAC/B,eAAe,EAAE,IAAI,GAAG,IAAI,CAAC;QAC7B,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;QACvC,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;KAChC,GACC,IAAI,CAAC;IACR;;;;;;;;;;OAUG;IACH,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE;;;;;;;OAOG;IACH,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IACnC;;;;;;;OAOG;IACH,QAAQ,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC,CAAC;CAC/D;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,SAAS,MAAM,EAC1C,GAAG,EAAE,mBAAmB,EACxB,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,KACnB,IAAI,CAAC;AAEV;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,WAAW,CAAC;AAEhE,0CAA0C;AAC1C,MAAM,WAAW,yBAAyB;IACzC,oDAAoD;IACpD,EAAE,EAAE,EAAE,CAAC;IACP,qDAAqD;IACrD,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACzD;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,cAAc,CAAC;IAClC;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,yBAAyB,KAAG,YAoEzE,CAAC"}
@@ -2,11 +2,14 @@
2
2
  * Bound audit-emit capability.
3
3
  *
4
4
  * `AuditEmitter` closes over the pool-level `Db`, the `on_audit_event`
5
- * subscriber chain, and the optional `AuditLogConfig` at backend-assembly
6
- * time. Consumers reach for `deps.audit.emit(ctx, input)` and never see the
7
- * pool handlers cannot accidentally emit an audit event against the
8
- * request's transactional `db` (which would be rolled back with the parent
9
- * on a handler throw).
5
+ * subscriber chain, and the optional `AuditLogConfig`. Built by the
6
+ * consumer's `audit_factory` callback on `CreateAppBackendOptions`
7
+ * `create_app_backend` invokes the factory once with its constructed
8
+ * `{db, log}` and lands the result on `AppDeps.audit`. Consumers reach
9
+ * for `deps.audit.emit(ctx, input)` and never see the pool — handlers
10
+ * cannot accidentally emit an audit event against the request's
11
+ * transactional `db` (which would be rolled back with the parent on a
12
+ * handler throw).
10
13
  *
11
14
  * Four methods cover every fan-out shape the auth domain needs:
12
15
  *
@@ -26,22 +29,29 @@
26
29
  * the query layer). Runs every listener on the chain; per-listener throws
27
30
  * are isolated.
28
31
  *
29
- * The chain is mutable so server assembly can append additional listeners
30
- * (e.g. the audit-log SSE registry composed by `create_app_server`) after
31
- * the backend is built but before the first request runs.
32
+ * The chain is a documented mutable seam `create_app_server` appends
33
+ * additional listeners after the backend is built (the factory-managed
34
+ * audit-log SSE, per-endpoint WS auth guards and logout closers, any
35
+ * `extra_audit_handlers` on a `WsEndpointSpec`) before the first request
36
+ * runs. Consumers can also append listeners directly on the emitter
37
+ * they return from `audit_factory` for setups that don't pass through
38
+ * `create_app_server`.
32
39
  *
33
40
  * @module
34
41
  */
35
42
  import { query_audit_log } from './audit_log_queries.js';
36
43
  import { builtin_audit_log_config, } from './audit_log_schema.js';
37
44
  /**
38
- * Build a bound `AuditEmitter`. Called once at `create_app_backend` time.
45
+ * Build a bound `AuditEmitter`. Typical caller is the consumer's
46
+ * `audit_factory` callback on `CreateAppBackendOptions` —
47
+ * `create_app_backend` invokes that callback with its constructed
48
+ * `{db, log}` and lands the result on `AppDeps.audit`.
39
49
  *
40
50
  * @param options - pool, logger, optional initial subscriber, optional config
41
51
  * @returns the bound emitter; closes over the pool + config + listener chain
42
52
  */
43
53
  export const create_audit_emitter = (options) => {
44
- const { db, log, audit_log_config = builtin_audit_log_config } = options;
54
+ const { db, log, audit_log_config = builtin_audit_log_config, emit_decorator } = options;
45
55
  const on_event_chain = [];
46
56
  if (options.on_audit_event)
47
57
  on_event_chain.push(options.on_audit_event);
@@ -64,9 +74,14 @@ export const create_audit_emitter = (options) => {
64
74
  log.error('Audit log write failed:', err);
65
75
  }
66
76
  };
67
- const emit = (ctx, input) => {
77
+ const base_emit = (ctx, input) => {
68
78
  ctx.pending_effects.push(emit_pool(input));
69
79
  };
80
+ // The decorated `emit` is what `emit_role_grant_target` captures below
81
+ // and what gets exposed on the returned object — both call shapes
82
+ // route through any `emit_decorator` the caller supplied. Production
83
+ // passes no decorator, so this collapses to `base_emit`.
84
+ const emit = emit_decorator ? emit_decorator(base_emit) : base_emit;
70
85
  const emit_role_grant_target = (ctx, auth, input) => {
71
86
  emit(ctx, {
72
87
  event_type: input.event_type,
@@ -79,5 +94,16 @@ export const create_audit_emitter = (options) => {
79
94
  metadata: input.metadata,
80
95
  });
81
96
  };
82
- return { emit, emit_role_grant_target, emit_pool, notify, on_event_chain };
97
+ // Freeze the slot layout so consumers cannot hot-patch `emit` /
98
+ // `emit_role_grant_target` / `emit_pool` / `notify` after construction.
99
+ // The previous test helper `patch_audit_emit_capture` did exactly this
100
+ // and only happened to work because the four slots were writable —
101
+ // `emit_role_grant_target` calls the closed-over inner `emit`, not
102
+ // `this.emit`, so the patch silently bypassed role-grant-shape emits.
103
+ // Tests that need instrumentation pass `emit_decorator` so the wrap
104
+ // is captured by the closure before the freeze (see
105
+ // `create_emit_ordering_audit_factory`). `on_event_chain` is a
106
+ // frozen reference but its array contents stay mutable —
107
+ // `create_app_server` appends to it post-assembly, by design.
108
+ return Object.freeze({ emit, emit_role_grant_target, emit_pool, notify, on_event_chain });
83
109
  };
@@ -7,7 +7,7 @@
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
- * alongside the broadcaster in `../realtime/sse_auth_guard.ts`.
10
+ * alongside the broadcaster in `realtime/sse_auth_guard.ts`.
11
11
  *
12
12
  * @module
13
13
  */
@@ -7,7 +7,7 @@
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
- * alongside the broadcaster in `../realtime/sse_auth_guard.ts`.
10
+ * alongside the broadcaster in `realtime/sse_auth_guard.ts`.
11
11
  *
12
12
  * @module
13
13
  */
@@ -85,21 +85,46 @@ export declare const audit_metadata_schemas: Readonly<{
85
85
  reason: z.ZodOptional<z.ZodEnum<{
86
86
  concurrent_change: "concurrent_change";
87
87
  }>>;
88
+ credential_type: z.ZodOptional<z.ZodEnum<{
89
+ daemon_token: "daemon_token";
90
+ session: "session";
91
+ api_token: "api_token";
92
+ }>>;
88
93
  }, z.core.$loose>>;
89
94
  session_revoke: z.ZodObject<{
90
95
  session_id: z.ZodString;
96
+ credential_type: z.ZodOptional<z.ZodEnum<{
97
+ daemon_token: "daemon_token";
98
+ session: "session";
99
+ api_token: "api_token";
100
+ }>>;
91
101
  }, z.core.$loose>;
92
102
  session_revoke_all: z.ZodObject<{
93
103
  count: z.ZodOptional<z.ZodNumber>;
94
104
  reason: z.ZodOptional<z.ZodString>;
95
105
  attempted_account_id: z.ZodOptional<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
106
+ credential_type: z.ZodOptional<z.ZodEnum<{
107
+ daemon_token: "daemon_token";
108
+ session: "session";
109
+ api_token: "api_token";
110
+ }>>;
96
111
  }, z.core.$loose>;
97
112
  token_create: z.ZodObject<{
98
113
  token_id: z.ZodString;
99
114
  name: z.ZodString;
115
+ credential_type: z.ZodOptional<z.ZodEnum<{
116
+ daemon_token: "daemon_token";
117
+ session: "session";
118
+ api_token: "api_token";
119
+ }>>;
100
120
  }, z.core.$loose>;
101
121
  token_revoke: z.ZodObject<{
102
122
  token_id: z.ZodString;
123
+ credential_type: z.ZodOptional<z.ZodEnum<{
124
+ daemon_token: "daemon_token";
125
+ session: "session";
126
+ api_token: "api_token";
127
+ }>>;
103
128
  }, z.core.$loose>;
104
129
  token_revoke_all: z.ZodObject<{
105
130
  count: z.ZodOptional<z.ZodNumber>;
@@ -311,9 +336,11 @@ export interface CreateAuditLogConfigOptions {
311
336
  * Throws when an `extra_events` key collides with a builtin event type, or
312
337
  * fails `AuditEventTypeName` format validation.
313
338
  *
314
- * Call once at startup; pass the result to `create_app_backend` (which
315
- * threads it into `AppDeps.audit`). Builtin handlers omit the
316
- * `audit_log_config` slot and pick up `builtin_audit_log_config`.
339
+ * Call once at startup; pass the result into the consumer's `audit_factory`
340
+ * body typically `({db, log}) => create_audit_emitter({db, log,
341
+ * audit_log_config, ...})` so it gets captured inside the bound
342
+ * `AppDeps.audit` emitter. Builtin handlers omit the `audit_log_config`
343
+ * slot and pick up `builtin_audit_log_config`.
317
344
  *
318
345
  * @throws Error when an `extra_events` key collides with a builtin event type or fails `AuditEventTypeName` format validation
319
346
  */
@@ -1 +1 @@
1
- {"version":3,"file":"audit_log_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_log_schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audit_log_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_log_schema.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,17 @@ import { Blake3Hash } from '@fuzdev/fuz_util/hash_blake3.js';
14
14
  import { AuthSessionJson } from './account_schema.js';
15
15
  import { Email } from '../primitive_schemas.js';
16
16
  import { ApiTokenId } from './api_token.js';
17
+ import { BuiltinCredentialType } from './credential_type_schema.js';
18
+ /**
19
+ * Defense-in-depth audit field — records the credential channel
20
+ * (`session` / `api_token` / `daemon_token`) the request arrived on.
21
+ * Present on events whose specs declare `credential_types: ['session']`
22
+ * so forensics survive a future loosening or bypass of the spec gate.
23
+ * See `docs/security.md` §Credential-channel gating.
24
+ */
25
+ const credential_type_meta = BuiltinCredentialType.optional().meta({
26
+ description: 'Credential channel the request arrived on. Defense in depth — the spec gate restricts to `session`, but the row preserves what actually authenticated the request in case the gate is loosened or bypassed in a future refactor.',
27
+ });
17
28
  /**
18
29
  * All tracked auth event types. Frozen to convert accidental in-process
19
30
  * mutation (test cross-contamination, cast escapes) into loud TypeErrors.
@@ -102,10 +113,12 @@ export const audit_metadata_schemas = Object.freeze({
102
113
  reason: z.enum(['concurrent_change']).optional().meta({
103
114
  description: 'Failure category. `concurrent_change` indicates another password change committed first against the same starting hash (verify-write race loser). Absent for typed-wrong-password failures.',
104
115
  }),
116
+ credential_type: credential_type_meta,
105
117
  })
106
118
  .nullable(),
107
119
  session_revoke: z.looseObject({
108
120
  session_id: Blake3Hash.meta({ description: 'Blake3 hash identifying the revoked session row.' }),
121
+ credential_type: credential_type_meta,
109
122
  }),
110
123
  session_revoke_all: z.looseObject({
111
124
  // Omitted on `outcome='failure'` (no revocation attempted — e.g. target
@@ -122,13 +135,16 @@ export const audit_metadata_schemas = Object.freeze({
122
135
  attempted_account_id: Uuid.optional().meta({
123
136
  description: 'Probed account id when the target lookup missed (FK constraint forces `target_account_id` to null).',
124
137
  }),
138
+ credential_type: credential_type_meta,
125
139
  }),
126
140
  token_create: z.looseObject({
127
141
  token_id: ApiTokenId.meta({ description: 'Public id of the created API token (`tok_…`).' }),
128
142
  name: z.string().meta({ description: 'Operator-supplied label for the token.' }),
143
+ credential_type: credential_type_meta,
129
144
  }),
130
145
  token_revoke: z.looseObject({
131
146
  token_id: ApiTokenId.meta({ description: 'Public id of the revoked API token (`tok_…`).' }),
147
+ credential_type: credential_type_meta,
132
148
  }),
133
149
  token_revoke_all: z.looseObject({
134
150
  // Same shape as `session_revoke_all` for failures.
@@ -275,9 +291,11 @@ export const builtin_audit_log_config = Object.freeze({
275
291
  * Throws when an `extra_events` key collides with a builtin event type, or
276
292
  * fails `AuditEventTypeName` format validation.
277
293
  *
278
- * Call once at startup; pass the result to `create_app_backend` (which
279
- * threads it into `AppDeps.audit`). Builtin handlers omit the
280
- * `audit_log_config` slot and pick up `builtin_audit_log_config`.
294
+ * Call once at startup; pass the result into the consumer's `audit_factory`
295
+ * body typically `({db, log}) => create_audit_emitter({db, log,
296
+ * audit_log_config, ...})` so it gets captured inside the bound
297
+ * `AppDeps.audit` emitter. Builtin handlers omit the `audit_log_config`
298
+ * slot and pick up `builtin_audit_log_config`.
281
299
  *
282
300
  * @throws Error when an `extra_events` key collides with a builtin event type or fails `AuditEventTypeName` format validation
283
301
  */
@@ -19,7 +19,7 @@ import type { StatResult } from '../runtime/deps.js';
19
19
  /** Input for `POST /bootstrap`. `token` is the one-shot token file contents. */
20
20
  export declare const BootstrapInput: z.ZodObject<{
21
21
  token: z.ZodString;
22
- username: z.ZodString;
22
+ username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
23
23
  password: z.ZodString;
24
24
  }, z.core.$strict>;
25
25
  export type BootstrapInput = z.infer<typeof BootstrapInput>;
@@ -23,7 +23,7 @@ export interface Invite {
23
23
  export declare const InviteJson: z.ZodObject<{
24
24
  id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
25
25
  email: z.ZodNullable<z.ZodEmail>;
26
- username: z.ZodNullable<z.ZodString>;
26
+ username: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
27
27
  claimed_by: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
28
28
  claimed_at: z.ZodNullable<z.ZodString>;
29
29
  created_at: z.ZodString;
@@ -34,7 +34,7 @@ export type InviteJson = z.infer<typeof InviteJson>;
34
34
  export declare const InviteWithUsernamesJson: z.ZodObject<{
35
35
  id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
36
36
  email: z.ZodNullable<z.ZodEmail>;
37
- username: z.ZodNullable<z.ZodString>;
37
+ username: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
38
38
  claimed_by: z.ZodNullable<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
39
39
  claimed_at: z.ZodNullable<z.ZodString>;
40
40
  created_at: z.ZodString;
@@ -340,7 +340,7 @@ export declare const build_account_context: (deps: QueryDeps, account_id: string
340
340
  *
341
341
  * The auth phase deliberately stops short of constructing a `Response` so
342
342
  * the same failure flows through every transport without the auth-domain
343
- * code knowing about JSON-RPC. See `fuz_app/CLAUDE.md` § Cleanest
343
+ * code knowing about JSON-RPC. See `../../../CLAUDE.md` §Cleanest
344
344
  * architecture takes priority for the rationale.
345
345
  */
346
346
  export type AuthorizationFailureBody = {
@@ -24,7 +24,7 @@ export interface SignupRouteOptions extends AuthSessionRouteOptions {
24
24
  }
25
25
  /** Input for `POST /signup`. `email` is optional and must match any referenced invite. */
26
26
  export declare const SignupInput: z.ZodObject<{
27
- username: z.ZodString;
27
+ username: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
28
28
  password: z.ZodString;
29
29
  email: z.ZodOptional<z.ZodEmail>;
30
30
  }, z.core.$strict>;
@@ -10,6 +10,7 @@
10
10
  * Option routing: shared `roles` flows to both admin and role-grant-offer;
11
11
  * `app_settings` goes to admin only; `default_ttl_ms` and `authorize` go
12
12
  * to role-grant-offer only; `max_tokens` goes to account only;
13
+ * shared `connection_closer` flows to admin + account (role-grant-offer ignores);
13
14
  * `notification_sender` reaches role-grant-offer transparently (admin + account
14
15
  * ignore it).
15
16
  *
@@ -1 +1 @@
1
- {"version":3,"file":"standard_rpc_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/standard_rpc_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAuB,KAAK,kBAAkB,EAAC,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAEN,KAAK,2BAA2B,EAChC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAyB,KAAK,oBAAoB,EAAC,MAAM,sBAAsB,CAAC;AACvF,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,qCAAqC,CAAC;AAC5E,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAExD;;;;;;;;GAQG;AACH,MAAM,WAAW,yBAChB,SAAQ,kBAAkB,EAAE,2BAA2B,EAAE,oBAAoB;CAAG;AAEjF;;;;;;;GAOG;AACH,MAAM,WAAW,sBAAuB,SAAQ,IAAI,CAAC,gBAAgB,EAAE,KAAK,GAAG,OAAO,CAAC;IACtF,mBAAmB,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;CAChD;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,2BAA2B,GACvC,MAAM,sBAAsB,EAC5B,UAAS,yBAA8B,KACrC,KAAK,CAAC,SAAS,CAIjB,CAAC"}
1
+ {"version":3,"file":"standard_rpc_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/standard_rpc_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAuB,KAAK,kBAAkB,EAAC,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAEN,KAAK,2BAA2B,EAChC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAyB,KAAK,oBAAoB,EAAC,MAAM,sBAAsB,CAAC;AACvF,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,qCAAqC,CAAC;AAC5E,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAExD;;;;;;;;GAQG;AACH,MAAM,WAAW,yBAChB,SAAQ,kBAAkB,EAAE,2BAA2B,EAAE,oBAAoB;CAAG;AAEjF;;;;;;;GAOG;AACH,MAAM,WAAW,sBAAuB,SAAQ,IAAI,CAAC,gBAAgB,EAAE,KAAK,GAAG,OAAO,CAAC;IACtF,mBAAmB,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;CAChD;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,2BAA2B,GACvC,MAAM,sBAAsB,EAC5B,UAAS,yBAA8B,KACrC,KAAK,CAAC,SAAS,CAIjB,CAAC"}
@@ -10,6 +10,7 @@
10
10
  * Option routing: shared `roles` flows to both admin and role-grant-offer;
11
11
  * `app_settings` goes to admin only; `default_ttl_ms` and `authorize` go
12
12
  * to role-grant-offer only; `max_tokens` goes to account only;
13
+ * shared `connection_closer` flows to admin + account (role-grant-offer ignores);
13
14
  * `notification_sender` reaches role-grant-offer transparently (admin + account
14
15
  * ignore it).
15
16
  *
@@ -70,7 +70,7 @@ export const update_env_variable = async (key, value, options) => {
70
70
  const updated_content = updated_lines.join('\n') + (has_trailing_newline ? '\n' : '');
71
71
  await write_file(file_path, updated_content, 'utf-8');
72
72
  };
73
- // Keep this tokenization aligned with `parse_dotenv` in `./dotenv.ts`:
73
+ // Keep this tokenization aligned with `parse_dotenv` in `env/dotenv.ts`:
74
74
  // trim, skip empties/comments, split on the first `=`.
75
75
  const find_last_key_line_index = (lines, key) => {
76
76
  if (!key)