@fuzdev/fuz_app 0.61.0 → 0.63.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 (48) hide show
  1. package/dist/actions/CLAUDE.md +43 -35
  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/perform_action.d.ts.map +1 -1
  8. package/dist/actions/perform_action.js +1 -0
  9. package/dist/auth/CLAUDE.md +47 -26
  10. package/dist/auth/account_action_specs.d.ts +6 -0
  11. package/dist/auth/account_action_specs.d.ts.map +1 -1
  12. package/dist/auth/account_action_specs.js +11 -4
  13. package/dist/auth/account_actions.d.ts.map +1 -1
  14. package/dist/auth/account_actions.js +9 -4
  15. package/dist/auth/account_routes.d.ts.map +1 -1
  16. package/dist/auth/account_routes.js +8 -4
  17. package/dist/auth/account_schema.d.ts +2 -2
  18. package/dist/auth/account_schema.js +2 -2
  19. package/dist/auth/actor_lookup_actions.d.ts +1 -1
  20. package/dist/auth/actor_lookup_actions.js +1 -1
  21. package/dist/auth/actor_lookup_queries.d.ts +1 -1
  22. package/dist/auth/actor_lookup_queries.js +1 -1
  23. package/dist/auth/actor_search_action_specs.d.ts +1 -1
  24. package/dist/auth/actor_search_action_specs.js +1 -1
  25. package/dist/auth/actor_search_actions.d.ts +1 -1
  26. package/dist/auth/actor_search_actions.js +1 -1
  27. package/dist/auth/actor_search_queries.d.ts +1 -1
  28. package/dist/auth/actor_search_queries.js +1 -1
  29. package/dist/auth/all_action_spec_registries.d.ts +2 -2
  30. package/dist/auth/all_action_spec_registries.js +2 -2
  31. package/dist/auth/audit_log_routes.d.ts +1 -1
  32. package/dist/auth/audit_log_routes.js +1 -1
  33. package/dist/auth/audit_log_schema.d.ts +25 -0
  34. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  35. package/dist/auth/audit_log_schema.js +16 -0
  36. package/dist/auth/request_context.d.ts +1 -1
  37. package/dist/env/update_env_variable.js +1 -1
  38. package/dist/http/CLAUDE.md +15 -15
  39. package/dist/server/app_server.d.ts +54 -6
  40. package/dist/server/app_server.d.ts.map +1 -1
  41. package/dist/server/app_server.js +32 -4
  42. package/dist/testing/CLAUDE.md +34 -50
  43. package/dist/testing/audit_completeness.d.ts.map +1 -1
  44. package/dist/testing/audit_completeness.js +17 -1
  45. package/dist/ui/CLAUDE.md +13 -18
  46. package/dist/ui/keyed_async_slot.svelte.d.ts +1 -1
  47. package/dist/ui/keyed_async_slot.svelte.js +1 -1
  48. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"account_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_routes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"account_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_routes.ts"],"names":[],"mappings":"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"}
@@ -29,7 +29,7 @@ import { hash_session_token, query_session_revoke_all_for_account, query_session
29
29
  import { query_account_by_username_or_email, query_update_account_password, } from './account_queries.js';
30
30
  import { query_revoke_all_api_tokens_for_account } from './api_token_queries.js';
31
31
  import { build_account_context, build_request_context, get_request_context, require_request_context, resolve_acting_actor, } from './request_context.js';
32
- import { ACCOUNT_ID_KEY } from '../hono_context.js';
32
+ import { ACCOUNT_ID_KEY, CREDENTIAL_TYPE_KEY } from '../hono_context.js';
33
33
  import { get_route_input } from '../http/route_spec.js';
34
34
  import { get_client_ip } from '../http/proxy.js';
35
35
  import { rate_limit_exceeded_response } from '../rate_limiter.js';
@@ -355,7 +355,8 @@ export const create_account_route_specs = (deps, options) => {
355
355
  {
356
356
  method: 'POST',
357
357
  path: '/password',
358
- auth: { account: 'required', actor: 'none' },
358
+ // `credential_types: ['session']` see `docs/security.md` §Credential-channel gating.
359
+ auth: { account: 'required', actor: 'none', credential_types: ['session'] },
359
360
  description: 'Change password (revokes all sessions and API tokens)',
360
361
  input: PasswordChangeInput,
361
362
  output: PasswordChangeOutput,
@@ -376,6 +377,8 @@ export const create_account_route_specs = (deps, options) => {
376
377
  }
377
378
  const ctx = require_request_context(c);
378
379
  const { current_password, new_password } = get_route_input(c);
380
+ // Defense in depth — see `docs/security.md` §Credential-channel gating.
381
+ const credential_type = c.get(CREDENTIAL_TYPE_KEY) ?? undefined;
379
382
  // per-account rate limit check (after context resolution, before argon2 work)
380
383
  if (login_account_rate_limiter) {
381
384
  const check = login_account_rate_limiter.check(ctx.account.id);
@@ -394,6 +397,7 @@ export const create_account_route_specs = (deps, options) => {
394
397
  outcome: 'failure',
395
398
  account_id: ctx.account.id,
396
399
  ip: get_client_ip(c),
400
+ metadata: { credential_type },
397
401
  });
398
402
  return c.json({ error: ERROR_INVALID_CREDENTIALS }, 401);
399
403
  }
@@ -426,7 +430,7 @@ export const create_account_route_specs = (deps, options) => {
426
430
  outcome: 'failure',
427
431
  account_id: ctx.account.id,
428
432
  ip: get_client_ip(c),
429
- metadata: { reason: 'concurrent_change' },
433
+ metadata: { reason: 'concurrent_change', credential_type },
430
434
  });
431
435
  return c.json({ error: ERROR_INVALID_CREDENTIALS }, 401);
432
436
  }
@@ -442,7 +446,7 @@ export const create_account_route_specs = (deps, options) => {
442
446
  event_type: 'password_change',
443
447
  account_id: ctx.account.id,
444
448
  ip: get_client_ip(c),
445
- metadata: { sessions_revoked, tokens_revoked },
449
+ metadata: { sessions_revoked, tokens_revoked, credential_type },
446
450
  });
447
451
  return c.json({ ok: true, sessions_revoked, tokens_revoked });
448
452
  },
@@ -5,9 +5,9 @@
5
5
  * `Account`, `Actor`, `RoleGrant`, `AuthSession`, and `ApiToken`.
6
6
  *
7
7
  * Identifier primitives (`Username`, `UsernameProvided`, `Email`) live
8
- * in `../primitive_schemas.ts` — they're general validator shapes that
8
+ * in `primitive_schemas.ts` — they're general validator shapes that
9
9
  * don't depend on the auth domain. The auth-shape request-contract
10
- * primitive `ActingActor` lives in `../http/auth_shape.ts` next to
10
+ * primitive `ActingActor` lives in `http/auth_shape.ts` next to
11
11
  * `RouteAuth` (the two pair: `auth.actor !== 'none'` ⟺ input declares
12
12
  * `acting?: ActingActor`).
13
13
  *
@@ -5,9 +5,9 @@
5
5
  * `Account`, `Actor`, `RoleGrant`, `AuthSession`, and `ApiToken`.
6
6
  *
7
7
  * Identifier primitives (`Username`, `UsernameProvided`, `Email`) live
8
- * in `../primitive_schemas.ts` — they're general validator shapes that
8
+ * in `primitive_schemas.ts` — they're general validator shapes that
9
9
  * don't depend on the auth domain. The auth-shape request-contract
10
- * primitive `ActingActor` lives in `../http/auth_shape.ts` next to
10
+ * primitive `ActingActor` lives in `http/auth_shape.ts` next to
11
11
  * `RouteAuth` (the two pair: `auth.actor !== 'none'` ⟺ input declares
12
12
  * `acting?: ActingActor`).
13
13
  *
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Pure read — no audit, no side effects. Auth (`account: 'required'`) +
5
5
  * rate-limit (`account`-grain) enforced at the spec layer; see
6
- * `./actor_lookup_action_specs.ts` for the info-leak audit.
6
+ * `auth/actor_lookup_action_specs.ts` for the info-leak audit.
7
7
  *
8
8
  * `display_name` is omitted (not `null`) when `actor.name` is blank,
9
9
  * matching the wire shape `display_name?` so the typed client sees an
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Pure read — no audit, no side effects. Auth (`account: 'required'`) +
5
5
  * rate-limit (`account`-grain) enforced at the spec layer; see
6
- * `./actor_lookup_action_specs.ts` for the info-leak audit.
6
+ * `auth/actor_lookup_action_specs.ts` for the info-leak audit.
7
7
  *
8
8
  * `display_name` is omitted (not `null`) when `actor.name` is blank,
9
9
  * matching the wire shape `display_name?` so the typed client sees an
@@ -10,7 +10,7 @@
10
10
  * The inner join still resolves one row per actor — `actor.account_id`
11
11
  * is `NOT NULL` so every actor has exactly one account.
12
12
  *
13
- * Info-leak posture (see `actor_lookup_action_specs.ts` § audit):
13
+ * Info-leak posture (see `actor_lookup_action_specs.ts` §audit):
14
14
  *
15
15
  * - Row shape **omits** `account_id` — the join is control-plane,
16
16
  * not wire-visible.
@@ -10,7 +10,7 @@
10
10
  * The inner join still resolves one row per actor — `actor.account_id`
11
11
  * is `NOT NULL` so every actor has exactly one account.
12
12
  *
13
- * Info-leak posture (see `actor_lookup_action_specs.ts` § audit):
13
+ * Info-leak posture (see `actor_lookup_action_specs.ts` §audit):
14
14
  *
15
15
  * - Row shape **omits** `account_id` — the join is control-plane,
16
16
  * not wire-visible.
@@ -45,7 +45,7 @@
45
45
  * ## Wire shape — info-leak audit
46
46
  *
47
47
  * Output `{actors: [{id, username, display_name?}]}` is identical to
48
- * `actor_lookup`'s — see `./actor_lookup_action_specs.ts` for the full
48
+ * `actor_lookup`'s — see `auth/actor_lookup_action_specs.ts` for the full
49
49
  * field-by-field audit. Same omissions (`account_id`, email,
50
50
  * timestamps, role / role_grants / session state), same `display_name`
51
51
  * omitted-not-null contract, same response-order-unspecified rule.
@@ -45,7 +45,7 @@
45
45
  * ## Wire shape — info-leak audit
46
46
  *
47
47
  * Output `{actors: [{id, username, display_name?}]}` is identical to
48
- * `actor_lookup`'s — see `./actor_lookup_action_specs.ts` for the full
48
+ * `actor_lookup`'s — see `auth/actor_lookup_action_specs.ts` for the full
49
49
  * field-by-field audit. Same omissions (`account_id`, email,
50
50
  * timestamps, role / role_grants / session state), same `display_name`
51
51
  * omitted-not-null contract, same response-order-unspecified rule.
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Pure read — no audit, no side effects. Auth (`account: 'required'`,
5
5
  * `actor: 'none'`) + rate-limit (`account`-grain) enforced at the spec
6
- * layer; see `./actor_search_action_specs.ts` for the info-leak audit
6
+ * layer; see `auth/actor_search_action_specs.ts` for the info-leak audit
7
7
  * and threat model.
8
8
  *
9
9
  * The handler adds two checks the spec layer can't express:
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Pure read — no audit, no side effects. Auth (`account: 'required'`,
5
5
  * `actor: 'none'`) + rate-limit (`account`-grain) enforced at the spec
6
- * layer; see `./actor_search_action_specs.ts` for the info-leak audit
6
+ * layer; see `auth/actor_search_action_specs.ts` for the info-leak audit
7
7
  * and threat model.
8
8
  *
9
9
  * The handler adds two checks the spec layer can't express:
@@ -29,7 +29,7 @@
29
29
  * gates), no role_grant join — every actor with a matching prefix is
30
30
  * returned.
31
31
  *
32
- * ## Info-leak posture (see `actor_search_action_specs.ts` § audit)
32
+ * ## Info-leak posture (see `actor_search_action_specs.ts` §audit)
33
33
  *
34
34
  * - Row shape **omits** `account_id` — the join is control-plane, not
35
35
  * wire-visible. Identical to `actor_lookup_queries.ts`.
@@ -29,7 +29,7 @@
29
29
  * gates), no role_grant join — every actor with a matching prefix is
30
30
  * returned.
31
31
  *
32
- * ## Info-leak posture (see `actor_search_action_specs.ts` § audit)
32
+ * ## Info-leak posture (see `actor_search_action_specs.ts` §audit)
33
33
  *
34
34
  * - Row shape **omits** `account_id` — the join is control-plane, not
35
35
  * wire-visible. Identical to `actor_lookup_queries.ts`.
@@ -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
@@ -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>;
@@ -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":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAoB5C;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,8aAsBnB,CAAC;AAEZ,wCAAwC;AACxC,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;EAA4B,CAAC;AACxD,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,QAA+B,CAAC;AAExE,0DAA0D;AAC1D,eAAO,MAAM,kBAAkB,aAE7B,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,2CAA2C;AAC3C,eAAO,MAAM,YAAY;;;EAAiC,CAAC;AAC3D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkNW,CAAC;AAE/C,+EAA+E;AAC/E,MAAM,MAAM,gBAAgB,GAAG;KAC7B,CAAC,IAAI,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC;CAClE,CAAC;AAEF,oGAAoG;AACpG,MAAM,WAAW,aAAa;IAC7B,EAAE,EAAE,IAAI,CAAC;IACT,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,kBAAkB,CAAC;IAC/B,OAAO,EAAE,YAAY,CAAC;IACtB;;;;;;;;;;;;;OAaG;IACH,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,iBAAiB,EAAE,IAAI,GAAG,IAAI,CAAC;IAC/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;IACH,eAAe,EAAE,IAAI,GAAG,IAAI,CAAC;IAC7B,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACzC;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,SAAS,cAAc,EAC1D,OAAO,aAAa,GAAG;IAAC,UAAU,EAAE,CAAC,CAAA;CAAC,KACpC,gBAAgB,CAAC,CAAC,CAAC,GAAG,IAExB,CAAC;AAEF,6CAA6C;AAC7C,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,MAAM,GAAG,cAAc;IAC/D,UAAU,EAAE,CAAC,CAAC;IACd,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,iBAAiB,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAChC,eAAe,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC9B,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,SAAS,cAAc,GAChC,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GACtD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,cAAc;IAC9B,iFAAiF;IACjF,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5C;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;CAC/D;AAED,4FAA4F;AAC5F,eAAO,MAAM,wBAAwB,EAAE,cAGrC,CAAC;AAEH,6CAA6C;AAC7C,MAAM,WAAW,2BAA2B;IAC3C;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC;CAC1D;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,uBAAuB,GAAI,UAAU,2BAA2B,KAAG,cA2B/E,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C,6CAA6C;AAC7C,MAAM,WAAW,mBAAmB;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9B,UAAU,CAAC,EAAE,IAAI,CAAC;IAClB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,0GAA0G;IAC1G,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;kBAY5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,+DAA+D;AAC/D,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;kBAGzC,CAAC;AACH,MAAM,MAAM,8BAA8B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC;AAE5F,wEAAwE;AACxE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;kBAGpC,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAElF,iEAAiE;AACjE,eAAO,MAAM,gBAAgB;;;;;;;kBAE3B,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
@@ -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.
@@ -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 = {
@@ -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)
@@ -5,12 +5,12 @@ surface introspection, JSON-RPC envelope + error taxonomy, proxy/origin
5
5
  middleware primitives, post-commit effect helper, generic admin route specs.
6
6
 
7
7
  **Nothing in this directory is auth-specific.** Auth middleware, routes, and
8
- guards live in `../auth/` and consume these primitives. Routes and actions in
8
+ guards live in `auth/` and consume these primitives. Routes and actions in
9
9
  other domains should do the same — extend, don't special-case.
10
10
 
11
11
  For the design rationale behind declarative routes, DEV-only output
12
12
  validation, the three-layer error-schema merge, and fire-and-forget effects,
13
- see `../../docs/architecture.md`.
13
+ see ../../docs/architecture.md.
14
14
 
15
15
  ## Module Map
16
16
 
@@ -42,7 +42,7 @@ by `generate_app_surface`. Same-shaped data, different consumers.
42
42
 
43
43
  - `method` — `'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'`
44
44
  - `path` — Hono path (supports `:param` segments)
45
- - `auth: RouteAuth` — flat record `{account, actor, roles?, credential_types?}` from `auth_shape.ts`. Each axis is `'none' | 'optional' | 'required'`. Same shape governs `ActionSpec.auth` (see `../actions/CLAUDE.md`).
45
+ - `auth: RouteAuth` — flat record `{account, actor, roles?, credential_types?}` from `auth_shape.ts`. Each axis is `'none' | 'optional' | 'required'`. Same shape governs `ActionSpec.auth` (see `actions/CLAUDE.md`).
46
46
  - `handler: RouteHandler` — `(c: Context, route: RouteContext) => Response | Promise<Response>`
47
47
  - `description` — free-text, surfaced in `AppSurface`
48
48
  - `params?: z.ZodObject` — strict-object schema for URL path params
@@ -84,7 +84,7 @@ interface RouteContext {
84
84
  `db.transaction`.
85
85
 
86
86
  Pool-level fire-and-forget writes (audit logs, etc.) run through the bound
87
- `AppDeps.audit` capability — see `../auth/CLAUDE.md` §Deps. Handlers that
87
+ `AppDeps.audit` capability — see `auth/CLAUDE.md` §Deps. Handlers that
88
88
  need rollback-resilient writes call `deps.audit.emit(route, input)`, which
89
89
  captures the pool inside the bound emitter so the row lands even when
90
90
  the handler's transaction rolls back.
@@ -98,7 +98,7 @@ the handler's transaction rolls back.
98
98
 
99
99
  Override explicitly when a mutation route must manage its own transactions
100
100
  (e.g. signup, which does a multi-step flow that can't live inside a single
101
- wrapper). See `../auth/signup_routes.ts`.
101
+ wrapper). See `auth/signup_routes.ts`.
102
102
 
103
103
  ### Validation pipeline (per-route middleware order)
104
104
 
@@ -114,7 +114,7 @@ wrapper). See `../auth/signup_routes.ts`.
114
114
  or `auth.actor === 'required'`. Fires before any body parsing so
115
115
  unauthenticated callers never see route-shape information from
116
116
  input parse failures. The `AuthGuardResolver` (e.g.
117
- `fuz_auth_guard_resolver` from `../auth/auth_guard_resolver.ts`) returns
117
+ `fuz_auth_guard_resolver` from `auth/auth_guard_resolver.ts`) returns
118
118
  this set as `pre_validation: Array<MiddlewareHandler>`.
119
119
  4. **Input validation** — JSON body parsed + validated; mismatch returns
120
120
  400 `ERROR_INVALID_JSON_BODY` (not JSON) or `ERROR_INVALID_REQUEST_BODY`
@@ -191,7 +191,7 @@ are the contract with external callers.
191
191
 
192
192
  The production behavior short-circuits to the unwrapped handler — no
193
193
  parse work on the hot path. Uniform across all three action-handler
194
- surfaces (REST, RPC, WS); see `../../docs/architecture.md` §DEV-only
194
+ surfaces (REST, RPC, WS); see ../../docs/architecture.md §DEV-only
195
195
  Output Validation.
196
196
 
197
197
  ### Helpers
@@ -205,8 +205,8 @@ Output Validation.
205
205
 
206
206
  `error_schemas.ts` is the **declarative** error surface:
207
207
 
208
- - `ERROR_*` snake*case string constants — single source of truth; use
209
- `.literal(ERROR*\*)` in Zod schemas and inline checks in handlers
208
+ - `ERROR_*` `snake_case` string constants — single source of truth; use
209
+ `.literal(ERROR_*)` in Zod schemas and inline checks in handlers
210
210
  - `ApiError`, `ValidationError`, `PermissionError`,
211
211
  `CredentialTypeRequiredError`, `RateLimitError`, `PayloadTooLargeError`,
212
212
  `ForeignKeyError` — standard shapes
@@ -413,7 +413,7 @@ connection is from a configured trusted proxy. Without this middleware,
413
413
  `get_client_ip(c)` returns `'unknown'`.
414
414
 
415
415
  Must run **before** auth and rate-limiting middleware. See the root
416
- `../../CLAUDE.md` §Middleware Ordering.
416
+ ../../CLAUDE.md §Middleware Ordering.
417
417
 
418
418
  - `normalize_ip(ip)` — idempotent: lowercase + strip `::ffff:` prefix on
419
419
  IPv4-mapped IPv6 addresses; safe on non-IP strings (`'unknown'` → `'unknown'`).
@@ -465,7 +465,7 @@ don't include ports — bounded by operator configuration in practice.
465
465
 
466
466
  Origin allowlisting for locally-running services — **not** the CSRF
467
467
  layer. CSRF is handled by `SameSite: strict` on session cookies (see
468
- `../auth/session_middleware.ts`).
468
+ `auth/session_middleware.ts`).
469
469
 
470
470
  - `parse_allowed_origins(env_value)` — comma-separated patterns → `Array<RegExp>`
471
471
  - `should_allow_origin(origin, patterns)` — case-insensitive match
@@ -664,7 +664,7 @@ emit_after_commit(ctx, () => notification_sender.send_to_account(account_id, msg
664
664
  ```
665
665
 
666
666
  Used for WS sends (`NotificationSender.send_to_account` for
667
- role-grant-offer notifications — see `../auth/CLAUDE.md` §WS notifications)
667
+ role-grant-offer notifications — see `auth/CLAUDE.md` §WS notifications)
668
668
  and any side effect that must run only after the transaction commits.
669
669
 
670
670
  ### Key properties
@@ -712,7 +712,7 @@ auth-domain dependencies:
712
712
  surface data reveals API structure (schemas, auth, routes)
713
713
 
714
714
  Auth-aware variants (account status, bootstrap status) live in
715
- `../auth/` — `common_routes.ts` stays generic.
715
+ `auth/` — `common_routes.ts` stays generic.
716
716
 
717
717
  ## DB Routes (Generic Browser)
718
718
 
@@ -743,7 +743,7 @@ Interfaces exported for consumer use: `TableInfo`, `TableWithCount`,
743
743
  ## Cross-Module Notes
744
744
 
745
745
  - **Middleware ordering** is assembled by `create_app_server` — see the
746
- root `../../CLAUDE.md` §Middleware Ordering. The invariants `http/`
746
+ root ../../CLAUDE.md §Middleware Ordering. The invariants `http/`
747
747
  needs consumers to uphold: trusted-proxy runs before auth/rate-limit;
748
748
  origin verification runs before session parsing; `client_ip` must be
749
749
  set before any handler or rate limiter reads it
@@ -753,7 +753,7 @@ Interfaces exported for consumer use: `TableInfo`, `TableWithCount`,
753
753
  - **Input/output schemas align with SAES.** When wiring RPC via
754
754
  `actions/action_rpc.ts` or bridging to `RouteSpec` via
755
755
  `actions/action_bridge.ts`, the same Zod types flow through unchanged
756
- (see `../actions/CLAUDE.md` §Single JSON-RPC 2.0 endpoint and §HTTP bridge)
756
+ (see `actions/CLAUDE.md` §Single JSON-RPC 2.0 endpoint and §HTTP bridge)
757
757
  - **Error modules are complementary, not redundant.** `error_schemas.ts`
758
758
  is Zod-first (for routes and surface); `jsonrpc_errors.ts` is
759
759
  throw-first (for handlers and the catch layer). A single `ERROR_*`
@@ -137,7 +137,10 @@ export interface AppServerOptions {
137
137
  * listener to `backend.deps.audit.on_event_chain` (composing with the
138
138
  * consumer's `on_audit_event` callback rather than rebuilding `AppDeps`), and
139
139
  * auto-includes `audit_log_event_specs` in the surface. The result is exposed
140
- * on `AppServerContext` (for route factories) and `AppServer` (for the caller).
140
+ * on `AppServerContext` (for route factories) and `AppServer` (for the caller),
141
+ * always typed as `AuditLogSse | null` — when this option is set, the field
142
+ * is non-null. Use `require_audit_sse(ctx)` to assert the invariant in
143
+ * route factories that depend on it.
141
144
  *
142
145
  * Pass `true` for defaults (admin role), or `{role: 'custom'}` for a custom role.
143
146
  * Omit to wire audit SSE manually.
@@ -160,14 +163,27 @@ export interface AppServerOptions {
160
163
  * `create_standard_rpc_actions(ctx.deps, {app_settings: ctx.app_settings})`.
161
164
  */
162
165
  rpc_endpoints?: Array<RpcEndpointSpec> | ((context: AppServerContext) => Array<RpcEndpointSpec>);
163
- /** Env schema for surface generation. Pass `z.object({})` when there are no env vars beyond `BaseServerEnv`. */
164
- env_schema: z.ZodObject;
166
+ /**
167
+ * Env schema for surface generation. Defaults to `BaseServerEnv` —
168
+ * pass an extended schema (typically `BaseServerEnv.extend({...})`)
169
+ * when the consumer adds app-specific env vars.
170
+ */
171
+ env_schema?: z.ZodObject;
165
172
  /** Middleware applied after routes, before static serving. Included in surface. */
166
173
  post_route_middleware?: Array<MiddlewareSpec>;
167
174
  /** Static file serving. Omit if not serving static files. */
168
175
  static_serving?: {
169
176
  serve_static: ServeStaticFactory;
177
+ /** Root directory for static files. Default `'./build'`. */
178
+ root?: string;
179
+ /** Optional SPA fallback path served for client-side routes. */
170
180
  spa_fallback?: string;
181
+ /**
182
+ * Predicate deciding which paths receive the SPA fallback.
183
+ * Default: every path that is not under `/api/`. Only consulted
184
+ * when `spa_fallback` is set.
185
+ */
186
+ is_spa_route?: (path: string) => boolean;
171
187
  };
172
188
  /**
173
189
  * Await all pending fire-and-forget effects before returning the response.
@@ -202,7 +218,11 @@ export interface AppServerContext {
202
218
  action_account_rate_limiter: RateLimiter | null;
203
219
  /** Global app settings (mutable ref — mutated by settings admin route). */
204
220
  app_settings: AppSettings;
205
- /** Factory-managed audit log SSE. `null` when `audit_log_sse` option is not set. */
221
+ /**
222
+ * Factory-managed audit log SSE. Non-null when the `audit_log_sse`
223
+ * option was passed to `create_app_server`, `null` when omitted.
224
+ * Use `require_audit_sse(ctx)` to assert the invariant.
225
+ */
206
226
  audit_sse: AuditLogSse | null;
207
227
  }
208
228
  /** Result of `create_app_server()`. */
@@ -215,11 +235,35 @@ export interface AppServer {
215
235
  app_settings: AppSettings;
216
236
  /** Migration results from `create_app_backend` (auth + any `migration_namespaces` passed there). */
217
237
  migration_results: ReadonlyArray<MigrationResult>;
218
- /** Factory-managed audit log SSE. `null` when `audit_log_sse` option is not set. */
238
+ /**
239
+ * Factory-managed audit log SSE. Non-null when the `audit_log_sse`
240
+ * option was passed to `create_app_server`, `null` when omitted.
241
+ * Use `require_audit_sse(server)` to assert the invariant.
242
+ */
219
243
  audit_sse: AuditLogSse | null;
220
244
  /** Close the database connection. Propagated from `AppBackend`. */
221
245
  close: () => Promise<void>;
222
246
  }
247
+ /**
248
+ * Assert that `audit_sse` was wired by `create_app_server` and return it
249
+ * as a non-null `AuditLogSse`. Throws a labelled error when the
250
+ * `audit_log_sse` option was not passed to `create_app_server`.
251
+ *
252
+ * Use in route factories that depend on factory-managed audit SSE:
253
+ *
254
+ * ```ts
255
+ * create_route_specs: (ctx) => create_audit_log_route_specs({
256
+ * stream: require_audit_sse(ctx),
257
+ * }),
258
+ * ```
259
+ *
260
+ * Preferred over `ctx.audit_sse!` — `!` lies to the type system and
261
+ * produces a downstream cannot-read-property crash if a consumer wires
262
+ * the route without enabling the option.
263
+ */
264
+ export declare const require_audit_sse: (source: {
265
+ audit_sse: AuditLogSse | null;
266
+ }) => AuditLogSse;
223
267
  /** Default maximum request body size: 1 MiB. */
224
268
  export declare const DEFAULT_MAX_BODY_SIZE: number;
225
269
  /**
@@ -231,7 +275,11 @@ export declare const DEFAULT_MAX_BODY_SIZE: number;
231
275
  * pass `migration_namespaces` to `create_app_backend`.
232
276
  *
233
277
  * When `audit_log_sse` is set, the SSE registry's listener is appended to
234
- * `backend.deps.audit.on_event_chain` — no shallow-copy of `AppDeps`.
278
+ * `backend.deps.audit.on_event_chain` — no shallow-copy of `AppDeps`. The
279
+ * `audit_sse` field on the returned `AppServer` (and the
280
+ * `AppServerContext` passed to `create_route_specs`) is non-null in that
281
+ * case; consumers can call `require_audit_sse(ctx)` / `require_audit_sse(server)`
282
+ * to assert the invariant.
235
283
  *
236
284
  * @returns assembled Hono app, backend, surface build, and bootstrap status
237
285
  */