@fuzdev/fuz_app 0.140.0 → 0.141.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.
- package/dist/actions/CLAUDE.md +62 -17
- package/dist/actions/action_rpc.d.ts +5 -1
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +9 -1
- package/dist/actions/connection_closer.d.ts +25 -2
- package/dist/actions/connection_closer.d.ts.map +1 -1
- package/dist/actions/connection_closer.js +8 -1
- package/dist/actions/register_action_ws.d.ts +42 -6
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +87 -19
- package/dist/actions/register_ws_endpoint.d.ts +15 -6
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +5 -2
- package/dist/actions/socket.svelte.d.ts +19 -4
- package/dist/actions/socket.svelte.d.ts.map +1 -1
- package/dist/actions/socket.svelte.js +31 -4
- package/dist/actions/transports.d.ts +30 -1
- package/dist/actions/transports.d.ts.map +1 -1
- package/dist/actions/transports.js +30 -1
- package/dist/actions/transports_ws_auth_guard.d.ts +13 -6
- package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
- package/dist/actions/transports_ws_auth_guard.js +24 -10
- package/dist/actions/transports_ws_backend.d.ts +51 -5
- package/dist/actions/transports_ws_backend.d.ts.map +1 -1
- package/dist/actions/transports_ws_backend.js +62 -9
- package/dist/actions/ws_endpoint_spec.d.ts +11 -10
- package/dist/actions/ws_endpoint_spec.d.ts.map +1 -1
- package/dist/auth/CLAUDE.md +11 -9
- package/dist/auth/account_route_schema.d.ts +1 -0
- package/dist/auth/account_route_schema.d.ts.map +1 -1
- package/dist/auth/account_route_schema.js +4 -0
- package/dist/auth/api_token.d.ts +20 -0
- package/dist/auth/api_token.d.ts.map +1 -1
- package/dist/auth/api_token.js +21 -1
- package/dist/auth/api_token_queries.d.ts +22 -4
- package/dist/auth/api_token_queries.d.ts.map +1 -1
- package/dist/auth/api_token_queries.js +11 -6
- package/dist/auth/audit_log_routes.d.ts +10 -0
- package/dist/auth/audit_log_routes.d.ts.map +1 -1
- package/dist/auth/audit_log_routes.js +20 -0
- package/dist/auth/audit_log_schema.d.ts +30 -5
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +30 -0
- package/dist/auth/bearer_auth.d.ts +4 -2
- package/dist/auth/bearer_auth.d.ts.map +1 -1
- package/dist/auth/bearer_auth.js +19 -8
- package/dist/auth/bootstrap_route_schema.d.ts +1 -0
- package/dist/auth/bootstrap_route_schema.d.ts.map +1 -1
- package/dist/auth/bootstrap_route_schema.js +1 -0
- package/dist/auth/cleanup.d.ts +9 -9
- package/dist/auth/cleanup.js +9 -9
- package/dist/auth/credential_deadline.d.ts +95 -0
- package/dist/auth/credential_deadline.d.ts.map +1 -0
- package/dist/auth/credential_deadline.js +139 -0
- package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
- package/dist/auth/daemon_token_middleware.js +4 -1
- package/dist/auth/middleware.d.ts +90 -12
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +115 -16
- package/dist/auth/request_context.d.ts +4 -2
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +14 -3
- package/dist/auth/resolved_auth.d.ts +13 -2
- package/dist/auth/resolved_auth.d.ts.map +1 -1
- package/dist/auth/resolved_auth.js +5 -4
- package/dist/auth/session_cookie.d.ts +4 -0
- package/dist/auth/session_cookie.d.ts.map +1 -1
- package/dist/auth/session_cookie.js +3 -2
- package/dist/auth/session_middleware.d.ts +1 -1
- package/dist/auth/session_middleware.d.ts.map +1 -1
- package/dist/auth/session_middleware.js +4 -1
- package/dist/auth/session_queries.d.ts +19 -2
- package/dist/auth/session_queries.d.ts.map +1 -1
- package/dist/auth/session_queries.js +8 -2
- package/dist/auth/signup_route_schema.d.ts.map +1 -1
- package/dist/auth/signup_route_schema.js +1 -0
- package/dist/hono_context.d.ts +23 -0
- package/dist/hono_context.d.ts.map +1 -1
- package/dist/hono_context.js +19 -0
- package/dist/http/CLAUDE.md +41 -15
- package/dist/http/middleware_spec.d.ts +16 -0
- package/dist/http/middleware_spec.d.ts.map +1 -1
- package/dist/http/origin.d.ts +113 -18
- package/dist/http/origin.d.ts.map +1 -1
- package/dist/http/origin.js +247 -127
- package/dist/http/route_spec.d.ts +21 -0
- package/dist/http/route_spec.d.ts.map +1 -1
- package/dist/http/schema_helpers.d.ts +22 -0
- package/dist/http/schema_helpers.d.ts.map +1 -1
- package/dist/http/schema_helpers.js +24 -0
- package/dist/http/surface.d.ts +6 -1
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +12 -5
- package/dist/realtime/sse_auth_guard.d.ts +22 -6
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/realtime/sse_auth_guard.js +31 -12
- package/dist/realtime/subscriber_registry.d.ts +10 -1
- package/dist/realtime/subscriber_registry.d.ts.map +1 -1
- package/dist/realtime/subscriber_registry.js +10 -1
- package/dist/server/app_server.d.ts +20 -7
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +8 -3
- package/dist/server/env.d.ts +2 -1
- package/dist/server/env.d.ts.map +1 -1
- package/dist/server/env.js +7 -17
- package/dist/server/serve_fact_route.d.ts.map +1 -1
- package/dist/server/serve_fact_route.js +1 -0
- package/dist/testing/CLAUDE.md +154 -22
- package/dist/testing/adversarial_headers.d.ts.map +1 -1
- package/dist/testing/adversarial_headers.js +13 -6
- package/dist/testing/connection_closer_helpers.d.ts +6 -1
- package/dist/testing/connection_closer_helpers.d.ts.map +1 -1
- package/dist/testing/connection_closer_helpers.js +4 -0
- package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size.js +14 -65
- package/dist/testing/cross_backend/capabilities.d.ts +26 -2
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/capabilities.js +9 -3
- package/dist/testing/cross_backend/conformance_case.d.ts +11 -3
- package/dist/testing/cross_backend/conformance_case.d.ts.map +1 -1
- package/dist/testing/cross_backend/conformance_case.js +6 -0
- package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
- package/dist/testing/cross_backend/conformance_table.js +20 -19
- package/dist/testing/cross_backend/credential_deadline.d.ts +25 -0
- package/dist/testing/cross_backend/credential_deadline.d.ts.map +1 -0
- package/dist/testing/cross_backend/credential_deadline.js +185 -0
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +13 -2
- package/dist/testing/cross_backend/full_spine_mount.d.ts +3 -0
- package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
- package/dist/testing/cross_backend/full_spine_mount.js +7 -1
- package/dist/testing/cross_backend/narrowed_api_token.d.ts +23 -0
- package/dist/testing/cross_backend/narrowed_api_token.d.ts.map +1 -0
- package/dist/testing/cross_backend/narrowed_api_token.js +40 -0
- package/dist/testing/cross_backend/origin.d.ts.map +1 -1
- package/dist/testing/cross_backend/origin.js +6 -26
- package/dist/testing/cross_backend/origin_conformance.d.ts +152 -0
- package/dist/testing/cross_backend/origin_conformance.d.ts.map +1 -0
- package/dist/testing/cross_backend/origin_conformance.js +136 -0
- package/dist/testing/cross_backend/origin_conformance.json +1679 -0
- package/dist/testing/cross_backend/origin_scope.d.ts +16 -0
- package/dist/testing/cross_backend/origin_scope.d.ts.map +1 -0
- package/dist/testing/cross_backend/origin_scope.js +140 -0
- package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts +35 -0
- package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts.map +1 -0
- package/dist/testing/cross_backend/role_grant_cross_helpers.js +64 -0
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts +8 -0
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.js +44 -33
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +6 -4
- package/dist/testing/cross_backend/spine_surface_constants.d.ts +19 -0
- package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
- package/dist/testing/cross_backend/spine_surface_constants.js +22 -0
- package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_backdoor.js +20 -7
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +180 -5
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +154 -5
- package/dist/testing/cross_backend/token_scope_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/token_scope_surface.js +25 -21
- package/dist/testing/cross_backend/transport_composition.d.ts +60 -0
- package/dist/testing/cross_backend/transport_composition.d.ts.map +1 -0
- package/dist/testing/cross_backend/transport_composition.js +364 -0
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/ts_spine_backend_config.js +21 -6
- package/dist/testing/cross_backend/ws_role_gated.d.ts +38 -0
- package/dist/testing/cross_backend/ws_role_gated.d.ts.map +1 -0
- package/dist/testing/cross_backend/ws_role_gated.js +142 -0
- package/dist/testing/entities.d.ts +23 -0
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +37 -0
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +3 -1
- package/dist/testing/middleware.d.ts +10 -0
- package/dist/testing/middleware.d.ts.map +1 -1
- package/dist/testing/middleware.js +31 -7
- package/dist/testing/stubs.d.ts +16 -3
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +20 -7
- package/dist/testing/surface_invariants.js +2 -2
- package/package.json +1 -1
package/dist/http/surface.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* App surface generation — JSON-serializable attack surface from route and middleware specs.
|
|
3
3
|
*
|
|
4
|
-
* Pure schema helpers (`is_null_schema`, `schema_to_surface`, `middleware_applies`,
|
|
4
|
+
* Pure schema helpers (`is_null_schema`, `schema_to_surface`, `middleware_applies`, `middleware_spec_applies`,
|
|
5
5
|
* `merge_error_schemas`) live in `http/schema_helpers.ts`.
|
|
6
6
|
*
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
9
|
import { z } from 'zod';
|
|
10
|
-
import { schema_to_surface,
|
|
10
|
+
import { schema_to_surface, middleware_spec_applies, merge_error_schemas, is_null_schema, is_strict_object_schema } from './schema_helpers.js';
|
|
11
11
|
// --- Surface generation ---
|
|
12
12
|
/**
|
|
13
13
|
* Collect error schemas from all middleware that applies to a route path.
|
|
@@ -17,7 +17,7 @@ import { schema_to_surface, middleware_applies, merge_error_schemas, is_null_sch
|
|
|
17
17
|
export const collect_middleware_errors = (middleware, route_path) => {
|
|
18
18
|
const errors = {};
|
|
19
19
|
for (const mw of middleware) {
|
|
20
|
-
if (mw.errors &&
|
|
20
|
+
if (mw.errors && middleware_spec_applies(mw, route_path)) {
|
|
21
21
|
Object.assign(errors, mw.errors);
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -89,11 +89,18 @@ export const generate_app_surface = (options) => {
|
|
|
89
89
|
mw_error_schemas = schemas;
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
-
return {
|
|
92
|
+
return {
|
|
93
|
+
name: m.name,
|
|
94
|
+
path: m.path,
|
|
95
|
+
error_schemas: mw_error_schemas,
|
|
96
|
+
...(m.carve_outs?.length && {
|
|
97
|
+
carve_outs: m.carve_outs.map(({ path, reason }) => ({ path, reason }))
|
|
98
|
+
})
|
|
99
|
+
};
|
|
93
100
|
}),
|
|
94
101
|
routes: route_specs.map((r) => {
|
|
95
102
|
const applicable_middleware = middleware_specs
|
|
96
|
-
.filter((m) =>
|
|
103
|
+
.filter((m) => middleware_spec_applies(m, r.path))
|
|
97
104
|
.map((m) => m.name);
|
|
98
105
|
// Merge auto-derived + middleware + explicit error schemas
|
|
99
106
|
const mw_errors = collect_middleware_errors(middleware_specs, r.path);
|
|
@@ -35,12 +35,20 @@ export declare const AUDIT_LOG_CHANNEL = "audit_log";
|
|
|
35
35
|
* their `groups` — pending registrations included. The audit stream route
|
|
36
36
|
* registers `scope = session hash` and `groups = [account id]`; a route that
|
|
37
37
|
* admits bearer tokens registers the API token id too, and
|
|
38
|
-
* `close_sockets_for_token` then reaches it. `
|
|
39
|
-
* `
|
|
38
|
+
* `close_sockets_for_token` then reaches it. `close_sockets_for_role` closes
|
|
39
|
+
* the account's streams when `role` is the one the registry's streams are
|
|
40
|
+
* gated on — the registry keeps one gating role for all its streams, not one
|
|
41
|
+
* per stream. `close_all_sockets` is `SubscriberRegistry.close_all` — every
|
|
42
|
+
* stream, for shutdown.
|
|
40
43
|
*
|
|
41
44
|
* Twin of the Rust spine's `SocketRevoker` impl on `SseRegistry`.
|
|
45
|
+
*
|
|
46
|
+
* @param registry - the registry to close streams on
|
|
47
|
+
* @param required_role - the role every stream of `registry` was gated on,
|
|
48
|
+
* or `null` when they are not role-gated (`close_sockets_for_role` then
|
|
49
|
+
* closes nothing)
|
|
42
50
|
*/
|
|
43
|
-
export declare const create_sse_connection_closer: <T>(registry: SubscriberRegistry<T
|
|
51
|
+
export declare const create_sse_connection_closer: <T>(registry: SubscriberRegistry<T>, required_role: string | null) => ConnectionCloser;
|
|
44
52
|
/**
|
|
45
53
|
* Create an audit event handler that closes SSE streams on a successful
|
|
46
54
|
* revocation row, dispatching on the event's `RevocationScope`
|
|
@@ -56,9 +64,12 @@ export declare const create_sse_connection_closer: <T>(registry: SubscriberRegis
|
|
|
56
64
|
* registers the token id and is closed here.
|
|
57
65
|
* - `account` → every stream of `target_account_id ?? account_id`.
|
|
58
66
|
* - `role` → every stream of the target account, when the revoked role
|
|
59
|
-
* (`metadata.role`) is `required_role
|
|
60
|
-
*
|
|
61
|
-
*
|
|
67
|
+
* (`metadata.role`) is `required_role` and the revoked grant was global
|
|
68
|
+
* (`to_global_role_revocation`): the stream's role gate admits on global
|
|
69
|
+
* grants only, so revoking a scoped grant of the same role takes away
|
|
70
|
+
* nothing it admitted on. A stream is authorized once at open and has no
|
|
71
|
+
* later check; the WebSocket guard closes its role-gated sockets on the
|
|
72
|
+
* same rows.
|
|
62
73
|
* - `none` → nothing.
|
|
63
74
|
*
|
|
64
75
|
* Over-closing a one-way feed is cheap: the client reconnects if it is still
|
|
@@ -94,6 +105,11 @@ export interface AuditLogSse {
|
|
|
94
105
|
log: Logger;
|
|
95
106
|
/** Combined broadcast + guard callback. Wired by `create_app_server`'s `audit_log_sse` option, or compose inside the consumer's `audit_factory` body. */
|
|
96
107
|
on_audit_event: (event: AuditLogEvent) => void;
|
|
108
|
+
/**
|
|
109
|
+
* The role the stream requires — what a caller adapting `registry` passes
|
|
110
|
+
* `create_sse_connection_closer`, so its role close matches the guard's.
|
|
111
|
+
*/
|
|
112
|
+
required_role: string;
|
|
97
113
|
}
|
|
98
114
|
/**
|
|
99
115
|
* SSE event specs for audit log events.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse_auth_guard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/realtime/sse_auth_guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAGtD,OAAO,
|
|
1
|
+
{"version":3,"file":"sse_auth_guard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/realtime/sse_auth_guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAGtD,OAAO,EAKN,KAAK,aAAa,EAClB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACxF,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE3D,2DAA2D;AAC3D,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,4BAA4B,GAAI,CAAC,EAC7C,UAAU,kBAAkB,CAAC,CAAC,CAAC,EAC/B,eAAe,MAAM,GAAG,IAAI,KAC1B,gBAOD,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,qBAAqB,GAAI,CAAC,EACtC,UAAU,kBAAkB,CAAC,CAAC,CAAC,EAC/B,eAAe,MAAM,GAAG,IAAI,EAC5B,KAAK,MAAM,KACT,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAoEjC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC3B;;;;;OAKG;IACH,QAAQ,EAAE,kBAAkB,CAAC,eAAe,CAAC,CAAC;IAC9C,kFAAkF;IAClF,GAAG,EAAE,MAAM,CAAC;IACZ,yJAAyJ;IACzJ,cAAc,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,SAAS,CAOlD,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS;IAC7C,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;;;OASG;IACH,iBAAiB,EAAE,cAAc,GAAG,IAAI,CAAC;IACzC;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,KAAG,WAiBH,CAAC"}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* @module
|
|
21
21
|
*/
|
|
22
22
|
import { UnreachableError } from '@fuzdev/fuz_util/error.ts';
|
|
23
|
-
import { AUDIT_EVENT_TYPES, AuditLogEventJson, to_revocation_scope } from '../auth/audit_log_schema.js';
|
|
23
|
+
import { AUDIT_EVENT_TYPES, AuditLogEventJson, to_global_role_revocation, to_revocation_scope } from '../auth/audit_log_schema.js';
|
|
24
24
|
import { SubscriberRegistry } from './subscriber_registry.js';
|
|
25
25
|
/** SSE channel the audit-log stream route publishes on. */
|
|
26
26
|
export const AUDIT_LOG_CHANNEL = 'audit_log';
|
|
@@ -33,15 +33,24 @@ export const AUDIT_LOG_CHANNEL = 'audit_log';
|
|
|
33
33
|
* their `groups` — pending registrations included. The audit stream route
|
|
34
34
|
* registers `scope = session hash` and `groups = [account id]`; a route that
|
|
35
35
|
* admits bearer tokens registers the API token id too, and
|
|
36
|
-
* `close_sockets_for_token` then reaches it. `
|
|
37
|
-
* `
|
|
36
|
+
* `close_sockets_for_token` then reaches it. `close_sockets_for_role` closes
|
|
37
|
+
* the account's streams when `role` is the one the registry's streams are
|
|
38
|
+
* gated on — the registry keeps one gating role for all its streams, not one
|
|
39
|
+
* per stream. `close_all_sockets` is `SubscriberRegistry.close_all` — every
|
|
40
|
+
* stream, for shutdown.
|
|
38
41
|
*
|
|
39
42
|
* Twin of the Rust spine's `SocketRevoker` impl on `SseRegistry`.
|
|
43
|
+
*
|
|
44
|
+
* @param registry - the registry to close streams on
|
|
45
|
+
* @param required_role - the role every stream of `registry` was gated on,
|
|
46
|
+
* or `null` when they are not role-gated (`close_sockets_for_role` then
|
|
47
|
+
* closes nothing)
|
|
40
48
|
*/
|
|
41
|
-
export const create_sse_connection_closer = (registry) => ({
|
|
49
|
+
export const create_sse_connection_closer = (registry, required_role) => ({
|
|
42
50
|
close_sockets_for_session: (session_token_hash) => registry.close_by_identity(session_token_hash),
|
|
43
51
|
close_sockets_for_token: (api_token_id) => registry.close_by_identity(api_token_id),
|
|
44
52
|
close_sockets_for_account: (account_id) => registry.close_by_identity(account_id),
|
|
53
|
+
close_sockets_for_role: (account_id, role) => required_role !== null && role === required_role ? registry.close_by_identity(account_id) : 0,
|
|
45
54
|
close_all_sockets: () => registry.close_all()
|
|
46
55
|
});
|
|
47
56
|
/**
|
|
@@ -59,9 +68,12 @@ export const create_sse_connection_closer = (registry) => ({
|
|
|
59
68
|
* registers the token id and is closed here.
|
|
60
69
|
* - `account` → every stream of `target_account_id ?? account_id`.
|
|
61
70
|
* - `role` → every stream of the target account, when the revoked role
|
|
62
|
-
* (`metadata.role`) is `required_role
|
|
63
|
-
*
|
|
64
|
-
*
|
|
71
|
+
* (`metadata.role`) is `required_role` and the revoked grant was global
|
|
72
|
+
* (`to_global_role_revocation`): the stream's role gate admits on global
|
|
73
|
+
* grants only, so revoking a scoped grant of the same role takes away
|
|
74
|
+
* nothing it admitted on. A stream is authorized once at open and has no
|
|
75
|
+
* later check; the WebSocket guard closes its role-gated sockets on the
|
|
76
|
+
* same rows.
|
|
65
77
|
* - `none` → nothing.
|
|
66
78
|
*
|
|
67
79
|
* Over-closing a one-way feed is cheap: the client reconnects if it is still
|
|
@@ -111,20 +123,26 @@ export const create_sse_auth_guard = (registry, required_role, log) => {
|
|
|
111
123
|
}
|
|
112
124
|
return;
|
|
113
125
|
}
|
|
114
|
-
case 'role':
|
|
126
|
+
case 'role': {
|
|
115
127
|
// `null` means the stream isn't gated by a specific role_grant, so
|
|
116
128
|
// a role revocation closes nothing.
|
|
117
129
|
if (required_role === null)
|
|
118
130
|
return;
|
|
119
|
-
|
|
131
|
+
// global grants only — the gate never admitted on a scoped one
|
|
132
|
+
const revoked = to_global_role_revocation(event);
|
|
133
|
+
if (!revoked || revoked.role !== required_role)
|
|
120
134
|
return;
|
|
121
|
-
|
|
135
|
+
const closed = registry.close_by_identity(revoked.account_id);
|
|
136
|
+
if (closed > 0) {
|
|
137
|
+
log.info(`SSE auth guard: closed ${closed} stream(s) for account ${revoked.account_id} (${event.event_type})`);
|
|
138
|
+
}
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
122
141
|
case 'account':
|
|
123
142
|
break;
|
|
124
143
|
default:
|
|
125
144
|
throw new UnreachableError(scope);
|
|
126
145
|
}
|
|
127
|
-
// `role` and `account` both close the affected account's streams —
|
|
128
146
|
// admin actions set target_account_id, self-service actions only set
|
|
129
147
|
// account_id
|
|
130
148
|
const target = event.target_account_id ?? event.account_id;
|
|
@@ -207,9 +225,10 @@ export const create_audit_log_sse = (options) => {
|
|
|
207
225
|
const max_per_scope = options.max_per_scope === undefined ? AUDIT_LOG_SSE_MAX_PER_SCOPE : options.max_per_scope;
|
|
208
226
|
const registry = new SubscriberRegistry({ max_per_scope, log: options.log });
|
|
209
227
|
const guard = create_sse_auth_guard(registry, role, options.log);
|
|
210
|
-
options.connection_closer?.add(create_sse_connection_closer(registry));
|
|
228
|
+
options.connection_closer?.add(create_sse_connection_closer(registry, role));
|
|
211
229
|
return {
|
|
212
230
|
log: options.log,
|
|
231
|
+
required_role: role,
|
|
213
232
|
on_audit_event: (event) => {
|
|
214
233
|
registry.broadcast(AUDIT_LOG_CHANNEL, { method: event.event_type, params: event });
|
|
215
234
|
guard(event);
|
|
@@ -165,7 +165,16 @@ export interface PendingSubscription {
|
|
|
165
165
|
* // first: a client that already left closes the stream, its listener runs at
|
|
166
166
|
* // once, and `admit` then refuses the removed registration
|
|
167
167
|
* stream.on_close(pending.unsubscribe);
|
|
168
|
-
* if (!registry.admit(pending, stream))
|
|
168
|
+
* if (!registry.admit(pending, stream)) {
|
|
169
|
+
* stream.close(); // the body is the connect comment alone
|
|
170
|
+
* } else {
|
|
171
|
+
* // expiry writes nothing a revocation closes on: end at the credential's deadline
|
|
172
|
+
* stream.on_close(
|
|
173
|
+
* schedule_credential_deadline(get_resolved_auth(c)?.credential_deadline ?? null, () =>
|
|
174
|
+
* stream.close()
|
|
175
|
+
* )
|
|
176
|
+
* );
|
|
177
|
+
* }
|
|
169
178
|
* return response;
|
|
170
179
|
* ```
|
|
171
180
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscriber_registry.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/realtime/subscriber_registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,0EAA0E;AAC1E,MAAM,WAAW,UAAU,CAAC,CAAC;IAC5B,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IACrB,sEAAsE;IACtE,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IAC7B,mDAAmD;IACnD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,qEAAqE;IACrE,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;CAC3B;AASD,wCAAwC;AACxC,MAAM,WAAW,yBAAyB;IACzC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,0EAA0E;AAC1E,MAAM,WAAW,gBAAgB;IAChC,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACjC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC/B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,mBAAmB;IACnC;;;;OAIG;IACH,WAAW,EAAE,MAAM,IAAI,CAAC;CACxB;AAED
|
|
1
|
+
{"version":3,"file":"subscriber_registry.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/realtime/subscriber_registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,0EAA0E;AAC1E,MAAM,WAAW,UAAU,CAAC,CAAC;IAC5B,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IACrB,sEAAsE;IACtE,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IAC7B,mDAAmD;IACnD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,qEAAqE;IACrE,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;CAC3B;AASD,wCAAwC;AACxC,MAAM,WAAW,yBAAyB;IACzC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,0EAA0E;AAC1E,MAAM,WAAW,gBAAgB;IAChC,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACjC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC/B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,mBAAmB;IACnC;;;;OAIG;IACH,WAAW,EAAE,MAAM,IAAI,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8FG;AACH,qBAAa,kBAAkB,CAAC,CAAC;;gBAYpB,OAAO,CAAC,EAAE,yBAAyB;IAK/C;;;OAGG;IACH,IAAI,KAAK,IAAI,MAAM,CAMlB;IAED;;;OAGG;IACH,IAAI,aAAa,IAAI,MAAM,CAM1B;IAED;;;;;OAKG;IACH,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM,IAAI;IAavE;;;;;;;;;;;;;;;OAeG;IACH,iBAAiB,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,mBAAmB;IAYlE;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,OAAO,EAAE,mBAAmB,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,OAAO;IASlE;;;;;;;OAOG;IACH,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI;IASzC;;;;;;;;;;;;;;;;;;OAkBG;IACH,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAM3C;;;;;;;;;;;;OAYG;IACH,SAAS,IAAI,MAAM;CAsFnB"}
|
|
@@ -105,7 +105,16 @@ import { Logger } from '@fuzdev/fuz_util/log.ts';
|
|
|
105
105
|
* // first: a client that already left closes the stream, its listener runs at
|
|
106
106
|
* // once, and `admit` then refuses the removed registration
|
|
107
107
|
* stream.on_close(pending.unsubscribe);
|
|
108
|
-
* if (!registry.admit(pending, stream))
|
|
108
|
+
* if (!registry.admit(pending, stream)) {
|
|
109
|
+
* stream.close(); // the body is the connect comment alone
|
|
110
|
+
* } else {
|
|
111
|
+
* // expiry writes nothing a revocation closes on: end at the credential's deadline
|
|
112
|
+
* stream.on_close(
|
|
113
|
+
* schedule_credential_deadline(get_resolved_auth(c)?.credential_deadline ?? null, () =>
|
|
114
|
+
* stream.close()
|
|
115
|
+
* )
|
|
116
|
+
* );
|
|
117
|
+
* }
|
|
109
118
|
* return response;
|
|
110
119
|
* ```
|
|
111
120
|
*/
|
|
@@ -23,7 +23,7 @@ import '../hono_context.js';
|
|
|
23
23
|
import { type ServeStaticFactory } from './static.js';
|
|
24
24
|
import { type AppSurfaceSpec, type AppSurfaceDiagnostic, type RpcEndpointSpec } from '../http/surface.js';
|
|
25
25
|
import { type RouteSpec } from '../http/route_spec.js';
|
|
26
|
-
import type { MiddlewareSpec } from '../http/middleware_spec.js';
|
|
26
|
+
import type { MiddlewareCarveOut, MiddlewareSpec } from '../http/middleware_spec.js';
|
|
27
27
|
import { type BootstrapStatus } from '../auth/bootstrap_routes.js';
|
|
28
28
|
import { type WsEndpointSpec } from '../actions/ws_endpoint_spec.js';
|
|
29
29
|
import type { BackendWebsocketTransport } from '../actions/transports_ws_backend.js';
|
|
@@ -90,6 +90,18 @@ export interface AppServerOptions {
|
|
|
90
90
|
session_options: SessionOptions<string>;
|
|
91
91
|
/** Parsed allowed origin patterns. */
|
|
92
92
|
allowed_origins: Array<RegExp>;
|
|
93
|
+
/**
|
|
94
|
+
* Paths the origin check skips, each with its reason. The check covers
|
|
95
|
+
* every path by default (`ORIGIN_MIDDLEWARE_PATH`) — a request with no
|
|
96
|
+
* `Origin` passes it anyway, so probes and CLI clients need no carve-out;
|
|
97
|
+
* one is for a path that refuses browser callers by other means. Copied at
|
|
98
|
+
* assembly, so a later change to this array changes nothing; assembly
|
|
99
|
+
* throws on a carve-out with no reason, or one that overlaps the auth
|
|
100
|
+
* middleware's scope (`AUTH_MIDDLEWARE_PATH`) — every route that reads or
|
|
101
|
+
* mints a credential, and every RPC, WS, and bootstrap endpoint, sits
|
|
102
|
+
* there (`assert_origin_carve_outs_valid`).
|
|
103
|
+
*/
|
|
104
|
+
origin_carve_outs?: Array<MiddlewareCarveOut>;
|
|
93
105
|
/** Trusted proxy options. */
|
|
94
106
|
proxy: {
|
|
95
107
|
trusted_proxies: Array<string>;
|
|
@@ -212,7 +224,8 @@ export interface AppServerOptions {
|
|
|
212
224
|
* Add layers anywhere (before the proxy spec, between it and the auth
|
|
213
225
|
* stack, between the auth layers, or after), but keep each of those specs
|
|
214
226
|
* — its `handler`, not just its `name` — mounted once at its original path
|
|
215
|
-
* (`'*'` for the proxy, `AUTH_MIDDLEWARE_PATH` for
|
|
227
|
+
* (`'*'` for the proxy and the origin check, `AUTH_MIDDLEWARE_PATH` for the
|
|
228
|
+
* rest of the auth stack) in its original
|
|
216
229
|
* order: the endpoint auth-scope check (`assert_endpoint_in_auth_scope`)
|
|
217
230
|
* and client-IP rate limiting (`get_client_ip`) assume it, so assembly
|
|
218
231
|
* throws (`assert_middleware_stack_mounted`) if the transform moves, drops,
|
|
@@ -249,11 +262,11 @@ export interface AppServerOptions {
|
|
|
249
262
|
* audits each newly expired role_grant offer once.
|
|
250
263
|
*
|
|
251
264
|
* **Off by default; a production server should turn it on.** Without it
|
|
252
|
-
* nothing sweeps: expired session rows accumulate
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
265
|
+
* nothing sweeps: expired session rows accumulate and an offer's expiry is
|
|
266
|
+
* never audited. Connections do not depend on it — each closes at its
|
|
267
|
+
* credential's deadline (`auth/credential_deadline.ts`), and the sweep's
|
|
268
|
+
* close repeats that one. The Rust spine's servers all schedule the twin
|
|
269
|
+
* (`spawn_auth_cleanup`).
|
|
257
270
|
*
|
|
258
271
|
* Pass `true` for the default interval
|
|
259
272
|
* (`DEFAULT_AUTH_CLEANUP_INTERVAL_MS`), or `{interval_ms}` for another.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,KAAK,OAAO,EAAE,MAAM,MAAM,CAAC;AAE1C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAEN,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAIN,KAAK,WAAW,EAChB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EACN,WAAW,EAMX,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,OAAO,oBAAoB,CAAC;AAE5B,OAAO,EAA4B,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEhF,OAAO,EAEN,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,KAAK,OAAO,EAAE,MAAM,MAAM,CAAC;AAE1C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAEN,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAIN,KAAK,WAAW,EAChB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EACN,WAAW,EAMX,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,OAAO,oBAAoB,CAAC;AAE5B,OAAO,EAA4B,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEhF,OAAO,EAEN,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACrF,OAAO,EAGN,KAAK,eAAe,EACpB,MAAM,6BAA6B,CAAC;AAcrC,OAAO,EAGN,KAAK,cAAc,EACnB,MAAM,gCAAgC,CAAC;AAExC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AACrF,OAAO,EAAsB,KAAK,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAEzF;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,sBAAsB,GACjC,wBAAwB,GAAG,2BAA2B,GAAG,oBAAoB,CAAC;AAE/E,MAAM,WAAW,wBAAwB;IACxC,IAAI,EAAE,UAAU,CAAC;CACjB;AAED,MAAM,WAAW,2BAA2B;IAC3C,IAAI,EAAE,cAAc,CAAC;IACrB,6FAA6F;IAC7F,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,2FAA2F;IAC3F,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,uBAAuB,EAAE,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9E;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAChC,2DAA2D;IAC3D,OAAO,EAAE,UAAU,CAAC;IACpB,6CAA6C;IAC7C,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,sCAAsC;IACtC,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,EAAE,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAE9C,6BAA6B;IAC7B,KAAK,EAAE;QACN,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QAC/B,iBAAiB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;KACtD,CAAC;IAEF;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,eAAe,CAAC;IAChC;;;;;;;;;;;;;OAaG;IACH,qBAAqB,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC3C;;;;;;;OAOG;IACH,sBAAsB,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5C;;;;;;;OAOG;IACH,yBAAyB,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/C;;;;;OAKG;IACH,0BAA0B,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAChD;;;;;OAKG;IACH,2BAA2B,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACjD;;;;;;;;;OASG;IACH,sBAAsB,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5C;;;;;;;;;;OAUG;IACH,2BAA2B,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACjD;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,2DAA2D;IAC3D,kBAAkB,CAAC,EAAE,gBAAgB,CAAC;IAEtC,yEAAyE;IACzE,SAAS,CAAC,EAAE,sBAAsB,CAAC;IAEnC;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;OAGG;IACH,kBAAkB,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAEpE;;;;;;;;;;;;;;;;OAgBG;IACH,oBAAoB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,KAAK,CAAC,cAAc,CAAC,CAAC;IAE/E;;;;;;;;;;;;;;;OAeG;IACH,aAAa,CAAC,EAAE,IAAI,GAAG;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAEzC;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,YAAY,CAAC,EAAE,OAAO,GAAG,0BAA0B,CAAC;IAEpD,gFAAgF;IAChF,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAE/B;;;;;;;;;;;;;;OAcG;IACH,aAAa,CAAC,EAAE,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,gBAAgB,KAAK,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;IAEjG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACH,wBAAwB,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,gBAAgB,CAAC;IAE3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA0CG;IACH,YAAY,CAAC,EACZ,aAAa,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,gBAAgB,KAAK,aAAa,CAAC,cAAc,CAAC,CAAC,CAAC;IAEhG;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IAEzB,mFAAmF;IACnF,qBAAqB,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;IAE9C,6DAA6D;IAC7D,cAAc,CAAC,EAAE;QAChB,YAAY,EAAE,kBAAkB,CAAC;QACjC,4DAA4D;QAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,gEAAgE;QAChE,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB;;;;WAIG;QACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;KACzC,CAAC;IAEF;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAEhC;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAExE,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,uCAAuC;AACvC,MAAM,WAAW,SAAS;IACzB,GAAG,EAAE,IAAI,CAAC;IACV,wEAAwE;IACxE,YAAY,EAAE,cAAc,CAAC;IAC7B,gBAAgB,EAAE,eAAe,CAAC;IAClC,oGAAoG;IACpG,iBAAiB,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAClD;;;;OAIG;IACH,SAAS,EAAE,WAAW,GAAG,IAAI,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC,CAAC;IAClE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;IACH,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ;IAAE,SAAS,EAAE,WAAW,GAAG,IAAI,CAAA;CAAE,KAAG,WAO7E,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,qBAAqB,QAAc,CAAC;AAEjD;;;;;GAKG;AACH,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiB4D,CAAC;AAErF;;;;;GAKG;AACH,MAAM,MAAM,uBAAuB,GAAG,MAAM,OAAO,kBAAkB,CAAC;AAEtE;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,EAEpC,aAAa,CAAC,uBAAuB,CAAC,CAAC;AAE5C;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACpC,2DAA2D;IAC3D,IAAI,EAAE,eAAe,CAAC;IACtB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC,uBAAuB,EAAE,WAAW,GAAG,IAAI,CAAC,CAAC;IAC9D;;;OAGG;IACH,KAAK,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;CAC1B;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,GACjC,SAAS,IAAI,CAAC,gBAAgB,EAAE,uBAAuB,GAAG,eAAe,CAAC,KACxE,oBAkBF,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACtC,6EAA6E;IAC7E,cAAc,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,SAAS,CAAC;IAC1D,2CAA2C;IAC3C,iBAAiB,EAAE,eAAe,CAAC;IACnC,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAC,uBAAuB,EAAE,WAAW,GAAG,IAAI,CAAC,CAAC;CACnE;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,GACtC,OAAO,sBAAsB,KAC3B,KAAK,CAAC,oBAAoB,CAiD5B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,eAAO,MAAM,iBAAiB,GAAU,SAAS,gBAAgB,KAAG,OAAO,CAAC,SAAS,CAQpF,CAAC"}
|
|
@@ -25,7 +25,7 @@ import { apply_middleware_specs, apply_route_specs, prefix_route_specs } from '.
|
|
|
25
25
|
import { check_bootstrap_status, create_bootstrap_route_specs } from '../auth/bootstrap_routes.js';
|
|
26
26
|
import { create_surface_route_spec } from './surface_route.js';
|
|
27
27
|
import { flush_pending_effects, flush_post_commit_effects } from '../http/pending_effects.js';
|
|
28
|
-
import { assert_endpoint_in_auth_scope, assert_middleware_stack_mounted, create_auth_middleware_specs } from '../auth/middleware.js';
|
|
28
|
+
import { assert_credential_routes_in_auth_scope, assert_endpoint_in_auth_scope, assert_middleware_stack_mounted, create_auth_middleware_specs } from '../auth/middleware.js';
|
|
29
29
|
import { fuz_auth_guard_resolver } from '../auth/auth_guard_resolver.js';
|
|
30
30
|
import { create_fuz_authorization_handler } from '../auth/request_context.js';
|
|
31
31
|
import { create_body_limit_middleware } from '../http/body_limit.js';
|
|
@@ -270,7 +270,7 @@ const assemble_app_server = async (options, releases) => {
|
|
|
270
270
|
})
|
|
271
271
|
: null;
|
|
272
272
|
if (audit_sse) {
|
|
273
|
-
releases.push(deps.connection_closer.add(create_sse_connection_closer(audit_sse.registry)));
|
|
273
|
+
releases.push(deps.connection_closer.add(create_sse_connection_closer(audit_sse.registry, audit_sse.required_role)));
|
|
274
274
|
releases.push(deps.audit.add_listener(audit_sse.on_audit_event));
|
|
275
275
|
}
|
|
276
276
|
// Proxy middleware
|
|
@@ -279,7 +279,8 @@ const assemble_app_server = async (options, releases) => {
|
|
|
279
279
|
const auth_middleware = await create_auth_middleware_specs(deps, {
|
|
280
280
|
allowed_origins: options.allowed_origins,
|
|
281
281
|
session_options: options.session_options,
|
|
282
|
-
daemon_token_state: options.daemon_token_state
|
|
282
|
+
daemon_token_state: options.daemon_token_state,
|
|
283
|
+
origin_carve_outs: options.origin_carve_outs
|
|
283
284
|
});
|
|
284
285
|
const stack_specs = [proxy_spec, ...auth_middleware];
|
|
285
286
|
let middleware_specs = stack_specs;
|
|
@@ -384,6 +385,10 @@ const assemble_app_server = async (options, releases) => {
|
|
|
384
385
|
factory_routes.push(create_surface_route_spec(surface_ref));
|
|
385
386
|
}
|
|
386
387
|
const route_specs = [...consumer_routes, ...factory_routes];
|
|
388
|
+
// a route that reads or mints a credential outside the auth scope would
|
|
389
|
+
// skip session parsing or fall under an origin carve-out — consumer
|
|
390
|
+
// prefixes included (account and signup specs are relative)
|
|
391
|
+
assert_credential_routes_in_auth_scope(route_specs);
|
|
387
392
|
// WS path checks — pure data, so they run before anything mounts.
|
|
388
393
|
// `register_ws_endpoint` mounts a `GET path` upgrade route: a duplicate
|
|
389
394
|
// path, or one a `RouteSpec` already registered as `GET path`, would be
|
package/dist/server/env.d.ts
CHANGED
|
@@ -60,7 +60,8 @@ export type ServerEnvOptionsResult = ServerEnvOptions | ServerEnvOptionsError;
|
|
|
60
60
|
/**
|
|
61
61
|
* Validate a loaded `BaseServerEnv` and produce the artifacts needed for server init.
|
|
62
62
|
*
|
|
63
|
-
* Handles keyring validation, origin parsing
|
|
63
|
+
* Handles keyring validation, origin parsing (every refused allowlist entry is
|
|
64
|
+
* reported, each on its own line), and bootstrap token path extraction.
|
|
64
65
|
* Returns a Result so callers handle errors their own way (exit, logging, etc).
|
|
65
66
|
*
|
|
66
67
|
* @param env - a loaded and Zod-validated `BaseServerEnv`
|
package/dist/server/env.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAA4B,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG5E;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;kBAoDxB,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;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,GAAI,KAAK,aAAa,KAAG,sBAmBxD,CAAC"}
|
package/dist/server/env.js
CHANGED
|
@@ -9,9 +9,8 @@
|
|
|
9
9
|
* @module
|
|
10
10
|
*/
|
|
11
11
|
import { z } from 'zod';
|
|
12
|
-
import { to_error_message } from '@fuzdev/fuz_util/error.ts';
|
|
13
12
|
import { create_validated_keyring } from '../auth/keyring.js';
|
|
14
|
-
import { parse_allowed_origins } from '../http/origin.js';
|
|
13
|
+
import { format_allowed_origins_error, parse_allowed_origins } from '../http/origin.js';
|
|
15
14
|
/**
|
|
16
15
|
* Base Zod schema for server environment variables.
|
|
17
16
|
*
|
|
@@ -76,7 +75,8 @@ export const BaseServerEnv = z.strictObject({
|
|
|
76
75
|
/**
|
|
77
76
|
* Validate a loaded `BaseServerEnv` and produce the artifacts needed for server init.
|
|
78
77
|
*
|
|
79
|
-
* Handles keyring validation, origin parsing
|
|
78
|
+
* Handles keyring validation, origin parsing (every refused allowlist entry is
|
|
79
|
+
* reported, each on its own line), and bootstrap token path extraction.
|
|
80
80
|
* Returns a Result so callers handle errors their own way (exit, logging, etc).
|
|
81
81
|
*
|
|
82
82
|
* @param env - a loaded and Zod-validated `BaseServerEnv`
|
|
@@ -87,28 +87,18 @@ export const validate_server_env = (env) => {
|
|
|
87
87
|
if (!keyring_result.ok) {
|
|
88
88
|
return { ok: false, field: 'SECRET_FUZ_COOKIE_KEYS', errors: keyring_result.errors };
|
|
89
89
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
allowed_origins = parse_allowed_origins(env.FUZ_ALLOWED_ORIGINS);
|
|
93
|
-
}
|
|
94
|
-
catch (err) {
|
|
95
|
-
return {
|
|
96
|
-
ok: false,
|
|
97
|
-
field: 'FUZ_ALLOWED_ORIGINS',
|
|
98
|
-
errors: [to_error_message(err, 'Invalid FUZ_ALLOWED_ORIGINS')]
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
if (allowed_origins.length === 0) {
|
|
90
|
+
const origins_result = parse_allowed_origins(env.FUZ_ALLOWED_ORIGINS);
|
|
91
|
+
if (!origins_result.ok) {
|
|
102
92
|
return {
|
|
103
93
|
ok: false,
|
|
104
94
|
field: 'FUZ_ALLOWED_ORIGINS',
|
|
105
|
-
errors:
|
|
95
|
+
errors: origins_result.errors.map(format_allowed_origins_error)
|
|
106
96
|
};
|
|
107
97
|
}
|
|
108
98
|
return {
|
|
109
99
|
ok: true,
|
|
110
100
|
keyring: keyring_result.keyring,
|
|
111
|
-
allowed_origins,
|
|
101
|
+
allowed_origins: origins_result.origins,
|
|
112
102
|
bootstrap_token_path: env.FUZ_BOOTSTRAP_TOKEN_PATH ?? null
|
|
113
103
|
};
|
|
114
104
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve_fact_route.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/serve_fact_route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2FG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAetD,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAE5F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAM/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAkEjD,MAAM,WAAW,+BAA+B;IAC/C;;;;;;OAMG;IACH,IAAI,EAAE,OAAO,CAAC;IACd,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;CACZ;AAkHD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iCAAiC,GAC7C,SAAS,+BAA+B,KACtC,
|
|
1
|
+
{"version":3,"file":"serve_fact_route.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/serve_fact_route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2FG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAetD,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAE5F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAM/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAkEjD,MAAM,WAAW,+BAA+B;IAC/C;;;;;;OAMG;IACH,IAAI,EAAE,OAAO,CAAC;IACd,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;CACZ;AAkHD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iCAAiC,GAC7C,SAAS,+BAA+B,KACtC,SAiDF,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,4BAA4B,GACxC,SAAS,+BAA+B,KACtC,SA2CF,CAAC"}
|
|
@@ -268,6 +268,7 @@ export const create_serve_cell_fact_route_spec = (options) => {
|
|
|
268
268
|
method: 'GET',
|
|
269
269
|
path: '/api/cells/:cell_id/facts/:hash',
|
|
270
270
|
auth: { account: 'none', actor: 'none' },
|
|
271
|
+
reads_credential: true,
|
|
271
272
|
description: 'Serve content-addressed fact bytes through a named referencing cell. 404 unless the cell admits the caller via can_view_cell AND references the hash (per-reference, never union-of-referrers).',
|
|
272
273
|
params: cell_fact_params_schema,
|
|
273
274
|
input: z.null(),
|