@fuzdev/fuz_app 0.63.0 → 0.65.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 (181) hide show
  1. package/dist/actions/CLAUDE.md +525 -827
  2. package/dist/actions/broadcast_api.d.ts +1 -1
  3. package/dist/actions/broadcast_api.js +1 -1
  4. package/dist/actions/cancel.d.ts +2 -2
  5. package/dist/actions/cancel.js +3 -3
  6. package/dist/actions/connection_closer.d.ts +65 -0
  7. package/dist/actions/connection_closer.d.ts.map +1 -0
  8. package/dist/actions/connection_closer.js +38 -0
  9. package/dist/actions/register_action_ws.d.ts +2 -2
  10. package/dist/actions/register_action_ws.d.ts.map +1 -1
  11. package/dist/actions/register_action_ws.js +23 -2
  12. package/dist/actions/register_ws_endpoint.d.ts +12 -10
  13. package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
  14. package/dist/actions/register_ws_endpoint.js +5 -5
  15. package/dist/actions/transports_ws_auth_guard.d.ts +25 -10
  16. package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
  17. package/dist/actions/transports_ws_auth_guard.js +24 -9
  18. package/dist/actions/ws_endpoint_spec.d.ts +119 -0
  19. package/dist/actions/ws_endpoint_spec.d.ts.map +1 -0
  20. package/dist/actions/ws_endpoint_spec.js +13 -0
  21. package/dist/auth/CLAUDE.md +592 -1808
  22. package/dist/auth/account_action_specs.d.ts +1 -1
  23. package/dist/auth/account_actions.d.ts +13 -0
  24. package/dist/auth/account_actions.d.ts.map +1 -1
  25. package/dist/auth/account_actions.js +31 -1
  26. package/dist/auth/account_routes.d.ts +12 -2
  27. package/dist/auth/account_routes.d.ts.map +1 -1
  28. package/dist/auth/account_routes.js +55 -8
  29. package/dist/auth/account_schema.d.ts +4 -4
  30. package/dist/auth/account_schema.d.ts.map +1 -1
  31. package/dist/auth/admin_action_specs.d.ts +8 -8
  32. package/dist/auth/admin_actions.d.ts +11 -0
  33. package/dist/auth/admin_actions.d.ts.map +1 -1
  34. package/dist/auth/admin_actions.js +25 -0
  35. package/dist/auth/api_token_queries.js +1 -1
  36. package/dist/auth/audit_emitter.d.ts +56 -12
  37. package/dist/auth/audit_emitter.d.ts.map +1 -1
  38. package/dist/auth/audit_emitter.js +38 -12
  39. package/dist/auth/audit_log_ddl.d.ts +1 -1
  40. package/dist/auth/audit_log_ddl.d.ts.map +1 -1
  41. package/dist/auth/audit_log_ddl.js +1 -1
  42. package/dist/auth/audit_log_schema.d.ts +5 -3
  43. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  44. package/dist/auth/audit_log_schema.js +5 -3
  45. package/dist/auth/bootstrap_account.d.ts.map +1 -1
  46. package/dist/auth/bootstrap_account.js +1 -5
  47. package/dist/auth/bootstrap_routes.d.ts +8 -2
  48. package/dist/auth/bootstrap_routes.d.ts.map +1 -1
  49. package/dist/auth/bootstrap_routes.js +15 -11
  50. package/dist/auth/invite_schema.d.ts +2 -2
  51. package/dist/auth/keyring.d.ts +6 -6
  52. package/dist/auth/keyring.js +8 -8
  53. package/dist/auth/role_grant_offer_actions.d.ts.map +1 -1
  54. package/dist/auth/role_grant_offer_actions.js +4 -2
  55. package/dist/auth/signup_routes.d.ts +1 -1
  56. package/dist/auth/standard_rpc_actions.d.ts +1 -0
  57. package/dist/auth/standard_rpc_actions.d.ts.map +1 -1
  58. package/dist/auth/standard_rpc_actions.js +1 -0
  59. package/dist/db/create_db.d.ts.map +1 -1
  60. package/dist/db/create_db.js +13 -0
  61. package/dist/dev/setup.d.ts +2 -2
  62. package/dist/dev/setup.js +3 -3
  63. package/dist/http/CLAUDE.md +225 -483
  64. package/dist/http/error_schemas.d.ts +0 -4
  65. package/dist/http/error_schemas.d.ts.map +1 -1
  66. package/dist/http/error_schemas.js +0 -4
  67. package/dist/http/ip_canonical.d.ts +100 -0
  68. package/dist/http/ip_canonical.d.ts.map +1 -0
  69. package/dist/http/ip_canonical.js +195 -0
  70. package/dist/http/origin.d.ts +14 -6
  71. package/dist/http/origin.d.ts.map +1 -1
  72. package/dist/http/origin.js +14 -32
  73. package/dist/http/pending_effects.d.ts +1 -1
  74. package/dist/http/pending_effects.js +1 -1
  75. package/dist/http/proxy.d.ts +13 -5
  76. package/dist/http/proxy.d.ts.map +1 -1
  77. package/dist/http/proxy.js +15 -23
  78. package/dist/http/surface.d.ts +50 -0
  79. package/dist/http/surface.d.ts.map +1 -1
  80. package/dist/http/surface.js +27 -1
  81. package/dist/primitive_schemas.d.ts +20 -4
  82. package/dist/primitive_schemas.d.ts.map +1 -1
  83. package/dist/primitive_schemas.js +25 -4
  84. package/dist/realtime/sse_auth_guard.d.ts +16 -4
  85. package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
  86. package/dist/realtime/sse_auth_guard.js +15 -3
  87. package/dist/runtime/mock.js +1 -1
  88. package/dist/server/app_backend.d.ts +66 -19
  89. package/dist/server/app_backend.d.ts.map +1 -1
  90. package/dist/server/app_backend.js +57 -34
  91. package/dist/server/app_server.d.ts +101 -10
  92. package/dist/server/app_server.d.ts.map +1 -1
  93. package/dist/server/app_server.js +105 -6
  94. package/dist/server/env.d.ts +7 -7
  95. package/dist/server/env.d.ts.map +1 -1
  96. package/dist/server/env.js +14 -14
  97. package/dist/server/startup.d.ts.map +1 -1
  98. package/dist/server/startup.js +12 -0
  99. package/dist/server/static.d.ts +4 -4
  100. package/dist/server/static.js +7 -7
  101. package/dist/testing/CLAUDE.md +269 -59
  102. package/dist/testing/admin_integration.d.ts +18 -23
  103. package/dist/testing/admin_integration.d.ts.map +1 -1
  104. package/dist/testing/admin_integration.js +159 -202
  105. package/dist/testing/adversarial_headers.d.ts +6 -0
  106. package/dist/testing/adversarial_headers.d.ts.map +1 -1
  107. package/dist/testing/adversarial_headers.js +13 -5
  108. package/dist/testing/app_server.d.ts +148 -60
  109. package/dist/testing/app_server.d.ts.map +1 -1
  110. package/dist/testing/app_server.js +143 -54
  111. package/dist/testing/attack_surface.d.ts +8 -7
  112. package/dist/testing/attack_surface.d.ts.map +1 -1
  113. package/dist/testing/attack_surface.js +12 -8
  114. package/dist/testing/audit_completeness.d.ts +23 -22
  115. package/dist/testing/audit_completeness.d.ts.map +1 -1
  116. package/dist/testing/audit_completeness.js +199 -158
  117. package/dist/testing/audit_drift_guard.d.ts +116 -0
  118. package/dist/testing/audit_drift_guard.d.ts.map +1 -0
  119. package/dist/testing/audit_drift_guard.js +134 -0
  120. package/dist/testing/bootstrap_success.d.ts +28 -0
  121. package/dist/testing/bootstrap_success.d.ts.map +1 -0
  122. package/dist/testing/bootstrap_success.js +144 -0
  123. package/dist/testing/connection_closer_helpers.d.ts +44 -0
  124. package/dist/testing/connection_closer_helpers.d.ts.map +1 -0
  125. package/dist/testing/connection_closer_helpers.js +48 -0
  126. package/dist/testing/cross_backend/capabilities.d.ts +64 -0
  127. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -0
  128. package/dist/testing/cross_backend/capabilities.js +47 -0
  129. package/dist/testing/cross_backend/setup.d.ts +215 -0
  130. package/dist/testing/cross_backend/setup.d.ts.map +1 -0
  131. package/dist/testing/cross_backend/setup.js +101 -0
  132. package/dist/testing/data_exposure.d.ts +14 -15
  133. package/dist/testing/data_exposure.d.ts.map +1 -1
  134. package/dist/testing/data_exposure.js +127 -146
  135. package/dist/testing/db_entities.d.ts +11 -1
  136. package/dist/testing/db_entities.d.ts.map +1 -1
  137. package/dist/testing/db_entities.js +13 -1
  138. package/dist/testing/integration.d.ts +35 -21
  139. package/dist/testing/integration.d.ts.map +1 -1
  140. package/dist/testing/integration.js +231 -293
  141. package/dist/testing/integration_helpers.d.ts +16 -6
  142. package/dist/testing/integration_helpers.d.ts.map +1 -1
  143. package/dist/testing/integration_helpers.js +7 -7
  144. package/dist/testing/mock_fs.d.ts.map +1 -1
  145. package/dist/testing/mock_fs.js +0 -2
  146. package/dist/testing/rate_limiting.d.ts.map +1 -1
  147. package/dist/testing/rate_limiting.js +13 -4
  148. package/dist/testing/role_grant_helpers.d.ts +31 -0
  149. package/dist/testing/role_grant_helpers.d.ts.map +1 -0
  150. package/dist/testing/role_grant_helpers.js +46 -0
  151. package/dist/testing/round_trip.d.ts +21 -16
  152. package/dist/testing/round_trip.d.ts.map +1 -1
  153. package/dist/testing/round_trip.js +65 -86
  154. package/dist/testing/rpc_helpers.d.ts +2 -1
  155. package/dist/testing/rpc_helpers.d.ts.map +1 -1
  156. package/dist/testing/rpc_round_trip.d.ts +24 -21
  157. package/dist/testing/rpc_round_trip.d.ts.map +1 -1
  158. package/dist/testing/rpc_round_trip.js +91 -106
  159. package/dist/testing/schema_introspect.d.ts +106 -0
  160. package/dist/testing/schema_introspect.d.ts.map +1 -0
  161. package/dist/testing/schema_introspect.js +123 -0
  162. package/dist/testing/schema_parity.d.ts +144 -0
  163. package/dist/testing/schema_parity.d.ts.map +1 -0
  164. package/dist/testing/schema_parity.js +233 -0
  165. package/dist/testing/sse_round_trip.d.ts.map +1 -1
  166. package/dist/testing/sse_round_trip.js +12 -6
  167. package/dist/testing/standard.d.ts +57 -25
  168. package/dist/testing/standard.d.ts.map +1 -1
  169. package/dist/testing/standard.js +62 -5
  170. package/dist/testing/stubs.d.ts +22 -3
  171. package/dist/testing/stubs.d.ts.map +1 -1
  172. package/dist/testing/stubs.js +28 -21
  173. package/dist/testing/surface_invariants.d.ts +66 -1
  174. package/dist/testing/surface_invariants.d.ts.map +1 -1
  175. package/dist/testing/surface_invariants.js +103 -1
  176. package/dist/testing/transports/surface_source.d.ts +51 -0
  177. package/dist/testing/transports/surface_source.d.ts.map +1 -0
  178. package/dist/testing/transports/surface_source.js +19 -0
  179. package/dist/ui/SurfaceExplorer.svelte +161 -2
  180. package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
  181. package/package.json +4 -4
@@ -8,6 +8,7 @@
8
8
  * @module
9
9
  */
10
10
  import { Hono, type Context } from 'hono';
11
+ import type { UpgradeWebSocket } from 'hono/ws';
11
12
  import { z } from 'zod';
12
13
  import { type SessionOptions } from '../auth/session_cookie.js';
13
14
  import type { BootstrapAccountSuccess } from '../auth/bootstrap_account.js';
@@ -25,6 +26,8 @@ import { type AppSurfaceSpec, type RpcEndpointSpec } from '../http/surface.js';
25
26
  import { type RouteSpec } from '../http/route_spec.js';
26
27
  import type { MiddlewareSpec } from '../http/middleware_spec.js';
27
28
  import { type BootstrapStatus } from '../auth/bootstrap_routes.js';
29
+ import type { WsEndpointSpec } from '../actions/ws_endpoint_spec.js';
30
+ import { BackendWebsocketTransport } from '../actions/transports_ws_backend.js';
28
31
  /**
29
32
  * Context passed to `on_effect_error` when a pending effect rejects.
30
33
  */
@@ -34,6 +37,46 @@ export interface EffectErrorContext {
34
37
  /** URL path of the request that spawned the effect. */
35
38
  path: string;
36
39
  }
40
+ /**
41
+ * Bootstrap configuration for `AppServerOptions.bootstrap`.
42
+ *
43
+ * Discriminated union over three deployment intents. Distinct from
44
+ * `BootstrapRouteOptions` in `auth/bootstrap_routes.ts` — that one is
45
+ * per-factory runtime state (mutable `bootstrap_status` ref, rate
46
+ * limiter); this one is the consumer-facing server option that
47
+ * `create_app_server` reads at startup to decide whether to mount the
48
+ * routes and where.
49
+ *
50
+ * Three modes:
51
+ * - `disabled` — no route mounted, nothing in `/api/surface`.
52
+ * Equivalent to omitting `bootstrap` entirely; the explicit mode is
53
+ * for documentation and reviewable intent at the wiring layer.
54
+ * - `surface_only` — route present, permanent 403 via
55
+ * `check_bootstrap_status`. For tests asserting on the
56
+ * disabled-but-present wire shape.
57
+ * - `live` — route mounted, real token verification. Success path
58
+ * reachable. `token_path` is required (non-nullable).
59
+ */
60
+ export type BootstrapServerOptions = BootstrapDisabledOptions | BootstrapSurfaceOnlyOptions | BootstrapLiveOptions;
61
+ export interface BootstrapDisabledOptions {
62
+ mode: 'disabled';
63
+ }
64
+ export interface BootstrapSurfaceOnlyOptions {
65
+ mode: 'surface_only';
66
+ /** Route prefix for surface generation. Default `'/api/account'`. */
67
+ route_prefix?: string;
68
+ }
69
+ export interface BootstrapLiveOptions {
70
+ mode: 'live';
71
+ token_path: string;
72
+ /** Route prefix for bootstrap routes. Default `'/api/account'`. */
73
+ route_prefix?: string;
74
+ /**
75
+ * Called after successful bootstrap (account + session created).
76
+ * Use for app-specific post-bootstrap work like generating API tokens.
77
+ */
78
+ on_bootstrap?: (result: BootstrapAccountSuccess, c: Context) => Promise<void>;
79
+ }
37
80
  /**
38
81
  * Configuration for `create_app_server()`.
39
82
  *
@@ -108,16 +151,7 @@ export interface AppServerOptions {
108
151
  /** Daemon token state for keeper auth. Omit to disable. */
109
152
  daemon_token_state?: DaemonTokenState;
110
153
  /** Bootstrap options. Omit to skip bootstrap status check and routes. */
111
- bootstrap?: {
112
- token_path: string | null;
113
- /** Route prefix for bootstrap routes. Default `'/api/account'`. */
114
- route_prefix?: string;
115
- /**
116
- * Called after successful bootstrap (account + session created).
117
- * Use for app-specific post-bootstrap work like generating API tokens.
118
- */
119
- on_bootstrap?: (result: BootstrapAccountSuccess, c: Context) => Promise<void>;
120
- };
154
+ bootstrap?: BootstrapServerOptions;
121
155
  /**
122
156
  * Set to `false` to disable the auto-created surface route (`GET /api/surface`).
123
157
  * Default: auto-created (authenticated).
@@ -163,6 +197,50 @@ export interface AppServerOptions {
163
197
  * `create_standard_rpc_actions(ctx.deps, {app_settings: ctx.app_settings})`.
164
198
  */
165
199
  rpc_endpoints?: Array<RpcEndpointSpec> | ((context: AppServerContext) => Array<RpcEndpointSpec>);
200
+ /**
201
+ * Hono adapter's `upgradeWebSocket` helper. Required whenever
202
+ * `ws_endpoints` resolves to a non-empty array — `create_app_server`
203
+ * throws at assembly otherwise. Omit (along with `ws_endpoints`)
204
+ * when the consumer doesn't mount any WS endpoints. The same
205
+ * adapter helper services every `WsEndpointSpec` mounted from
206
+ * `ws_endpoints` — one adapter per app.
207
+ *
208
+ * For Node, `import {upgradeWebSocket} from '@hono/node-ws'`. For
209
+ * Deno, `import {upgradeWebSocket} from 'hono/deno'`. Test harnesses
210
+ * use `create_stub_upgrade` from `$lib/testing/ws_round_trip.ts`.
211
+ */
212
+ upgradeWebSocket?: UpgradeWebSocket;
213
+ /**
214
+ * WebSocket endpoint specs — single source of truth for both surface
215
+ * generation *and* live dispatch. Each entry is auto-mounted via
216
+ * `register_ws_endpoint` against the assembled Hono app, so
217
+ * consumers no longer call `register_ws_endpoint` themselves.
218
+ *
219
+ * Accepts either an array (evaluated eagerly) or a factory
220
+ * `(ctx: AppServerContext) => ReadonlyArray<WsEndpointSpec>`
221
+ * (evaluated after the server context is assembled). Use the factory
222
+ * form when action lists depend on `ctx.deps` /
223
+ * `ctx.action_*_rate_limiter` — e.g. when spreading
224
+ * `create_standard_rpc_actions(ctx.deps, ...)` over WS.
225
+ *
226
+ * When non-empty, `upgradeWebSocket` must be supplied (throws
227
+ * otherwise). A factory returning `[]` does NOT trip the check —
228
+ * feature-flag gated WS surfaces stay safe.
229
+ *
230
+ * Duplicate `path` values across two `WsEndpointSpec`s throw at
231
+ * mount time (Hono would silently shadow them otherwise).
232
+ *
233
+ * Each spec's `auth_guard?` defaults to `true` — the factory
234
+ * composes `create_ws_auth_guard` + `create_ws_logout_closer`
235
+ * against the mounted transport and appends them to
236
+ * `deps.audit.on_event_chain`. Wiring is deduped by transport
237
+ * **reference identity** so two specs sharing one
238
+ * `BackendWebsocketTransport` instance get a single pair of
239
+ * listeners; wrapped / proxied transports dedupe as separate
240
+ * entries (set `auth_guard: false` on duplicates and compose
241
+ * against the underlying transport once).
242
+ */
243
+ ws_endpoints?: ReadonlyArray<WsEndpointSpec> | ((context: AppServerContext) => ReadonlyArray<WsEndpointSpec>);
166
244
  /**
167
245
  * Env schema for surface generation. Defaults to `BaseServerEnv` —
168
246
  * pass an extended schema (typically `BaseServerEnv.extend({...})`)
@@ -241,6 +319,19 @@ export interface AppServer {
241
319
  * Use `require_audit_sse(server)` to assert the invariant.
242
320
  */
243
321
  audit_sse: AuditLogSse | null;
322
+ /**
323
+ * Path-keyed map of mounted WS endpoints. Each value is the
324
+ * `BackendWebsocketTransport` `create_app_server` registered
325
+ * connections against — supplied via `WsEndpointSpec.transport` or
326
+ * auto-created when omitted. Retain for broadcast / fan-out:
327
+ *
328
+ * ```ts
329
+ * app_server.ws_endpoints['/api/ws'].send_to_account(account_id, msg);
330
+ * ```
331
+ *
332
+ * Empty when no `ws_endpoints` were mounted.
333
+ */
334
+ ws_endpoints: Readonly<Record<string, BackendWebsocketTransport>>;
244
335
  /** Close the database connection. Propagated from `AppBackend`. */
245
336
  close: () => Promise<void>;
246
337
  }
@@ -1 +1 @@
1
- {"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"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"}
@@ -32,6 +32,9 @@ import { fuz_auth_guard_resolver } from '../auth/auth_guard_resolver.js';
32
32
  import { create_fuz_authorization_handler } from '../auth/request_context.js';
33
33
  import { ERROR_PAYLOAD_TOO_LARGE } from '../http/error_schemas.js';
34
34
  import { create_rpc_endpoint } from '../actions/action_rpc.js';
35
+ import { register_ws_endpoint } from '../actions/register_ws_endpoint.js';
36
+ import { create_ws_auth_guard, create_ws_logout_closer, } from '../actions/transports_ws_auth_guard.js';
37
+ import { BackendWebsocketTransport } from '../actions/transports_ws_backend.js';
35
38
  /**
36
39
  * Assert that `audit_sse` was wired by `create_app_server` and return it
37
40
  * as a non-null `AuditLogSse`. Throws a labelled error when the
@@ -121,13 +124,24 @@ export const create_app_server = async (options) => {
121
124
  middleware_specs = options.transform_middleware(middleware_specs);
122
125
  }
123
126
  // Bootstrap status + app settings
124
- const bootstrap_status = options.bootstrap
127
+ // - undefined / 'disabled': no route mounted; placeholder status.
128
+ // - 'surface_only': route mounted but permanently unavailable; status placeholder.
129
+ // - 'live': real disk + lock check via `check_bootstrap_status`.
130
+ const bootstrap_status = options.bootstrap?.mode === 'live'
125
131
  ? await check_bootstrap_status(deps, { token_path: options.bootstrap.token_path })
126
132
  : { available: false, token_path: null };
127
133
  const app_settings = await query_app_settings_load({ db: deps.db });
128
134
  // Surface route ref — factory manages the circular ref
129
135
  const surface_ref = {
130
- surface: { middleware: [], routes: [], rpc_endpoints: [], env: [], events: [], diagnostics: [] },
136
+ surface: {
137
+ middleware: [],
138
+ routes: [],
139
+ rpc_endpoints: [],
140
+ ws_endpoints: [],
141
+ env: [],
142
+ events: [],
143
+ diagnostics: [],
144
+ },
131
145
  };
132
146
  // Route specs (consumer routes + factory-managed routes)
133
147
  const context = {
@@ -146,12 +160,15 @@ export const create_app_server = async (options) => {
146
160
  const consumer_routes = options.create_route_specs(context);
147
161
  // Factory-managed routes appended after consumer routes
148
162
  const factory_routes = [];
149
- // Bootstrap routes
150
- if (options.bootstrap) {
163
+ // Bootstrap routes — mounted for 'surface_only' and 'live'; omitted for
164
+ // 'disabled' / undefined. The route handler short-circuits to 403 when
165
+ // `bootstrap_status.available === false`, which is the steady state for
166
+ // 'surface_only' and the post-bootstrap state for 'live'.
167
+ if (options.bootstrap && options.bootstrap.mode !== 'disabled') {
151
168
  const bootstrap_routes = create_bootstrap_route_specs(deps, {
152
169
  session_options: options.session_options,
153
170
  bootstrap_status,
154
- on_bootstrap: options.bootstrap.on_bootstrap,
171
+ on_bootstrap: options.bootstrap.mode === 'live' ? options.bootstrap.on_bootstrap : undefined,
155
172
  ip_rate_limiter,
156
173
  });
157
174
  const prefix = options.bootstrap.route_prefix ?? '/api/account';
@@ -173,6 +190,15 @@ export const create_app_server = async (options) => {
173
190
  }));
174
191
  }
175
192
  }
193
+ // WS endpoint resolution — done here (alongside RPC) so the captured
194
+ // array threads into surface generation below. Actual mount happens
195
+ // after `apply_route_specs` because `register_ws_endpoint` mutates the
196
+ // live Hono `app` (origin / auth / role / authorization middleware +
197
+ // the `app.get(path, ...)` upgrade route), and `app` does not exist
198
+ // until the assembly phase below.
199
+ const resolved_ws_endpoints = typeof options.ws_endpoints === 'function'
200
+ ? options.ws_endpoints(context)
201
+ : options.ws_endpoints;
176
202
  // Surface route (default: enabled)
177
203
  if (options.surface_route !== false) {
178
204
  factory_routes.push(create_surface_route_spec(surface_ref));
@@ -192,6 +218,7 @@ export const create_app_server = async (options) => {
192
218
  env_schema: options.env_schema ?? BaseServerEnv,
193
219
  event_specs: all_event_specs,
194
220
  rpc_endpoints: resolved_rpc_endpoints,
221
+ ws_endpoints: resolved_ws_endpoints,
195
222
  });
196
223
  // Config-level diagnostics (concatenated after spec-level from generate_app_surface)
197
224
  const config_diagnostics = [];
@@ -215,7 +242,7 @@ export const create_app_server = async (options) => {
215
242
  config_diagnostics.push({
216
243
  level: 'warning',
217
244
  category: 'security',
218
- message: 'Session cookie httpOnly=false — cookie accessible to JavaScript',
245
+ message: 'Session cookie httpOnly=false — cookie accessible to JS',
219
246
  });
220
247
  }
221
248
  }
@@ -291,6 +318,77 @@ export const create_app_server = async (options) => {
291
318
  apply_middleware_specs(app, middleware_specs);
292
319
  const authorize = create_fuz_authorization_handler({ db: deps.db });
293
320
  apply_route_specs(app, route_specs, fuz_auth_guard_resolver, log, deps.db, authorize);
321
+ // WS endpoint auto-mount — must run after `app` exists and
322
+ // `apply_route_specs` has registered the request routes. Each spec
323
+ // becomes a `register_ws_endpoint` call, plus optional `auth_guard`
324
+ // wiring onto the audit chain. `post_route_middleware` and static
325
+ // serving register after this loop, so WS upgrade routes sit
326
+ // adjacent to the consumer routes and ahead of the static fallback —
327
+ // matches the "WS mount is route registration" mental model.
328
+ const mounted_ws_endpoints = {};
329
+ if (resolved_ws_endpoints?.length) {
330
+ if (options.upgradeWebSocket === undefined) {
331
+ throw new Error('create_app_server: ws_endpoints resolved non-empty but upgradeWebSocket is missing. ' +
332
+ "Pass the Hono adapter's upgradeWebSocket helper as a top-level option.");
333
+ }
334
+ // Cross-surface collision: `register_ws_endpoint` mounts a `GET path`
335
+ // upgrade route. If a `RouteSpec` already registered `GET path`,
336
+ // Hono's last-wins semantics would silently shadow the consumer's
337
+ // GET route — fail fast instead.
338
+ const route_spec_get_paths = new Set();
339
+ for (const r of route_specs) {
340
+ if (r.method === 'GET')
341
+ route_spec_get_paths.add(r.path);
342
+ }
343
+ const seen_paths = new Set();
344
+ // Dedupe `auth_guard` wiring by transport reference — two specs
345
+ // sharing one transport instance get a single pair of listeners,
346
+ // otherwise revocation events would fire `close_sockets_for_*`
347
+ // twice per event (idempotent on the transport but log-spammy).
348
+ // Cross-spec OR-semantics: any spec with `auth_guard !== false`
349
+ // wires the guard for that transport; once wired, sibling specs
350
+ // (even with explicit `auth_guard: false`) cannot opt out. To
351
+ // disable, every spec sharing the transport must pass `auth_guard: false`.
352
+ const guarded_transports = new WeakSet();
353
+ for (const endpoint of resolved_ws_endpoints) {
354
+ if (seen_paths.has(endpoint.path)) {
355
+ throw new Error(`create_app_server: duplicate ws_endpoints path: ${endpoint.path}`);
356
+ }
357
+ if (route_spec_get_paths.has(endpoint.path)) {
358
+ throw new Error(`create_app_server: ws_endpoints path collides with a GET RouteSpec: ${endpoint.path}`);
359
+ }
360
+ seen_paths.add(endpoint.path);
361
+ const endpoint_transport = endpoint.transport ?? new BackendWebsocketTransport();
362
+ register_ws_endpoint({
363
+ app,
364
+ path: endpoint.path,
365
+ upgradeWebSocket: options.upgradeWebSocket,
366
+ allowed_origins: endpoint.allowed_origins,
367
+ db: deps.db,
368
+ actions: endpoint.actions,
369
+ transport: endpoint_transport,
370
+ heartbeat: endpoint.heartbeat,
371
+ artificial_delay: endpoint.artificial_delay,
372
+ on_socket_open: endpoint.on_socket_open,
373
+ on_socket_close: endpoint.on_socket_close,
374
+ log,
375
+ required_roles: endpoint.required_roles,
376
+ action_ip_rate_limiter,
377
+ action_account_rate_limiter,
378
+ });
379
+ mounted_ws_endpoints[endpoint.path] = endpoint_transport;
380
+ if (endpoint.auth_guard !== false && !guarded_transports.has(endpoint_transport)) {
381
+ guarded_transports.add(endpoint_transport);
382
+ deps.audit.on_event_chain.push(create_ws_auth_guard(endpoint_transport, log));
383
+ deps.audit.on_event_chain.push(create_ws_logout_closer(endpoint_transport, log));
384
+ }
385
+ if (endpoint.extra_audit_handlers?.length) {
386
+ for (const handler of endpoint.extra_audit_handlers) {
387
+ deps.audit.on_event_chain.push(handler);
388
+ }
389
+ }
390
+ }
391
+ }
294
392
  // Post-route middleware (before static serving)
295
393
  if (options.post_route_middleware) {
296
394
  apply_middleware_specs(app, options.post_route_middleware);
@@ -309,6 +407,7 @@ export const create_app_server = async (options) => {
309
407
  app_settings,
310
408
  migration_results: backend.migration_results,
311
409
  audit_sse,
410
+ ws_endpoints: mounted_ws_endpoints,
312
411
  close: backend.close,
313
412
  };
314
413
  };
@@ -26,12 +26,12 @@ export declare const BaseServerEnv: z.ZodObject<{
26
26
  PORT: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
27
27
  HOST: z.ZodDefault<z.ZodString>;
28
28
  DATABASE_URL: z.ZodString;
29
- SECRET_COOKIE_KEYS: z.ZodString;
30
- ALLOWED_ORIGINS: z.ZodString;
31
- PUBLIC_API_URL: z.ZodDefault<z.ZodString>;
32
- PUBLIC_WEBSOCKET_URL: z.ZodOptional<z.ZodString>;
33
- PUBLIC_CONTACT_EMAIL: z.ZodOptional<z.ZodUnion<readonly [z.ZodEmail, z.ZodLiteral<"">]>>;
34
- BOOTSTRAP_TOKEN_PATH: z.ZodOptional<z.ZodString>;
29
+ SECRET_FUZ_COOKIE_KEYS: z.ZodString;
30
+ FUZ_ALLOWED_ORIGINS: z.ZodString;
31
+ PUBLIC_FUZ_API_URL: z.ZodDefault<z.ZodString>;
32
+ PUBLIC_FUZ_WEBSOCKET_URL: z.ZodOptional<z.ZodString>;
33
+ PUBLIC_FUZ_CONTACT_EMAIL: z.ZodOptional<z.ZodUnion<readonly [z.ZodEmail, z.ZodLiteral<"">]>>;
34
+ FUZ_BOOTSTRAP_TOKEN_PATH: z.ZodOptional<z.ZodString>;
35
35
  SMTP_HOST: z.ZodOptional<z.ZodString>;
36
36
  SMTP_USER: z.ZodOptional<z.ZodUnion<readonly [z.ZodEmail, z.ZodLiteral<"">]>>;
37
37
  SMTP_PASSWORD: z.ZodOptional<z.ZodString>;
@@ -51,7 +51,7 @@ export interface ServerEnvOptions {
51
51
  */
52
52
  export interface ServerEnvOptionsError {
53
53
  ok: false;
54
- field: 'SECRET_COOKIE_KEYS' | 'ALLOWED_ORIGINS';
54
+ field: 'SECRET_FUZ_COOKIE_KEYS' | 'FUZ_ALLOWED_ORIGINS';
55
55
  errors: Array<string>;
56
56
  }
57
57
  export type ServerEnvOptionsResult = ServerEnvOptions | ServerEnvOptionsError;
@@ -1 +1 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,EAA2B,KAAK,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAG1E;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;kBAkCxB,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,IAAI,CAAC;IACT,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACrC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,oBAAoB,GAAG,iBAAiB,CAAC;IAChD,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACtB;AAED,MAAM,MAAM,sBAAsB,GAAG,gBAAgB,GAAG,qBAAqB,CAAC;AAE9E;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAAI,KAAK,aAAa,KAAG,sBA4BxD,CAAC"}
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,EAA2B,KAAK,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAG1E;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;kBAkCxB,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,IAAI,CAAC;IACT,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACrC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,wBAAwB,GAAG,qBAAqB,CAAC;IACxD,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACtB;AAED,MAAM,MAAM,sBAAsB,GAAG,gBAAgB,GAAG,qBAAqB,CAAC;AAE9E;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAAI,KAAK,aAAa,KAAG,sBA4BxD,CAAC"}
@@ -27,20 +27,20 @@ export const BaseServerEnv = z.strictObject({
27
27
  description: 'Database URL (postgres://, file://, or memory://)',
28
28
  sensitivity: 'secret',
29
29
  }),
30
- SECRET_COOKIE_KEYS: z.string().min(32).meta({
30
+ SECRET_FUZ_COOKIE_KEYS: z.string().min(32).meta({
31
31
  description: 'Cookie signing keys, separated by __ for rotation',
32
32
  sensitivity: 'secret',
33
33
  }),
34
- ALLOWED_ORIGINS: z.string().min(1, 'ALLOWED_ORIGINS is required').meta({
34
+ FUZ_ALLOWED_ORIGINS: z.string().min(1, 'FUZ_ALLOWED_ORIGINS is required').meta({
35
35
  description: 'Comma-separated origin patterns for API verification',
36
36
  }),
37
- PUBLIC_API_URL: z.string().default('/api').meta({ description: 'Public API base URL' }),
38
- PUBLIC_WEBSOCKET_URL: z.string().optional().meta({ description: 'Public WebSocket URL' }),
39
- PUBLIC_CONTACT_EMAIL: z
37
+ PUBLIC_FUZ_API_URL: z.string().default('/api').meta({ description: 'Public API base URL' }),
38
+ PUBLIC_FUZ_WEBSOCKET_URL: z.string().optional().meta({ description: 'Public WebSocket URL' }),
39
+ PUBLIC_FUZ_CONTACT_EMAIL: z
40
40
  .union([z.email(), z.literal('')])
41
41
  .optional()
42
42
  .meta({ description: 'Public contact email address' }),
43
- BOOTSTRAP_TOKEN_PATH: z
43
+ FUZ_BOOTSTRAP_TOKEN_PATH: z
44
44
  .string()
45
45
  .optional()
46
46
  .meta({ description: 'Path to one-shot admin bootstrap token', sensitivity: 'secret' }),
@@ -64,32 +64,32 @@ export const BaseServerEnv = z.strictObject({
64
64
  * @returns `{ok: true, keyring, allowed_origins, bootstrap_token_path}` or `{ok: false, field, errors}`
65
65
  */
66
66
  export const validate_server_env = (env) => {
67
- const keyring_result = create_validated_keyring(env.SECRET_COOKIE_KEYS);
67
+ const keyring_result = create_validated_keyring(env.SECRET_FUZ_COOKIE_KEYS);
68
68
  if (!keyring_result.ok) {
69
- return { ok: false, field: 'SECRET_COOKIE_KEYS', errors: keyring_result.errors };
69
+ return { ok: false, field: 'SECRET_FUZ_COOKIE_KEYS', errors: keyring_result.errors };
70
70
  }
71
71
  let allowed_origins;
72
72
  try {
73
- allowed_origins = parse_allowed_origins(env.ALLOWED_ORIGINS);
73
+ allowed_origins = parse_allowed_origins(env.FUZ_ALLOWED_ORIGINS);
74
74
  }
75
75
  catch (err) {
76
76
  return {
77
77
  ok: false,
78
- field: 'ALLOWED_ORIGINS',
79
- errors: [err instanceof Error ? err.message : 'Invalid ALLOWED_ORIGINS'],
78
+ field: 'FUZ_ALLOWED_ORIGINS',
79
+ errors: [err instanceof Error ? err.message : 'Invalid FUZ_ALLOWED_ORIGINS'],
80
80
  };
81
81
  }
82
82
  if (allowed_origins.length === 0) {
83
83
  return {
84
84
  ok: false,
85
- field: 'ALLOWED_ORIGINS',
86
- errors: ['ALLOWED_ORIGINS contains no valid patterns'],
85
+ field: 'FUZ_ALLOWED_ORIGINS',
86
+ errors: ['FUZ_ALLOWED_ORIGINS contains no valid patterns'],
87
87
  };
88
88
  }
89
89
  return {
90
90
  ok: true,
91
91
  keyring: keyring_result.keyring,
92
92
  allowed_origins,
93
- bootstrap_token_path: env.BOOTSTRAP_TOKEN_PATH ?? null,
93
+ bootstrap_token_path: env.FUZ_BOOTSTRAP_TOKEN_PATH ?? null,
94
94
  };
95
95
  };
@@ -1 +1 @@
1
- {"version":3,"file":"startup.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/startup.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAGpD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,oBAAoB,CAAC;AAEnD;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAC/B,SAAS,UAAU,EACnB,KAAK,MAAM,EACX,aAAa,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAClC,IAqCF,CAAC"}
1
+ {"version":3,"file":"startup.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/startup.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAGpD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,oBAAoB,CAAC;AAEnD;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAC/B,SAAS,UAAU,EACnB,KAAK,MAAM,EACX,aAAa,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAClC,IAkDF,CAAC"}
@@ -17,6 +17,18 @@ import { format_env_display_value } from '../env/mask.js';
17
17
  */
18
18
  export const log_startup_summary = (surface, log, env_values) => {
19
19
  log.info(`Surface: ${surface.routes.length} routes, ${surface.middleware.length} middleware layers`);
20
+ // Endpoint surfaces — logged when non-empty so operators can confirm
21
+ // auto-mount picked up the expected actions (and so a factory that
22
+ // silently returns `[]` is loud at boot instead of a method_not_found
23
+ // at first call).
24
+ if (surface.rpc_endpoints.length) {
25
+ const rpc_method_count = surface.rpc_endpoints.reduce((sum, ep) => sum + ep.methods.length, 0);
26
+ log.info(`RPC: ${surface.rpc_endpoints.length} endpoint(s), ${rpc_method_count} method(s)`);
27
+ }
28
+ if (surface.ws_endpoints.length) {
29
+ const ws_method_count = surface.ws_endpoints.reduce((sum, ep) => sum + ep.methods.length, 0);
30
+ log.info(`WS: ${surface.ws_endpoints.length} endpoint(s), ${ws_method_count} method(s)`);
31
+ }
20
32
  if (surface.env.length) {
21
33
  const required = surface.env.filter((e) => !e.optional);
22
34
  const secret = surface.env.filter((e) => e.sensitivity === 'secret');
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * Static file serving middleware for SvelteKit static builds.
3
3
  *
4
- * Provides multi-phase static serving:
5
- * - Phase 1: Exact path match (handles /, assets, images)
6
- * - Phase 2: `.html` fallback for clean URLs (`/about` → `/about.html`)
7
- * - Phase 3 (optional): SPA fallback for client-side routes
4
+ * Multi-step static serving:
5
+ * - Step 1: Exact path match (handles /, assets, images)
6
+ * - Step 2: `.html` fallback for clean URLs (`/about` → `/about.html`)
7
+ * - Step 3 (optional): SPA fallback for client-side routes
8
8
  *
9
9
  * @module
10
10
  */
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * Static file serving middleware for SvelteKit static builds.
3
3
  *
4
- * Provides multi-phase static serving:
5
- * - Phase 1: Exact path match (handles /, assets, images)
6
- * - Phase 2: `.html` fallback for clean URLs (`/about` → `/about.html`)
7
- * - Phase 3 (optional): SPA fallback for client-side routes
4
+ * Multi-step static serving:
5
+ * - Step 1: Exact path match (handles /, assets, images)
6
+ * - Step 2: `.html` fallback for clean URLs (`/about` → `/about.html`)
7
+ * - Step 3 (optional): SPA fallback for client-side routes
8
8
  *
9
9
  * @module
10
10
  */
@@ -21,16 +21,16 @@ const is_spa_route_default = (path) => !path.startsWith('/api/');
21
21
  export const create_static_middleware = (serve_static, options) => {
22
22
  const root = options?.root ?? './build';
23
23
  const handlers = [];
24
- // Phase 1: exact path match
24
+ // Step 1: exact path match
25
25
  handlers.push(serve_static({ root }));
26
- // Phase 2: .html fallback for clean URLs (/about → /about.html)
26
+ // Step 2: .html fallback for clean URLs (/about → /about.html)
27
27
  handlers.push(async (c, next) => {
28
28
  const path = c.req.path;
29
29
  if (path === '/' || path.includes('.'))
30
30
  return next();
31
31
  return serve_static({ root, rewriteRequestPath: () => `${path}.html` })(c, next);
32
32
  });
33
- // Phase 3: optional SPA fallback for client-side routes
33
+ // Step 3: optional SPA fallback for client-side routes
34
34
  if (options?.spa_fallback) {
35
35
  const fallback = options.spa_fallback;
36
36
  const is_spa_route = options.is_spa_route ?? is_spa_route_default;