@fuzdev/fuz_app 0.128.0 → 0.130.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 (112) hide show
  1. package/dist/actions/CLAUDE.md +23 -6
  2. package/dist/actions/connection_closer.d.ts +3 -1
  3. package/dist/actions/connection_closer.d.ts.map +1 -1
  4. package/dist/actions/register_action_ws.d.ts +46 -1
  5. package/dist/actions/register_action_ws.d.ts.map +1 -1
  6. package/dist/actions/register_action_ws.js +105 -14
  7. package/dist/actions/transports.d.ts +2 -1
  8. package/dist/actions/transports.d.ts.map +1 -1
  9. package/dist/actions/transports.js +2 -1
  10. package/dist/actions/transports_ws_backend.d.ts +37 -4
  11. package/dist/actions/transports_ws_backend.d.ts.map +1 -1
  12. package/dist/actions/transports_ws_backend.js +68 -4
  13. package/dist/auth/CLAUDE.md +1 -1
  14. package/dist/auth/actor_lookup_action_specs.d.ts +2 -2
  15. package/dist/auth/actor_lookup_action_specs.js +2 -2
  16. package/dist/auth/audit_emitter.d.ts +14 -7
  17. package/dist/auth/audit_emitter.d.ts.map +1 -1
  18. package/dist/auth/audit_emitter.js +6 -8
  19. package/dist/auth/audit_log_routes.d.ts +7 -3
  20. package/dist/auth/audit_log_routes.d.ts.map +1 -1
  21. package/dist/auth/audit_log_routes.js +48 -26
  22. package/dist/auth/bearer_auth.d.ts +3 -2
  23. package/dist/auth/bearer_auth.d.ts.map +1 -1
  24. package/dist/auth/bearer_auth.js +3 -2
  25. package/dist/auth/request_context.d.ts +1 -1
  26. package/dist/auth/request_context.js +1 -1
  27. package/dist/auth/token_scope.d.ts +4 -2
  28. package/dist/auth/token_scope.d.ts.map +1 -1
  29. package/dist/auth/token_scope.js +10 -2
  30. package/dist/http/CLAUDE.md +4 -5
  31. package/dist/http/auth_shape.d.ts +3 -2
  32. package/dist/http/auth_shape.d.ts.map +1 -1
  33. package/dist/http/auth_shape.js +3 -2
  34. package/dist/http/common_routes.d.ts +1 -14
  35. package/dist/http/common_routes.d.ts.map +1 -1
  36. package/dist/http/common_routes.js +1 -19
  37. package/dist/http/ip_canonical.d.ts +1 -1
  38. package/dist/http/ip_canonical.js +1 -1
  39. package/dist/http/jsonrpc_errors.d.ts.map +1 -1
  40. package/dist/http/jsonrpc_errors.js +7 -5
  41. package/dist/realtime/sse.d.ts +14 -1
  42. package/dist/realtime/sse.d.ts.map +1 -1
  43. package/dist/realtime/sse.js +77 -1
  44. package/dist/realtime/sse_auth_guard.d.ts +3 -1
  45. package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
  46. package/dist/realtime/sse_auth_guard.js +4 -2
  47. package/dist/realtime/subscriber_registry.d.ts +44 -8
  48. package/dist/realtime/subscriber_registry.d.ts.map +1 -1
  49. package/dist/realtime/subscriber_registry.js +64 -10
  50. package/dist/server/app_server.d.ts +122 -8
  51. package/dist/server/app_server.d.ts.map +1 -1
  52. package/dist/server/app_server.js +136 -91
  53. package/dist/server/surface_route.d.ts +32 -0
  54. package/dist/server/surface_route.d.ts.map +1 -0
  55. package/dist/server/surface_route.js +46 -0
  56. package/dist/testing/CLAUDE.md +33 -12
  57. package/dist/testing/app_server.d.ts +39 -4
  58. package/dist/testing/app_server.d.ts.map +1 -1
  59. package/dist/testing/app_server.js +84 -68
  60. package/dist/testing/audit_completeness.js +1 -1
  61. package/dist/testing/audit_drift_guard.d.ts +1 -1
  62. package/dist/testing/cross_backend/default_spine_surface.d.ts +2 -2
  63. package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
  64. package/dist/testing/cross_backend/default_spine_surface.js +5 -2
  65. package/dist/testing/cross_backend/identity_parity.js +1 -1
  66. package/dist/testing/cross_backend/in_process_setup.d.ts.map +1 -1
  67. package/dist/testing/cross_backend/in_process_setup.js +4 -1
  68. package/dist/testing/cross_backend/method_coverage.d.ts +1 -1
  69. package/dist/testing/cross_backend/method_coverage.d.ts.map +1 -1
  70. package/dist/testing/cross_backend/method_coverage.js +2 -2
  71. package/dist/testing/cross_backend/peer_ping_ws.d.ts +1 -1
  72. package/dist/testing/cross_backend/peer_ping_ws.d.ts.map +1 -1
  73. package/dist/testing/cross_backend/peer_ping_ws.js +4 -4
  74. package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts +1 -1
  75. package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts.map +1 -1
  76. package/dist/testing/cross_backend/setup.d.ts +4 -4
  77. package/dist/testing/cross_backend/setup.js +1 -1
  78. package/dist/testing/cross_backend/sse_round_trip.d.ts +1 -1
  79. package/dist/testing/cross_backend/sse_round_trip.d.ts.map +1 -1
  80. package/dist/testing/cross_backend/testing_reset_actions.d.ts +24 -14
  81. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
  82. package/dist/testing/cross_backend/testing_reset_actions.js +40 -32
  83. package/dist/testing/cross_backend/testing_server_bun.d.ts +1 -1
  84. package/dist/testing/cross_backend/testing_server_bun.d.ts.map +1 -1
  85. package/dist/testing/cross_backend/testing_server_bun.js +80 -64
  86. package/dist/testing/cross_backend/testing_server_core.d.ts +10 -4
  87. package/dist/testing/cross_backend/testing_server_core.d.ts.map +1 -1
  88. package/dist/testing/cross_backend/testing_server_deno.d.ts +1 -1
  89. package/dist/testing/cross_backend/testing_server_deno.d.ts.map +1 -1
  90. package/dist/testing/cross_backend/testing_server_deno.js +1 -1
  91. package/dist/testing/cross_backend/testing_server_node.d.ts +1 -1
  92. package/dist/testing/cross_backend/testing_server_node.d.ts.map +1 -1
  93. package/dist/testing/cross_backend/testing_server_node.js +2 -2
  94. package/dist/testing/cross_backend/ws_inbound_dispatch.d.ts +20 -0
  95. package/dist/testing/cross_backend/ws_inbound_dispatch.d.ts.map +1 -0
  96. package/dist/testing/cross_backend/ws_inbound_dispatch.js +177 -0
  97. package/dist/testing/cross_backend/ws_round_trip.d.ts +1 -1
  98. package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -1
  99. package/dist/testing/cross_backend/ws_round_trip.js +11 -9
  100. package/dist/testing/stubs.d.ts +45 -7
  101. package/dist/testing/stubs.d.ts.map +1 -1
  102. package/dist/testing/stubs.js +60 -31
  103. package/dist/testing/ws_round_trip.d.ts +1 -3
  104. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  105. package/dist/ui/AdminSurface.svelte +3 -1
  106. package/dist/ui/AdminSurface.svelte.d.ts.map +1 -1
  107. package/dist/ui/CLAUDE.md +3 -1
  108. package/dist/ui/async_slot.svelte.d.ts +4 -4
  109. package/dist/ui/async_slot.svelte.js +4 -4
  110. package/dist/ui/keyed_async_slot.svelte.d.ts +4 -4
  111. package/dist/ui/keyed_async_slot.svelte.js +3 -3
  112. package/package.json +1 -1
@@ -44,6 +44,21 @@
44
44
  * uses the two-phase form. The twin of the Rust `fuz_realtime`
45
45
  * `ConnectionRegistry`.
46
46
  *
47
+ * ## Shutdown
48
+ *
49
+ * `close_all_sockets` — the close `AppServer.close` runs — closes every
50
+ * connection, pending or admitted, with `WS_CLOSE_GOING_AWAY`, and leaves the
51
+ * transport **closing**: from then on every registration is born closed. A
52
+ * `register_pending` or `add_connection` inserts nothing — it aborts the
53
+ * caller's controller and closes the socket with `WS_CLOSE_GOING_AWAY` at once
54
+ * — so an upgrade still in flight when the server shuts down is closed rather
55
+ * than outliving it. An upgrade registered before the close-all is closed by
56
+ * it; one registered after is born closed; none is admitted. The flag is set
57
+ * in the same synchronous step that closes the live connections, so an `admit`
58
+ * that finds its entry gone and reads `is_closing()` sees the shutdown, never
59
+ * a revocation. Closing is permanent: a closed transport hosts no further
60
+ * connections.
61
+ *
47
62
  * @module
48
63
  */
49
64
  import { to_error_message } from '@fuzdev/fuz_util/error.ts';
@@ -78,6 +93,9 @@ export class BackendWebsocketTransport {
78
93
  // "oldest"). Adding a new identity scope (e.g. `device_id`) means adding a
79
94
  // field to `ConnectionIdentity`, not a parallel map.
80
95
  #connections = new Map();
96
+ // set once by `close_all_sockets`, never cleared — every later
97
+ // registration is born closed (module doc, "Shutdown")
98
+ #closing = false;
81
99
  // Server→client request correlation (ActionPeer). The transport owns the
82
100
  // sockets + the send; the registry owns the pending map, id allocation,
83
101
  // deadlines, and the per-connection in-flight cap (see `peer_request.ts`).
@@ -109,6 +127,11 @@ export class BackendWebsocketTransport {
109
127
  * The caller owns the entry until it is admitted: every refusal path calls
110
128
  * `remove_connection` with the returned id.
111
129
  *
130
+ * Once the transport is closing (`close_all_sockets`) the registration is
131
+ * born closed: nothing is inserted, `abort_controller` is aborted, and `ws`
132
+ * is closed with `WS_CLOSE_GOING_AWAY` before this returns. The id names
133
+ * nothing, so `admit` refuses it (module doc, "Shutdown").
134
+ *
112
135
  * @param ws - the socket to close on revocation
113
136
  * @param token_hash - blake3 session token hash, or `null` for non-session credentials
114
137
  * @param account_id - the authenticated account
@@ -116,9 +139,12 @@ export class BackendWebsocketTransport {
116
139
  * @param abort_controller - the socket-scoped controller to abort when the
117
140
  * transport closes this connection (revocation or cap eviction)
118
141
  * @returns the freshly assigned `connection_id` (branded `Uuid`)
119
- * @mutates this - inserts an un-admitted entry into `#connections`
142
+ * @mutates this - inserts an un-admitted entry into `#connections`, unless
143
+ * the transport is closing
120
144
  */
121
145
  register_pending(ws, token_hash, account_id, api_token_id = null, abort_controller = null) {
146
+ if (this.#closing)
147
+ return this.#close_born_closed(ws, abort_controller);
122
148
  return this.#insert(ws, token_hash, account_id, api_token_id, abort_controller, false);
123
149
  }
124
150
  /**
@@ -174,7 +200,8 @@ export class BackendWebsocketTransport {
174
200
  * inserted. A removed connection gets no further broadcast or peer request,
175
201
  * stops counting toward the cap at once, and has its `abort_controller`
176
202
  * aborted; its socket is closed when the close handshake completes. The new
177
- * connection is always admitted.
203
+ * connection is always admitted — unless the transport is closing: then it
204
+ * is born closed, as in `register_pending`, and evicts nothing.
178
205
  *
179
206
  * @returns the freshly assigned `connection_id` (branded `Uuid`)
180
207
  * @mutates this - inserts an admitted entry into `#connections`; past the
@@ -182,6 +209,8 @@ export class BackendWebsocketTransport {
182
209
  * their `WSContext`
183
210
  */
184
211
  add_connection(ws, token_hash, account_id, api_token_id = null, abort_controller = null) {
212
+ if (this.#closing)
213
+ return this.#close_born_closed(ws, abort_controller);
185
214
  if (this.#max_connections_per_account !== null) {
186
215
  this.#evict_oldest_for_account(account_id, this.#max_connections_per_account);
187
216
  }
@@ -305,14 +334,49 @@ export class BackendWebsocketTransport {
305
334
  * revocation does, so its heartbeat timer stops and nothing more dispatches
306
335
  * on it. A socket whose `close` throws does not stop the loop.
307
336
  *
337
+ * Leaves the transport closing, for good: every later registration is born
338
+ * closed (module doc, "Shutdown"). A second call closes nothing.
339
+ *
308
340
  * @returns the number of sockets closed
309
- * @mutates this - removes every connection, aborts its registered
310
- * controller, and closes its underlying `WSContext` with `WS_CLOSE_GOING_AWAY`
341
+ * @mutates this - sets the transport closing, removes every connection,
342
+ * aborts its registered controller, and closes its underlying `WSContext`
343
+ * with `WS_CLOSE_GOING_AWAY`
311
344
  * @throws the first error a socket's `close` threw, after every connection was attempted
312
345
  */
313
346
  close_all_sockets() {
347
+ // before any close, in the same synchronous step: an `admit` that finds
348
+ // its entry gone reads the shutdown, never a revocation — and set even
349
+ // when a socket's close throws
350
+ this.#closing = true;
314
351
  return this.#close_where(() => true, WS_CLOSE_GOING_AWAY, WS_CLOSE_GOING_AWAY_REASON);
315
352
  }
353
+ /**
354
+ * Whether `close_all_sockets` has run — the transport is shutting down and
355
+ * every registration from now on is born closed (module doc, "Shutdown").
356
+ * For telemetry and logs: `register_action_ws` reads it to say why an
357
+ * upgrade was refused.
358
+ */
359
+ is_closing() {
360
+ return this.#closing;
361
+ }
362
+ /**
363
+ * Close a registration that arrived after `close_all_sockets`, without
364
+ * inserting it. A socket whose `close` throws is logged, not thrown: the
365
+ * registration is refused either way, and its caller sees the aborted
366
+ * controller.
367
+ *
368
+ * @returns a fresh id that names no entry
369
+ */
370
+ #close_born_closed(ws, abort_controller) {
371
+ abort_controller?.abort();
372
+ try {
373
+ ws.close(WS_CLOSE_GOING_AWAY, WS_CLOSE_GOING_AWAY_REASON);
374
+ }
375
+ catch (error) {
376
+ this.#log?.error('error closing a socket registered during shutdown:', error);
377
+ }
378
+ return create_uuid();
379
+ }
316
380
  #cleanup_connection(connection_id) {
317
381
  this.#connections.delete(connection_id);
318
382
  // Wake any handler still awaiting a reply on this socket — the peer is
@@ -124,7 +124,7 @@ upstream in `auth/signup_routes.ts` via `query_invite_find_unclaimed_match_for_u
124
124
  its registered listeners + optional `AuditLogConfig`. Its methods:
125
125
 
126
126
  - `emit(ctx, input)` — fire-and-forget pool write, pushes to `ctx.pending_effects`; a success row's listener fan-out goes on `ctx.post_commit_effects`
127
- - `emit_role_grant_target(ctx, auth, input)` — lifts `actor_id` / `account_id` / `ip` boilerplate for role-grant-shape events
127
+ - `emit_role_grant_target(ctx, auth, input)` — lifts `actor_id` / `account_id` / `ip` boilerplate for role-grant-shape events; `input` is an `AuditEmitRoleGrantInput`
128
128
  - `emit_pool(input)` — awaitable pool write for code paths without a request context (ad-hoc maintenance scripts; not for success audits paired with a mutation — those write in-tx and `notify` post-commit). Writes, then notifies, whatever the outcome
129
129
  - `notify(event)` — fan out an already-written row to listeners (used by in-tx audit batches like `query_accept_offer.audit_events`)
130
130
  - `add_listener(listener)` — listener registration, returning a remover for that registration (idempotent; the same function registered twice is two registrations, each with its own remover). Twin of Rust `fuz_auth` `AuditEmitter::add_listener` in firing order and snapshot-at-notify
@@ -12,7 +12,7 @@
12
12
  * Account-grain — only that the caller is signed in matters, not which
13
13
  * actor is calling, so resolution skips the actor phase. The auth gate
14
14
  * + per-account rate limit (default 1200/15min) + the
15
- * {@link ACTOR_LOOKUP_IDS_MAX | per-call cap} bound the batched
15
+ * per-call cap (`ACTOR_LOOKUP_IDS_MAX`) bound the batched
16
16
  * username-enumeration surface that the `cell_list` ↔ `actor_lookup`
17
17
  * pair would otherwise present.
18
18
  *
@@ -38,7 +38,7 @@
38
38
  * ids), bounded by:
39
39
  *
40
40
  * 1. rate-limit (per-account, see above),
41
- * 2. {@link ACTOR_LOOKUP_IDS_MAX} cap per call,
41
+ * 2. `ACTOR_LOOKUP_IDS_MAX` cap per call,
42
42
  * 3. actor-uuid intractability (122-bit random),
43
43
  * 4. hard-deleted actors are indistinguishable from never-existed (no
44
44
  * tombstone oracle — see `auth/actor_lookup_queries.ts`).
@@ -12,7 +12,7 @@
12
12
  * Account-grain — only that the caller is signed in matters, not which
13
13
  * actor is calling, so resolution skips the actor phase. The auth gate
14
14
  * + per-account rate limit (default 1200/15min) + the
15
- * {@link ACTOR_LOOKUP_IDS_MAX | per-call cap} bound the batched
15
+ * per-call cap (`ACTOR_LOOKUP_IDS_MAX`) bound the batched
16
16
  * username-enumeration surface that the `cell_list` ↔ `actor_lookup`
17
17
  * pair would otherwise present.
18
18
  *
@@ -38,7 +38,7 @@
38
38
  * ids), bounded by:
39
39
  *
40
40
  * 1. rate-limit (per-account, see above),
41
- * 2. {@link ACTOR_LOOKUP_IDS_MAX} cap per call,
41
+ * 2. `ACTOR_LOOKUP_IDS_MAX` cap per call,
42
42
  * 3. actor-uuid intractability (122-bit random),
43
43
  * 4. hard-deleted actors are indistinguishable from never-existed (no
44
44
  * tombstone oracle — see `auth/actor_lookup_queries.ts`).
@@ -102,6 +102,19 @@ export interface AuditEmitRoleGrantContext extends AuditEmitterContext {
102
102
  /** Resolved client IP from the trusted-proxy middleware — `'unknown'` if not resolved. */
103
103
  client_ip: string;
104
104
  }
105
+ /**
106
+ * Input to `AuditEmitter.emit_role_grant_target` — the event fields that
107
+ * vary per call site; `actor_id`, `account_id`, and `ip` are lifted from
108
+ * the auth and request contexts.
109
+ */
110
+ export interface AuditEmitRoleGrantInput<T extends string> {
111
+ event_type: T;
112
+ target_account_id: Uuid | null;
113
+ target_actor_id: Uuid | null;
114
+ metadata: AuditLogInput<T>['metadata'];
115
+ /** Default `'success'`, as for `emit`. */
116
+ outcome?: 'success' | 'failure';
117
+ }
105
118
  /**
106
119
  * Bound audit-emit capability. Built once at backend assembly via
107
120
  * `create_audit_emitter`; lives on `AppDeps.audit` so factories never see
@@ -137,13 +150,7 @@ export interface AuditEmitter {
137
150
  * Reach for the lower-level `emit` only when the event is non-role-grant
138
151
  * shape (e.g. `app_settings_update`, bootstrap, signup).
139
152
  */
140
- emit_role_grant_target<T extends string>(ctx: AuditEmitRoleGrantContext, auth: RequestActorContext, input: {
141
- event_type: T;
142
- target_account_id: Uuid | null;
143
- target_actor_id: Uuid | null;
144
- metadata: AuditLogInput<T>['metadata'];
145
- outcome?: 'success' | 'failure';
146
- }): void;
153
+ emit_role_grant_target<T extends string>(ctx: AuditEmitRoleGrantContext, auth: RequestActorContext, input: AuditEmitRoleGrantInput<T>): void;
147
154
  /**
148
155
  * Awaitable pool write for code paths without a request context.
149
156
  *
@@ -1 +1 @@
1
- {"version":3,"file":"audit_emitter.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_emitter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audit_emitter.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_emitter.ts"],"names":[],"mappings":"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"}
@@ -187,16 +187,14 @@ export const create_audit_emitter = (options) => {
187
187
  };
188
188
  const listener_count = () => listeners.length;
189
189
  // Freeze the slot layout so consumers cannot hot-patch `emit` /
190
- // `emit_role_grant_target` / `emit_pool` / `notify` after construction.
191
- // The previous test helper `patch_audit_emit_capture` did exactly this
192
- // and only happened to work because the four slots were writable —
190
+ // `emit_role_grant_target` / `emit_pool` / `notify` after construction —
191
+ // a patched `emit` would miss role-grant-shape emits anyway, since
193
192
  // `emit_role_grant_target` calls the closed-over inner `emit`, not
194
- // `this.emit`, so the patch silently bypassed role-grant-shape emits.
195
- // Tests that need instrumentation pass `emit_decorator` so the wrap
196
- // is captured by the closure before the freeze. The listener list stays
193
+ // `this.emit`. Tests that need instrumentation pass `emit_decorator` so the
194
+ // wrap is captured by the closure before the freeze. The listener list stays
197
195
  // closure-private; it changes only through `add_listener` and the remover
198
- // it returns (`create_app_server` registers its SSE + WS listeners
199
- // post-assembly, by design, and removes them on close).
196
+ // it returns (`create_app_server` registers its SSE + WS listeners after
197
+ // the emitter is built, by design, and removes them on close).
200
198
  return Object.freeze({
201
199
  emit,
202
200
  emit_role_grant_target,
@@ -56,9 +56,13 @@ export interface AuditLogRouteOptions {
56
56
  * nothing else — to an `EventSource`, a stream closed an instant after it
57
57
  * opened (the client's reconnect is answered by the gates, with the precise
58
58
  * status). A pending registration receives no audit row, counts toward no cap,
59
- * and is removed when a re-read refuses the request. The twin of the Rust
60
- * spine's `audit_stream_router`, which registers before its role read rather
61
- * than repeating it.
59
+ * and is removed when a re-read refuses the request. Once the server is
60
+ * shutting down (`SubscriberRegistry` class doc, "Shutdown") the registration
61
+ * is born closed, the re-reads are skipped, and the answer is that same
62
+ * connect comment alone — as it is when the shutdown closes a registration
63
+ * whose re-read is in flight, even if the re-read then fails. The twin of the
64
+ * Rust spine's `audit_stream_router`, which registers before its role read
65
+ * rather than repeating it.
62
66
  *
63
67
  * @param options - optional stream wiring + role override
64
68
  * @returns the SSE route spec (when `options.stream` is provided) or an empty array
@@ -1 +1 @@
1
- {"version":3,"file":"audit_log_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_log_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAUvD,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAU7E,yCAAyC;AACzC,MAAM,WAAW,oBAAoB;IACpC,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE;QACR,QAAQ,EAAE,kBAAkB,CAAC,eAAe,CAAC,CAAC;QAC9C,GAAG,EAAE,MAAM,CAAC;KACZ,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,4BAA4B,GAAI,UAAU,oBAAoB,KAAG,KAAK,CAAC,SAAS,CAgF5F,CAAC"}
1
+ {"version":3,"file":"audit_log_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/audit_log_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAUvD,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAU7E,yCAAyC;AACzC,MAAM,WAAW,oBAAoB;IACpC,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE;QACR,QAAQ,EAAE,kBAAkB,CAAC,eAAe,CAAC,CAAC;QAC9C,GAAG,EAAE,MAAM,CAAC;KACZ,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,4BAA4B,GAAI,UAAU,oBAAoB,KAAG,KAAK,CAAC,SAAS,CAiG5F,CAAC"}
@@ -42,9 +42,13 @@ import { AUDIT_LOG_CHANNEL } from '../realtime/sse_auth_guard.js';
42
42
  * nothing else — to an `EventSource`, a stream closed an instant after it
43
43
  * opened (the client's reconnect is answered by the gates, with the precise
44
44
  * status). A pending registration receives no audit row, counts toward no cap,
45
- * and is removed when a re-read refuses the request. The twin of the Rust
46
- * spine's `audit_stream_router`, which registers before its role read rather
47
- * than repeating it.
45
+ * and is removed when a re-read refuses the request. Once the server is
46
+ * shutting down (`SubscriberRegistry` class doc, "Shutdown") the registration
47
+ * is born closed, the re-reads are skipped, and the answer is that same
48
+ * connect comment alone — as it is when the shutdown closes a registration
49
+ * whose re-read is in flight, even if the re-read then fails. The twin of the
50
+ * Rust spine's `audit_stream_router`, which registers before its role read
51
+ * rather than repeating it.
48
52
  *
49
53
  * @param options - optional stream wiring + role override
50
54
  * @returns the SSE route spec (when `options.stream` is provided) or an empty array
@@ -87,34 +91,52 @@ export const create_audit_log_route_specs = (options) => {
87
91
  // a revocation that closed before the registration is committed,
88
92
  // so these see it. A throw here fails closed — no stream opens
89
93
  // unchecked. Skipped under the test-preset escape hatch, whose
90
- // pre-baked context has no rows behind it to re-read.
91
- if (!c.get(TEST_CONTEXT_PRESET_KEY)) {
92
- const resolved = get_resolved_auth(c);
93
- const credential_is_live = resolved !== null && (await revalidate_resolved_auth(route, resolved));
94
- if (!credential_is_live) {
95
- log.info('audit stream: credential revoked during the request', ctx.account.id);
96
- return c.json({ error: ERROR_AUTHENTICATION_REQUIRED }, 401);
97
- }
98
- // Global grants only, like the `require_role` gate that ran in
99
- // the pipeline — a scoped grant must not open the instance-wide
100
- // stream.
101
- if (!has_scoped_role(await refresh_role_grants(ctx, route), required_role, null)) {
102
- log.info('audit stream: role revoked during the request', ctx.account.id);
103
- return c.json({ error: ERROR_INSUFFICIENT_PERMISSIONS, required_roles: [required_role] }, 403);
104
- }
94
+ // pre-baked context has no rows behind it to re-read, and for a
95
+ // registration born closed by the shutdown, which admission
96
+ // refuses whatever they answer.
97
+ if (!c.get(TEST_CONTEXT_PRESET_KEY) && !registry.closing) {
98
+ const reread = async () => {
99
+ const resolved = get_resolved_auth(c);
100
+ const credential_is_live = resolved !== null && (await revalidate_resolved_auth(route, resolved));
101
+ if (!credential_is_live) {
102
+ log.info('audit stream: credential revoked during the request', ctx.account.id);
103
+ return c.json({ error: ERROR_AUTHENTICATION_REQUIRED }, 401);
104
+ }
105
+ // Global grants only, like the `require_role` gate that ran in
106
+ // the pipeline — a scoped grant must not open the instance-wide
107
+ // stream.
108
+ if (!has_scoped_role(await refresh_role_grants(ctx, route), required_role, null)) {
109
+ log.info('audit stream: role revoked during the request', ctx.account.id);
110
+ return c.json({ error: ERROR_INSUFFICIENT_PERMISSIONS, required_roles: [required_role] }, 403);
111
+ }
112
+ return null;
113
+ };
114
+ const refusal = await reread().catch((error) => {
115
+ // the shutdown closed the registration and then the database
116
+ // under the re-read: the answer is the shutdown's — admission
117
+ // below refuses, since a closing registry admits nothing —
118
+ // not a failure
119
+ if (registry.closing)
120
+ return null;
121
+ throw error;
122
+ });
123
+ if (refusal)
124
+ return refusal;
105
125
  }
106
126
  // Admit — the cap's eviction happens here, after every gate. A
107
- // registration a revocation closed while it was pending is not
108
- // admitted: the stream is closed at once, so the body is the
127
+ // registration a revocation or the shutdown closed while it was
128
+ // pending, or one born closed, is not admitted: the stream is
129
+ // closed at once, so the body is the
109
130
  // connect comment and nothing else, and the client reconnects
110
- // into the gates.
131
+ // into the gates. The close listener goes on first: a client that
132
+ // left during the re-reads gets a stream that is already closed,
133
+ // whose listener runs at once and removes the registration, so
134
+ // `admit` refuses it rather than evicting a live stream for it.
111
135
  const { response, stream } = create_sse_response(c, log);
136
+ stream.on_close(pending.unsubscribe);
112
137
  admitted = registry.admit(pending, stream);
113
- if (admitted) {
114
- stream.on_close(pending.unsubscribe);
115
- }
116
- else {
117
- log.info('audit stream: closed by a revocation before admission', ctx.account.id);
138
+ if (!admitted) {
139
+ log.info('audit stream: closed before admission', ctx.account.id);
118
140
  stream.close();
119
141
  }
120
142
  return response;
@@ -28,8 +28,9 @@ import type { QueryDeps } from '../db/query_deps.js';
28
28
  * (`invalid_token`, `account_not_found`) that could aid enumeration attacks,
29
29
  * and ensures public actions are not blocked by bad credentials.
30
30
  *
31
- * Rejects bearer tokens when an `Origin` or `Referer` header is present —
32
- * browsers must use cookie auth to reduce attack surface.
31
+ * Discards bearer tokens when an `Origin` or `Referer` header is present —
32
+ * `next()` with no identity, as for a bad token — so browsers must use cookie
33
+ * auth, reducing attack surface.
33
34
  * Auth scheme matching is case-insensitive per RFC 7235.
34
35
  * On success, sets `c.var.auth_account_id`, `CREDENTIAL_TYPE_KEY = 'api_token'`,
35
36
  * and `AUTH_API_TOKEN_ID_KEY`. Skips when an account is already authenticated
@@ -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,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAUtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAIrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,6BAA6B,GAAI,MAAM,SAAS,EAAE,KAAK,MAAM,KAAG,iBAuE5E,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,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAUtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAIrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,6BAA6B,GAAI,MAAM,SAAS,EAAE,KAAK,MAAM,KAAG,iBAuE5E,CAAC"}
@@ -31,8 +31,9 @@ import { is_browser_context } from '../http/origin.js';
31
31
  * (`invalid_token`, `account_not_found`) that could aid enumeration attacks,
32
32
  * and ensures public actions are not blocked by bad credentials.
33
33
  *
34
- * Rejects bearer tokens when an `Origin` or `Referer` header is present —
35
- * browsers must use cookie auth to reduce attack surface.
34
+ * Discards bearer tokens when an `Origin` or `Referer` header is present —
35
+ * `next()` with no identity, as for a bad token — so browsers must use cookie
36
+ * auth, reducing attack surface.
36
37
  * Auth scheme matching is case-insensitive per RFC 7235.
37
38
  * On success, sets `c.var.auth_account_id`, `CREDENTIAL_TYPE_KEY = 'api_token'`,
38
39
  * and `AUTH_API_TOKEN_ID_KEY`. Skips when an account is already authenticated
@@ -251,7 +251,7 @@ export declare const create_request_context_middleware: (deps: QueryDeps, sessio
251
251
  * across route-spec declarations and direct calls alike, and the twin of the
252
252
  * Rust signature. It needs no parse: the surface arm's identifier is pure label,
253
253
  * since rule 3 never asks *which* surface. And it is deliberately not narrowed
254
- * to the spine's four — the name decides nothing, so there is nothing for a
254
+ * to the spine's own — the name decides nothing, so there is nothing for a
255
255
  * closed set to protect, while a consumer surface would have nothing to pass.
256
256
  * fuz_app's own uses are pinned two ways by the surface census: the exact call
257
257
  * site, and the scan asserting every `'surface:<name>'` literal in `src/lib`
@@ -281,7 +281,7 @@ const token_scope_denial = (c, capability) => {
281
281
  * across route-spec declarations and direct calls alike, and the twin of the
282
282
  * Rust signature. It needs no parse: the surface arm's identifier is pure label,
283
283
  * since rule 3 never asks *which* surface. And it is deliberately not narrowed
284
- * to the spine's four — the name decides nothing, so there is nothing for a
284
+ * to the spine's own — the name decides nothing, so there is nothing for a
285
285
  * closed set to protect, while a consumer surface would have nothing to pass.
286
286
  * fuz_app's own uses are pinned two ways by the surface census: the exact call
287
287
  * site, and the scan asserting every `'surface:<name>'` literal in `src/lib`
@@ -106,7 +106,9 @@ export declare const token_scope_admits_method: (scope: TokenScope, method: stri
106
106
  export declare const token_scope_admits_non_rpc: (scope: TokenScope) => boolean;
107
107
  /**
108
108
  * The non-RPC surfaces the **spine itself** mounts (rule 3). Twin of the Rust
109
- * `TokenSurface` enum, name for name.
109
+ * `TokenSurface` enum, name for name, except `app_surface` — the opt-in
110
+ * `GET /api/surface` explorer (`server/surface_route.ts`), which only the TS
111
+ * spine serves.
110
112
  *
111
113
  * Not a registry a consumer registers into. `RouteAuth.required_scope` accepts
112
114
  * any well-formed `surface:<name>` (see `parse_token_scope_capability`), so a
@@ -120,7 +122,7 @@ export declare const token_scope_admits_non_rpc: (scope: TokenScope) => boolean;
120
122
  * refused, in the `required_scope` a denial reports. That is the same field
121
123
  * whose `rpc:<method>` arm has always carried arbitrary consumer method names.
122
124
  */
123
- export declare const TOKEN_SURFACES: readonly ["db_admin", "fact_bare", "audit_stream", "ws_upgrade"];
125
+ export declare const TOKEN_SURFACES: readonly ["db_admin", "fact_bare", "audit_stream", "ws_upgrade", "app_surface"];
124
126
  export type TokenSurface = (typeof TOKEN_SURFACES)[number];
125
127
  /**
126
128
  * Is `value` one of the surfaces the spine mounts?
@@ -1 +1 @@
1
- {"version":3,"file":"token_scope.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/token_scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,wFAAwF;AACxF,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAAW,CAAC;AAE9C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,UAAU;;;;;;;;;4BAYrB,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe;;;;;4BAG1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,kDAAkD;AAClD,eAAO,MAAM,gBAAgB,QAAO,UAGlC,CAAC;AAEH,uCAAuC;AACvC,eAAO,MAAM,mBAAmB,GAAI,SAAS,KAAK,CAAC,MAAM,CAAC,KAAG,UAI3D,CAAC;AAEH,qDAAqD;AACrD,eAAO,MAAM,sBAAsB,GAAI,OAAO,eAAe,KAAG,UACgB,CAAC;AAEjF;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,GAAI,OAAO,UAAU,EAAE,QAAQ,MAAM,KAAG,OACtB,CAAC;AAEzD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,0BAA0B,GAAI,OAAO,UAAU,KAAG,OAAgC,CAAC;AAEhG;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,cAAc,kEAAmE,CAAC;AAC/F,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,KAAG,KAAK,IAAI,YACA,CAAC;AAE3D;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,QAAgC,CAAC;AAQtE;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,GAAI,QAAQ,MAAM,KAAG,MAC3B,CAAC;AAErC;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,oBAAoB,GAC/B;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/F;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,4BAA4B,GAAI,OAAO,MAAM,KAAG,oBAAoB,GAAG,IAUnF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,6BAA6B,GACzC,OAAO,UAAU,EACjB,YAAY,oBAAoB,KAC9B,OAGkC,CAAC;AAEtC;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,GACnC,YAAY,MAAM,KAChB;IAAE,KAAK,EAAE,sBAAsB,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAGxD,CAAC;AAEH,4CAA4C;AAC5C,eAAO,MAAM,iBAAiB,GAAI,OAAO,UAAU,KAAG,MAGrD,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,KAAG,UAAU,GAAG,IAU5D,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,UAAU,KAAG,MAQzD,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,GAAI,OAAO,OAAO,KAAG,UAAU,GAAG,IAQtE,CAAC"}
1
+ {"version":3,"file":"token_scope.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/token_scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,wFAAwF;AACxF,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAAW,CAAC;AAE9C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,UAAU;;;;;;;;;4BAYrB,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe;;;;;4BAG1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,kDAAkD;AAClD,eAAO,MAAM,gBAAgB,QAAO,UAGlC,CAAC;AAEH,uCAAuC;AACvC,eAAO,MAAM,mBAAmB,GAAI,SAAS,KAAK,CAAC,MAAM,CAAC,KAAG,UAI3D,CAAC;AAEH,qDAAqD;AACrD,eAAO,MAAM,sBAAsB,GAAI,OAAO,eAAe,KAAG,UACgB,CAAC;AAEjF;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,GAAI,OAAO,UAAU,EAAE,QAAQ,MAAM,KAAG,OACtB,CAAC;AAEzD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,0BAA0B,GAAI,OAAO,UAAU,KAAG,OAAgC,CAAC;AAEhG;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,cAAc,iFAMjB,CAAC;AACX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,KAAG,KAAK,IAAI,YACA,CAAC;AAE3D;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,QAAgC,CAAC;AAQtE;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,GAAI,QAAQ,MAAM,KAAG,MAC3B,CAAC;AAErC;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,oBAAoB,GAC/B;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/F;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,4BAA4B,GAAI,OAAO,MAAM,KAAG,oBAAoB,GAAG,IAUnF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,6BAA6B,GACzC,OAAO,UAAU,EACjB,YAAY,oBAAoB,KAC9B,OAGkC,CAAC;AAEtC;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,GACnC,YAAY,MAAM,KAChB;IAAE,KAAK,EAAE,sBAAsB,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAGxD,CAAC;AAEH,4CAA4C;AAC5C,eAAO,MAAM,iBAAiB,GAAI,OAAO,UAAU,KAAG,MAGrD,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,KAAG,UAAU,GAAG,IAU5D,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,UAAU,KAAG,MAQzD,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,GAAI,OAAO,OAAO,KAAG,UAAU,GAAG,IAQtE,CAAC"}
@@ -112,7 +112,9 @@ export const token_scope_admits_method = (scope, method) => scope.kind === 'full
112
112
  export const token_scope_admits_non_rpc = (scope) => scope.kind === 'full';
113
113
  /**
114
114
  * The non-RPC surfaces the **spine itself** mounts (rule 3). Twin of the Rust
115
- * `TokenSurface` enum, name for name.
115
+ * `TokenSurface` enum, name for name, except `app_surface` — the opt-in
116
+ * `GET /api/surface` explorer (`server/surface_route.ts`), which only the TS
117
+ * spine serves.
116
118
  *
117
119
  * Not a registry a consumer registers into. `RouteAuth.required_scope` accepts
118
120
  * any well-formed `surface:<name>` (see `parse_token_scope_capability`), so a
@@ -126,7 +128,13 @@ export const token_scope_admits_non_rpc = (scope) => scope.kind === 'full';
126
128
  * refused, in the `required_scope` a denial reports. That is the same field
127
129
  * whose `rpc:<method>` arm has always carried arbitrary consumer method names.
128
130
  */
129
- export const TOKEN_SURFACES = ['db_admin', 'fact_bare', 'audit_stream', 'ws_upgrade'];
131
+ export const TOKEN_SURFACES = [
132
+ 'db_admin',
133
+ 'fact_bare',
134
+ 'audit_stream',
135
+ 'ws_upgrade',
136
+ 'app_surface'
137
+ ];
130
138
  /**
131
139
  * Is `value` one of the surfaces the spine mounts?
132
140
  *
@@ -28,7 +28,7 @@ effects, see ../../../docs/architecture.md.
28
28
  - `http/jsonrpc.ts` — JSON-RPC 2.0 envelope schemas (MCP superset), `JsonrpcErrorCode`, `_meta`.
29
29
  - `http/jsonrpc_errors.ts` — `ThrownJsonrpcError`, `jsonrpc_errors` throwers, HTTP-status mappings.
30
30
  - `http/jsonrpc_helpers.ts` — message builders, type guards, input/result normalizers.
31
- - `http/common_routes.ts` — health check + readiness probe (`/ready` schema-drift deploy gate) + authenticated server-status + surface route specs.
31
+ - `http/common_routes.ts` — health check + readiness probe (`/ready` schema-drift deploy gate) + authenticated server-status route specs (the surface explorer route lives in `server/surface_route.ts`).
32
32
  - `http/db_routes.ts` — allowlist-gated keeper-only table browser route specs (`public` schema, minus the `NON_BROWSABLE_TABLES` credential floor).
33
33
  - `http/pending_effects.ts` — `emit_after_commit` + `dispatch_with_post_commit_rollback` (the shared rollback-discard wrapper both dispatch sites use) + `flush_pending_effects` + `flush_post_commit_effects` + `EmitAfterCommitContext`.
34
34
 
@@ -383,7 +383,7 @@ own by casting `as JsonrpcErrorCode`):
383
383
  - `rate_limited` (-32006, HTTP 429) — server-side policy.
384
384
  - `service_unavailable` (-32007, HTTP 503) — upstream down / maintenance.
385
385
  - `timeout` (-32008, HTTP 504) — handler exceeded time budget.
386
- - `queue_overflow` (-32009, HTTP 429) — **client-side** backpressure (WS reconnect queue full).
386
+ - `queue_overflow` (-32009, HTTP 429) — backpressure: a buffer refused a new request because it was full — client-side (the WS reconnect queue) or server-side (a socket past `MAX_INBOUND_DISPATCHES_PER_CONNECTION` in-flight requests).
387
387
  - `request_cancelled` (-32010, HTTP 499) — caller-initiated cancellation (nginx "client closed").
388
388
 
389
389
  **`invalid_params` vs `validation_error`** — use `invalid_params` (standard
@@ -484,13 +484,12 @@ at send time.
484
484
 
485
485
  ## Common Routes
486
486
 
487
- `http/common_routes.ts` exposes four generic route-spec factories with no
487
+ `http/common_routes.ts` exposes three generic route-spec factories with no
488
488
  auth-domain dependencies:
489
489
 
490
490
  - `create_health_route_spec()` — `GET /health`, public, returns `{status: 'ok'}`. Infrastructure endpoint for uptime monitors
491
- - `create_ready_route_spec({expected, log})` — `GET /ready`, public, the deploy gate. Introspects the live DB's columns (`db/schema_ready.ts`) and compares against the committed `expected` column map: `200 {ready: true}` on match, else `503 {error: 'schema_drift' | 'db_unreachable'}`. Detailed drift logs server-side only (minimal body — no schema leak, mirrors why `/api/surface` is authenticated). Throws at assembly on an empty `expected` (fail-loud). Opt-in like `/health`; pair with `load_expected_schema(url)` — loads + URL-caches a consumer's committed `expected_schema.json` and throws on an empty map
491
+ - `create_ready_route_spec({expected, log})` — `GET /ready`, public, the deploy gate. Introspects the live DB's columns (`db/schema_ready.ts`) and compares against the committed `expected` column map: `200 {ready: true}` on match, else `503 {error: 'schema_drift' | 'db_unreachable'}`. Detailed drift logs server-side only (minimal body — no schema leak, mirrors why `/api/surface` is admin-only). Throws at assembly on an empty `expected` (fail-loud). Opt-in like `/health`; pair with `load_expected_schema(url)` — loads + URL-caches a consumer's committed `expected_schema.json` and throws on an empty map
492
492
  - `create_server_status_route_spec({version, get_uptime_ms})` — `GET /api/server/status`, authenticated, returns `{version, uptime_ms}`
493
- - `create_surface_route_spec({surface})` — `GET /api/surface`, authenticated, serves the `AppSurface` JSON. Authenticated because surface data reveals API structure (schemas, auth, routes)
494
493
 
495
494
  Auth-aware variants (account status, bootstrap status) live in `auth/` —
496
495
  `http/common_routes.ts` stays generic.
@@ -141,8 +141,9 @@ export declare const RouteAuth: z.ZodObject<{
141
141
  *
142
142
  * - `surface:<name>` — a non-RPC surface, refused to every narrowed token
143
143
  * whatever its method list says (token scoping's rule that a narrowed
144
- * token is RPC-only). `surface:audit_stream` and `surface:fact_bare` are
145
- * the spine's; a consumer names its own without registering it.
144
+ * token is RPC-only). `surface:audit_stream`, `surface:fact_bare`, and
145
+ * `surface:app_surface` are the spine's route-spec ones; a consumer names
146
+ * its own without registering it.
146
147
  * - `rpc:<method>` — one action method, refused unless the token lists it.
147
148
  * What a route bridged from an `ActionSpec` declares: it carries the
148
149
  * method's identity but never reaches the dispatcher's per-method gate.
@@ -1 +1 @@
1
- {"version":3,"file":"auth_shape.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/auth_shape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,WAAW,6DAGtB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,SAGtD,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,aAAa;;;;EAA2C,CAAC;AACtE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;IAMpB;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;;kBAyCF,CAAC;AACJ,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAUlD;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,SAAS,KAAG,OACA,CAAC;AAElD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,SAAS,KAAG,OAAgC,CAAC;AAE/E;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,KAAG,OAAkC,CAAC;AAEnF,wEAAwE;AACxE,eAAO,MAAM,YAAY,GAAI,MAAM,SAAS,KAAG,OAA+B,CAAC;AAE/E,8FAA8F;AAC9F,eAAO,MAAM,wBAAwB,GAAI,MAAM,SAAS,KAAG,OAC3B,CAAC;AAEjC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,SAAS,KAAG,OACQ,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,GAAI,MAAM,SAAS,KAAG,OACwB,CAAC;AAYvF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAIhE,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,sCAAsC,GAClD,MAAM,SAAS,EACf,OAAO,WAAW,EAClB,SAAS,MAAM,KACb,IAmBF,CAAC"}
1
+ {"version":3,"file":"auth_shape.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/auth_shape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,WAAW,6DAGtB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,SAGtD,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,aAAa;;;;EAA2C,CAAC;AACtE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;IAMpB;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;;kBAyCF,CAAC;AACJ,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAUlD;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,SAAS,KAAG,OACA,CAAC;AAElD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,SAAS,KAAG,OAAgC,CAAC;AAE/E;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,KAAG,OAAkC,CAAC;AAEnF,wEAAwE;AACxE,eAAO,MAAM,YAAY,GAAI,MAAM,SAAS,KAAG,OAA+B,CAAC;AAE/E,8FAA8F;AAC9F,eAAO,MAAM,wBAAwB,GAAI,MAAM,SAAS,KAAG,OAC3B,CAAC;AAEjC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,SAAS,KAAG,OACQ,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,GAAI,MAAM,SAAS,KAAG,OACwB,CAAC;AAYvF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAIhE,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,sCAAsC,GAClD,MAAM,SAAS,EACf,OAAO,WAAW,EAClB,SAAS,MAAM,KACb,IAmBF,CAAC"}
@@ -135,8 +135,9 @@ export const RouteAuth = z
135
135
  *
136
136
  * - `surface:<name>` — a non-RPC surface, refused to every narrowed token
137
137
  * whatever its method list says (token scoping's rule that a narrowed
138
- * token is RPC-only). `surface:audit_stream` and `surface:fact_bare` are
139
- * the spine's; a consumer names its own without registering it.
138
+ * token is RPC-only). `surface:audit_stream`, `surface:fact_bare`, and
139
+ * `surface:app_surface` are the spine's route-spec ones; a consumer names
140
+ * its own without registering it.
140
141
  * - `rpc:<method>` — one action method, refused unless the token lists it.
141
142
  * What a route bridged from an `ActionSpec` declares: it carries the
142
143
  * method's identity but never reaches the dispatcher's per-method gate.
@@ -8,7 +8,6 @@
8
8
  */
9
9
  import type { Logger } from '@fuzdev/fuz_util/log.ts';
10
10
  import type { RouteSpec } from './route_spec.js';
11
- import type { AppSurface } from './surface.js';
12
11
  import { type ExpectedSchema } from '../db/schema_ready.js';
13
12
  /**
14
13
  * Create a public health check route spec.
@@ -57,7 +56,7 @@ export interface ReadyRouteOptions {
57
56
  * else `503 {error}` (`schema_drift` when columns are missing, `db_unreachable`
58
57
  * when the introspection query throws). The detailed drift goes to the server
59
58
  * log only — the public body stays a minimal code so the endpoint doesn't leak
60
- * schema structure (mirrors why `/api/surface` is authenticated). A deploy poll
59
+ * schema structure (mirrors why `/api/surface` is admin-only). A deploy poll
61
60
  * treats `503` as a failed release and rolls back, turning a silent
62
61
  * schema-drift auth outage into a loud blocked deploy. See `db/schema_ready.ts`
63
62
  * for the column-presence rationale and `auth/migrations.ts` for the
@@ -78,16 +77,4 @@ export interface ServerStatusOptions {
78
77
  * this requires authentication.
79
78
  */
80
79
  export declare const create_server_status_route_spec: (options: ServerStatusOptions) => RouteSpec;
81
- /** Options for the surface explorer route. */
82
- export interface SurfaceRouteOptions {
83
- /** The generated app surface to serve. */
84
- surface: AppSurface;
85
- }
86
- /**
87
- * Create an authenticated route spec that serves the `AppSurface` as JSON.
88
- *
89
- * Surface data reveals API structure (routes, auth, schemas), so this
90
- * requires authentication like the server status route.
91
- */
92
- export declare const create_surface_route_spec: (options: SurfaceRouteOptions) => RouteSpec;
93
80
  //# sourceMappingURL=common_routes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"common_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/common_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAIN,KAAK,cAAc,EACnB,MAAM,uBAAuB,CAAC;AAE/B;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,QAAO,SAQ1C,CAAC;AAKH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,GAAG,GAAG,MAAM,KAAG,cAkBxD,CAAC;AAEF,6CAA6C;AAC7C,MAAM,WAAW,iBAAiB;IACjC;;;;OAIG;IACH,QAAQ,EAAE,cAAc,CAAC;IACzB,gFAAgF;IAChF,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,GAAI,SAAS,iBAAiB,KAAG,SAoCpE,CAAC;AAEF,yDAAyD;AACzD,MAAM,WAAW,mBAAmB;IACnC,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,aAAa,EAAE,MAAM,MAAM,CAAC;CAC5B;AAED;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,GAAI,SAAS,mBAAmB,KAAG,SAQ7E,CAAC;AAEH,8CAA8C;AAC9C,MAAM,WAAW,mBAAmB;IACnC,0CAA0C;IAC1C,OAAO,EAAE,UAAU,CAAC;CACpB;AAED;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,mBAAmB,KAAG,SAWvE,CAAC"}
1
+ {"version":3,"file":"common_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/common_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAIN,KAAK,cAAc,EACnB,MAAM,uBAAuB,CAAC;AAE/B;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,QAAO,SAQ1C,CAAC;AAKH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,GAAG,GAAG,MAAM,KAAG,cAkBxD,CAAC;AAEF,6CAA6C;AAC7C,MAAM,WAAW,iBAAiB;IACjC;;;;OAIG;IACH,QAAQ,EAAE,cAAc,CAAC;IACzB,gFAAgF;IAChF,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,GAAI,SAAS,iBAAiB,KAAG,SAoCpE,CAAC;AAEF,yDAAyD;AACzD,MAAM,WAAW,mBAAmB;IACnC,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,aAAa,EAAE,MAAM,MAAM,CAAC;CAC5B;AAED;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,GAAI,SAAS,mBAAmB,KAAG,SAQ7E,CAAC"}