@fuzdev/fuz_app 0.117.1 → 0.118.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 (40) hide show
  1. package/dist/auth/CLAUDE.md +17 -4
  2. package/dist/auth/audit_emitter.d.ts +13 -8
  3. package/dist/auth/audit_emitter.d.ts.map +1 -1
  4. package/dist/auth/audit_emitter.js +5 -4
  5. package/dist/auth/bootstrap_account.d.ts +3 -2
  6. package/dist/auth/bootstrap_account.d.ts.map +1 -1
  7. package/dist/auth/bootstrap_account.js +3 -3
  8. package/dist/auth/cleanup.d.ts +25 -11
  9. package/dist/auth/cleanup.d.ts.map +1 -1
  10. package/dist/auth/cleanup.js +47 -28
  11. package/dist/auth/deps.d.ts +1 -1
  12. package/dist/auth/migrations.d.ts.map +1 -1
  13. package/dist/auth/migrations.js +27 -0
  14. package/dist/auth/role_grant_offer_ddl.d.ts +4 -2
  15. package/dist/auth/role_grant_offer_ddl.d.ts.map +1 -1
  16. package/dist/auth/role_grant_offer_ddl.js +10 -3
  17. package/dist/auth/role_grant_offer_queries.d.ts +16 -7
  18. package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
  19. package/dist/auth/role_grant_offer_queries.js +33 -15
  20. package/dist/auth/role_grant_offer_schema.d.ts +9 -0
  21. package/dist/auth/role_grant_offer_schema.d.ts.map +1 -1
  22. package/dist/db/migrate.d.ts +8 -13
  23. package/dist/db/migrate.d.ts.map +1 -1
  24. package/dist/db/migrate.js +9 -38
  25. package/dist/db/status.d.ts +0 -6
  26. package/dist/db/status.d.ts.map +1 -1
  27. package/dist/db/status.js +3 -23
  28. package/dist/runtime/deno.d.ts.map +1 -1
  29. package/dist/runtime/deno.js +9 -6
  30. package/dist/runtime/deps.d.ts +3 -2
  31. package/dist/runtime/deps.d.ts.map +1 -1
  32. package/dist/runtime/secure_file.d.ts +23 -9
  33. package/dist/runtime/secure_file.d.ts.map +1 -1
  34. package/dist/runtime/secure_file.js +29 -10
  35. package/dist/server/app_backend.d.ts +1 -1
  36. package/dist/testing/audit_drift_guard.d.ts +3 -5
  37. package/dist/testing/audit_drift_guard.d.ts.map +1 -1
  38. package/dist/testing/audit_drift_guard.js +3 -5
  39. package/dist/testing/cross_backend/expected_schema.json +1 -0
  40. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"role_grant_offer_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAI9C,mEAAmE;AACnE,eAAO,MAAM,mCAAmC,MAAM,CAAC;AAEvD,yFAAyF;AACzF,eAAO,MAAM,+BAA+B,QAA2B,CAAC;AAExE,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,IAAI,CAAC;IACT,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB;;;;;;;;;;;OAWG;IACH,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;;OAKG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,uBAAuB,EAAE,IAAI,GAAG,IAAI,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACtD,eAAe,EAAE,IAAI,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,yBAAyB;IACzC,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,IAAI,CAAC;CACjB;AAED,wDAAwD;AACxD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;kBAoD4D,CAAC;AAC5F,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,gEAAgE;AAChE,eAAO,MAAM,wBAAwB,GAAI,OAAO,cAAc,KAAG,kBAiB/D,CAAC"}
1
+ {"version":3,"file":"role_grant_offer_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAI9C,mEAAmE;AACnE,eAAO,MAAM,mCAAmC,MAAM,CAAC;AAEvD,yFAAyF;AACzF,eAAO,MAAM,+BAA+B,QAA2B,CAAC;AAExE,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,IAAI,CAAC;IACT,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB;;;;;;;;;;;OAWG;IACH,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;;OAKG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,uBAAuB,EAAE,IAAI,GAAG,IAAI,CAAC;IACrC;;;;;;;OAOG;IACH,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACtD,eAAe,EAAE,IAAI,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,yBAAyB;IACzC,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,IAAI,CAAC;CACjB;AAED,wDAAwD;AACxD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;kBAoD4D,CAAC;AAC5F,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,gEAAgE;AAChE,eAAO,MAAM,wBAAwB,GAAI,OAAO,cAAc,KAAG,kBAiB/D,CAAC"}
@@ -86,7 +86,7 @@ export interface MigrationResult {
86
86
  * Callers branch on `.kind` rather than matching error messages — message
87
87
  * text is for operators, not control flow.
88
88
  */
89
- export type MigrationErrorKind = 'binary-older-than-db' | 'name-divergence-at-N' | 'old-tracker-shape' | 'migration-failed' | 'baseline-name-not-in-code' | 'baseline-name-out-of-order' | 'baseline-namespace-already-populated';
89
+ export type MigrationErrorKind = 'binary-older-than-db' | 'name-divergence-at-N' | 'migration-failed' | 'baseline-name-not-in-code' | 'baseline-name-out-of-order' | 'baseline-namespace-already-populated';
90
90
  /** Structured context passed alongside a `MigrationError`. */
91
91
  export interface MigrationErrorContext {
92
92
  namespace?: string;
@@ -137,26 +137,22 @@ export declare class MigrationError extends Error {
137
137
  * namespaces are omitted)
138
138
  * @mutates schema_version - inserts one row per applied migration
139
139
  * @throws MigrationError with `kind` of `binary-older-than-db`,
140
- * `name-divergence-at-N`, `old-tracker-shape`, or `migration-failed`
140
+ * `name-divergence-at-N`, or `migration-failed`
141
141
  */
142
142
  export declare const run_migrations: (db: Db, namespaces: Array<MigrationNamespace>) => Promise<Array<MigrationResult>>;
143
143
  /**
144
144
  * Insert tracker rows for the named migrations of a namespace **without
145
145
  * executing them**.
146
146
  *
147
- * Used to promote an existing schema (e.g. produced by a pre-0.42 build,
148
- * preserved through a tracker-shape upgrade) into the new identity tracker.
147
+ * Used to promote an existing schema (e.g. produced by an out-of-band
148
+ * bootstrap, or a hand-applied DDL set) into the identity tracker.
149
149
  * `baseline()` trusts the operator-supplied list — it does not verify that
150
150
  * the schema actually matches what the named migrations would have produced.
151
151
  * Pair with a schema-assertion script post-baseline before re-enabling traffic.
152
152
  *
153
153
  * Contract:
154
- * - Probes for the pre-0.42 tracker shape; throws `old-tracker-shape` if
155
- * found (DDL with `IF NOT EXISTS` would otherwise no-op against the old
156
- * table and the INSERT would fail with a confusing column-not-found).
157
- * - Creates the new-shape `schema_version` table if missing — cutover
158
- * scripts that just dropped the old-shape table can call `baseline()`
159
- * directly with no separate DDL step.
154
+ * - Creates the `schema_version` table if missing — a promotion script can
155
+ * call `baseline()` against a fresh tracker with no separate DDL step.
160
156
  * - Acquires the same per-namespace advisory lock as `run_migrations` (with
161
157
  * the same try/catch fallback for environments lacking `pg_advisory_lock`).
162
158
  * - Refuses if any tracker rows already exist *for this namespace* — lets
@@ -172,9 +168,8 @@ export declare const run_migrations: (db: Db, namespaces: Array<MigrationNamespa
172
168
  * @param names - prefix of `ns.migrations[].name` to record as already-applied
173
169
  * @mutates schema_version - inserts tracker rows for `names` without running
174
170
  * the corresponding migration bodies
175
- * @throws MigrationError with `kind` of `old-tracker-shape`,
176
- * `baseline-name-not-in-code`, `baseline-name-out-of-order`, or
177
- * `baseline-namespace-already-populated`
171
+ * @throws MigrationError with `kind` of `baseline-name-not-in-code`,
172
+ * `baseline-name-out-of-order`, or `baseline-namespace-already-populated`
178
173
  */
179
174
  export declare const baseline: (db: Db, ns: MigrationNamespace, names: ReadonlyArray<string>) => Promise<void>;
180
175
  //# sourceMappingURL=migrate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/migrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAIH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAElC;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAC7B;AAED,2DAA2D;AAC3D,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC7B;AAED;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAC3B,sBAAsB,GACtB,sBAAsB,GACtB,mBAAmB,GACnB,kBAAkB,GAClB,2BAA2B,GAC3B,4BAA4B,GAC5B,sCAAsC,CAAC;AAE1C,8DAA8D;AAC9D,MAAM,WAAW,qBAAqB;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;gBAEnC,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB;CAQtF;AA6ED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,cAAc,GAC1B,IAAI,EAAE,EACN,YAAY,KAAK,CAAC,kBAAkB,CAAC,KACnC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAuFhC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,QAAQ,GACpB,IAAI,EAAE,EACN,IAAI,kBAAkB,EACtB,OAAO,aAAa,CAAC,MAAM,CAAC,KAC1B,OAAO,CAAC,IAAI,CA+Dd,CAAC"}
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/migrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAIH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAElC;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAC7B;AAED,2DAA2D;AAC3D,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC7B;AAED;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAC3B,sBAAsB,GACtB,sBAAsB,GACtB,kBAAkB,GAClB,2BAA2B,GAC3B,4BAA4B,GAC5B,sCAAsC,CAAC;AAE1C,8DAA8D;AAC9D,MAAM,WAAW,qBAAqB;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;gBAEnC,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB;CAQtF;AAsDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,cAAc,GAC1B,IAAI,EAAE,EACN,YAAY,KAAK,CAAC,kBAAkB,CAAC,KACnC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAmFhC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,QAAQ,GACpB,IAAI,EAAE,EACN,IAAI,kBAAkB,EACtB,OAAO,aAAa,CAAC,MAAM,CAAC,KAC1B,OAAO,CAAC,IAAI,CA2Dd,CAAC"}
@@ -83,24 +83,6 @@ CREATE TABLE IF NOT EXISTS schema_version (
83
83
  PRIMARY KEY (namespace, name),
84
84
  UNIQUE (namespace, sequence)
85
85
  )`;
86
- /**
87
- * Detect the pre-0.42 `schema_version` shape (`namespace`, `version`,
88
- * `applied_at`). The new-shape DDL uses `IF NOT EXISTS` and would silently
89
- * no-op against the old table, so this probe runs before DDL and before any
90
- * per-namespace lock.
91
- */
92
- const detect_old_tracker = async (db) => {
93
- const row = await db.query_one(`SELECT EXISTS (
94
- SELECT 1 FROM information_schema.columns
95
- WHERE table_schema = 'public'
96
- AND table_name = 'schema_version'
97
- AND column_name = 'version'
98
- ) as exists`);
99
- return row?.exists ?? false;
100
- };
101
- const OLD_TRACKER_HINT = 'Detected fuz_app < 0.42 tracker shape (schema_version.version column exists). ' +
102
- 'Hint: `DROP TABLE schema_version` and re-run, or call `baseline()` first if ' +
103
- 'preserving an existing schema.';
104
86
  /**
105
87
  * Compute a stable int32 advisory lock key from a namespace string.
106
88
  *
@@ -171,12 +153,9 @@ const with_namespace_lock = async (db, namespace, fn) => {
171
153
  * namespaces are omitted)
172
154
  * @mutates schema_version - inserts one row per applied migration
173
155
  * @throws MigrationError with `kind` of `binary-older-than-db`,
174
- * `name-divergence-at-N`, `old-tracker-shape`, or `migration-failed`
156
+ * `name-divergence-at-N`, or `migration-failed`
175
157
  */
176
158
  export const run_migrations = async (db, namespaces) => {
177
- if (await detect_old_tracker(db)) {
178
- throw new MigrationError('old-tracker-shape', OLD_TRACKER_HINT);
179
- }
180
159
  await db.query(SCHEMA_VERSION_DDL);
181
160
  const results = [];
182
161
  for (const { namespace, migrations } of namespaces) {
@@ -206,10 +185,10 @@ export const run_migrations = async (db, namespaces) => {
206
185
  `that follow it, or delete from row ${i} onward to re-apply the tail).`, { namespace, at_index: i });
207
186
  }
208
187
  }
209
- // Step 5: up-to-date case
188
+ // up-to-date case
210
189
  if (applied.length === migrations.length)
211
190
  return;
212
- // Step 6: run pending tail in a single chain-tx
191
+ // Step 5: run pending tail in a single chain-tx
213
192
  let next_sequence = applied.length > 0 ? applied[applied.length - 1].sequence + 1 : 0;
214
193
  const applied_names = [];
215
194
  await db.transaction(async (tx) => {
@@ -240,19 +219,15 @@ export const run_migrations = async (db, namespaces) => {
240
219
  * Insert tracker rows for the named migrations of a namespace **without
241
220
  * executing them**.
242
221
  *
243
- * Used to promote an existing schema (e.g. produced by a pre-0.42 build,
244
- * preserved through a tracker-shape upgrade) into the new identity tracker.
222
+ * Used to promote an existing schema (e.g. produced by an out-of-band
223
+ * bootstrap, or a hand-applied DDL set) into the identity tracker.
245
224
  * `baseline()` trusts the operator-supplied list — it does not verify that
246
225
  * the schema actually matches what the named migrations would have produced.
247
226
  * Pair with a schema-assertion script post-baseline before re-enabling traffic.
248
227
  *
249
228
  * Contract:
250
- * - Probes for the pre-0.42 tracker shape; throws `old-tracker-shape` if
251
- * found (DDL with `IF NOT EXISTS` would otherwise no-op against the old
252
- * table and the INSERT would fail with a confusing column-not-found).
253
- * - Creates the new-shape `schema_version` table if missing — cutover
254
- * scripts that just dropped the old-shape table can call `baseline()`
255
- * directly with no separate DDL step.
229
+ * - Creates the `schema_version` table if missing — a promotion script can
230
+ * call `baseline()` against a fresh tracker with no separate DDL step.
256
231
  * - Acquires the same per-namespace advisory lock as `run_migrations` (with
257
232
  * the same try/catch fallback for environments lacking `pg_advisory_lock`).
258
233
  * - Refuses if any tracker rows already exist *for this namespace* — lets
@@ -268,14 +243,10 @@ export const run_migrations = async (db, namespaces) => {
268
243
  * @param names - prefix of `ns.migrations[].name` to record as already-applied
269
244
  * @mutates schema_version - inserts tracker rows for `names` without running
270
245
  * the corresponding migration bodies
271
- * @throws MigrationError with `kind` of `old-tracker-shape`,
272
- * `baseline-name-not-in-code`, `baseline-name-out-of-order`, or
273
- * `baseline-namespace-already-populated`
246
+ * @throws MigrationError with `kind` of `baseline-name-not-in-code`,
247
+ * `baseline-name-out-of-order`, or `baseline-namespace-already-populated`
274
248
  */
275
249
  export const baseline = async (db, ns, names) => {
276
- if (await detect_old_tracker(db)) {
277
- throw new MigrationError('old-tracker-shape', OLD_TRACKER_HINT, { namespace: ns.namespace });
278
- }
279
250
  await db.query(SCHEMA_VERSION_DDL);
280
251
  const code_names = ns.migrations.map((m) => m.name);
281
252
  // Validate every supplied name exists in code (catches drift between cutover
@@ -82,12 +82,6 @@ export interface DbStatus {
82
82
  tables: Array<TableStatus>;
83
83
  /** Per-namespace migration status. */
84
84
  migrations: Array<MigrationStatus>;
85
- /**
86
- * True if the pre-0.42 `schema_version` shape (with a `version` column)
87
- * was detected. The runner refuses to start in this state — operators
88
- * see this flag as their cue to drop the table or call `baseline()`.
89
- */
90
- old_tracker_shape?: boolean;
91
85
  }
92
86
  /**
93
87
  * Query database status including connectivity, tables, and migration state.
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GACnB;IACA,mEAAmE;IACnE,IAAI,EAAE,eAAe,CAAC;IACtB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;CAChB,GACD;IACA,kEAAkE;IAClE,IAAI,EAAE,cAAc,CAAC;IACrB,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;CAChB,CAAC;AAEL;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,2EAA2E;IAC3E,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B;;;OAGG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACxB,yCAAyC;IACzC,SAAS,EAAE,OAAO,CAAC;IACnB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;IAC3B,sCAAsC;IACtC,UAAU,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;IACnC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC5B;AA8BD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,GAC3B,IAAI,EAAE,EACN,aAAa,KAAK,CAAC,kBAAkB,CAAC,KACpC,OAAO,CAAC,QAAQ,CA2GlB,CAAC;AAeF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,QAAQ,QAAQ,KAAG,MAoDnD,CAAC"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GACnB;IACA,mEAAmE;IACnE,IAAI,EAAE,eAAe,CAAC;IACtB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;CAChB,GACD;IACA,kEAAkE;IAClE,IAAI,EAAE,cAAc,CAAC;IACrB,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;CAChB,CAAC;AAEL;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,2EAA2E;IAC3E,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B;;;OAGG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACxB,yCAAyC;IACzC,SAAS,EAAE,OAAO,CAAC;IACnB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;IAC3B,sCAAsC;IACtC,UAAU,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;CACnC;AAaD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,GAC3B,IAAI,EAAE,EACN,aAAa,KAAK,CAAC,kBAAkB,CAAC,KACpC,OAAO,CAAC,QAAQ,CAsGlB,CAAC;AAeF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,QAAQ,QAAQ,KAAG,MA4CnD,CAAC"}
package/dist/db/status.js CHANGED
@@ -12,15 +12,6 @@
12
12
  * @module
13
13
  */
14
14
  import { to_error_message } from '@fuzdev/fuz_util/error.ts';
15
- const has_table_column = async (db, table_name, column_name) => {
16
- const row = await db.query_one(`SELECT EXISTS (
17
- SELECT 1 FROM information_schema.columns
18
- WHERE table_schema = 'public'
19
- AND table_name = $1
20
- AND column_name = $2
21
- ) as exists`, [table_name, column_name]);
22
- return row?.exists ?? false;
23
- };
24
15
  const has_table = async (db, table_name) => {
25
16
  const row = await db.query_one(`SELECT EXISTS (
26
17
  SELECT 1 FROM information_schema.tables
@@ -69,14 +60,9 @@ export const query_db_status = async (db, namespaces) => {
69
60
  }
70
61
  // check migration state
71
62
  const migrations = [];
72
- let old_tracker_shape;
73
63
  if (namespaces?.length) {
74
64
  const sv_exists = await has_table(db, 'schema_version');
75
- // pre-0.42 shape carries a `version` column; new shape carries `name`
76
- const old_shape = sv_exists ? await has_table_column(db, 'schema_version', 'version') : false;
77
- if (old_shape)
78
- old_tracker_shape = true;
79
- if (sv_exists && !old_shape) {
65
+ if (sv_exists) {
80
66
  for (const { namespace, migrations: ns_migrations } of namespaces) {
81
67
  const rows = await db.query(`SELECT name FROM schema_version
82
68
  WHERE namespace = $1
@@ -118,7 +104,7 @@ export const query_db_status = async (db, namespaces) => {
118
104
  }
119
105
  }
120
106
  else {
121
- // no tracker, or pre-0.42 shape — every namespace shows as "nothing applied yet"
107
+ // no tracker — every namespace shows as "nothing applied yet"
122
108
  for (const { namespace, migrations: ns_migrations } of namespaces) {
123
109
  const code_names = ns_migrations.map((m) => m.name);
124
110
  migrations.push({
@@ -134,8 +120,7 @@ export const query_db_status = async (db, namespaces) => {
134
120
  connected: true,
135
121
  table_count: tables.length,
136
122
  tables,
137
- migrations,
138
- ...(old_tracker_shape ? { old_tracker_shape: true } : {})
123
+ migrations
139
124
  };
140
125
  };
141
126
  /**
@@ -171,11 +156,6 @@ export const format_db_status = (status) => {
171
156
  lines.push(` ${t.name.padEnd(max_name)} ${t.row_count} rows`);
172
157
  }
173
158
  }
174
- if (status.old_tracker_shape) {
175
- lines.push('');
176
- lines.push(' Migrations: pre-0.42 schema_version shape detected.');
177
- lines.push(' Drop the table and re-run, or call `baseline()` first if preserving the schema.');
178
- }
179
159
  if (status.migrations.length > 0) {
180
160
  lines.push('');
181
161
  lines.push(' Migrations:');
@@ -1 +1 @@
1
- {"version":3,"file":"deno.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/deno.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,WAAW,EAA6B,MAAM,WAAW,CAAC;AAoExE;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAAI,MAAM,aAAa,CAAC,MAAM,CAAC,KAAG,WAuKhE,CAAC"}
1
+ {"version":3,"file":"deno.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/deno.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,WAAW,EAA6B,MAAM,WAAW,CAAC;AAyExE;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAAI,MAAM,aAAa,CAAC,MAAM,CAAC,KAAG,WA0KhE,CAAC"}
@@ -8,7 +8,7 @@
8
8
  * @module
9
9
  */
10
10
  import { to_error_message } from '@fuzdev/fuz_util/error.ts';
11
- import { assert_secure_mode, assert_secure_size, read_secure_bounded } from './secure_file.js';
11
+ import { assert_secure_mode, assert_secure_regular_file, assert_secure_size, read_secure_bounded } from './secure_file.js';
12
12
  /**
13
13
  * Create a runtime backed by Deno APIs.
14
14
  *
@@ -46,17 +46,20 @@ export const create_deno_runtime = (args) => ({
46
46
  read_text_file: (path) => Deno.readTextFile(path),
47
47
  read_file: (path) => Deno.readFile(path),
48
48
  read_secure_file: async (path) => {
49
- // Deno exposes no O_NOFOLLOW, so the symlink check is a pre-open lstat
50
- // (a TOCTOU window the Node impl doesn't have — the mode check below
51
- // still runs on the open descriptor, so a post-open swap can't defeat
52
- // it). The checks themselves are the shared `runtime/secure_file.ts`
53
- // helpers, same as `load_secure_file_node`.
49
+ // Deno exposes no O_NOFOLLOW or O_NONBLOCK, so the symlink and
50
+ // regular-file checks are a pre-open lstat (a TOCTOU window the Node
51
+ // impl doesn't have — a FIFO swapped in after it would block the open;
52
+ // the checks below still run on the open descriptor, so a post-open swap
53
+ // can't defeat them). The checks themselves are the shared
54
+ // `runtime/secure_file.ts` helpers, same as `load_secure_file_node`.
54
55
  const l = await Deno.lstat(path);
55
56
  if (l.isSymlink)
56
57
  throw new Error(`secure file is a symlink: ${path}`);
58
+ assert_secure_regular_file(path, l.isFile);
57
59
  const file = await Deno.open(path, { read: true });
58
60
  try {
59
61
  const s = await file.stat();
62
+ assert_secure_regular_file(path, s.isFile);
60
63
  // `mode` is null where POSIX modes don't apply (Windows).
61
64
  if (s.mode !== null)
62
65
  assert_secure_mode(path, s.mode);
@@ -103,8 +103,9 @@ export interface WriteFileOptions {
103
103
  */
104
104
  export interface FsSecureReadDeps {
105
105
  /**
106
- * Read a small secret file with fail-loud checks: rejects symlinks, any
107
- * group/other-accessible mode (must be `0600`/`0400`; skipped where POSIX
106
+ * Read a small secret file with fail-loud checks: rejects symlinks,
107
+ * non-regular files (FIFO, device, directory — real runtimes only; the mock
108
+ * has no file types), any group/other-accessible mode (must be `0600`/`0400`; skipped where POSIX
108
109
  * modes don't apply), and files over a 4 KiB cap. Permission checks run on
109
110
  * the open descriptor where the platform allows, so the file can't be
110
111
  * swapped between check and read. Throws on any refusal — a refused secret
@@ -1 +1 @@
1
- {"version":3,"file":"deps.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/deps.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"deps.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/deps.ts"],"names":[],"mappings":"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"}
@@ -4,18 +4,20 @@
4
4
  *
5
5
  * The file this guards is typically the highest-value credential on the host
6
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.
7
+ * rather than degrading: a symlink, a non-regular file (FIFO, device node,
8
+ * directory), a group/other-accessible mode, or an oversized file is refused,
9
+ * never returned. The deploy recipe places the token at `0600`, so the check
10
+ * ratifies the shape zap produces; a hand-placed `0644` file fails at boot
11
+ * instead of being read.
11
12
  *
12
13
  * The Node implementation lives here (`load_secure_file_node`, wired as
13
14
  * `create_node_runtime().read_secure_file`); the Deno runtime implements the
14
15
  * 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.
16
+ * the mode/size checks over its in-memory map (it has no file types). The real
17
+ * runtimes refuse through the shared `assert_secure_regular_file` /
18
+ * `assert_secure_mode` / `assert_secure_size` / `read_secure_bounded` helpers,
19
+ * and the mock through the mode and size ones, so the checks — and their
20
+ * operator-facing messages — can't drift between runtimes.
19
21
  *
20
22
  * @module
21
23
  */
@@ -25,6 +27,13 @@
25
27
  * `MAX_SECURE_FILE_SIZE`.
26
28
  */
27
29
  export declare const MAX_SECURE_FILE_SIZE = 4096;
30
+ /**
31
+ * Refuse anything but a regular file — a FIFO, device node, socket, or
32
+ * directory at the secret's path.
33
+ *
34
+ * @throws Error naming the path
35
+ */
36
+ export declare const assert_secure_regular_file: (path: string, is_file: boolean) => void;
28
37
  /**
29
38
  * Refuse any group/other-accessible mode (only `0600`/`0400` pass).
30
39
  *
@@ -57,6 +66,11 @@ export declare const read_secure_bounded: (path: string, read_chunk: (target: Ui
57
66
  * Read a secret file with fail-loud checks (Node implementation).
58
67
  *
59
68
  * - `O_NOFOLLOW` atomically rejects symlinks during open (no TOCTOU window)
69
+ * - `O_NONBLOCK` keeps the open from waiting on a FIFO that has no writer (it
70
+ * has no effect on a regular file); the regular-file check on the open
71
+ * descriptor then refuses it, a device node, or a directory
72
+ * - `O_NOCTTY` keeps a terminal device at the path from becoming the process's
73
+ * controlling terminal in the moment before that check refuses it
60
74
  * - the permission check runs on the open descriptor, not the path, so the
61
75
  * file can't be swapped between check and read — any group/other access
62
76
  * (not `0600`/`0400`) is refused
@@ -65,7 +79,7 @@ export declare const read_secure_bounded: (path: string, read_chunk: (target: Ui
65
79
  *
66
80
  * @param path - path to the secret file
67
81
  * @returns the file's bytes
68
- * @throws Error on a missing file, symlink, permissive mode, oversized file, or I/O failure
82
+ * @throws Error on a missing file, symlink, non-regular file, permissive mode, oversized file, or I/O failure
69
83
  */
70
84
  export declare const load_secure_file_node: (path: string) => Promise<Uint8Array>;
71
85
  //# sourceMappingURL=secure_file.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"secure_file.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/secure_file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAMH;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAUzC;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,IAM/D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,IAI/D,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,GAC/B,MAAM,MAAM,EACZ,YAAY,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,KACxD,OAAO,CAAC,UAAU,CAUpB,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,qBAAqB,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,UAAU,CAuB5E,CAAC"}
1
+ {"version":3,"file":"secure_file.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/secure_file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAMH;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAUzC;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,GAAI,MAAM,MAAM,EAAE,SAAS,OAAO,KAAG,IAI3E,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,IAM/D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,IAI/D,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,GAC/B,MAAM,MAAM,EACZ,YAAY,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,KACxD,OAAO,CAAC,UAAU,CAUpB,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,qBAAqB,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,UAAU,CA2B5E,CAAC"}
@@ -4,18 +4,20 @@
4
4
  *
5
5
  * The file this guards is typically the highest-value credential on the host
6
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.
7
+ * rather than degrading: a symlink, a non-regular file (FIFO, device node,
8
+ * directory), a group/other-accessible mode, or an oversized file is refused,
9
+ * never returned. The deploy recipe places the token at `0600`, so the check
10
+ * ratifies the shape zap produces; a hand-placed `0644` file fails at boot
11
+ * instead of being read.
11
12
  *
12
13
  * The Node implementation lives here (`load_secure_file_node`, wired as
13
14
  * `create_node_runtime().read_secure_file`); the Deno runtime implements the
14
15
  * 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.
16
+ * the mode/size checks over its in-memory map (it has no file types). The real
17
+ * runtimes refuse through the shared `assert_secure_regular_file` /
18
+ * `assert_secure_mode` / `assert_secure_size` / `read_secure_bounded` helpers,
19
+ * and the mock through the mode and size ones, so the checks — and their
20
+ * operator-facing messages — can't drift between runtimes.
19
21
  *
20
22
  * @module
21
23
  */
@@ -35,6 +37,17 @@ export const MAX_SECURE_FILE_SIZE = 4096;
35
37
  * The mock runtime checks unconditionally — its modes are simulated.
36
38
  */
37
39
  const modes_apply = process.platform !== 'win32';
40
+ /**
41
+ * Refuse anything but a regular file — a FIFO, device node, socket, or
42
+ * directory at the secret's path.
43
+ *
44
+ * @throws Error naming the path
45
+ */
46
+ export const assert_secure_regular_file = (path, is_file) => {
47
+ if (!is_file) {
48
+ throw new Error(`not a regular file: ${path}`);
49
+ }
50
+ };
38
51
  /**
39
52
  * Refuse any group/other-accessible mode (only `0600`/`0400` pass).
40
53
  *
@@ -86,6 +99,11 @@ export const read_secure_bounded = async (path, read_chunk) => {
86
99
  * Read a secret file with fail-loud checks (Node implementation).
87
100
  *
88
101
  * - `O_NOFOLLOW` atomically rejects symlinks during open (no TOCTOU window)
102
+ * - `O_NONBLOCK` keeps the open from waiting on a FIFO that has no writer (it
103
+ * has no effect on a regular file); the regular-file check on the open
104
+ * descriptor then refuses it, a device node, or a directory
105
+ * - `O_NOCTTY` keeps a terminal device at the path from becoming the process's
106
+ * controlling terminal in the moment before that check refuses it
89
107
  * - the permission check runs on the open descriptor, not the path, so the
90
108
  * file can't be swapped between check and read — any group/other access
91
109
  * (not `0600`/`0400`) is refused
@@ -94,12 +112,12 @@ export const read_secure_bounded = async (path, read_chunk) => {
94
112
  *
95
113
  * @param path - path to the secret file
96
114
  * @returns the file's bytes
97
- * @throws Error on a missing file, symlink, permissive mode, oversized file, or I/O failure
115
+ * @throws Error on a missing file, symlink, non-regular file, permissive mode, oversized file, or I/O failure
98
116
  */
99
117
  export const load_secure_file_node = async (path) => {
100
118
  let handle;
101
119
  try {
102
- handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
120
+ handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK | constants.O_NOCTTY);
103
121
  }
104
122
  catch (err) {
105
123
  // ELOOP means the path was a symlink (O_NOFOLLOW refused it).
@@ -110,6 +128,7 @@ export const load_secure_file_node = async (path) => {
110
128
  }
111
129
  try {
112
130
  const s = await handle.stat();
131
+ assert_secure_regular_file(path, s.isFile());
113
132
  if (modes_apply)
114
133
  assert_secure_mode(path, s.mode);
115
134
  assert_secure_size(path, s.size);
@@ -85,7 +85,7 @@ export interface CreateAppBackendOptions {
85
85
  /**
86
86
  * Hardened secret-file read for the bootstrap token — pass the runtime's
87
87
  * `read_secure_file` (see `FsSecureReadDeps`). Rejects symlinks,
88
- * group/other-accessible modes, and oversized files.
88
+ * non-regular files, group/other-accessible modes, and oversized files.
89
89
  */
90
90
  read_secure_file: (path: string) => Promise<Uint8Array>;
91
91
  /** Delete a file. */
@@ -60,9 +60,8 @@ export interface RecordingAuditEmitter {
60
60
  * `listener_count` reports (registered listeners never fire — this emitter
61
61
  * captures `emit` shapes, not fan-out).
62
62
  *
63
- * `emit` AND `emit_pool` both append to `calls` so cleanup-sweep tests
64
- * (which use `emit_pool` exclusively — see `auth/cleanup.ts`) can also
65
- * read assertions off the same array.
63
+ * `emit` AND `emit_pool` both append to `calls`, so a test reads either
64
+ * entry point's writes off the same array.
66
65
  *
67
66
  * Pass `calls_ref` to write into a caller-owned array (callers that
68
67
  * declared `const events: Array<AuditLogInput> = []` and want to keep
@@ -99,8 +98,7 @@ export declare const create_recording_audit_emitter: (calls_ref?: Array<AuditLog
99
98
  * role-grant-shape emissions land in `events_ref` alongside bare `emit`
100
99
  * calls. `emit_pool` and `notify` are not decorated — they take
101
100
  * `AuditLogInput` / `AuditLogEvent` directly without going through
102
- * `emit`, so handler-side `emit_pool` writes (today only
103
- * `auth/cleanup.ts`) skip capture. Close-firing handlers all reach for
101
+ * `emit`, so `emit_pool` writes skip capture. Close-firing handlers all reach for
104
102
  * `emit` or `emit_role_grant_target`, so the ordering test sees them
105
103
  * regardless of which entry point a future refactor picks.
106
104
  *
@@ -1 +1 @@
1
- {"version":3,"file":"audit_drift_guard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_drift_guard.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAU7B,OAAO,EAEN,KAAK,YAAY,EACjB,KAAK,yBAAyB,EAC9B,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAChF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,yBAAyB,QAAO,IAiB5C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACX;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACrC,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,8BAA8B,GAC1C,YAAY,KAAK,CAAC,aAAa,CAAC,KAC9B,qBA8BF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,kCAAkC,GAAI,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EACxF,SAAS;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,EAC1B,YAAY,KAAK,CAAC,eAAe,GAAG,CAAC,CAAC,EACtC,gBAAgB,IAAI,CAAC,yBAAyB,EAAE,IAAI,GAAG,KAAK,GAAG,gBAAgB,CAAC,KAC9E,YAWF,CAAC"}
1
+ {"version":3,"file":"audit_drift_guard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_drift_guard.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAU7B,OAAO,EAEN,KAAK,YAAY,EACjB,KAAK,yBAAyB,EAC9B,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAChF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,yBAAyB,QAAO,IAiB5C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACX;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACrC,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,8BAA8B,GAC1C,YAAY,KAAK,CAAC,aAAa,CAAC,KAC9B,qBA8BF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,kCAAkC,GAAI,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EACxF,SAAS;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,EAC1B,YAAY,KAAK,CAAC,eAAe,GAAG,CAAC,CAAC,EACtC,gBAAgB,IAAI,CAAC,yBAAyB,EAAE,IAAI,GAAG,KAAK,GAAG,gBAAgB,CAAC,KAC9E,YAWF,CAAC"}
@@ -48,9 +48,8 @@ export const install_audit_drift_guard = () => {
48
48
  * `listener_count` reports (registered listeners never fire — this emitter
49
49
  * captures `emit` shapes, not fan-out).
50
50
  *
51
- * `emit` AND `emit_pool` both append to `calls` so cleanup-sweep tests
52
- * (which use `emit_pool` exclusively — see `auth/cleanup.ts`) can also
53
- * read assertions off the same array.
51
+ * `emit` AND `emit_pool` both append to `calls`, so a test reads either
52
+ * entry point's writes off the same array.
54
53
  *
55
54
  * Pass `calls_ref` to write into a caller-owned array (callers that
56
55
  * declared `const events: Array<AuditLogInput> = []` and want to keep
@@ -117,8 +116,7 @@ export const create_recording_audit_emitter = (calls_ref) => {
117
116
  * role-grant-shape emissions land in `events_ref` alongside bare `emit`
118
117
  * calls. `emit_pool` and `notify` are not decorated — they take
119
118
  * `AuditLogInput` / `AuditLogEvent` directly without going through
120
- * `emit`, so handler-side `emit_pool` writes (today only
121
- * `auth/cleanup.ts`) skip capture. Close-firing handlers all reach for
119
+ * `emit`, so `emit_pool` writes skip capture. Close-firing handlers all reach for
122
120
  * `emit` or `emit_role_grant_target`, so the ordering test sees them
123
121
  * regardless of which entry point a future refactor picks.
124
122
  *
@@ -101,6 +101,7 @@
101
101
  "created_at",
102
102
  "decline_reason",
103
103
  "declined_at",
104
+ "expire_audited_at",
104
105
  "expires_at",
105
106
  "from_actor_id",
106
107
  "id",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fuzdev/fuz_app",
3
- "version": "0.117.1",
3
+ "version": "0.118.0",
4
4
  "description": "fullstack app library",
5
5
  "glyph": "🗝",
6
6
  "logo": "logo.svg",
@@ -78,7 +78,7 @@
78
78
  "@fuzdev/fuz_css": "^0.64.0",
79
79
  "@fuzdev/fuz_ui": "^0.208.0",
80
80
  "@fuzdev/fuz_util": "^0.69.0",
81
- "@fuzdev/gro": "^0.212.0",
81
+ "@fuzdev/gro": "^0.213.0",
82
82
  "@fuzdev/mdz": "^0.3.0",
83
83
  "@hono/node-server": "^1.19.14",
84
84
  "@hono/node-ws": "^1.3.1",