@fuzdev/fuz_app 0.109.0 → 0.110.1

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 (84) hide show
  1. package/dist/auth/CLAUDE.md +6 -5
  2. package/dist/auth/account_action_specs.d.ts +18 -0
  3. package/dist/auth/account_action_specs.d.ts.map +1 -1
  4. package/dist/auth/account_action_specs.js +14 -1
  5. package/dist/auth/account_actions.d.ts.map +1 -1
  6. package/dist/auth/account_actions.js +6 -2
  7. package/dist/auth/bootstrap_account.d.ts +7 -2
  8. package/dist/auth/bootstrap_account.d.ts.map +1 -1
  9. package/dist/auth/bootstrap_account.js +9 -4
  10. package/dist/auth/bootstrap_routes.d.ts +12 -4
  11. package/dist/auth/bootstrap_routes.d.ts.map +1 -1
  12. package/dist/auth/bootstrap_routes.js +15 -6
  13. package/dist/auth/daemon_token_middleware.d.ts +7 -69
  14. package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
  15. package/dist/auth/daemon_token_middleware.js +8 -104
  16. package/dist/auth/deps.d.ts +9 -5
  17. package/dist/auth/deps.d.ts.map +1 -1
  18. package/dist/auth/middleware.js +1 -1
  19. package/dist/auth/request_context.d.ts +1 -3
  20. package/dist/auth/request_context.d.ts.map +1 -1
  21. package/dist/auth/request_context.js +2 -5
  22. package/dist/auth/session_cookie.d.ts +18 -36
  23. package/dist/auth/session_cookie.d.ts.map +1 -1
  24. package/dist/auth/session_cookie.js +16 -26
  25. package/dist/auth/session_middleware.d.ts +1 -1
  26. package/dist/auth/session_middleware.d.ts.map +1 -1
  27. package/dist/auth/session_middleware.js +1 -4
  28. package/dist/auth/session_queries.d.ts +14 -30
  29. package/dist/auth/session_queries.d.ts.map +1 -1
  30. package/dist/auth/session_queries.js +15 -45
  31. package/dist/auth/token_lifetime.d.ts +51 -0
  32. package/dist/auth/token_lifetime.d.ts.map +1 -0
  33. package/dist/auth/token_lifetime.js +54 -0
  34. package/dist/dev/setup.d.ts +5 -9
  35. package/dist/dev/setup.d.ts.map +1 -1
  36. package/dist/dev/setup.js +18 -21
  37. package/dist/runtime/deno.d.ts.map +1 -1
  38. package/dist/runtime/deno.js +27 -1
  39. package/dist/runtime/deps.d.ts +38 -4
  40. package/dist/runtime/deps.d.ts.map +1 -1
  41. package/dist/runtime/fs.d.ts +24 -5
  42. package/dist/runtime/fs.d.ts.map +1 -1
  43. package/dist/runtime/fs.js +38 -7
  44. package/dist/runtime/mock.d.ts +7 -0
  45. package/dist/runtime/mock.d.ts.map +1 -1
  46. package/dist/runtime/mock.js +41 -1
  47. package/dist/runtime/node.d.ts.map +1 -1
  48. package/dist/runtime/node.js +7 -1
  49. package/dist/runtime/secure_file.d.ts +71 -0
  50. package/dist/runtime/secure_file.d.ts.map +1 -0
  51. package/dist/runtime/secure_file.js +125 -0
  52. package/dist/server/app_backend.d.ts +6 -5
  53. package/dist/server/app_backend.d.ts.map +1 -1
  54. package/dist/server/app_backend.js +2 -3
  55. package/dist/testing/CLAUDE.md +15 -1
  56. package/dist/testing/admin_integration.d.ts.map +1 -1
  57. package/dist/testing/admin_integration.js +57 -20
  58. package/dist/testing/app_server.d.ts.map +1 -1
  59. package/dist/testing/app_server.js +13 -8
  60. package/dist/testing/audit_completeness.d.ts.map +1 -1
  61. package/dist/testing/audit_completeness.js +5 -1
  62. package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -1
  63. package/dist/testing/cross_backend/cell_grant_role.js +34 -1
  64. package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
  65. package/dist/testing/cross_backend/conformance_table.js +2 -1
  66. package/dist/testing/cross_backend/setup.js +1 -1
  67. package/dist/testing/cross_backend/token_lifetime.d.ts +15 -0
  68. package/dist/testing/cross_backend/token_lifetime.d.ts.map +1 -0
  69. package/dist/testing/cross_backend/token_lifetime.js +99 -0
  70. package/dist/testing/cross_backend/token_scope_surface.js +1 -1
  71. package/dist/testing/daemon_token_rotation.d.ts +83 -0
  72. package/dist/testing/daemon_token_rotation.d.ts.map +1 -0
  73. package/dist/testing/daemon_token_rotation.js +99 -0
  74. package/dist/testing/integration.d.ts.map +1 -1
  75. package/dist/testing/integration.js +15 -3
  76. package/dist/testing/stubs.d.ts.map +1 -1
  77. package/dist/testing/stubs.js +4 -4
  78. package/dist/ui/AccountSessions.svelte +0 -5
  79. package/dist/ui/AccountSessions.svelte.d.ts.map +1 -1
  80. package/dist/ui/AdminOverview.svelte +7 -4
  81. package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
  82. package/dist/ui/AdminSessions.svelte +0 -5
  83. package/dist/ui/AdminSessions.svelte.d.ts.map +1 -1
  84. package/package.json +1 -1
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Hardened secret-file read — the TS twin of the Rust spine's
3
+ * `fuz_sys::secure_file::load_secure_file`.
4
+ *
5
+ * The file this guards is typically the highest-value credential on the host
6
+ * (the bootstrap token mints the keeper account), so the read fails loud
7
+ * rather than degrading: a symlink, a group/other-accessible mode, or an
8
+ * oversized file is refused, never returned. The deploy recipe places the
9
+ * token at `0600`, so the check ratifies the shape zap produces; a
10
+ * hand-placed `0644` file fails at boot instead of being read.
11
+ *
12
+ * The Node implementation lives here (`load_secure_file_node`, wired as
13
+ * `create_node_runtime().read_secure_file`); the Deno runtime implements the
14
+ * same contract over `Deno.open` in `runtime/deno.ts`, and the mock honors
15
+ * the mode/size checks over its in-memory map. All three refuse through the
16
+ * shared `assert_secure_mode` / `assert_secure_size` / `read_secure_bounded`
17
+ * helpers so the checks — and their operator-facing messages — can't drift
18
+ * between runtimes.
19
+ *
20
+ * @module
21
+ */
22
+ import { constants } from 'node:fs';
23
+ import { open } from 'node:fs/promises';
24
+ import process from 'node:process';
25
+ /**
26
+ * Maximum size for secure files. Prevents DoS from unexpectedly large files;
27
+ * 4 KiB is generous for token/key material. Matches the Rust twin's
28
+ * `MAX_SECURE_FILE_SIZE`.
29
+ */
30
+ export const MAX_SECURE_FILE_SIZE = 4096;
31
+ /**
32
+ * Whether POSIX file modes are meaningful on this platform. On Windows Node
33
+ * synthesizes modes (always group/other-readable), so enforcing the check
34
+ * would refuse every file; the Rust twin is Unix-only and never faces this.
35
+ * The mock runtime checks unconditionally — its modes are simulated.
36
+ */
37
+ const modes_apply = process.platform !== 'win32';
38
+ /**
39
+ * Refuse any group/other-accessible mode (only `0600`/`0400` pass).
40
+ *
41
+ * Callers own the platform gating: the real runtimes skip the check where
42
+ * modes aren't meaningful (Node on Windows, a `null` Deno mode); the mock
43
+ * checks its simulated modes unconditionally.
44
+ *
45
+ * @throws Error naming the path, the offending mode, and the `chmod` fix
46
+ */
47
+ export const assert_secure_mode = (path, mode) => {
48
+ if ((mode & 0o077) !== 0) {
49
+ throw new Error(`insecure permissions on ${path}: ${(mode & 0o777).toString(8)} (expected 0600) — fix with: chmod 600 ${path}`);
50
+ }
51
+ };
52
+ /**
53
+ * Refuse a byte count over `MAX_SECURE_FILE_SIZE`.
54
+ *
55
+ * @throws Error naming the path, the size, and the cap
56
+ */
57
+ export const assert_secure_size = (path, size) => {
58
+ if (size > MAX_SECURE_FILE_SIZE) {
59
+ throw new Error(`secure file too large: ${path} (${size} bytes, max ${MAX_SECURE_FILE_SIZE})`);
60
+ }
61
+ };
62
+ /**
63
+ * Drain `read_chunk` into a cap-bounded buffer.
64
+ *
65
+ * Reads up to cap + 1 bytes so a file growing between stat and read is still
66
+ * caught (the stat-time size check alone races). `read_chunk` fills the given
67
+ * target from the file's current position and returns the bytes read
68
+ * (`null`/`0` = EOF) — the seam that lets the Node and Deno handle APIs share
69
+ * one loop.
70
+ *
71
+ * @throws Error when more than `MAX_SECURE_FILE_SIZE` bytes arrive
72
+ */
73
+ export const read_secure_bounded = async (path, read_chunk) => {
74
+ const buffer = new Uint8Array(MAX_SECURE_FILE_SIZE + 1);
75
+ let total = 0;
76
+ while (total < buffer.length) {
77
+ const n = await read_chunk(buffer.subarray(total));
78
+ if (n === null || n === 0)
79
+ break;
80
+ total += n;
81
+ }
82
+ assert_secure_size(path, total);
83
+ return buffer.subarray(0, total);
84
+ };
85
+ /**
86
+ * Read a secret file with fail-loud checks (Node implementation).
87
+ *
88
+ * - `O_NOFOLLOW` atomically rejects symlinks during open (no TOCTOU window)
89
+ * - the permission check runs on the open descriptor, not the path, so the
90
+ * file can't be swapped between check and read — any group/other access
91
+ * (not `0600`/`0400`) is refused
92
+ * - a size cap bounds the read; the read itself is limited to cap + 1 so a
93
+ * file growing between stat and read is still caught
94
+ *
95
+ * @param path - path to the secret file
96
+ * @returns the file's bytes
97
+ * @throws Error on a missing file, symlink, permissive mode, oversized file, or I/O failure
98
+ */
99
+ export const load_secure_file_node = async (path) => {
100
+ let handle;
101
+ try {
102
+ handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
103
+ }
104
+ catch (err) {
105
+ // ELOOP means the path was a symlink (O_NOFOLLOW refused it).
106
+ if (err.code === 'ELOOP') {
107
+ throw new Error(`secure file is a symlink: ${path}`);
108
+ }
109
+ throw err;
110
+ }
111
+ try {
112
+ const s = await handle.stat();
113
+ if (modes_apply)
114
+ assert_secure_mode(path, s.mode);
115
+ assert_secure_size(path, s.size);
116
+ // `position: null` reads sequentially from the handle's own cursor.
117
+ return await read_secure_bounded(path, async (target) => {
118
+ const { bytesRead } = await handle.read(target, 0, target.length, null);
119
+ return bytesRead;
120
+ });
121
+ }
122
+ finally {
123
+ await handle.close();
124
+ }
125
+ };
@@ -16,7 +16,6 @@ import { type AuditEmitter } from '../auth/audit_emitter.js';
16
16
  import type { DbType, Db } from '../db/db.js';
17
17
  import type { Keyring } from '../auth/keyring.js';
18
18
  import type { PasswordHashDeps } from '../auth/password.js';
19
- import type { StatResult } from '../runtime/deps.js';
20
19
  import { type MigrationNamespace, type MigrationResult } from '../db/migrate.js';
21
20
  /**
22
21
  * Result of `create_app_backend()` — database metadata + deps bundle.
@@ -83,10 +82,12 @@ export declare const default_audit_factory: AuditFactory;
83
82
  * (e.g., tx uses `runtime.exit(1)` on invalid keys).
84
83
  */
85
84
  export interface CreateAppBackendOptions {
86
- /** Get file/directory stats, or null if path doesn't exist. */
87
- stat: (path: string) => Promise<StatResult | null>;
88
- /** Read a file as text. */
89
- read_text_file: (path: string) => Promise<string>;
85
+ /**
86
+ * Hardened secret-file read for the bootstrap token — pass the runtime's
87
+ * `read_secure_file` (see `FsSecureReadDeps`). Rejects symlinks,
88
+ * group/other-accessible modes, and oversized files.
89
+ */
90
+ read_secure_file: (path: string) => Promise<Uint8Array>;
90
91
  /** Delete a file. */
91
92
  delete_file: (path: string) => Promise<void>;
92
93
  /** Database connection URL (`postgres://`, `file://`, or `memory://`). */
@@ -1 +1 @@
1
- {"version":3,"file":"app_backend.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAwB,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACnF,OAAO,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAkB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAIjG;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,oIAAoI;IACpI,QAAQ,CAAC,iBAAiB,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC3D,iEAAiE;IACjE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE;IAAE,EAAE,EAAE,EAAE,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,KAAK,YAAY,CAAC;AAE7E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,EAAE,YACF,CAAC;AAEnC;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACvC,+DAA+D;IAC/D,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACnD,2BAA2B;IAC3B,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAClD,qBAAqB;IACrB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,0EAA0E;IAC1E,YAAY,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,iFAAiF;IACjF,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,6EAA6E;IAC7E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAAa,EAAE,YAAY,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,oBAAoB,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;CACzD;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,GAAU,SAAS,uBAAuB,KAAG,OAAO,CAAC,UAAU,CAmD7F,CAAC"}
1
+ {"version":3,"file":"app_backend.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAwB,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACnF,OAAO,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAkB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAIjG;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,oIAAoI;IACpI,QAAQ,CAAC,iBAAiB,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC3D,iEAAiE;IACjE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE;IAAE,EAAE,EAAE,EAAE,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,KAAK,YAAY,CAAC;AAE7E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,EAAE,YACF,CAAC;AAEnC;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACvC;;;;OAIG;IACH,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACxD,qBAAqB;IACrB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,0EAA0E;IAC1E,YAAY,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,iFAAiF;IACjF,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,6EAA6E;IAC7E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAAa,EAAE,YAAY,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,oBAAoB,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;CACzD;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,GAAU,SAAS,uBAAuB,KAAG,OAAO,CAAC,UAAU,CAiD7F,CAAC"}
@@ -39,7 +39,7 @@ export const default_audit_factory = ({ db, log }) => create_audit_emitter({ db,
39
39
  * @throws Error if `migration_namespaces` contains a namespace in `reserved_migration_namespaces`
40
40
  */
41
41
  export const create_app_backend = async (options) => {
42
- const { database_url, keyring, password, stat, read_text_file, delete_file, audit_factory } = options;
42
+ const { database_url, keyring, password, read_secure_file, delete_file, audit_factory } = options;
43
43
  const log = options.log ?? new Logger('server');
44
44
  const { db, close, db_type, db_name } = await create_db(database_url);
45
45
  // Everything after `create_db` can throw — reserved-namespace check,
@@ -69,8 +69,7 @@ export const create_app_backend = async (options) => {
69
69
  keyring,
70
70
  password,
71
71
  db,
72
- stat,
73
- read_text_file,
72
+ read_secure_file,
74
73
  delete_file,
75
74
  log,
76
75
  audit
@@ -32,7 +32,7 @@ time (never runtime), where a throwing guard would break `vite build`.
32
32
  - `stub_handler()` — fresh `Response('stub')`.
33
33
  - `stub_mw` — pass-through middleware (`async (_c, next) => next()`).
34
34
  - `stub_app_deps` — frozen `AppDeps`, every capability throwing, `audit` a no-op `AuditEmitter` from `create_test_audit_emitter`.
35
- - `create_stub_app_deps()` — factory: fresh `AppDeps` with no-op FS/keyring/password, a `create_noop_stub` DB, silent `Logger`, no-op `audit`.
35
+ - `create_stub_app_deps()` — factory: fresh `AppDeps` with no-op keyring/password/`delete_file`, a `read_secure_file` that throws ENOENT (the no-token-file state), a `create_noop_stub` DB, silent `Logger`, no-op `audit`.
36
36
  - `create_test_audit_emitter()` — no-op `AuditEmitter`; `emit` / `emit_role_grant_target` no-op, `emit_pool` resolves immediately, `notify` no-op, `add_listener` throws (use `create_recording_audit_emitter` for a listener-accepting emitter), `listener_count` returns 0.
37
37
  - `create_stub_audit_sse()` — no-op `AuditLogSse` for surface-test wiring without booting real SSE. `subscribe` returns a no-op cleanup; `on_audit_event` no-op; `registry` is a fresh `SubscriberRegistry` (live `.size` / `.close_*` for registry-state tests, isolated per call). For real SSE plumbing build via `create_audit_log_sse` against `create_test_app`.
38
38
  - `create_stub_api_middleware({include_daemon_token?})` — stub `MiddlewareSpec[]` matching `create_auth_middleware_specs`'s output (origin/session/request_context/bearer_auth, optional daemon_token) for surface generation without booting real auth. See `auth/CLAUDE.md` §Middleware for the real stack.
@@ -1418,6 +1418,20 @@ module's own tests (`account_status.test.ts`, `action_rpc.test.ts`,
1418
1418
  `serve_fact_route.db.test.ts`); fuz_app's own wiring is
1419
1419
  `cross_backend/query_shape.cross.test.ts`.
1420
1420
 
1421
+ ### API-token lifetime parity — `cross_backend/token_lifetime.ts`
1422
+
1423
+ `describe_token_lifetime_cross_tests({setup_test, rpc_path?})` — the
1424
+ imperative round-trip on `account_token_create`'s required `lifetime` union:
1425
+ a `{kind:'ttl', days}` mint returns and stores a bounded `expires_at`
1426
+ (create output + `account_token_list` read-back), `{kind:'eternal'}` stores
1427
+ NULL, and an omitted or out-of-range lifetime 400s identically on both
1428
+ spines. Exists because the action-manifest parity gate captures no param
1429
+ schemas while the Rust spine hand-parses params — a spine that silently
1430
+ dropped the field would answer the same mint with a 200 and an eternal
1431
+ token; only the stored-state read-back catches it. Ungated (mint + list are
1432
+ on every spine's standard surface). Runs both legs
1433
+ (`token_lifetime_parity.db.test.ts` + `token_lifetime.cross.test.ts`).
1434
+
1421
1435
  ### Readiness probe parity — `cross_backend/ready.ts`
1422
1436
 
1423
1437
  `describe_ready_cross_tests({setup_test, capabilities, ready_path?})` — the
@@ -1 +1 @@
1
- {"version":3,"file":"admin_integration.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/admin_integration.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAgC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAW3E,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAkB1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,0BAA0B,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,mCAAmC;IACnD;;;;OAIG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,uFAAuF;IACvF,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,4GAA4G;IAC5G,KAAK,EAAE,gBAAgB,CAAC;IACxB;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAiBD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yCAAyC,GACrD,SAAS,mCAAmC,KAC1C,IAm3BF,CAAC"}
1
+ {"version":3,"file":"admin_integration.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/admin_integration.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAgC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAW3E,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAmB1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,0BAA0B,CAAC;AAYvE;;GAEG;AACH,MAAM,WAAW,mCAAmC;IACnD;;;;OAIG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,uFAAuF;IACvF,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,4GAA4G;IAC5G,KAAK,EAAE,gBAAgB,CAAC;IACxB;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAiBD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yCAAyC,GACrD,SAAS,mCAAmC,KAC1C,IAm5BF,CAAC"}
@@ -36,6 +36,14 @@ import { rpc_call_for_spec, require_rpc_endpoint_path, resolve_rpc_endpoints_for
36
36
  import { role_grant_offer_create_action_spec, role_grant_revoke_action_spec } from '../auth/role_grant_offer_action_specs.js';
37
37
  import { admin_account_list_action_spec, ADMIN_ACCOUNT_LIST_LIMIT_MAX, admin_session_list_action_spec, admin_session_revoke_all_action_spec, admin_token_revoke_all_action_spec, audit_log_list_action_spec, audit_log_role_grant_history_action_spec } from '../auth/admin_action_specs.js';
38
38
  import { account_token_create_action_spec, account_verify_action_spec } from '../auth/account_action_specs.js';
39
+ /**
40
+ * How long a failure-audit read waits for the pool-routed row to land.
41
+ */
42
+ const AUDIT_FAILURE_ROW_TIMEOUT = 2000;
43
+ /**
44
+ * Gap between failure-audit read attempts.
45
+ */
46
+ const AUDIT_FAILURE_ROW_POLL_INTERVAL = 20;
39
47
  /**
40
48
  * Pick a role for admin-grant testing, preferring a non-admin app-defined
41
49
  * role whose `RoleSpec.grant_paths` includes `'admin'` (the
@@ -112,6 +120,43 @@ export const describe_standard_admin_integration_tests = (options) => {
112
120
  * the cross-suite contract.
113
121
  */
114
122
  const offer_and_accept = (args) => role_grant_offer_and_accept({ ...args, rpc_path });
123
+ /**
124
+ * Read `audit_log_list` for `event_type` until a failure-outcome row
125
+ * appears, or the deadline passes.
126
+ *
127
+ * Failure audits are **pool-routed** on both spines — the write is
128
+ * deliberately detached from the request transaction so the forensic row
129
+ * survives the rollback that discards the attempted mutation. A single
130
+ * read taken straight after the refusal therefore races that write. The
131
+ * TS spine wins the race by construction (`create_test_app` sets
132
+ * `await_pending_effects: true`, so the emit is awaited before the
133
+ * response returns) and the first attempt succeeds; the Rust spine emits
134
+ * from a detached task, so under load the row can land after the read —
135
+ * observed as a load-dependent drop of the revoke-all failure-audit
136
+ * cases in a busy cross-process run.
137
+ *
138
+ * The cross-backend conformance suites reach for the deterministic
139
+ * `_testing_drain_effects` barrier instead. That action is test-binary
140
+ * only, and this suite also runs against consumers' production RPC
141
+ * surfaces where it isn't mounted — hence a bounded poll.
142
+ */
143
+ const read_failure_audit_event = async (fixture, event_type) => {
144
+ const deadline = Date.now() + AUDIT_FAILURE_ROW_TIMEOUT;
145
+ for (;;) {
146
+ const audit_res = await rpc_call_for_spec({
147
+ app: { request: fixture.transport },
148
+ path: rpc_path,
149
+ spec: audit_log_list_action_spec,
150
+ params: { event_type },
151
+ headers: fixture.create_session_headers()
152
+ });
153
+ assert.ok(audit_res.ok, 'audit_log_list should succeed');
154
+ const failure = audit_res.result.events.find((e) => e.outcome === 'failure');
155
+ if (failure || Date.now() >= deadline)
156
+ return failure;
157
+ await new Promise((resolve) => setTimeout(resolve, AUDIT_FAILURE_ROW_POLL_INTERVAL));
158
+ }
159
+ };
115
160
  // --- 1. Admin account listing (RPC) ---
116
161
  describe('admin account listing', () => {
117
162
  test('admin can list all accounts', async () => {
@@ -476,15 +521,7 @@ export const describe_standard_admin_integration_tests = (options) => {
476
521
  // Failure audit row should be visible on the audit-log feed.
477
522
  // `target_account_id` is null (FK prevents referencing a missing id)
478
523
  // — the probed id is preserved under `metadata.attempted_account_id`.
479
- const audit_res = await rpc_call_for_spec({
480
- app: { request: fixture.transport },
481
- path: rpc_path,
482
- spec: audit_log_list_action_spec,
483
- params: { event_type: 'session_revoke_all' },
484
- headers: fixture.create_session_headers()
485
- });
486
- assert.ok(audit_res.ok, 'audit_log_list should succeed');
487
- const failure = audit_res.result.events.find((e) => e.outcome === 'failure');
524
+ const failure = await read_failure_audit_event(fixture, 'session_revoke_all');
488
525
  assert.ok(failure, 'Expected a failure-outcome session_revoke_all audit event');
489
526
  assert.strictEqual(failure.target_account_id, null);
490
527
  const failure_meta = failure.metadata;
@@ -504,15 +541,7 @@ export const describe_standard_admin_integration_tests = (options) => {
504
541
  assert.ok(!res.ok, 'Expected 404 for missing account');
505
542
  assert.strictEqual(res.status, 404);
506
543
  assert.strictEqual(res.error.data.reason, 'account_not_found');
507
- const audit_res = await rpc_call_for_spec({
508
- app: { request: fixture.transport },
509
- path: rpc_path,
510
- spec: audit_log_list_action_spec,
511
- params: { event_type: 'token_revoke_all' },
512
- headers: fixture.create_session_headers()
513
- });
514
- assert.ok(audit_res.ok, 'audit_log_list should succeed');
515
- const failure = audit_res.result.events.find((e) => e.outcome === 'failure');
544
+ const failure = await read_failure_audit_event(fixture, 'token_revoke_all');
516
545
  assert.ok(failure, 'Expected a failure-outcome token_revoke_all audit event');
517
546
  assert.strictEqual(failure.target_account_id, null);
518
547
  const failure_meta = failure.metadata;
@@ -594,7 +623,11 @@ export const describe_standard_admin_integration_tests = (options) => {
594
623
  app: { request: fixture.transport },
595
624
  path: rpc_path,
596
625
  spec: account_token_create_action_spec,
597
- params: { name: 'audit-test-token', scope: { kind: 'full' } },
626
+ params: {
627
+ name: 'audit-test-token',
628
+ scope: { kind: 'full' },
629
+ lifetime: { kind: 'eternal' }
630
+ },
598
631
  headers: fixture.create_session_headers()
599
632
  });
600
633
  assert.ok(token_res.ok, `account_token_create failed: ${token_res.ok ? '' : JSON.stringify(token_res.error)}`);
@@ -721,7 +754,11 @@ export const describe_standard_admin_integration_tests = (options) => {
721
754
  app: { request: fixture.transport },
722
755
  path: rpc_path,
723
756
  spec: account_token_create_action_spec,
724
- params: { name: 'admin-b-token', scope: { kind: 'full' } },
757
+ params: {
758
+ name: 'admin-b-token',
759
+ scope: { kind: 'full' },
760
+ lifetime: { kind: 'eternal' }
761
+ },
725
762
  headers: create_headers(admin_b.session_cookie)
726
763
  });
727
764
  assert.ok(token_res.ok, `account_token_create failed: ${token_res.ok ? '' : JSON.stringify(token_res.error)}`);
@@ -1 +1 @@
1
- {"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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"}
@@ -157,8 +157,9 @@ export const bootstrap_test_keeper = async (options) => {
157
157
  /** Silent logger for tests — suppresses all output. */
158
158
  const test_log = new Logger('test', { level: 'off' });
159
159
  const default_test_fs_stubs = {
160
- stat: async () => null,
161
- read_text_file: async () => '',
160
+ read_secure_file: async (path) => {
161
+ throw new Error(`ENOENT: no such file or directory: ${path}`);
162
+ },
162
163
  delete_file: async () => { }
163
164
  };
164
165
  /**
@@ -375,14 +376,18 @@ export const create_test_app_for_bootstrap = async (options) => {
375
376
  if (options.db) {
376
377
  await options.db.query('UPDATE bootstrap_lock SET bootstrapped = false WHERE bootstrapped = true');
377
378
  }
378
- // Token-aware fs stubs: the bootstrap route's filesystem operations resolve
379
- // against the configured token_path; everything else returns no-op defaults.
379
+ // Token-aware fs stubs: the bootstrap route's secure read resolves against
380
+ // the configured token_path (both the boot-time availability probe and the
381
+ // request-time read go through it); a deleted or unknown path throws like
382
+ // the real runtimes' ENOENT.
380
383
  let token_file_deleted = false;
381
384
  const fs_stubs = {
382
- stat: async (path) => path === bootstrap.token_path && !token_file_deleted
383
- ? { is_file: true, is_directory: false }
384
- : null,
385
- read_text_file: async (path) => path === bootstrap.token_path && !token_file_deleted ? bootstrap_token : '',
385
+ read_secure_file: async (path) => {
386
+ if (path === bootstrap.token_path && !token_file_deleted) {
387
+ return new TextEncoder().encode(bootstrap_token);
388
+ }
389
+ throw new Error(`ENOENT: no such file or directory: ${path}`);
390
+ },
386
391
  delete_file: async (path) => {
387
392
  if (path === bootstrap.token_path)
388
393
  token_file_deleted = true;
@@ -1 +1 @@
1
- {"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA4B7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAUhE,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AA2B1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,0BAA0B,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;;;OAMG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,yEAAyE;IACzE,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;CACvC;AAgGD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IA6mBzF,CAAC"}
1
+ {"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA4B7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAUhE,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AA2B1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,0BAA0B,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;;;OAMG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,yEAAyE;IACzE,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;CACvC;AAgGD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IAinBzF,CAAC"}
@@ -169,7 +169,11 @@ export const describe_audit_completeness_tests = (options) => {
169
169
  app: { request: fixture.transport },
170
170
  path: rpc_path,
171
171
  spec: account_token_create_action_spec,
172
- params: { name: 'audit-test', scope: { kind: 'full' } },
172
+ params: {
173
+ name: 'audit-test',
174
+ scope: { kind: 'full' },
175
+ lifetime: { kind: 'eternal' }
176
+ },
173
177
  headers: fixture.create_session_headers()
174
178
  });
175
179
  assert.ok(res.ok, `account_token_create failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
@@ -1 +1 @@
1
- {"version":3,"file":"cell_grant_role.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cell_grant_role.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA8C9B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAG3D,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,gBAAyB,CAAC;AAEvD,oFAAoF;AACpF,eAAO,MAAM,yBAAyB,qBAAqB,CAAC;AAK5D,eAAO,MAAM,oCAAoC,GAAI,SAAS,wBAAwB,KAAG,IAuIxF,CAAC"}
1
+ {"version":3,"file":"cell_grant_role.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cell_grant_role.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAmD9B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAG3D,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,gBAAyB,CAAC;AAEvD,oFAAoF;AACpF,eAAO,MAAM,yBAAyB,qBAAqB,CAAC;AAK5D,eAAO,MAAM,oCAAoC,GAAI,SAAS,wBAAwB,KAAG,IAsOxF,CAAC"}
@@ -34,7 +34,7 @@ import '../assert_dev_env.js';
34
34
  * @module
35
35
  */
36
36
  import { describe, assert } from 'vitest';
37
- import { CellCreateOutput, CellGetOutput, CellUpdateOutput } from '../../auth/cell_action_specs.js';
37
+ import { CellCreateOutput, CellGetOutput, CellListOutput, CellUpdateOutput } from '../../auth/cell_action_specs.js';
38
38
  import { CellGrantCreateOutput, ERROR_CELL_GRANT_UNKNOWN_ROLE } from '../../auth/cell_grant_action_specs.js';
39
39
  import { test_if } from './capabilities.js';
40
40
  import { cross_rpc_call, error_reason, expect_output } from './cell_cross_helpers.js';
@@ -71,6 +71,39 @@ export const describe_cell_grant_role_cross_tests = (options) => {
71
71
  assert.ok(!stranger_view.ok, 'non-holder must not see the cell');
72
72
  assert.strictEqual(error_reason(stranger_view), 'cell_not_found');
73
73
  });
74
+ test_if(capabilities.cell_relations, "cell_list shared_with:'me' admits through the role branch of the grant predicate", async () => {
75
+ // The role-admit arm of the shared-with predicate (the
76
+ // `caller_role_grants` branch of the CTE in the Rust
77
+ // `query_cell_list` / the TS list query) — pinned nowhere else:
78
+ // the actor-shaped `shared_with:'me'` case in `cell_relations.ts`
79
+ // runs the CTE but its role branch never matches, and the
80
+ // storage-half copy in `fuz_cell/tests` is a textually separate
81
+ // EXISTS the CTE can silently drift from. An ungranted control
82
+ // cell keeps the semi-join non-vacuous (dropping it must change
83
+ // the result), and a non-holder proves the branch keys on the
84
+ // role, not on authentication.
85
+ const fixture = await setup_test();
86
+ const t = fixture.transport;
87
+ const owner = await fixture.create_account({ username: 'cell_role_list_owner' });
88
+ const owner_h = owner.create_session_headers();
89
+ const holder = fixture.extra_accounts[CELL_ROLE_HOLDER_USERNAME];
90
+ assert.ok(holder, `fixture must seed the ${CELL_ROLE_HOLDER_USERNAME} extra account`);
91
+ const stranger = await fixture.create_account({ username: 'cell_role_list_stranger' });
92
+ const granted = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: { label: 'role-shared' } }, owner_h), CellCreateOutput);
93
+ // control: same owner, same visibility, no grant — must stay out
94
+ const ungranted = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: { label: 'unshared control' } }, owner_h), CellCreateOutput);
95
+ expect_output(await cross_rpc_call(t, rpc_path, 'cell_grant_create', {
96
+ cell_id: granted.cell.id,
97
+ level: 'viewer',
98
+ principal: { kind: 'role', role: CELL_EDITOR_ROLE }
99
+ }, owner_h), CellGrantCreateOutput);
100
+ const holder_list = expect_output(await cross_rpc_call(t, rpc_path, 'cell_list', { shared_with: 'me' }, holder.create_session_headers()), CellListOutput);
101
+ const holder_ids = holder_list.cells.map((c) => c.id);
102
+ assert.ok(holder_ids.includes(granted.cell.id), 'the role-granted cell must reach a holder of the role via shared_with:me');
103
+ assert.ok(!holder_ids.includes(ungranted.cell.id), 'the ungranted control cell must not — the semi-join is load-bearing');
104
+ const stranger_list = expect_output(await cross_rpc_call(t, rpc_path, 'cell_list', { shared_with: 'me' }, stranger.create_session_headers()), CellListOutput);
105
+ assert.ok(!stranger_list.cells.some((c) => c.id === granted.cell.id), 'a non-holder must not be admitted through the role branch');
106
+ });
74
107
  test_if(capabilities.cell_relations, 'role-shaped grant for an unregistered role is rejected at create', async () => {
75
108
  const fixture = await setup_test();
76
109
  const t = fixture.transport;
@@ -1 +1 @@
1
- {"version":3,"file":"conformance_table.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/conformance_table.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA8B9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAOnE,OAAO,EAMN,KAAK,uBAAuB,EAC5B,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAEN,KAAK,eAAe,EAEpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC;AAGzD;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IAC1C,iEAAiE;IACjE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,gEAAgE;IAChE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,sDAAsD;AACtD,MAAM,WAAW,uBAAuB;IACvC,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC/C,+DAA+D;IAC/D,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,0EAA0E;IAC1E,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,kEAAkE;IAClE,QAAQ,CAAC,aAAa,EAAE,uBAAuB,CAAC;IAChD,4EAA4E;IAC5E,QAAQ,CAAC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACjD,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,CAAC,EAAE,0BAA0B,CAAC;IACjD,iEAAiE;IACjE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAoPD;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,EAAE,aAAa,CAAC,MAAM,CAIrD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,6BAA6B,GACzC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,OAAO,MAAM,KACX,IAQF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GACnC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,UAAU,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,EACvC,OAAO,MAAM,KACX,IAYF,CAAC;AAwKF;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAAI,SAAS,uBAAuB,KAAG,IAkEnF,CAAC"}
1
+ {"version":3,"file":"conformance_table.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/conformance_table.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA8B9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAOnE,OAAO,EAMN,KAAK,uBAAuB,EAC5B,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAEN,KAAK,eAAe,EAEpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC;AAGzD;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IAC1C,iEAAiE;IACjE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,gEAAgE;IAChE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,sDAAsD;AACtD,MAAM,WAAW,uBAAuB;IACvC,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC/C,+DAA+D;IAC/D,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,0EAA0E;IAC1E,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,kEAAkE;IAClE,QAAQ,CAAC,aAAa,EAAE,uBAAuB,CAAC;IAChD,4EAA4E;IAC5E,QAAQ,CAAC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACjD,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,CAAC,EAAE,0BAA0B,CAAC;IACjD,iEAAiE;IACjE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAqPD;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,EAAE,aAAa,CAAC,MAAM,CAIrD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,6BAA6B,GACzC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,OAAO,MAAM,KACX,IAQF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GACnC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,UAAU,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,EACvC,OAAO,MAAM,KACX,IAYF,CAAC;AAwKF;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAAI,SAAS,uBAAuB,KAAG,IAkEnF,CAAC"}
@@ -71,7 +71,8 @@ const mint_scoped_token = async (fixture, resolved_rpc_endpoints) => {
71
71
  spec: account_token_create_action_spec,
72
72
  params: {
73
73
  name: 'conformance scoped token',
74
- scope: { kind: 'methods', methods: [SCOPED_TOKEN_ADMITTED_METHOD] }
74
+ scope: { kind: 'methods', methods: [SCOPED_TOKEN_ADMITTED_METHOD] },
75
+ lifetime: { kind: 'eternal' }
75
76
  },
76
77
  headers: fixture.create_session_headers()
77
78
  });
@@ -375,7 +375,7 @@ const mint_account = async (handle, options) => {
375
375
  // point of the default-deny design. A minted-account fixture stands in for an
376
376
  // ordinary account credential, so it takes full account authority; the
377
377
  // narrowed credential is the conformance table's `scoped_token` principal.
378
- const token_result = await rpc_via_transport(transport, handle.config.rpc_path, 'account_token_create', { scope: { kind: 'full' } }, handle.config.name);
378
+ const token_result = await rpc_via_transport(transport, handle.config.rpc_path, 'account_token_create', { scope: { kind: 'full' }, lifetime: { kind: 'eternal' } }, handle.config.name);
379
379
  const token_parsed = TokenCreateResponseShape.safeParse(token_result);
380
380
  if (!token_parsed.success) {
381
381
  throw new Error(`account_token_create(${handle.config.name}) returned unexpected result: ${JSON.stringify(token_result)}`);
@@ -0,0 +1,15 @@
1
+ import '../assert_dev_env.js';
2
+ import type { SetupTest } from './setup.js';
3
+ /**
4
+ * Options for the token-lifetime parity suite. Ungated — the mint + list pair
5
+ * is on every spine's standard surface, so there is no capability flag (the
6
+ * sibling of `identity_parity.ts` rather than the cell suites).
7
+ */
8
+ export interface TokenLifetimeCrossTestOptions {
9
+ /** Per-test fixture-producing function (fresh keeper + db per call). */
10
+ readonly setup_test: SetupTest;
11
+ /** RPC endpoint path the methods are mounted on. Default `/api/rpc`. */
12
+ readonly rpc_path?: string;
13
+ }
14
+ export declare const describe_token_lifetime_cross_tests: (options: TokenLifetimeCrossTestOptions) => void;
15
+ //# sourceMappingURL=token_lifetime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token_lifetime.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/token_lifetime.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA+C9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C;;;;GAIG;AACH,MAAM,WAAW,6BAA6B;IAC7C,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAaD,eAAO,MAAM,mCAAmC,GAC/C,SAAS,6BAA6B,KACpC,IAiHF,CAAC"}