@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
@@ -10,18 +10,18 @@
10
10
  import { Hono, type Context } from 'hono';
11
11
  import type { UpgradeWebSocket } from 'hono/ws';
12
12
  import { z } from 'zod';
13
- import { type SessionOptions } from '../auth/session_cookie.js';
13
+ import { type SessionOptions, type SessionCookieOptions } from '../auth/session_cookie.js';
14
14
  import type { BootstrapAccountSuccess } from '../auth/bootstrap_account.js';
15
15
  import type { EventSpec } from '../realtime/sse.js';
16
16
  import { type AuditLogSse } from '../realtime/sse_auth_guard.js';
17
- import { RateLimiter, type RateLimiterMode } from '../rate_limiter.js';
17
+ import { RateLimiter, type RateLimiterMode, type RateLimiterOptions } from '../rate_limiter.js';
18
18
  import type { DaemonTokenState } from '../auth/daemon_token.js';
19
19
  import type { MigrationResult } from '../db/migrate.js';
20
20
  import type { AppBackend } from './app_backend.js';
21
21
  import type { AppServerContext } from './app_server_context.js';
22
22
  import '../hono_context.js';
23
23
  import { type ServeStaticFactory } from './static.js';
24
- import { type AppSurfaceSpec, type RpcEndpointSpec } from '../http/surface.js';
24
+ import { type AppSurfaceSpec, type AppSurfaceDiagnostic, type RpcEndpointSpec } from '../http/surface.js';
25
25
  import { type RouteSpec } from '../http/route_spec.js';
26
26
  import type { MiddlewareSpec } from '../http/middleware_spec.js';
27
27
  import { type BootstrapStatus } from '../auth/bootstrap_routes.js';
@@ -186,10 +186,19 @@ export interface AppServerOptions {
186
186
  /** Bootstrap options. Omit to skip bootstrap status check and routes. */
187
187
  bootstrap?: BootstrapServerOptions;
188
188
  /**
189
- * Set to `false` to disable the auto-created surface route (`GET /api/surface`).
190
- * Default: auto-created (authenticated).
189
+ * Mount `GET /api/surface`, which serves this server's generated
190
+ * `AppSurface` (every route, its auth, and its schemas) to an admin UI such
191
+ * as `AdminSurface.svelte`. Off by default — the Rust spine serves no such
192
+ * route, and a map of the API is nothing to publish unasked.
193
+ *
194
+ * When mounted the route is admin-only (`roles: ['admin']`, so it takes the
195
+ * `?acting=` selector) and a rule-3 token surface
196
+ * (`required_scope: 'surface:app_surface'`): a narrowed API token is refused
197
+ * it whatever its method list says. See `server/surface_route.ts`.
198
+ *
199
+ * @default false
191
200
  */
192
- surface_route?: false;
201
+ surface_route?: boolean;
193
202
  /**
194
203
  * Build route specs from the initialized backend.
195
204
  * Called after all middleware is ready.
@@ -424,7 +433,10 @@ export interface AppServer {
424
433
  * audit stream — including transports supplied through
425
434
  * `WsEndpointSpec.transport` or added by hand, since they are registered
426
435
  * on the backend's closer and the backend is going away; a close that
427
- * throws is logged and the shutdown goes on
436
+ * throws is logged and the shutdown goes on. Each transport and registry
437
+ * is left closing, so an upgrade or stream that registers afterward is
438
+ * born closed — refused at once rather than outliving the server — and
439
+ * a closed transport hosts no other server
428
440
  * 3. release what assembly acquired, last acquired first: remove the
429
441
  * audit listeners `create_app_server` registered on `deps.audit` (the
430
442
  * audit-log SSE listener, the WS auth guards, each
@@ -469,6 +481,106 @@ export declare const require_audit_sse: (source: {
469
481
  }) => AuditLogSse;
470
482
  /** Default maximum request body size: 1 MiB. */
471
483
  export declare const DEFAULT_MAX_BODY_SIZE: number;
484
+ /**
485
+ * Each `AppServerOptions` rate-limiter field, with the defaults an omitted one
486
+ * is built from under `rate_limiters: 'enforced'` and the name its
487
+ * explicitly-null diagnostic uses. Insertion order is the order limiters are
488
+ * built and their diagnostics are listed.
489
+ */
490
+ declare const rate_limiter_slots: {
491
+ readonly login_ip_rate_limiter: {
492
+ readonly label: "login IP";
493
+ readonly defaults: RateLimiterOptions;
494
+ };
495
+ readonly signup_ip_rate_limiter: {
496
+ readonly label: "signup IP";
497
+ readonly defaults: RateLimiterOptions;
498
+ };
499
+ readonly bootstrap_ip_rate_limiter: {
500
+ readonly label: "bootstrap IP";
501
+ readonly defaults: RateLimiterOptions;
502
+ };
503
+ readonly login_account_rate_limiter: {
504
+ readonly label: "login account";
505
+ readonly defaults: RateLimiterOptions;
506
+ };
507
+ readonly signup_account_rate_limiter: {
508
+ readonly label: "signup account";
509
+ readonly defaults: RateLimiterOptions;
510
+ };
511
+ readonly action_ip_rate_limiter: {
512
+ readonly label: "action IP";
513
+ readonly defaults: RateLimiterOptions;
514
+ };
515
+ readonly action_account_rate_limiter: {
516
+ readonly label: "action account";
517
+ readonly defaults: RateLimiterOptions;
518
+ };
519
+ };
520
+ /**
521
+ * Name of an `AppServerOptions` rate-limiter field — also the
522
+ * `AppServerContext` field its resolved limiter lands on.
523
+ *
524
+ * @internal
525
+ */
526
+ export type AppServerRateLimiterKey = keyof typeof rate_limiter_slots;
527
+ /**
528
+ * Every `AppServerOptions` rate-limiter field, in build order.
529
+ *
530
+ * @internal
531
+ */
532
+ export declare const APP_SERVER_RATE_LIMITER_KEYS: ReadonlyArray<AppServerRateLimiterKey>;
533
+ /**
534
+ * The rate limiters `create_app_server` resolved from its options.
535
+ *
536
+ * @internal
537
+ */
538
+ export interface ResolvedRateLimiters {
539
+ /** The `rate_limiters` mode, defaulted to `'enforced'`. */
540
+ mode: RateLimiterMode;
541
+ /** Each limiter by option name — `null` when off. */
542
+ limiters: Record<AppServerRateLimiterKey, RateLimiter | null>;
543
+ /**
544
+ * The limiters built here for omitted options, in build order — the
545
+ * server's to dispose. Never one passed in, which its caller owns.
546
+ */
547
+ built: Array<RateLimiter>;
548
+ }
549
+ /**
550
+ * Resolve each rate-limiter option: an explicit option (instance or `null`)
551
+ * wins; an omitted one is a default instance under `'enforced'` and `null`
552
+ * under `'disabled_for_testing'`, which also announces the mode on stderr
553
+ * (`announce_rate_limiters_disabled`). One instance per surface — see
554
+ * `AppServerOptions.login_ip_rate_limiter` for why these are not a single
555
+ * shared bucket.
556
+ *
557
+ * @param options - the rate-limiter fields and `rate_limiters` mode of `AppServerOptions`
558
+ * @internal
559
+ */
560
+ export declare const resolve_rate_limiters: (options: Pick<AppServerOptions, AppServerRateLimiterKey | "rate_limiters">) => ResolvedRateLimiters;
561
+ /**
562
+ * Inputs to `collect_config_diagnostics`.
563
+ *
564
+ * @internal
565
+ */
566
+ export interface ConfigDiagnosticsInput {
567
+ /** The session cookie overrides, from `AppServerOptions.session_options`. */
568
+ cookie_options: Partial<SessionCookieOptions> | undefined;
569
+ /** The `rate_limiters` mode, defaulted. */
570
+ rate_limiter_mode: RateLimiterMode;
571
+ /** The resolved limiters, from `resolve_rate_limiters`. */
572
+ rate_limiters: Record<AppServerRateLimiterKey, RateLimiter | null>;
573
+ }
574
+ /**
575
+ * The config-level diagnostics `create_app_server` appends to the surface's
576
+ * spec-level ones: a weakened session cookie, and an open rate-limited
577
+ * surface — one warning for `'disabled_for_testing'`, otherwise one per
578
+ * explicitly-null limiter, so a deployment that disables login's limiter but
579
+ * keeps signup's reads differently from one that disables both.
580
+ *
581
+ * @internal
582
+ */
583
+ export declare const collect_config_diagnostics: (input: ConfigDiagnosticsInput) => Array<AppSurfaceDiagnostic>;
472
584
  /**
473
585
  * Create a fully assembled Hono app with auth, middleware, and routes.
474
586
  *
@@ -503,8 +615,10 @@ export declare const DEFAULT_MAX_BODY_SIZE: number;
503
615
  * live connection on the backend's closer, runs the same releases, and
504
616
  * closes the database (`AppServer.close`).
505
617
  *
506
- * @returns assembled Hono app, backend, surface build, and bootstrap status
618
+ * @returns the assembled Hono app with its surface, bootstrap status, migration results,
619
+ * audit SSE, mounted WS transports, and `close`
507
620
  * @throws Error from any assembly step, after releasing what assembly acquired
508
621
  */
509
622
  export declare const create_app_server: (options: AppServerOptions) => Promise<AppServer>;
623
+ export {};
510
624
  //# sourceMappingURL=app_server.d.ts.map
@@ -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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"}
@@ -24,7 +24,7 @@ import { log_startup_summary } from './startup.js';
24
24
  import { create_app_surface_spec } from '../http/surface.js';
25
25
  import { apply_middleware_specs, apply_route_specs, prefix_route_specs } from '../http/route_spec.js';
26
26
  import { check_bootstrap_status, create_bootstrap_route_specs } from '../auth/bootstrap_routes.js';
27
- import { create_surface_route_spec } from '../http/common_routes.js';
27
+ import { create_surface_route_spec } from './surface_route.js';
28
28
  import { flush_pending_effects, flush_post_commit_effects } from '../http/pending_effects.js';
29
29
  import { assert_endpoint_in_auth_scope, assert_middleware_stack_mounted, create_auth_middleware_specs } from '../auth/middleware.js';
30
30
  import { fuz_auth_guard_resolver } from '../auth/auth_guard_resolver.js';
@@ -60,6 +60,125 @@ export const require_audit_sse = (source) => {
60
60
  };
61
61
  /** Default maximum request body size: 1 MiB. */
62
62
  export const DEFAULT_MAX_BODY_SIZE = 1024 * 1024;
63
+ /**
64
+ * Each `AppServerOptions` rate-limiter field, with the defaults an omitted one
65
+ * is built from under `rate_limiters: 'enforced'` and the name its
66
+ * explicitly-null diagnostic uses. Insertion order is the order limiters are
67
+ * built and their diagnostics are listed.
68
+ */
69
+ const rate_limiter_slots = {
70
+ login_ip_rate_limiter: { label: 'login IP', defaults: default_login_ip_rate_limit },
71
+ signup_ip_rate_limiter: { label: 'signup IP', defaults: default_login_ip_rate_limit },
72
+ bootstrap_ip_rate_limiter: { label: 'bootstrap IP', defaults: default_login_ip_rate_limit },
73
+ login_account_rate_limiter: {
74
+ label: 'login account',
75
+ defaults: default_login_account_rate_limit
76
+ },
77
+ signup_account_rate_limiter: {
78
+ label: 'signup account',
79
+ defaults: default_login_account_rate_limit
80
+ },
81
+ action_ip_rate_limiter: { label: 'action IP', defaults: default_action_ip_rate_limit },
82
+ action_account_rate_limiter: {
83
+ label: 'action account',
84
+ defaults: default_action_account_rate_limit
85
+ }
86
+ };
87
+ /**
88
+ * Every `AppServerOptions` rate-limiter field, in build order.
89
+ *
90
+ * @internal
91
+ */
92
+ export const APP_SERVER_RATE_LIMITER_KEYS = Object.keys(rate_limiter_slots);
93
+ /**
94
+ * Resolve each rate-limiter option: an explicit option (instance or `null`)
95
+ * wins; an omitted one is a default instance under `'enforced'` and `null`
96
+ * under `'disabled_for_testing'`, which also announces the mode on stderr
97
+ * (`announce_rate_limiters_disabled`). One instance per surface — see
98
+ * `AppServerOptions.login_ip_rate_limiter` for why these are not a single
99
+ * shared bucket.
100
+ *
101
+ * @param options - the rate-limiter fields and `rate_limiters` mode of `AppServerOptions`
102
+ * @internal
103
+ */
104
+ export const resolve_rate_limiters = (options) => {
105
+ const mode = options.rate_limiters ?? 'enforced';
106
+ const limiters = {};
107
+ const built = [];
108
+ for (const key of APP_SERVER_RATE_LIMITER_KEYS) {
109
+ const option = options[key];
110
+ if (option !== undefined) {
111
+ limiters[key] = option;
112
+ }
113
+ else if (mode === 'disabled_for_testing') {
114
+ announce_rate_limiters_disabled();
115
+ limiters[key] = null;
116
+ }
117
+ else {
118
+ const limiter = new RateLimiter({ ...rate_limiter_slots[key].defaults });
119
+ built.push(limiter);
120
+ limiters[key] = limiter;
121
+ }
122
+ }
123
+ return { mode, limiters, built };
124
+ };
125
+ /**
126
+ * The config-level diagnostics `create_app_server` appends to the surface's
127
+ * spec-level ones: a weakened session cookie, and an open rate-limited
128
+ * surface — one warning for `'disabled_for_testing'`, otherwise one per
129
+ * explicitly-null limiter, so a deployment that disables login's limiter but
130
+ * keeps signup's reads differently from one that disables both.
131
+ *
132
+ * @internal
133
+ */
134
+ export const collect_config_diagnostics = (input) => {
135
+ const diagnostics = [];
136
+ const { cookie_options } = input;
137
+ if (cookie_options) {
138
+ if (cookie_options.secure === false) {
139
+ diagnostics.push({
140
+ level: 'warning',
141
+ category: 'security',
142
+ message: 'Session cookie secure=false — cookies sent over HTTP'
143
+ });
144
+ }
145
+ if (cookie_options.sameSite && cookie_options.sameSite !== session_cookie_options.sameSite) {
146
+ diagnostics.push({
147
+ level: 'warning',
148
+ category: 'security',
149
+ message: `Session cookie sameSite='${cookie_options.sameSite}' — weakened from default '${session_cookie_options.sameSite}'`
150
+ });
151
+ }
152
+ if (cookie_options.httpOnly === false) {
153
+ diagnostics.push({
154
+ level: 'warning',
155
+ category: 'security',
156
+ message: 'Session cookie httpOnly=false — cookie accessible to JS'
157
+ });
158
+ }
159
+ }
160
+ if (input.rate_limiter_mode === 'disabled_for_testing') {
161
+ // one warning for the mode rather than one per limiter it nulled
162
+ diagnostics.push({
163
+ level: 'warning',
164
+ category: 'security',
165
+ message: "rate limiters disabled for testing (rate_limiters: 'disabled_for_testing') — " +
166
+ 'every limiter not passed explicitly is off; never use in production'
167
+ });
168
+ }
169
+ else {
170
+ for (const key of APP_SERVER_RATE_LIMITER_KEYS) {
171
+ if (input.rate_limiters[key] === null) {
172
+ diagnostics.push({
173
+ level: 'warning',
174
+ category: 'security',
175
+ message: `${rate_limiter_slots[key].label} rate limiter explicitly disabled (null)`
176
+ });
177
+ }
178
+ }
179
+ }
180
+ return diagnostics;
181
+ };
63
182
  /**
64
183
  * Create a fully assembled Hono app with auth, middleware, and routes.
65
184
  *
@@ -94,7 +213,8 @@ export const DEFAULT_MAX_BODY_SIZE = 1024 * 1024;
94
213
  * live connection on the backend's closer, runs the same releases, and
95
214
  * closes the database (`AppServer.close`).
96
215
  *
97
- * @returns assembled Hono app, backend, surface build, and bootstrap status
216
+ * @returns the assembled Hono app with its surface, bootstrap status, migration results,
217
+ * audit SSE, mounted WS transports, and `close`
98
218
  * @throws Error from any assembly step, after releasing what assembly acquired
99
219
  */
100
220
  export const create_app_server = async (options) => {
@@ -130,31 +250,13 @@ const assemble_app_server = async (options, releases) => {
130
250
  const { backend } = options;
131
251
  const { deps } = backend;
132
252
  const { log } = deps;
133
- // Rate limiters: an explicit option (instance or `null`) wins; an omitted
134
- // one is a default instance under 'enforced', `null` under
135
- // 'disabled_for_testing'. One instance per surface — see
136
- // `AppServerOptions.login_ip_rate_limiter` for why these are not a single
137
- // shared bucket. The ones built here are the server's to dispose — on
138
- // close, or when assembly fails.
139
- const rate_limiter_mode = options.rate_limiters ?? 'enforced';
140
- const resolve_rate_limiter = (option, defaults) => {
141
- if (option !== undefined)
142
- return option;
143
- if (rate_limiter_mode === 'disabled_for_testing') {
144
- announce_rate_limiters_disabled();
145
- return null;
146
- }
147
- const limiter = new RateLimiter({ ...defaults });
253
+ // Rate limiters — the ones built here are the server's to dispose, on
254
+ // close or when assembly fails.
255
+ const { mode: rate_limiter_mode, limiters: rate_limiters, built: built_rate_limiters } = resolve_rate_limiters(options);
256
+ for (const limiter of built_rate_limiters) {
148
257
  releases.push(() => limiter.dispose());
149
- return limiter;
150
- };
151
- const login_ip_rate_limiter = resolve_rate_limiter(options.login_ip_rate_limiter, default_login_ip_rate_limit);
152
- const signup_ip_rate_limiter = resolve_rate_limiter(options.signup_ip_rate_limiter, default_login_ip_rate_limit);
153
- const bootstrap_ip_rate_limiter = resolve_rate_limiter(options.bootstrap_ip_rate_limiter, default_login_ip_rate_limit);
154
- const login_account_rate_limiter = resolve_rate_limiter(options.login_account_rate_limiter, default_login_account_rate_limit);
155
- const signup_account_rate_limiter = resolve_rate_limiter(options.signup_account_rate_limiter, default_login_account_rate_limit);
156
- const action_ip_rate_limiter = resolve_rate_limiter(options.action_ip_rate_limiter, default_action_ip_rate_limit);
157
- const action_account_rate_limiter = resolve_rate_limiter(options.action_account_rate_limiter, default_action_account_rate_limit);
258
+ }
259
+ const { bootstrap_ip_rate_limiter, action_ip_rate_limiter, action_account_rate_limiter } = rate_limiters;
158
260
  // Factory-managed audit SSE — registers a listener on the bound emitter
159
261
  // so SSE fan-out runs alongside the consumer's `on_audit_event`
160
262
  // without rebuilding `AppDeps`, and adds the registry to the backend's
@@ -213,13 +315,7 @@ const assemble_app_server = async (options, releases) => {
213
315
  backend,
214
316
  bootstrap_status,
215
317
  session_options: options.session_options,
216
- login_ip_rate_limiter,
217
- signup_ip_rate_limiter,
218
- bootstrap_ip_rate_limiter,
219
- login_account_rate_limiter,
220
- signup_account_rate_limiter,
221
- action_ip_rate_limiter,
222
- action_account_rate_limiter,
318
+ ...rate_limiters,
223
319
  audit_sse
224
320
  };
225
321
  const consumer_routes = options.create_route_specs(context);
@@ -280,8 +376,8 @@ const assemble_app_server = async (options, releases) => {
280
376
  }
281
377
  ws_mount = { endpoints: resolved_ws_endpoints, create_upgrade_websocket };
282
378
  }
283
- // Surface route (default: enabled)
284
- if (options.surface_route !== false) {
379
+ // Surface route (opt-in)
380
+ if (options.surface_route) {
285
381
  factory_routes.push(create_surface_route_spec(surface_ref));
286
382
  }
287
383
  const route_specs = [...consumer_routes, ...factory_routes];
@@ -323,62 +419,11 @@ const assemble_app_server = async (options, releases) => {
323
419
  ws_endpoints: resolved_ws_endpoints
324
420
  });
325
421
  // Config-level diagnostics (concatenated after spec-level from generate_app_surface)
326
- const config_diagnostics = [];
327
- const cookie_opts = options.session_options.cookie_options;
328
- if (cookie_opts) {
329
- if (cookie_opts.secure === false) {
330
- config_diagnostics.push({
331
- level: 'warning',
332
- category: 'security',
333
- message: 'Session cookie secure=false — cookies sent over HTTP'
334
- });
335
- }
336
- if (cookie_opts.sameSite && cookie_opts.sameSite !== session_cookie_options.sameSite) {
337
- config_diagnostics.push({
338
- level: 'warning',
339
- category: 'security',
340
- message: `Session cookie sameSite='${cookie_opts.sameSite}' — weakened from default '${session_cookie_options.sameSite}'`
341
- });
342
- }
343
- if (cookie_opts.httpOnly === false) {
344
- config_diagnostics.push({
345
- level: 'warning',
346
- category: 'security',
347
- message: 'Session cookie httpOnly=false — cookie accessible to JS'
348
- });
349
- }
350
- }
351
- if (rate_limiter_mode === 'disabled_for_testing') {
352
- // one warning for the mode rather than one per limiter it nulled
353
- config_diagnostics.push({
354
- level: 'warning',
355
- category: 'security',
356
- message: "rate limiters disabled for testing (rate_limiters: 'disabled_for_testing') — " +
357
- 'every limiter not passed explicitly is off; never use in production'
358
- });
359
- }
360
- else {
361
- // One diagnostic per explicitly-null limiter — a deployment that disables
362
- // login's limiter but keeps signup's is a different posture than one that
363
- // disables both, and a single collapsed warning hid which surface was open.
364
- for (const [name, limiter] of [
365
- ['login IP', login_ip_rate_limiter],
366
- ['signup IP', signup_ip_rate_limiter],
367
- ['bootstrap IP', bootstrap_ip_rate_limiter],
368
- ['login account', login_account_rate_limiter],
369
- ['signup account', signup_account_rate_limiter],
370
- ['action IP', action_ip_rate_limiter],
371
- ['action account', action_account_rate_limiter]
372
- ]) {
373
- if (limiter === null) {
374
- config_diagnostics.push({
375
- level: 'warning',
376
- category: 'security',
377
- message: `${name} rate limiter explicitly disabled (null)`
378
- });
379
- }
380
- }
381
- }
422
+ const config_diagnostics = collect_config_diagnostics({
423
+ cookie_options: options.session_options.cookie_options,
424
+ rate_limiter_mode,
425
+ rate_limiters
426
+ });
382
427
  if (config_diagnostics.length) {
383
428
  surface_spec.surface.diagnostics = [...surface_spec.surface.diagnostics, ...config_diagnostics];
384
429
  }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The surface explorer route — `GET /api/surface`, serving the generated
3
+ * `AppSurface` as JSON.
4
+ *
5
+ * Opt-in on `create_app_server` (`surface_route: true`), and admin-only when
6
+ * mounted: the surface is a map of every route, its auth, and its schemas.
7
+ *
8
+ * @module
9
+ */
10
+ import type { RouteSpec } from '../http/route_spec.js';
11
+ import type { AppSurface } from '../http/surface.js';
12
+ /** Options for the surface explorer route. */
13
+ export interface SurfaceRouteOptions {
14
+ /** The generated app surface to serve — read per request, so a backfilled ref serves the final surface. */
15
+ surface: AppSurface;
16
+ }
17
+ /**
18
+ * Create the admin-only route spec that serves the `AppSurface` as JSON.
19
+ *
20
+ * Surface data reveals API structure (routes, auth, schemas), so the route
21
+ * requires the global admin role. It is also a non-RPC surface: it declares
22
+ * `required_scope: 'surface:app_surface'`, so a narrowed API token is refused
23
+ * it whatever its method list says (403 `token_scope_required`, ahead of the
24
+ * role gate), while a full-scope admin bearer passes. The admin role implies
25
+ * `actor: 'required'`, so the route takes the `?acting=` selector a
26
+ * multi-actor admin disambiguates with.
27
+ *
28
+ * @param options - the surface to serve
29
+ * @returns the `GET /api/surface` route spec
30
+ */
31
+ export declare const create_surface_route_spec: (options: SurfaceRouteOptions) => RouteSpec;
32
+ //# sourceMappingURL=surface_route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"surface_route.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/surface_route.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAIrD,8CAA8C;AAC9C,MAAM,WAAW,mBAAmB;IACnC,2GAA2G;IAC3G,OAAO,EAAE,UAAU,CAAC;CACpB;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,mBAAmB,KAAG,SAmBvE,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The surface explorer route — `GET /api/surface`, serving the generated
3
+ * `AppSurface` as JSON.
4
+ *
5
+ * Opt-in on `create_app_server` (`surface_route: true`), and admin-only when
6
+ * mounted: the surface is a map of every route, its auth, and its schemas.
7
+ *
8
+ * @module
9
+ */
10
+ import { z } from 'zod';
11
+ import { ActingActor } from '../http/auth_shape.js';
12
+ import { ROLE_ADMIN } from '../auth/role_schema.js';
13
+ /**
14
+ * Create the admin-only route spec that serves the `AppSurface` as JSON.
15
+ *
16
+ * Surface data reveals API structure (routes, auth, schemas), so the route
17
+ * requires the global admin role. It is also a non-RPC surface: it declares
18
+ * `required_scope: 'surface:app_surface'`, so a narrowed API token is refused
19
+ * it whatever its method list says (403 `token_scope_required`, ahead of the
20
+ * role gate), while a full-scope admin bearer passes. The admin role implies
21
+ * `actor: 'required'`, so the route takes the `?acting=` selector a
22
+ * multi-actor admin disambiguates with.
23
+ *
24
+ * @param options - the surface to serve
25
+ * @returns the `GET /api/surface` route spec
26
+ */
27
+ export const create_surface_route_spec = (options) => ({
28
+ method: 'GET',
29
+ path: '/api/surface',
30
+ auth: {
31
+ account: 'required',
32
+ actor: 'required',
33
+ roles: [ROLE_ADMIN],
34
+ // rule 3 — the surface map is not an RPC method, so a narrowed token
35
+ // gets none of it
36
+ required_scope: 'surface:app_surface'
37
+ },
38
+ handler: (c) => c.json(options.surface),
39
+ description: 'Application surface (routes, middleware, schemas) — admin only',
40
+ query: z.strictObject({ acting: ActingActor }),
41
+ input: z.null(),
42
+ output: z.looseObject({
43
+ routes: z.array(z.looseObject({})),
44
+ middleware: z.array(z.looseObject({}))
45
+ })
46
+ });