@fuzdev/fuz_app 0.95.0 → 0.97.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 (51) hide show
  1. package/dist/auth/CLAUDE.md +1 -1
  2. package/dist/auth/account_queries.d.ts +20 -5
  3. package/dist/auth/account_queries.d.ts.map +1 -1
  4. package/dist/auth/account_queries.js +21 -4
  5. package/dist/auth/bearer_auth.d.ts.map +1 -1
  6. package/dist/auth/bearer_auth.js +6 -7
  7. package/dist/auth/daemon_token_middleware.d.ts +21 -4
  8. package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
  9. package/dist/auth/daemon_token_middleware.js +61 -10
  10. package/dist/auth/middleware.d.ts.map +1 -1
  11. package/dist/auth/middleware.js +6 -2
  12. package/dist/auth/request_context.js +2 -2
  13. package/dist/db/CLAUDE.md +6 -3
  14. package/dist/db/cell_queries.js +8 -1
  15. package/dist/db/create_db.d.ts.map +1 -1
  16. package/dist/db/create_db.js +2 -14
  17. package/dist/db/db_pg.d.ts +30 -2
  18. package/dist/db/db_pg.d.ts.map +1 -1
  19. package/dist/db/db_pg.js +33 -2
  20. package/dist/db/fact_queries.d.ts.map +1 -1
  21. package/dist/http/CLAUDE.md +3 -2
  22. package/dist/http/error_schemas.d.ts +0 -6
  23. package/dist/http/error_schemas.d.ts.map +1 -1
  24. package/dist/http/error_schemas.js +0 -6
  25. package/dist/http/origin.d.ts +18 -1
  26. package/dist/http/origin.d.ts.map +1 -1
  27. package/dist/http/origin.js +17 -0
  28. package/dist/server/serve_fact_route.d.ts +12 -0
  29. package/dist/server/serve_fact_route.d.ts.map +1 -1
  30. package/dist/server/serve_fact_route.js +40 -0
  31. package/dist/testing/CLAUDE.md +23 -3
  32. package/dist/testing/audit_completeness.d.ts.map +1 -1
  33. package/dist/testing/audit_completeness.js +4 -1
  34. package/dist/testing/cross_backend/bootstrap_backend.d.ts.map +1 -1
  35. package/dist/testing/cross_backend/bootstrap_backend.js +8 -0
  36. package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
  37. package/dist/testing/cross_backend/cell_crud.js +23 -0
  38. package/dist/testing/cross_backend/conformance_case.d.ts +40 -0
  39. package/dist/testing/cross_backend/conformance_case.d.ts.map +1 -1
  40. package/dist/testing/cross_backend/conformance_case.js +34 -0
  41. package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
  42. package/dist/testing/cross_backend/conformance_table.js +58 -0
  43. package/dist/testing/cross_backend/fact_serving.d.ts.map +1 -1
  44. package/dist/testing/cross_backend/fact_serving.js +5 -1
  45. package/dist/testing/cross_backend/setup.d.ts +17 -0
  46. package/dist/testing/cross_backend/setup.d.ts.map +1 -1
  47. package/dist/testing/cross_backend/setup.js +29 -4
  48. package/dist/testing/db.d.ts.map +1 -1
  49. package/dist/testing/db.js +6 -1
  50. package/dist/testing/middleware.js +4 -4
  51. package/package.json +6 -2
@@ -157,7 +157,7 @@ Everything else listed under §RPC action surfaces.
157
157
  - `auth/request_context.ts` — `RequestContext`, `resolve_acting_actor`, `build_request_context`, predicates (`has_role`, `has_scoped_role`, `has_any_scoped_role`), guards (`require_auth`, `require_role`, `require_credential_types`), `refresh_role_grants`.
158
158
  - `auth/session_middleware.ts` — `process_session_cookie` integration, `create_session_and_set_cookie` (shared by login / signup / bootstrap).
159
159
  - `auth/bearer_auth.ts` — soft-fail bearer middleware; rejects when `Origin` or `Referer` present (browser context).
160
- - `auth/daemon_token_middleware.ts` — `start_daemon_token_rotation` + `create_daemon_token_middleware` (atomic file write, fail-closed validation, keeper account resolution).
160
+ - `auth/daemon_token_middleware.ts` — `start_daemon_token_rotation` + `create_daemon_token_middleware(state, deps, log)` (atomic file write, soft-fail validation, keeper account resolution). Soft-fails — discards the credential (pass-through, no own 401/503) on **every** non-success path: browser context (`Origin`/`Referer` present), malformed/invalid token, and valid-token-but-no-keeper all `next()` through to the dispatcher's `credential_type_required` (403) gate, mirroring the bearer guard and the Rust spine's `resolve.rs`. Daemon tokens are loopback-only, so browser context never arises in practice — the discard is defense-in-depth.
161
161
 
162
162
  See root ../../../CLAUDE.md §Middleware Ordering for canonical assembly
163
163
  order. The auth-specific invariants are described below in §Cross-cutting
@@ -159,14 +159,29 @@ export declare const query_account_has_any: (deps: QueryDeps) => Promise<boolean
159
159
  */
160
160
  export declare const query_create_actor: (deps: QueryDeps, account_id: string, name: string) => Promise<Actor>;
161
161
  /**
162
- * List every actor on an account, ordered by `created_at`.
163
- *
164
- * Used by `resolve_acting_actor` to resolve the acting actor for a
165
- * request: 1 actor picks transparently, multiple require an explicit
166
- * `acting` field on the request payload. For lookups by id, use
162
+ * List every actor on an account, ordered by `created_at`, **including
163
+ * soft-deleted (tombstoned) rows**.
164
+ *
165
+ * Used by the admin/snapshot handlers (`account_delete` / `account_purge` /
166
+ * `account_undelete`) that must enumerate every actor that ever existed on
167
+ * the account to snapshot names into per-actor audit events. For the
168
+ * acting-actor resolution path — which must never resolve a tombstoned
169
+ * actor — use `query_active_actors_by_account`. For lookups by id, use
167
170
  * `query_actor_by_id` instead.
168
171
  */
169
172
  export declare const query_actors_by_account: (deps: QueryDeps, account_id: string) => Promise<Array<Actor>>;
173
+ /**
174
+ * List **active** (non-tombstoned) actors on an account, ordered by
175
+ * `created_at`.
176
+ *
177
+ * Filters `deleted_at IS NULL` so a soft-deleted actor can never be resolved
178
+ * as the acting actor (and carry its `role_grants` into the role gate). Used
179
+ * by `resolve_acting_actor` to resolve the acting actor for a request: 1
180
+ * actor picks transparently, multiple require an explicit `acting` field on
181
+ * the request payload. The admin/snapshot handlers that legitimately need
182
+ * tombstoned rows stay on the unfiltered `query_actors_by_account`.
183
+ */
184
+ export declare const query_active_actors_by_account: (deps: QueryDeps, account_id: string) => Promise<Array<Actor>>;
170
185
  /**
171
186
  * Find an actor by id.
172
187
  */
@@ -1 +1 @@
1
- {"version":3,"file":"account_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_queries.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"account_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_queries.ts"],"names":[],"mappings":"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"}
@@ -218,16 +218,33 @@ export const query_create_actor = async (deps, account_id, name) => {
218
218
  return assert_row(row, 'INSERT INTO actor');
219
219
  };
220
220
  /**
221
- * List every actor on an account, ordered by `created_at`.
221
+ * List every actor on an account, ordered by `created_at`, **including
222
+ * soft-deleted (tombstoned) rows**.
222
223
  *
223
- * Used by `resolve_acting_actor` to resolve the acting actor for a
224
- * request: 1 actor picks transparently, multiple require an explicit
225
- * `acting` field on the request payload. For lookups by id, use
224
+ * Used by the admin/snapshot handlers (`account_delete` / `account_purge` /
225
+ * `account_undelete`) that must enumerate every actor that ever existed on
226
+ * the account to snapshot names into per-actor audit events. For the
227
+ * acting-actor resolution path — which must never resolve a tombstoned
228
+ * actor — use `query_active_actors_by_account`. For lookups by id, use
226
229
  * `query_actor_by_id` instead.
227
230
  */
228
231
  export const query_actors_by_account = async (deps, account_id) => {
229
232
  return deps.db.query(`SELECT * FROM actor WHERE account_id = $1 ORDER BY created_at ASC, id ASC`, [account_id]);
230
233
  };
234
+ /**
235
+ * List **active** (non-tombstoned) actors on an account, ordered by
236
+ * `created_at`.
237
+ *
238
+ * Filters `deleted_at IS NULL` so a soft-deleted actor can never be resolved
239
+ * as the acting actor (and carry its `role_grants` into the role gate). Used
240
+ * by `resolve_acting_actor` to resolve the acting actor for a request: 1
241
+ * actor picks transparently, multiple require an explicit `acting` field on
242
+ * the request payload. The admin/snapshot handlers that legitimately need
243
+ * tombstoned rows stay on the unfiltered `query_actors_by_account`.
244
+ */
245
+ export const query_active_actors_by_account = async (deps, account_id) => {
246
+ return deps.db.query(`SELECT * FROM actor WHERE account_id = $1 AND deleted_at IS NULL ORDER BY created_at ASC, id ASC`, [account_id]);
247
+ };
231
248
  /**
232
249
  * Find an actor by id.
233
250
  */
@@ -1 +1 @@
1
- {"version":3,"file":"bearer_auth.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bearer_auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,MAAM,CAAC;AAC5C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAIpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAA+B,KAAK,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAElF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,6BAA6B,GACzC,MAAM,SAAS,EACf,iBAAiB,WAAW,GAAG,IAAI,EACnC,KAAK,MAAM,KACT,iBAkFF,CAAC"}
1
+ {"version":3,"file":"bearer_auth.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bearer_auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,MAAM,CAAC;AAC5C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAIpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAGnD,OAAO,EAA+B,KAAK,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAElF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,6BAA6B,GACzC,MAAM,SAAS,EACf,iBAAiB,WAAW,GAAG,IAAI,EACnC,KAAK,MAAM,KACT,iBAgFF,CAAC"}
@@ -18,6 +18,7 @@ import { DEV } from 'esm-env';
18
18
  import { AUTH_API_TOKEN_ID_KEY, ACCOUNT_ID_KEY, CREDENTIAL_TYPE_KEY } from '../hono_context.js';
19
19
  import { query_validate_api_token } from './api_token_queries.js';
20
20
  import { get_client_ip } from '../http/client_ip.js';
21
+ import { is_browser_context } from '../http/origin.js';
21
22
  import { rate_limit_exceeded_response } from '../rate_limiter.js';
22
23
  /**
23
24
  * Create middleware that authenticates via bearer token.
@@ -62,13 +63,11 @@ export const create_bearer_auth_middleware = (deps, ip_rate_limiter, log) => {
62
63
  await next();
63
64
  return;
64
65
  }
65
- // Silently discard bearer tokens in browser context — defense-in-depth:
66
- // checks both Origin and Referer (not just Origin) because some browser
67
- // requests send only Referer. Uses `!== undefined` so that empty-string
68
- // headers (e.g. `Origin: ''`) are still treated as browser context.
69
- // Discards rather than returning 403 so that the RPC dispatcher can still
70
- // handle public actions or fall through to cookie auth.
71
- if (c.req.header('Origin') !== undefined || c.req.header('Referer') !== undefined) {
66
+ // Silently discard bearer tokens in browser context (`is_browser_context`
67
+ // — Origin or Referer present). Discards rather than returning 403 so that
68
+ // the RPC dispatcher can still handle public actions or fall through to
69
+ // cookie auth.
70
+ if (is_browser_context(c)) {
72
71
  log.debug('bearer auth rejected: browser context (Origin/Referer present)');
73
72
  // The discard is silent on the wire by design (a stolen-token probe
74
73
  // gets an indistinguishable 401, not a "your token was dropped"
@@ -100,9 +100,24 @@ export declare const start_daemon_token_rotation: (runtime: DaemonTokenWriteDeps
100
100
  *
101
101
  * Checks the `X-Daemon-Token` header. Behavior:
102
102
  * - No header: pass through (don't touch existing context).
103
- * - Header present + Zod-invalid: return 401 (fail-closed).
104
- * - Header present + invalid value: return 401 (fail-closed, no downgrade).
105
- * - Header present + valid + `keeper_account_id` null: return 503.
103
+ * - Header present + `Origin` / `Referer` present: discard the credential
104
+ * (browser context) and pass through — daemon tokens are loopback-only and
105
+ * never carry an `Origin` in production, so a header-bearing request is not
106
+ * a legitimate daemon caller. Mirrors the bearer guard: `next()` rather than
107
+ * 401, so downstream auth enforcement returns `credential_type_required`
108
+ * (not a hard fail). Silent on the wire (anti-enumeration); in `DEV` only,
109
+ * sets `X-Fuz-Auth-Debug: daemon_token_discarded_browser_context`.
110
+ * - Header present + Zod-invalid (malformed): soft-fail discard (pass through,
111
+ * not 401) — mirrors the bearer guard and the Rust spine's `resolve.rs`
112
+ * (`None`). Downstream a daemon-gated action returns `credential_type_required`;
113
+ * a public action proceeds anonymous.
114
+ * - Header present + invalid value (not the current/previous token): soft-fail
115
+ * discard (pass through, not 401) — same downstream behavior.
116
+ * - Header present + valid + `keeper_account_id` null (still pre-bootstrap
117
+ * after the lazy refresh): soft-fail discard (pass through, not 503) —
118
+ * mirrors the Rust spine's `resolve.rs` (`None`), so the request falls
119
+ * through to anonymous and a daemon-gated action returns
120
+ * `credential_type_required` downstream.
106
121
  * - Header present + valid + ok: set `c.var.auth_account_id =
107
122
  * state.keeper_account_id`, `CREDENTIAL_TYPE_KEY = 'daemon_token'`
108
123
  * (overrides any existing session / bearer identity).
@@ -113,7 +128,9 @@ export declare const start_daemon_token_rotation: (runtime: DaemonTokenWriteDeps
113
128
  * an explicit `acting` value.
114
129
  *
115
130
  * @param state - the daemon token runtime state
131
+ * @param deps - query dependencies (pool-level db for keeper-account resolution)
132
+ * @param log - the logger instance
116
133
  * @mutates Hono context - sets `ACCOUNT_ID_KEY`, `CREDENTIAL_TYPE_KEY`, and `AUTH_API_TOKEN_ID_KEY` on a valid token
117
134
  */
118
- export declare const create_daemon_token_middleware: (state: DaemonTokenState, deps: QueryDeps) => MiddlewareHandler;
135
+ export declare const create_daemon_token_middleware: (state: DaemonTokenState, deps: QueryDeps, log: Logger) => MiddlewareHandler;
119
136
  //# sourceMappingURL=daemon_token_middleware.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"daemon_token_middleware.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/daemon_token_middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,MAAM,CAAC;AAC5C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EAAC,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,OAAO,EAAC,MAAM,oBAAoB,CAAC;AASrF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAKN,KAAK,gBAAgB,EACrB,MAAM,mBAAmB,CAAC;AAE3B,8DAA8D;AAC9D,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD,iDAAiD;AACjD,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,GAC1D,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,iBAAiB,GAAG,QAAQ,CAAC,GAAG;IAC3D,6FAA6F;IAC7F,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GACjC,SAAS,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,EACjC,MAAM,MAAM,KACV,MAAM,GAAG,IAGX,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,kBAAkB,GAC9B,SAAS,oBAAoB,EAC7B,YAAY,MAAM,EAClB,OAAO,MAAM,KACX,OAAO,CAAC,IAAI,CAKd,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,yBAAyB,GAAU,MAAM,SAAS,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAEtF,CAAC;AAEF,yCAAyC;AACzC,MAAM,WAAW,0BAA0B;IAC1C;;;;;;OAMG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,gDAAgD;AAChD,MAAM,WAAW,mBAAmB;IACnC,2EAA2E;IAC3E,KAAK,EAAE,gBAAgB,CAAC;IACxB,kGAAkG;IAClG,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,2BAA2B,GACvC,SAAS,oBAAoB,GAAG,YAAY,EAC5C,MAAM,SAAS,EACf,SAAS,0BAA0B,EACnC,KAAK,MAAM,KACT,OAAO,CAAC,mBAAmB,CAqD7B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,8BAA8B,GAC1C,OAAO,gBAAgB,EACvB,MAAM,SAAS,KACb,iBAsCF,CAAC"}
1
+ {"version":3,"file":"daemon_token_middleware.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/daemon_token_middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,MAAM,CAAC;AAC5C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EAAC,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAMrF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAKN,KAAK,gBAAgB,EACrB,MAAM,mBAAmB,CAAC;AAE3B,8DAA8D;AAC9D,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD,iDAAiD;AACjD,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,GAC1D,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,iBAAiB,GAAG,QAAQ,CAAC,GAAG;IAC3D,6FAA6F;IAC7F,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GACjC,SAAS,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,EACjC,MAAM,MAAM,KACV,MAAM,GAAG,IAGX,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,kBAAkB,GAC9B,SAAS,oBAAoB,EAC7B,YAAY,MAAM,EAClB,OAAO,MAAM,KACX,OAAO,CAAC,IAAI,CAKd,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,yBAAyB,GAAU,MAAM,SAAS,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAEtF,CAAC;AAEF,yCAAyC;AACzC,MAAM,WAAW,0BAA0B;IAC1C;;;;;;OAMG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,gDAAgD;AAChD,MAAM,WAAW,mBAAmB;IACnC,2EAA2E;IAC3E,KAAK,EAAE,gBAAgB,CAAC;IACxB,kGAAkG;IAClG,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,2BAA2B,GACvC,SAAS,oBAAoB,GAAG,YAAY,EAC5C,MAAM,SAAS,EACf,SAAS,0BAA0B,EACnC,KAAK,MAAM,KACT,OAAO,CAAC,mBAAmB,CAqD7B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,8BAA8B,GAC1C,OAAO,gBAAgB,EACvB,MAAM,SAAS,EACf,KAAK,MAAM,KACT,iBAuEF,CAAC"}
@@ -9,11 +9,12 @@
9
9
  *
10
10
  * @module
11
11
  */
12
+ import { DEV } from 'esm-env';
12
13
  import {} from '../runtime/deps.js';
13
14
  import { write_file_atomic } from '../runtime/fs.js';
14
15
  import { get_app_dir } from '../cli/config.js';
15
16
  import { ACCOUNT_ID_KEY, AUTH_API_TOKEN_ID_KEY, CREDENTIAL_TYPE_KEY } from '../hono_context.js';
16
- import { ERROR_INVALID_DAEMON_TOKEN, ERROR_KEEPER_ACCOUNT_NOT_CONFIGURED, } from '../http/error_schemas.js';
17
+ import { is_browser_context } from '../http/origin.js';
17
18
  import { query_role_grant_find_account_id_for_role } from './role_grant_queries.js';
18
19
  import { ROLE_KEEPER } from './role_schema.js';
19
20
  import { DaemonToken, DAEMON_TOKEN_HEADER, generate_daemon_token, validate_daemon_token, } from './daemon_token.js';
@@ -137,9 +138,24 @@ export const start_daemon_token_rotation = async (runtime, deps, options, log) =
137
138
  *
138
139
  * Checks the `X-Daemon-Token` header. Behavior:
139
140
  * - No header: pass through (don't touch existing context).
140
- * - Header present + Zod-invalid: return 401 (fail-closed).
141
- * - Header present + invalid value: return 401 (fail-closed, no downgrade).
142
- * - Header present + valid + `keeper_account_id` null: return 503.
141
+ * - Header present + `Origin` / `Referer` present: discard the credential
142
+ * (browser context) and pass through — daemon tokens are loopback-only and
143
+ * never carry an `Origin` in production, so a header-bearing request is not
144
+ * a legitimate daemon caller. Mirrors the bearer guard: `next()` rather than
145
+ * 401, so downstream auth enforcement returns `credential_type_required`
146
+ * (not a hard fail). Silent on the wire (anti-enumeration); in `DEV` only,
147
+ * sets `X-Fuz-Auth-Debug: daemon_token_discarded_browser_context`.
148
+ * - Header present + Zod-invalid (malformed): soft-fail discard (pass through,
149
+ * not 401) — mirrors the bearer guard and the Rust spine's `resolve.rs`
150
+ * (`None`). Downstream a daemon-gated action returns `credential_type_required`;
151
+ * a public action proceeds anonymous.
152
+ * - Header present + invalid value (not the current/previous token): soft-fail
153
+ * discard (pass through, not 401) — same downstream behavior.
154
+ * - Header present + valid + `keeper_account_id` null (still pre-bootstrap
155
+ * after the lazy refresh): soft-fail discard (pass through, not 503) —
156
+ * mirrors the Rust spine's `resolve.rs` (`None`), so the request falls
157
+ * through to anonymous and a daemon-gated action returns
158
+ * `credential_type_required` downstream.
143
159
  * - Header present + valid + ok: set `c.var.auth_account_id =
144
160
  * state.keeper_account_id`, `CREDENTIAL_TYPE_KEY = 'daemon_token'`
145
161
  * (overrides any existing session / bearer identity).
@@ -150,23 +166,50 @@ export const start_daemon_token_rotation = async (runtime, deps, options, log) =
150
166
  * an explicit `acting` value.
151
167
  *
152
168
  * @param state - the daemon token runtime state
169
+ * @param deps - query dependencies (pool-level db for keeper-account resolution)
170
+ * @param log - the logger instance
153
171
  * @mutates Hono context - sets `ACCOUNT_ID_KEY`, `CREDENTIAL_TYPE_KEY`, and `AUTH_API_TOKEN_ID_KEY` on a valid token
154
172
  */
155
- export const create_daemon_token_middleware = (state, deps) => {
173
+ export const create_daemon_token_middleware = (state, deps, log) => {
156
174
  return async (c, next) => {
157
175
  const token_header = c.req.header(DAEMON_TOKEN_HEADER);
158
176
  if (!token_header) {
159
177
  await next();
160
178
  return;
161
179
  }
162
- // Zod-validate the token format at the I/O boundary
180
+ // Silently discard daemon tokens in browser context (`is_browser_context`
181
+ // — Origin or Referer present) — mirrors the bearer guard (and the Rust
182
+ // spine's `resolve.rs`, which returns `None`). Daemon tokens are
183
+ // loopback-only and never carry an `Origin` in production, so a
184
+ // header-bearing request is not a legitimate daemon caller. Discards
185
+ // (next()) rather than 401 so the dispatcher returns
186
+ // `credential_type_required` downstream rather than a hard fail.
187
+ if (is_browser_context(c)) {
188
+ log.debug('daemon token auth rejected: browser context (Origin/Referer present)');
189
+ if (DEV)
190
+ c.header('X-Fuz-Auth-Debug', 'daemon_token_discarded_browser_context');
191
+ await next();
192
+ return;
193
+ }
194
+ // Zod-validate the token format at the I/O boundary. A malformed token is
195
+ // a soft-fail discard (pass through), not a 401 — mirroring the bearer
196
+ // guard and the Rust spine's `resolve.rs`, which return `None` on an
197
+ // unparseable credential. The request falls through: on a daemon-gated
198
+ // action the dispatcher returns `credential_type_required` downstream; on
199
+ // a public action it proceeds anonymous.
163
200
  const parse_result = DaemonToken.safeParse(token_header);
164
201
  if (!parse_result.success) {
165
- return c.json({ error: ERROR_INVALID_DAEMON_TOKEN }, 401);
202
+ log.debug('daemon token auth soft-fail: malformed token');
203
+ await next();
204
+ return;
166
205
  }
167
- // fail-closed: header present but invalid token value
206
+ // Well-formed but not the current/previous token — soft-fail discard, not
207
+ // a 401, for the same reason: no downgrade, falls through to downstream
208
+ // `credential_type_required` (matching the Rust spine's `None`).
168
209
  if (!validate_daemon_token(parse_result.data, state)) {
169
- return c.json({ error: ERROR_INVALID_DAEMON_TOKEN }, 401);
210
+ log.debug('daemon token auth soft-fail: token not found or invalid');
211
+ await next();
212
+ return;
170
213
  }
171
214
  // daemon token valid — resolve keeper account. `start_daemon_token_rotation`
172
215
  // resolves the keeper once at startup, but rotation often starts before the
@@ -176,8 +219,16 @@ export const create_daemon_token_middleware = (state, deps) => {
176
219
  if (!state.keeper_account_id) {
177
220
  state.keeper_account_id = await resolve_keeper_account_id(deps);
178
221
  }
222
+ // Valid token but no keeper configured (still pre-bootstrap after the lazy
223
+ // refresh) — soft-fail discard (pass through), not a 503. Mirrors the Rust
224
+ // spine's `resolve.rs`, which returns `None` when the daemon leg can't
225
+ // resolve a keeper: the request falls through to the next auth leg →
226
+ // anonymous, so a daemon-gated action returns `credential_type_required`
227
+ // downstream. The no-keeper state is a transient pre-bootstrap window.
179
228
  if (!state.keeper_account_id) {
180
- return c.json({ error: ERROR_KEEPER_ACCOUNT_NOT_CONFIGURED }, 503);
229
+ log.debug('daemon token auth soft-fail: keeper account not configured');
230
+ await next();
231
+ return;
181
232
  }
182
233
  c.set(ACCOUNT_ID_KEY, state.keeper_account_id);
183
234
  c.set(CREDENTIAL_TYPE_KEY, 'daemon_token');
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,4BAA4B,CAAC;AAG/D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACrC,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,yDAAyD;IACzD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,gBAAgB,CAAC;IACtC,oFAAoF;IACpF,sBAAsB,EAAE,WAAW,GAAG,IAAI,CAAC;CAC3C;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,4BAA4B,GACxC,MAAM,OAAO,EACb,SAAS,qBAAqB,KAC5B,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAwE/B,CAAC"}
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,4BAA4B,CAAC;AAG/D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACrC,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,yDAAyD;IACzD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,gBAAgB,CAAC;IACtC,oFAAoF;IACpF,sBAAsB,EAAE,WAAW,GAAG,IAAI,CAAC;CAC3C;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,4BAA4B,GACxC,MAAM,OAAO,EACb,SAAS,qBAAqB,KAC5B,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAgF/B,CAAC"}
@@ -61,12 +61,16 @@ export const create_auth_middleware_specs = async (deps, options) => {
61
61
  ];
62
62
  if (daemon_token_state) {
63
63
  const { create_daemon_token_middleware } = await import('./daemon_token_middleware.js');
64
- const daemon_token_middleware = create_daemon_token_middleware(daemon_token_state, query_deps);
64
+ const daemon_token_middleware = create_daemon_token_middleware(daemon_token_state, query_deps, deps.log);
65
65
  specs.push({
66
66
  name: 'daemon_token',
67
67
  path,
68
68
  handler: daemon_token_middleware,
69
- errors: { 401: ApiError, 500: ApiError, 503: ApiError },
69
+ // Soft-fails (discards) on every non-success path — browser context,
70
+ // malformed/invalid token, and no-keeper all `next()` through to the
71
+ // dispatcher's credential gate (matching the Rust spine's `None`). The
72
+ // layer returns no error response of its own.
73
+ errors: {},
70
74
  });
71
75
  }
72
76
  return specs;
@@ -36,7 +36,7 @@
36
36
  */
37
37
  import { is_role_grant_active } from './account_schema.js';
38
38
  import { hash_session_token, session_touch_fire_and_forget, query_session_get_valid, } from './session_queries.js';
39
- import { query_account_by_id, query_actor_by_id, query_actors_by_account, } from './account_queries.js';
39
+ import { query_account_by_id, query_actor_by_id, query_active_actors_by_account, } from './account_queries.js';
40
40
  import { query_role_grant_find_active_for_actor } from './role_grant_queries.js';
41
41
  import { ACCOUNT_ID_KEY, AUTH_API_TOKEN_ID_KEY, CREDENTIAL_TYPE_KEY, TEST_CONTEXT_PRESET_KEY, } from '../hono_context.js';
42
42
  import { is_public_auth, needs_actor } from '../http/auth_shape.js';
@@ -174,7 +174,7 @@ export const has_any_scoped_role = (ctx, roles, scope_id, now = new Date()) => {
174
174
  * @param acting_actor_id - the requested acting actor id, or `undefined`
175
175
  */
176
176
  export const resolve_acting_actor = async (deps, account_id, acting_actor_id) => {
177
- const actors = await query_actors_by_account(deps, account_id);
177
+ const actors = await query_active_actors_by_account(deps, account_id);
178
178
  if (actors.length === 0)
179
179
  return { ok: false, reason: 'no_actors' };
180
180
  if (acting_actor_id == null) {
package/dist/db/CLAUDE.md CHANGED
@@ -15,9 +15,12 @@ the code.
15
15
  - `db.ts` — `Db` abstraction over pg + PGlite, `query` / `query_one` /
16
16
  `transaction`, `no_nested_transaction` guard.
17
17
  - `create_db.ts` — URL-based driver auto-detection (`postgres` /
18
- `pglite-file` / `pglite-memory`); registers the int8 parser so
19
- `BIGSERIAL` columns read as JS numbers.
20
- - `db_pg.ts` / `db_pglite.ts` — driver adapters.
18
+ `pglite-file` / `pglite-memory`).
19
+ - `db_pg.ts` / `db_pglite.ts` — driver adapters. `db_pg.ts` also exports
20
+ `register_pg_type_parsers`, the shared int8→number coercion (so
21
+ `BIGSERIAL` columns read as JS numbers, matching PGlite) — called by both
22
+ `create_db` and the test pg factory (`testing/db.ts`) so prod and tests
23
+ read the same wire shape from one registration site.
21
24
  - `migrate.ts` — advisory-lock migration runner (`run_migrations`,
22
25
  `baseline`), `Migration` / `MigrationNamespace` / `MigrationError`.
23
26
  - `query_deps.ts` — `QueryDeps = {db}`, the first param to every `query_*`.
@@ -394,8 +394,15 @@ const build_shared_with_sql = (order_column, order_direction) => `${CALLER_ROLE_
394
394
  * Returns `null` (rather than `[]`) when no refs are present — the column
395
395
  * is nullable and the `idx_cell_refs` partial index is `WHERE refs IS NOT
396
396
  * NULL`, so an empty array would force every cell into the index.
397
+ *
398
+ * Sorts the deduped refs lexicographically so the stored `text[]` is
399
+ * deterministic and byte-identical to the Rust twin (`fuz_cell`'s
400
+ * `extract_refs`, which sorts the same `blake3:`-prefixed element form).
401
+ * The shared `fact_hash_extract_refs` deliberately preserves traversal
402
+ * order per its contract, so the sort lives here at the storage boundary,
403
+ * not in the util.
397
404
  */
398
405
  const derive_refs = (data) => {
399
406
  const refs = fact_hash_extract_refs(data);
400
- return refs.length > 0 ? refs : null;
407
+ return refs.length > 0 ? refs.sort() : null;
401
408
  };
@@ -1 +1 @@
1
- {"version":3,"file":"create_db.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/create_db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAC,EAAE,EAAE,MAAM,EAAC,MAAM,SAAS,CAAC;AAIxC,yCAAyC;AACzC,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,EAAE,CAAC;IACP,iFAAiF;IACjF,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,SAAS,GAAU,cAAc,MAAM,KAAG,OAAO,CAAC,cAAc,CA6C5E,CAAC"}
1
+ {"version":3,"file":"create_db.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/create_db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAC,EAAE,EAAE,MAAM,EAAC,MAAM,SAAS,CAAC;AAIxC,yCAAyC;AACzC,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,EAAE,CAAC;IACP,iFAAiF;IACjF,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,SAAS,GAAU,cAAc,MAAM,KAAG,OAAO,CAAC,cAAc,CAiC5E,CAAC"}
@@ -12,7 +12,7 @@
12
12
  *
13
13
  * @module
14
14
  */
15
- import { create_pg_db } from './db_pg.js';
15
+ import { create_pg_db, register_pg_type_parsers } from './db_pg.js';
16
16
  import { create_pglite_db } from './db_pglite.js';
17
17
  /**
18
18
  * Create a database connection based on a URL.
@@ -32,19 +32,7 @@ import { create_pglite_db } from './db_pglite.js';
32
32
  export const create_db = async (database_url) => {
33
33
  if (database_url.startsWith('postgres://') || database_url.startsWith('postgresql://')) {
34
34
  const { default: pg } = await import('pg');
35
- // Parse int8 (BIGINT) as a JS number. pg defaults to returning int8
36
- // as a string to avoid 2^53 precision loss; our only int8 column
37
- // today (`audit_log.seq`) stays well under that bound, and reading
38
- // as number keeps the wire shape uniform across the SERIAL→BIGSERIAL
39
- // widening.
40
- //
41
- // CAVEAT: pg.types.setTypeParser mutates pg.types globally — every
42
- // pg.Pool in the process inherits the coercion, including pools the
43
- // consumer constructs against unrelated databases. Any future int8
44
- // column that could legitimately exceed 2^53 (file sizes, byte
45
- // offsets) will silently round; if one lands, localize via a
46
- // per-pool `types` override instead of widening this global parser.
47
- pg.types.setTypeParser(20, (val) => Number(val));
35
+ await register_pg_type_parsers();
48
36
  const pool = new pg.Pool({ connectionString: database_url });
49
37
  const { db, close } = create_pg_db(pool);
50
38
  return {
@@ -2,13 +2,41 @@
2
2
  * PostgreSQL driver adapter for `Db`.
3
3
  *
4
4
  * Provides `create_pg_db()` to construct a `Db` backed by a `pg.Pool`.
5
- * Only imports `pg` types — the actual `pg` package is dynamically imported
6
- * by callers (e.g., `create_db`) that construct the `Pool`.
5
+ * Statically imports only `pg` types; the `pg` runtime is dynamically
6
+ * imported where needed — by callers (e.g., `create_db`) that construct the
7
+ * `Pool`, and by `register_pg_type_parsers` to reach `pg.types`. pglite-only
8
+ * consumers never reach either path, so `pg` stays an optional peer dep.
7
9
  *
8
10
  * @module
9
11
  */
10
12
  import type { Pool } from 'pg';
11
13
  import { type DbDriverResult } from './db.js';
14
+ /**
15
+ * Register the shared pg type-parser overrides on the module-global `pg.types`.
16
+ *
17
+ * Dynamically imports the `pg` runtime (so pglite-only consumers, who never
18
+ * call this, don't need `pg` installed) and coerces int8 (`BIGINT`, OID 20)
19
+ * to a JS number. pg defaults to returning
20
+ * int8 as a string to avoid 2^53 precision loss; our int8 columns today
21
+ * (`audit_log.seq`, `cell_history.id`, `fact.size`) stay well under that
22
+ * bound, and reading as a number keeps the wire shape uniform with PGlite —
23
+ * which returns int8 as a number — so `AuditLogEvent.seq` and friends
24
+ * validate identically across both drivers.
25
+ *
26
+ * Both `create_db` (production) and the test pg factory (`testing/db.ts`)
27
+ * register through this single site so test and prod read the same shape; a
28
+ * divergence here is exactly the test/prod write-semantics gap the parser
29
+ * exists to close.
30
+ *
31
+ * CAVEAT: `setTypeParser` mutates `pg.types` globally — every `pg.Pool` in
32
+ * the process inherits the coercion, including pools the consumer constructs
33
+ * against unrelated databases. Any future int8 column that could legitimately
34
+ * exceed 2^53 (byte offsets, counters) will silently round; if one lands,
35
+ * localize via a per-pool `types` override instead of widening this global.
36
+ *
37
+ * @mutates `pg.types` - registers the int8 parser on the global pg type registry
38
+ */
39
+ export declare const register_pg_type_parsers: () => Promise<void>;
12
40
  /**
13
41
  * Create a `Db` backed by a `pg.Pool`.
14
42
  *
@@ -1 +1 @@
1
- {"version":3,"file":"db_pg.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/db_pg.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,IAAI,CAAC;AAE7B,OAAO,EAA4B,KAAK,cAAc,EAAC,MAAM,SAAS,CAAC;AA2BvE;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,IAAI,KAAG,cAGxC,CAAC"}
1
+ {"version":3,"file":"db_pg.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/db_pg.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,IAAI,CAAC;AAE7B,OAAO,EAA4B,KAAK,cAAc,EAAC,MAAM,SAAS,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,wBAAwB,QAAa,OAAO,CAAC,IAAI,CAG7D,CAAC;AA2BF;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,IAAI,KAAG,cAGxC,CAAC"}
package/dist/db/db_pg.js CHANGED
@@ -2,12 +2,43 @@
2
2
  * PostgreSQL driver adapter for `Db`.
3
3
  *
4
4
  * Provides `create_pg_db()` to construct a `Db` backed by a `pg.Pool`.
5
- * Only imports `pg` types — the actual `pg` package is dynamically imported
6
- * by callers (e.g., `create_db`) that construct the `Pool`.
5
+ * Statically imports only `pg` types; the `pg` runtime is dynamically
6
+ * imported where needed — by callers (e.g., `create_db`) that construct the
7
+ * `Pool`, and by `register_pg_type_parsers` to reach `pg.types`. pglite-only
8
+ * consumers never reach either path, so `pg` stays an optional peer dep.
7
9
  *
8
10
  * @module
9
11
  */
10
12
  import { Db, no_nested_transaction } from './db.js';
13
+ /**
14
+ * Register the shared pg type-parser overrides on the module-global `pg.types`.
15
+ *
16
+ * Dynamically imports the `pg` runtime (so pglite-only consumers, who never
17
+ * call this, don't need `pg` installed) and coerces int8 (`BIGINT`, OID 20)
18
+ * to a JS number. pg defaults to returning
19
+ * int8 as a string to avoid 2^53 precision loss; our int8 columns today
20
+ * (`audit_log.seq`, `cell_history.id`, `fact.size`) stay well under that
21
+ * bound, and reading as a number keeps the wire shape uniform with PGlite —
22
+ * which returns int8 as a number — so `AuditLogEvent.seq` and friends
23
+ * validate identically across both drivers.
24
+ *
25
+ * Both `create_db` (production) and the test pg factory (`testing/db.ts`)
26
+ * register through this single site so test and prod read the same shape; a
27
+ * divergence here is exactly the test/prod write-semantics gap the parser
28
+ * exists to close.
29
+ *
30
+ * CAVEAT: `setTypeParser` mutates `pg.types` globally — every `pg.Pool` in
31
+ * the process inherits the coercion, including pools the consumer constructs
32
+ * against unrelated databases. Any future int8 column that could legitimately
33
+ * exceed 2^53 (byte offsets, counters) will silently round; if one lands,
34
+ * localize via a per-pool `types` override instead of widening this global.
35
+ *
36
+ * @mutates `pg.types` - registers the int8 parser on the global pg type registry
37
+ */
38
+ export const register_pg_type_parsers = async () => {
39
+ const { types } = await import('pg');
40
+ types.setTypeParser(20, (val) => Number(val));
41
+ };
11
42
  /**
12
43
  * Create a transaction implementation for a `pg.Pool`.
13
44
  *
@@ -1 +1 @@
1
- {"version":3,"file":"fact_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/fact_queries.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fact_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/fact_queries.ts"],"names":[],"mappings":"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"}
@@ -203,8 +203,8 @@ confirmation.
203
203
 
204
204
  - **Validation**: `ERROR_INVALID_REQUEST_BODY`, `ERROR_INVALID_JSON_BODY`, `ERROR_INVALID_ROUTE_PARAMS`, `ERROR_INVALID_QUERY_PARAMS`
205
205
  - **Auth**: `ERROR_AUTHENTICATION_REQUIRED`, `ERROR_INSUFFICIENT_PERMISSIONS`, `ERROR_CREDENTIAL_TYPE_REQUIRED`, `ERROR_RATE_LIMIT_EXCEEDED`, `ERROR_INVALID_CREDENTIALS`, `ERROR_PAYLOAD_TOO_LARGE`
206
- - **Origin + bearer**: `ERROR_FORBIDDEN_ORIGIN`, `ERROR_BEARER_REJECTED_BROWSER`, `ERROR_INVALID_TOKEN`, `ERROR_ACCOUNT_NOT_FOUND`
207
- - **Keeper/daemon**: `ERROR_INVALID_DAEMON_TOKEN`, `ERROR_KEEPER_ACCOUNT_NOT_CONFIGURED`, `ERROR_KEEPER_ACCOUNT_NOT_FOUND`
206
+ - **Origin + bearer**: `ERROR_FORBIDDEN_ORIGIN`, `ERROR_INVALID_TOKEN`, `ERROR_ACCOUNT_NOT_FOUND`
207
+ - **Keeper/daemon**: `ERROR_KEEPER_ACCOUNT_NOT_FOUND`
208
208
  - **Bootstrap**: `ERROR_ALREADY_BOOTSTRAPPED`, `ERROR_TOKEN_FILE_MISSING`
209
209
  - **Signup/invites**: `ERROR_NO_MATCHING_INVITE`, `ERROR_SIGNUP_CONFLICT`, `ERROR_INVITE_NOT_FOUND`, `ERROR_INVITE_DUPLICATE`, `ERROR_INVITE_ACCOUNT_EXISTS_USERNAME`, `ERROR_INVITE_ACCOUNT_EXISTS_EMAIL`
210
210
  - **Admin**: `ERROR_ROLE_NOT_WEB_GRANTABLE`, `ERROR_ROLE_GRANT_NOT_FOUND`, `ERROR_INVALID_EVENT_TYPE`
@@ -320,6 +320,7 @@ CSRF is handled by `SameSite: strict` on session cookies (`auth/session_middlewa
320
320
  - `parse_allowed_origins(env_value)` — comma-separated patterns → `Array<RegExp>`
321
321
  - `should_allow_origin(origin, patterns)` — case-insensitive match
322
322
  - `verify_request_source(allowed_patterns)` — Hono handler: `Origin` present → must match allowlist or 403 `ERROR_FORBIDDEN_ORIGIN`; no `Origin` → allow through (curl, CLI, token auth is primary control)
323
+ - `is_browser_context(c)` — true when `Origin` or `Referer` is present (`!== undefined`, so empty-string counts). The bearer + daemon-token middleware discard their credential when true; twin of the Rust spine's `bearer_auth::is_browser_context`. Distinct from `verify_request_source` (allowlist gate) — this only detects browser context
323
324
 
324
325
  **Origin-only by design.** Fetch spec mandates `Origin` on every unsafe
325
326
  method, so a real browser request on any state-changing surface always
@@ -43,8 +43,6 @@ export declare const ERROR_INVALID_CREDENTIALS: "invalid_credentials";
43
43
  export declare const ERROR_PAYLOAD_TOO_LARGE: "payload_too_large";
44
44
  /** Request origin not in allowlist. */
45
45
  export declare const ERROR_FORBIDDEN_ORIGIN: "forbidden_origin";
46
- /** Bearer token sent with Origin/Referer header (browser context). */
47
- export declare const ERROR_BEARER_REJECTED_BROWSER: "bearer_token_rejected_in_browser_context";
48
46
  /** Bearer token failed validation (missing, malformed, or revoked). */
49
47
  export declare const ERROR_INVALID_TOKEN: "invalid_token";
50
48
  /** Token references a deleted account. */
@@ -82,10 +80,6 @@ export declare const ERROR_NO_ACTORS_ON_ACCOUNT: "no_actors_on_account";
82
80
  * `ERROR_NO_ACTORS_ON_ACCOUNT` (the actor list enumerated empty).
83
81
  */
84
82
  export declare const ERROR_ACCOUNT_VANISHED: "account_vanished";
85
- /** Daemon token header present but malformed or not matching current/previous token. */
86
- export declare const ERROR_INVALID_DAEMON_TOKEN: "invalid_daemon_token";
87
- /** Daemon token valid but keeper account not yet resolved (pre-bootstrap). */
88
- export declare const ERROR_KEEPER_ACCOUNT_NOT_CONFIGURED: "keeper_account_not_configured";
89
83
  /** Keeper account ID set but account row not found. */
90
84
  export declare const ERROR_KEEPER_ACCOUNT_NOT_FOUND: "keeper_account_not_found";
91
85
  /** Bootstrap lock already acquired — system already bootstrapped. */
@@ -1 +1 @@
1
- {"version":3,"file":"error_schemas.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/error_schemas.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"error_schemas.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/error_schemas.ts"],"names":[],"mappings":"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"}