@fuzdev/fuz_app 0.62.0 → 0.64.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 +139 -24
- package/dist/actions/action_rpc.d.ts +10 -0
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +1 -1
- package/dist/actions/action_spec.d.ts +1 -1
- package/dist/actions/action_spec.js +1 -1
- package/dist/actions/connection_closer.d.ts +68 -0
- package/dist/actions/connection_closer.d.ts.map +1 -0
- package/dist/actions/connection_closer.js +41 -0
- package/dist/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +1 -0
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +23 -2
- package/dist/actions/register_ws_endpoint.d.ts +11 -9
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +5 -5
- package/dist/actions/transports_ws_auth_guard.d.ts +24 -8
- package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
- package/dist/actions/transports_ws_auth_guard.js +23 -7
- package/dist/actions/ws_endpoint_spec.d.ts +119 -0
- package/dist/actions/ws_endpoint_spec.d.ts.map +1 -0
- package/dist/actions/ws_endpoint_spec.js +13 -0
- package/dist/auth/CLAUDE.md +124 -39
- package/dist/auth/account_action_specs.d.ts +7 -1
- package/dist/auth/account_action_specs.d.ts.map +1 -1
- package/dist/auth/account_action_specs.js +11 -4
- package/dist/auth/account_actions.d.ts +13 -0
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +40 -5
- package/dist/auth/account_routes.d.ts +12 -2
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +63 -12
- package/dist/auth/account_schema.d.ts +5 -5
- package/dist/auth/account_schema.js +2 -2
- package/dist/auth/actor_lookup_actions.d.ts +1 -1
- package/dist/auth/actor_lookup_actions.js +1 -1
- package/dist/auth/actor_lookup_queries.d.ts +1 -1
- package/dist/auth/actor_lookup_queries.js +1 -1
- package/dist/auth/actor_search_action_specs.d.ts +1 -1
- package/dist/auth/actor_search_action_specs.js +1 -1
- package/dist/auth/actor_search_actions.d.ts +1 -1
- package/dist/auth/actor_search_actions.js +1 -1
- package/dist/auth/actor_search_queries.d.ts +1 -1
- package/dist/auth/actor_search_queries.js +1 -1
- package/dist/auth/admin_action_specs.d.ts +8 -8
- package/dist/auth/admin_actions.d.ts +11 -0
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +25 -0
- package/dist/auth/all_action_spec_registries.d.ts +2 -2
- package/dist/auth/all_action_spec_registries.js +2 -2
- package/dist/auth/audit_emitter.d.ts +56 -12
- package/dist/auth/audit_emitter.d.ts.map +1 -1
- package/dist/auth/audit_emitter.js +38 -12
- package/dist/auth/audit_log_routes.d.ts +1 -1
- package/dist/auth/audit_log_routes.js +1 -1
- package/dist/auth/audit_log_schema.d.ts +30 -3
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +21 -3
- package/dist/auth/bootstrap_routes.d.ts +1 -1
- package/dist/auth/invite_schema.d.ts +2 -2
- package/dist/auth/request_context.d.ts +1 -1
- package/dist/auth/signup_routes.d.ts +1 -1
- package/dist/auth/standard_rpc_actions.d.ts +1 -0
- package/dist/auth/standard_rpc_actions.d.ts.map +1 -1
- package/dist/auth/standard_rpc_actions.js +1 -0
- package/dist/env/update_env_variable.js +1 -1
- package/dist/http/CLAUDE.md +42 -26
- package/dist/http/ip_canonical.d.ts +99 -0
- package/dist/http/ip_canonical.d.ts.map +1 -0
- package/dist/http/ip_canonical.js +191 -0
- package/dist/http/origin.d.ts +13 -5
- package/dist/http/origin.d.ts.map +1 -1
- package/dist/http/origin.js +13 -31
- package/dist/http/pending_effects.d.ts +1 -1
- package/dist/http/pending_effects.js +1 -1
- package/dist/http/proxy.d.ts +13 -5
- package/dist/http/proxy.d.ts.map +1 -1
- package/dist/http/proxy.js +15 -23
- package/dist/http/surface.d.ts +50 -0
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +27 -1
- package/dist/primitive_schemas.d.ts +20 -4
- package/dist/primitive_schemas.d.ts.map +1 -1
- package/dist/primitive_schemas.js +25 -4
- package/dist/realtime/sse_auth_guard.d.ts +16 -4
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/realtime/sse_auth_guard.js +15 -3
- package/dist/server/app_backend.d.ts +66 -19
- package/dist/server/app_backend.d.ts.map +1 -1
- package/dist/server/app_backend.js +57 -34
- package/dist/server/app_server.d.ts +60 -0
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +95 -2
- package/dist/server/startup.d.ts.map +1 -1
- package/dist/server/startup.js +12 -0
- package/dist/testing/CLAUDE.md +91 -71
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +4 -5
- package/dist/testing/adversarial_headers.d.ts +6 -0
- package/dist/testing/adversarial_headers.d.ts.map +1 -1
- package/dist/testing/adversarial_headers.js +13 -5
- package/dist/testing/app_server.d.ts +33 -32
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +4 -13
- package/dist/testing/attack_surface.d.ts +8 -7
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/attack_surface.js +12 -8
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +20 -6
- package/dist/testing/audit_drift_guard.d.ts +116 -0
- package/dist/testing/audit_drift_guard.d.ts.map +1 -0
- package/dist/testing/audit_drift_guard.js +134 -0
- package/dist/testing/connection_closer_helpers.d.ts +44 -0
- package/dist/testing/connection_closer_helpers.d.ts.map +1 -0
- package/dist/testing/connection_closer_helpers.js +48 -0
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +7 -9
- package/dist/testing/rate_limiting.js +4 -4
- package/dist/testing/rpc_helpers.d.ts +2 -1
- package/dist/testing/rpc_helpers.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.js +6 -8
- package/dist/testing/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/sse_round_trip.js +12 -6
- package/dist/testing/stubs.d.ts +11 -0
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +4 -0
- package/dist/testing/surface_invariants.d.ts +66 -1
- package/dist/testing/surface_invariants.d.ts.map +1 -1
- package/dist/testing/surface_invariants.js +103 -1
- package/dist/ui/CLAUDE.md +13 -18
- package/dist/ui/SurfaceExplorer.svelte +161 -2
- package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
- package/dist/ui/keyed_async_slot.svelte.d.ts +1 -1
- package/dist/ui/keyed_async_slot.svelte.js +1 -1
- package/package.json +1 -1
package/dist/actions/CLAUDE.md
CHANGED
|
@@ -12,12 +12,13 @@ server-authoritative dispatch, role-grant-offer UI integration) see
|
|
|
12
12
|
../../docs/usage.md §Deriving Route/Event Specs, §Single JSON-RPC 2.0 Endpoint,
|
|
13
13
|
§WebSocket Endpoint. For DEV-only output validation semantics see
|
|
14
14
|
../../docs/architecture.md §DEV-only Output Validation. For the SAES
|
|
15
|
-
binding matrix and middleware ordering see the root
|
|
15
|
+
binding matrix and middleware ordering see the root ../../CLAUDE.md
|
|
16
16
|
§Action Spec System (SAES) and §Middleware Ordering.
|
|
17
17
|
|
|
18
18
|
IMPORTANT: Every exported Zod schema is paired with a same-named `z.infer`
|
|
19
|
-
type export
|
|
20
|
-
|
|
19
|
+
type export — the convention callers rely on for type imports. When adding
|
|
20
|
+
new schemas, keep the pair invariant (ecosystem-wide rule; see
|
|
21
|
+
Skill(fuz-stack) zod-schemas).
|
|
21
22
|
|
|
22
23
|
NOTE: `ActionRegistry` keeps a few pre-built getters (auth filters,
|
|
23
24
|
initiator-direction filters) that codegen doesn't consume today — kept
|
|
@@ -53,10 +54,10 @@ declarative metadata for consumers (codegen, UI form-state matching, docs)
|
|
|
53
54
|
to read off the spec instead of scanning handler code. No runtime
|
|
54
55
|
enforcement — drift between declared reasons and what handlers actually
|
|
55
56
|
throw is caught per-module by source-scanning unit tests (see
|
|
56
|
-
|
|
57
|
+
../../test/auth/role*grant_offer_actions.error_reasons.test.ts). Reuses
|
|
57
58
|
the same `as const` string constants the handler throws (e.g.
|
|
58
|
-
`
|
|
59
|
-
`ERROR_ROLE_GRANT_NOT_FOUND`
|
|
59
|
+
`ERROR_ROLE_GRANT_OFFER*\*`from`auth/role_grant_offer_action_specs.ts`,
|
|
60
|
+
`ERROR_ROLE_GRANT_NOT_FOUND`from`http/error_schemas.ts`) so call
|
|
60
61
|
sites can import either side. Standard transport errors (validation,
|
|
61
62
|
auth, rate-limit) stay implicit.
|
|
62
63
|
|
|
@@ -119,7 +120,7 @@ The remaining asymmetry today is runtime: there is no
|
|
|
119
120
|
`Promise<Result<{value}, {error}>>` shape `FrontendActionsApi` methods
|
|
120
121
|
return. Closing those gaps is on the deferred follow-up set in the
|
|
121
122
|
[SAES RPC closeout](https://github.com/ryanatkn/grimoire/blob/main/quests/HISTORY.md#saes-rpc-direction-2026-04)
|
|
122
|
-
(grimoire `lore/fuz_app/TODO.md` §
|
|
123
|
+
(grimoire `lore/fuz_app/TODO.md` §Future Directions tracks the symmetric
|
|
123
124
|
backend signature, backend RPC client, and local-call symmetry items) —
|
|
124
125
|
wait for a second backend runtime case.
|
|
125
126
|
|
|
@@ -217,7 +218,7 @@ and `FrontendActionHandlers`.
|
|
|
217
218
|
- `generate_action_event_datas(specs, imports, {same_file?, collections_path?, include_protocol_actions?})` — `ActionEventDatas` interface; per-spec variant by kind (`ActionEventRequestResponseData` / `ActionEventRemoteNotificationData` / `ActionEventLocalCallData`). `same_file` (default `true`) is the file-layout switch: when `true`, assumes `ActionInputs` / `ActionOutputs` are in the same module and adds no import (the zzz pattern); when `false`, adds the type imports from `collections_path` (default `'./action_collections.js'`). `collections_path` alone is a no-op — the surprising omit-vs-default behavior of earlier versions has been replaced.
|
|
218
219
|
- `generate_frontend_actions_api(specs, imports, {interface_name?, method_filter?, collections_path?, sync_returns_value?, include_protocol_actions?})` — emits the typed `FrontendActionsApi` interface (configurable via `interface_name`, default `'FrontendActionsApi'`). One method signature per spec via `generate_actions_api_method_signature`. Protocol actions filtered by default; `method_filter: (spec) => boolean` runs after the protocol-action filter. Renamed from `generate_actions_api` in API review III to make the side-of-the-wire intent visible at every consumer site.
|
|
219
220
|
- `generate_frontend_action_handlers(specs, imports, {collections_path?, include_protocol_actions?})` — `FrontendActionHandlers` interface (Tier 2 only — wraps `generate_phase_handlers` with `action_event_type: 'TypedActionEvent'`). Pair with `generate_typed_action_event_alias`.
|
|
220
|
-
- `generate_backend_actions_api(specs, imports, {interface_name?, spec_array_name?, specs_module?, collections_path?, qualify_spec?, include_protocol_actions?})` — `BackendActionsApi` interface AND `broadcast_action_specs: ReadonlyArray<ActionSpecUnion>` array (both names configurable). Filter: `kind === 'remote_notification' && initiator !== 'frontend'`, with `streams`-target methods (request-scoped progress notifications invoked via `ctx.notify`) excluded — the discriminator is `ActionSpec.streams`, not a manual list. Adds `ActionInputs` (from `collections_path`) + `ActionSpecUnion`, plus `* as specs` from `specs_module` unless `qualify_spec` is set. Method shape today is `(input) => Promise<void>` (matches `create_broadcast_api`'s fire-and-forget runtime); generalizing to per-kind shapes via `generate_actions_api_method_signature` is deferred until a second backend runtime constructor lands (tracked in grimoire `lore/fuz_app/TODO.md` §
|
|
221
|
+
- `generate_backend_actions_api(specs, imports, {interface_name?, spec_array_name?, specs_module?, collections_path?, qualify_spec?, include_protocol_actions?})` — `BackendActionsApi` interface AND `broadcast_action_specs: ReadonlyArray<ActionSpecUnion>` array (both names configurable). Filter: `kind === 'remote_notification' && initiator !== 'frontend'`, with `streams`-target methods (request-scoped progress notifications invoked via `ctx.notify`) excluded — the discriminator is `ActionSpec.streams`, not a manual list. Adds `ActionInputs` (from `collections_path`) + `ActionSpecUnion`, plus `* as specs` from `specs_module` unless `qualify_spec` is set. Method shape today is `(input) => Promise<void>` (matches `create_broadcast_api`'s fire-and-forget runtime); generalizing to per-kind shapes via `generate_actions_api_method_signature` is deferred until a second backend runtime constructor lands (tracked in grimoire `lore/fuz_app/TODO.md` §Future Directions, _Symmetric backend signature shape_).
|
|
221
222
|
- `generate_backend_action_handlers_map(imports, options?)` — emits the `BackendActionHandlers` mapped type (`{[K in BackendRequestResponseMethod]: (input: ActionInputs[K], ctx: BackendHandlerContext) => ActionOutputs[K] | Promise<ActionOutputs[K]>}`). Replaces the hand-maintained `Exclude<>` + parallel mapped-type pattern (zzz had this at `zzz/src/lib/server/zzz_action_handlers.ts:42-66`). Configurable type name, method enum name, and context type name; configurable `collections_path` / `metatypes_path` for the type imports.
|
|
222
223
|
|
|
223
224
|
### Wrapper + multi-source helper
|
|
@@ -319,6 +320,7 @@ interface ActionContext {
|
|
|
319
320
|
pending_effects: Array<Promise<void>>; // eager pool writes already in flight — see http/CLAUDE.md §Pending Effects
|
|
320
321
|
post_commit_effects: Array<() => void | Promise<void>>; // deferred — push via `emit_after_commit`
|
|
321
322
|
client_ip: string;
|
|
323
|
+
credential_type: CredentialType | null; // session | api_token | daemon_token (or null for anonymous) — same value the credential_types gate consumed
|
|
322
324
|
log: Logger;
|
|
323
325
|
notify: (method, params) => void; // HTTP: DEV-mode warn + drop (no streaming channel); WS: socket-scoped
|
|
324
326
|
signal: AbortSignal; // HTTP: client-disconnect; WS: AbortSignal.any([socket_close, request_cancel])
|
|
@@ -458,7 +460,7 @@ Fan-out:
|
|
|
458
460
|
|
|
459
461
|
- `send(notification)` — broadcasts to every connection (current `send(request)` returns an internal_error "not yet implemented" — backend cannot initiate request-response).
|
|
460
462
|
- `broadcast_filtered(message, predicate)` — per-connection predicate over `ConnectionIdentity`; skips non-matching. Returns count.
|
|
461
|
-
- `send_to_account(account_id, message)` — targeted wrapper over `broadcast_filtered`. Mirrors `close_sockets_for_account` on the send side (every connection for the account). Structurally satisfies the `NotificationSender` interface from `auth/role_grant_offer_notifications.ts` (see
|
|
463
|
+
- `send_to_account(account_id, message)` — targeted wrapper over `broadcast_filtered`. Mirrors `close_sockets_for_account` on the send side (every connection for the account). Structurally satisfies the `NotificationSender` interface from `auth/role_grant_offer_notifications.ts` (see `auth/CLAUDE.md` §WS notifications).
|
|
462
464
|
- `get_connection_count()` — telemetry counter over the connection map.
|
|
463
465
|
|
|
464
466
|
Return values are bookkeeping, not delivery receipts — `0` means no live
|
|
@@ -467,9 +469,18 @@ persistence + rehydration by the consumer.
|
|
|
467
469
|
|
|
468
470
|
## WS auth guard (`transports_ws_auth_guard.ts`)
|
|
469
471
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
472
|
+
Closes WS sockets on audit revoke events — per-message dispatch doesn't
|
|
473
|
+
re-check session/token validity, so this guard is the revocation seam
|
|
474
|
+
for open connections. Module TSDoc carries the full rationale.
|
|
475
|
+
|
|
476
|
+
`create_ws_auth_guard(transport, log)` returns an `on_audit_event` callback.
|
|
477
|
+
For standard WS endpoints mounted via `AppServerOptions.ws_endpoints`,
|
|
478
|
+
`create_app_server` composes this guard onto
|
|
479
|
+
`backend.deps.audit.on_event_chain` automatically (per
|
|
480
|
+
`WsEndpointSpec.auth_guard`). For custom wiring, append it inside the
|
|
481
|
+
consumer's `audit_factory` body (or via `audit.on_event_chain.push(...)`
|
|
482
|
+
post-assembly). Mirrors the SSE guard in `realtime/sse_auth_guard.ts`
|
|
483
|
+
but targets the WS transport.
|
|
473
484
|
|
|
474
485
|
`ws_disconnect_event_types` (ReadonlySet): `session_revoke`,
|
|
475
486
|
`token_revoke`, `session_revoke_all`, `token_revoke_all`, `password_change`.
|
|
@@ -508,26 +519,123 @@ Same `outcome === 'failure'` guard as `create_ws_auth_guard`. Closes via
|
|
|
508
519
|
`close_sockets_for_account(event.account_id)` — `logout` is always
|
|
509
520
|
self-service, so there is no `target_account_id` to fall back on.
|
|
510
521
|
|
|
511
|
-
##
|
|
522
|
+
## Connection closer (`connection_closer.ts`)
|
|
523
|
+
|
|
524
|
+
Narrow structural capability for handler-side eager WS socket closure
|
|
525
|
+
on revocation — the belt+suspenders layer that complements the audit-
|
|
526
|
+
listener guards above.
|
|
512
527
|
|
|
513
|
-
|
|
528
|
+
```ts
|
|
529
|
+
interface ConnectionCloser {
|
|
530
|
+
close_sockets_for_session: (session_token_hash: string) => number;
|
|
531
|
+
close_sockets_for_token: (api_token_id: string) => number;
|
|
532
|
+
close_sockets_for_account: (account_id: string) => number;
|
|
533
|
+
}
|
|
534
|
+
```
|
|
514
535
|
|
|
515
|
-
|
|
536
|
+
`BackendWebsocketTransport` satisfies this structurally — consumers
|
|
537
|
+
pass the transport instance directly (same shape as
|
|
538
|
+
`NotificationSender`). Wired into `AccountRouteOptions.connection_closer`
|
|
539
|
+
(logout / password), `AccountActionOptions.connection_closer`
|
|
540
|
+
(`account_session_revoke` / `_revoke_all` / `account_token_revoke`),
|
|
541
|
+
and `AdminActionOptions.connection_closer`
|
|
542
|
+
(`admin_session_revoke_all` / `admin_token_revoke_all`). Each handler
|
|
543
|
+
calls the appropriate `close_sockets_for_*` method synchronously
|
|
544
|
+
BEFORE the audit emit so revocation lands even on audit INSERT
|
|
545
|
+
failure. Listener-based close
|
|
546
|
+
(`create_ws_auth_guard` / `create_ws_logout_closer`) stays as a
|
|
547
|
+
fail-safe for out-of-band emit sites; `close_sockets_for_*` is
|
|
548
|
+
idempotent. Failure outcomes (`revoked: false`, 404 not-found) skip
|
|
549
|
+
the eager close — matches the listener's `outcome === 'failure'`
|
|
550
|
+
guard so attacker-guessable ids cannot be used to target arbitrary
|
|
551
|
+
sockets. Mirrors `zzz_server`'s handler-side `close_sockets_for_*`
|
|
552
|
+
calls (landed 2026-05-16; see
|
|
553
|
+
`~/dev/grimoire/lore/fuz_app/TODO_AUTH.md` § Audit-driven WS
|
|
554
|
+
revocation: handler-side belt+suspenders).
|
|
555
|
+
|
|
556
|
+
## WebSocket dispatch
|
|
557
|
+
|
|
558
|
+
Three layered entry points, in decreasing abstraction:
|
|
559
|
+
|
|
560
|
+
### `create_app_server.ws_endpoints` (`server/app_server.ts`) — canonical
|
|
561
|
+
|
|
562
|
+
The top-level mount surface — mirror of `rpc_endpoints` for WebSocket
|
|
563
|
+
endpoints. Accepts either an array of `WsEndpointSpec` or a factory
|
|
564
|
+
`(ctx: AppServerContext) => ReadonlyArray<WsEndpointSpec>`; the factory
|
|
565
|
+
form runs after the server context is assembled so action lists can
|
|
566
|
+
depend on `ctx.deps` / `ctx.action_*_rate_limiter`. Each entry is
|
|
567
|
+
auto-mounted via `register_ws_endpoint` against the assembled Hono app,
|
|
568
|
+
so consumers no longer call `register_ws_endpoint` themselves in their
|
|
569
|
+
server-assembly module.
|
|
570
|
+
|
|
571
|
+
`upgradeWebSocket` (the Hono adapter helper) is supplied once at the
|
|
572
|
+
top level — `create_app_server` throws when `ws_endpoints` resolves
|
|
573
|
+
non-empty but `upgradeWebSocket` is missing. A factory returning `[]`
|
|
574
|
+
does NOT trip the check, so feature-flag gated WS surfaces stay safe.
|
|
575
|
+
|
|
576
|
+
Per-endpoint `WsEndpointSpec` fields:
|
|
577
|
+
|
|
578
|
+
- `path` — Hono mount path
|
|
579
|
+
- `allowed_origins` — origin allowlist regexes (parsed via `parse_allowed_origins`)
|
|
580
|
+
- `actions` — the `ReadonlyArray<Action>` to dispatch (spread `protocol_actions` first)
|
|
581
|
+
- `required_roles?: ReadonlyArray<RoleName>` — any-of upgrade-time role gate; omit or `[]` to skip
|
|
582
|
+
- `transport?: BackendWebsocketTransport` — supplied or auto-created; returned on `AppServer.ws_endpoints[path]` either way
|
|
583
|
+
- `heartbeat?`, `artificial_delay?`, `on_socket_open?`, `on_socket_close?` — passed through to `register_ws_endpoint`
|
|
584
|
+
- `auth_guard?: boolean` — default `true`; auto-composes `create_ws_auth_guard` + `create_ws_logout_closer` against the endpoint's transport and appends them to `deps.audit.on_event_chain`. The wiring is deduped by **reference identity** (`WeakSet<BackendWebsocketTransport>`), so two `WsEndpointSpec`s sharing one `BackendWebsocketTransport` instance still get a single pair of listeners. Wrapped / DI-proxied transports dedupe as separate entries — set `auth_guard: false` on duplicates and compose `create_ws_auth_guard` / `create_ws_logout_closer` against the underlying transport once
|
|
585
|
+
- `extra_audit_handlers?: ReadonlyArray<AuditEventHandler>` — appended after the standard guards; consumer-owned, never deduped
|
|
586
|
+
|
|
587
|
+
`auth_guard: true` does NOT close sockets on `role_grant_revoke`
|
|
588
|
+
(deliberate — per-connection role tracking is out of scope). Consumers
|
|
589
|
+
that need role-revoke disconnection wire it via `extra_audit_handlers`.
|
|
590
|
+
|
|
591
|
+
The mounted transport is reachable at `app_server.ws_endpoints[path]` —
|
|
592
|
+
a `Readonly<Record<string, BackendWebsocketTransport>>` keyed by mount
|
|
593
|
+
path. Use this handle for fan-out (`send_to_account`) and broadcast.
|
|
594
|
+
|
|
595
|
+
Duplicate paths across `WsEndpointSpec`s throw at mount time
|
|
596
|
+
(`'create_app_server: duplicate ws_endpoints path: <path>'`), closing
|
|
597
|
+
the route-shadow hole Hono's silent `app.get` overwriting would leave.
|
|
598
|
+
Cross-surface collisions are also detected — registering `GET <path>`
|
|
599
|
+
as both a `RouteSpec` and a `WsEndpointSpec` throws
|
|
600
|
+
`'create_app_server: ws_endpoints path collides with a GET RouteSpec: <path>'`
|
|
601
|
+
at mount time. Exact-string match only; pattern overlap (e.g. a
|
|
602
|
+
`RouteSpec` at `GET /api/:resource` vs `WsEndpointSpec` at `/api/ws`)
|
|
603
|
+
is not detected — Hono's specific-before-wildcard routing keeps those
|
|
604
|
+
working, but if you need certainty avoid the overlap.
|
|
605
|
+
|
|
606
|
+
`auth_guard` semantics when multiple specs share a transport: **any**
|
|
607
|
+
spec with `auth_guard !== false` wires the guard for that transport
|
|
608
|
+
(OR-semantics, order-independent). To opt out for a shared transport,
|
|
609
|
+
every sibling spec must pass `auth_guard: false`. Documented on the
|
|
610
|
+
`WsEndpointSpec.auth_guard` TSDoc.
|
|
611
|
+
|
|
612
|
+
`AppSurfaceWsEndpoint.methods` surfaces `request_response` + `remote_notification`
|
|
613
|
+
specs only — `local_call` specs are filtered out because they don't
|
|
614
|
+
dispatch over WS (`compile_action_registry` routes only
|
|
615
|
+
`request_response` with a handler into `action_map`; `local_call` is
|
|
616
|
+
frontend-side registry metadata).
|
|
617
|
+
|
|
618
|
+
### `register_ws_endpoint` (`register_ws_endpoint.ts`) — middle tier
|
|
516
619
|
|
|
517
620
|
Composes the standard upgrade stack:
|
|
518
621
|
|
|
519
622
|
1. `verify_request_source(allowed_origins)`
|
|
520
623
|
2. `require_auth`
|
|
521
624
|
3. upgrade-time authorization phase — resolves the acting actor and seeds `REQUEST_CONTEXT_KEY` for the inner `register_action_ws`'s upgrade-time identity capture
|
|
522
|
-
4. optional `require_role(
|
|
625
|
+
4. optional `require_role(required_roles)` — any-of disjunction
|
|
523
626
|
5. delegates to `register_action_ws`
|
|
524
627
|
|
|
525
|
-
Extends `RegisterActionWsOptions` with `allowed_origins:
|
|
526
|
-
and optional `
|
|
527
|
-
`
|
|
528
|
-
each spec still applies at dispatch time via
|
|
628
|
+
Extends `RegisterActionWsOptions` with `allowed_origins: ReadonlyArray<RegExp>`
|
|
629
|
+
and optional `required_roles: ReadonlyArray<RoleName>`. Returns
|
|
630
|
+
`{transport}`. Note: `required_roles` is a **coarse upgrade-time gate**
|
|
631
|
+
— per-action `auth` in each spec still applies at dispatch time via
|
|
632
|
+
`perform_action`. Pass `[]` (or omit) to skip the role gate.
|
|
529
633
|
(`verify_request_source` and `require_auth` / `require_role` are from
|
|
530
|
-
|
|
634
|
+
`auth/`; see `auth/CLAUDE.md` §Middleware for their semantics.)
|
|
635
|
+
|
|
636
|
+
Most consumers reach for the higher-level `ws_endpoints` option above —
|
|
637
|
+
this is the entry test harnesses use when they need the upgrade stack
|
|
638
|
+
without `create_app_server`'s full assembly.
|
|
531
639
|
|
|
532
640
|
### `register_action_ws` (`register_action_ws.ts`) — lower-level
|
|
533
641
|
|
|
@@ -584,7 +692,7 @@ Per-message side-effect queues: `pending_effects: Array<Promise<void>>`
|
|
|
584
692
|
`flush_post_commit_effects`. Both flush in the same `try/finally` that
|
|
585
693
|
releases the request controller, so fire-and-forget audit / notification
|
|
586
694
|
effects pushed by the handler complete (or reject visibly) before the
|
|
587
|
-
next message dispatches. See
|
|
695
|
+
next message dispatches. See `http/CLAUDE.md` §Pending Effects.
|
|
588
696
|
|
|
589
697
|
Lifecycle hooks on `RegisterActionWsOptions`:
|
|
590
698
|
|
|
@@ -943,6 +1051,13 @@ HTTP transport is **not** registered. `local_call` specs in `specs`
|
|
|
943
1051
|
silently no-op because `lookup_action_handler` always returns
|
|
944
1052
|
`undefined`; this factory targets wire-dispatched actions.
|
|
945
1053
|
|
|
1054
|
+
`all_standard_action_specs` is transport-agnostic — when a consumer
|
|
1055
|
+
spreads `create_standard_rpc_actions(ctx.deps, opts)` into both
|
|
1056
|
+
`rpc_endpoints` AND `ws_endpoints` on `create_app_server`, every method
|
|
1057
|
+
is reachable on both transports and `transport_for_method` can route
|
|
1058
|
+
per-call (e.g. return `'frontend_websocket_rpc'` for `account_*` /
|
|
1059
|
+
`admin_*` methods to bind them to the live WS connection).
|
|
1060
|
+
|
|
946
1061
|
`transport_for_method` and `on_action_event` are pure pass-throughs to
|
|
947
1062
|
`create_rpc_client` — exposed so consumers needing per-method routing
|
|
948
1063
|
(zap-style WS-for-actions / HTTP-for-rest split) or per-dispatch event
|
|
@@ -950,9 +1065,9 @@ wiring (zzz-style reactive Cells observing `ActionEvent` lifecycle)
|
|
|
950
1065
|
don't have to drop down to manual `create_rpc_client` construction
|
|
951
1066
|
(which forfeits the bundled `api` / `api_result` pair).
|
|
952
1067
|
|
|
953
|
-
`all_standard_action_specs` (in
|
|
1068
|
+
`all_standard_action_specs` (in `auth/standard_action_specs.ts`) is
|
|
954
1069
|
the matching aggregate spec list mirroring `create_standard_rpc_actions`
|
|
955
|
-
on the backend — see
|
|
1070
|
+
on the backend — see `auth/CLAUDE.md` §`standard_rpc_actions.ts`.
|
|
956
1071
|
|
|
957
1072
|
## Broadcast API (`broadcast_api.ts`)
|
|
958
1073
|
|
|
@@ -17,6 +17,7 @@ import type { Uuid } from '@fuzdev/fuz_util/id.js';
|
|
|
17
17
|
import type { RequestResponseActionSpec } from './action_spec.js';
|
|
18
18
|
import { type RouteSpec } from '../http/route_spec.js';
|
|
19
19
|
import { type RequestActorContext, type RequestContext } from '../auth/request_context.js';
|
|
20
|
+
import { type CredentialType } from '../hono_context.js';
|
|
20
21
|
import type { Db } from '../db/db.js';
|
|
21
22
|
import { type JsonrpcRequestId } from '../http/jsonrpc.js';
|
|
22
23
|
import type { RateLimiter } from '../rate_limiter.js';
|
|
@@ -72,6 +73,15 @@ export interface ActionContext {
|
|
|
72
73
|
* `role_grant_offer_expire` cleanup sweep in `auth/cleanup.ts`).
|
|
73
74
|
*/
|
|
74
75
|
client_ip: string;
|
|
76
|
+
/**
|
|
77
|
+
* Credential channel the request arrived on (`'session'` | `'api_token'` |
|
|
78
|
+
* `'daemon_token'`), or `null` for anonymous requests. Same value the
|
|
79
|
+
* dispatcher's `credential_types` gate consumed at step 4 — exposed here
|
|
80
|
+
* so handlers can record it in audit metadata (defense in depth: the
|
|
81
|
+
* gate may be loosened or bypassed in a future refactor, but the audit
|
|
82
|
+
* row preserves what actually authenticated the request).
|
|
83
|
+
*/
|
|
84
|
+
credential_type: CredentialType | null;
|
|
75
85
|
/** Logger instance. */
|
|
76
86
|
log: Logger;
|
|
77
87
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action_rpc.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_rpc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AACpD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAEjD,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAoB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAExE,OAAO,EAEN,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"action_rpc.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_rpc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AACpD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAEjD,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAoB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAExE,OAAO,EAEN,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAIN,KAAK,cAAc,EACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAEpC,OAAO,EAGN,KAAK,gBAAgB,EAErB,MAAM,oBAAoB,CAAC;AAM5B,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAGpD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,aAAa;IAC7B,+DAA+D;IAC/D,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;IAC5B,iDAAiD;IACjD,UAAU,EAAE,gBAAgB,CAAC;IAC7B;;;;OAIG;IACH,aAAa,CAAC,EAAE,IAAI,CAAC;IACrB;;;;;OAKG;IACH,EAAE,EAAE,EAAE,CAAC;IACP;;;;;OAKG;IACH,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACtC;;;;;OAKG;IACH,mBAAmB,EAAE,KAAK,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACvD;;;;;;;OAOG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,eAAe,EAAE,cAAc,GAAG,IAAI,CAAC;IACvC,uBAAuB;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;;OAQG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD;;;;;OAKG;IACH,MAAM,EAAE,WAAW,CAAC;CACpB;AAED;;;;;GAKG;AACH,MAAM,MAAM,aAAa,CAAC,MAAM,GAAG,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,CACxD,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,aAAa,KACd,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC;IACrE,IAAI,EAAE,cAAc,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,CAAC,MAAM,GAAG,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,CAC5D,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,iBAAiB,KAClB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC;IACtE,IAAI,EAAE,mBAAmB,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,CAAC,MAAM,GAAG,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,CAC7D,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,kBAAkB,KACnB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,yBAAyB,CAAC;IAChC,OAAO,EAAE,aAAa,CAAC;CACvB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,cAAc,CAAC,KAAK,SAAS,yBAAyB,IAAI;IACrE,KAAK,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;CACtB,SAAS,CAAC,UAAU,CAAC,GACnB,kBAAkB,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,GACrE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,GAC9C,iBAAiB,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,GACpE,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AAErE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,eAAO,MAAM,UAAU,GAAI,KAAK,SAAS,yBAAyB,EACjE,MAAM,KAAK,EACX,SAAS,cAAc,CAAC,KAAK,CAAC,KAC5B,SAGD,CAAC;AAEH,yCAAyC;AACzC,MAAM,WAAW,wBAAwB;IACxC,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC1B,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5C;;;;;;;OAOG;IACH,2BAA2B,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CACjD;AAkBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,wBAAwB,KAAG,KAAK,CAAC,SAAS,CAoMtF,CAAC"}
|
|
@@ -16,7 +16,7 @@ import { DEV } from 'esm-env';
|
|
|
16
16
|
import {} from '../http/route_spec.js';
|
|
17
17
|
import { get_client_ip } from '../http/proxy.js';
|
|
18
18
|
import { get_request_context, } from '../auth/request_context.js';
|
|
19
|
-
import { ACCOUNT_ID_KEY, CREDENTIAL_TYPE_KEY, TEST_CONTEXT_PRESET_KEY } from '../hono_context.js';
|
|
19
|
+
import { ACCOUNT_ID_KEY, CREDENTIAL_TYPE_KEY, TEST_CONTEXT_PRESET_KEY, } from '../hono_context.js';
|
|
20
20
|
import { compile_action_registry } from './compile_action_registry.js';
|
|
21
21
|
import { JSONRPC_VERSION, JsonrpcRequest, } from '../http/jsonrpc.js';
|
|
22
22
|
import { jsonrpc_error_messages, jsonrpc_error_code_to_http_status, JSONRPC_ERROR_CODES, } from '../http/jsonrpc_errors.js';
|
|
@@ -42,7 +42,7 @@ export declare const ActionSpec: z.ZodObject<{
|
|
|
42
42
|
* `null` for `remote_notification` and `local_call` — those don't
|
|
43
43
|
* dispatch through the request/response auth gate.
|
|
44
44
|
*
|
|
45
|
-
* See
|
|
45
|
+
* See `http/auth_shape.ts` for the design rationale (orthogonal
|
|
46
46
|
* authentication / account-resolution / actor-resolution / role-and-
|
|
47
47
|
* credential authorization axes).
|
|
48
48
|
*/
|
|
@@ -25,7 +25,7 @@ export const ActionSpec = z.strictObject({
|
|
|
25
25
|
* `null` for `remote_notification` and `local_call` — those don't
|
|
26
26
|
* dispatch through the request/response auth gate.
|
|
27
27
|
*
|
|
28
|
-
* See
|
|
28
|
+
* See `http/auth_shape.ts` for the design rationale (orthogonal
|
|
29
29
|
* authentication / account-resolution / actor-resolution / role-and-
|
|
30
30
|
* credential authorization axes).
|
|
31
31
|
*/
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Narrow structural capability for closing live WebSocket connections
|
|
3
|
+
* tied to a session token hash, API token id, or account id.
|
|
4
|
+
*
|
|
5
|
+
* **Why this exists.** Per-message authorization phase on WebSocket
|
|
6
|
+
* (`actions/perform_action.ts`) reloads role_grants from the DB on every
|
|
7
|
+
* message but does NOT re-query session / token validity — that
|
|
8
|
+
* trade-off keeps chatty connections fast. The cost: revocation
|
|
9
|
+
* doesn't actually disconnect open sockets unless something closes
|
|
10
|
+
* them. `transports_ws_auth_guard.ts` is the listener-based seam
|
|
11
|
+
* (audit-event → close), but it only fires after the audit INSERT
|
|
12
|
+
* succeeds — if the INSERT fails (DB error, pool exhausted, handler
|
|
13
|
+
* dies mid-flight) the listener never runs and the live socket keeps
|
|
14
|
+
* working with a stale `RequestContext` until disconnect.
|
|
15
|
+
*
|
|
16
|
+
* Used by self-service revocation handlers (`account_session_revoke` /
|
|
17
|
+
* `_revoke_all`, `account_token_revoke`, `logout`, `password`) and the
|
|
18
|
+
* admin revoke-all handlers (`admin_session_revoke_all`,
|
|
19
|
+
* `admin_token_revoke_all`) to eagerly drop affected sockets BEFORE
|
|
20
|
+
* emitting the corresponding audit event. The audit listener stays as
|
|
21
|
+
* a fail-safe for out-of-band emit sites (admin tools, scheduled
|
|
22
|
+
* jobs, SSE-driven flows). `close_sockets_for_*` is idempotent so the
|
|
23
|
+
* second pass is a no-op.
|
|
24
|
+
*
|
|
25
|
+
* Mirrors `zzz_server`'s `close_sockets_for_*` calls in
|
|
26
|
+
* `account.rs::logout_inner` / `_password_inner` /
|
|
27
|
+
* `handlers/account.rs::handle_account_session_revoke[_all]` /
|
|
28
|
+
* `_token_revoke` (landed 2026-05-16); see
|
|
29
|
+
* `~/dev/grimoire/lore/fuz_app/TODO_AUTH.md` §Audit-driven WS
|
|
30
|
+
* revocation: handler-side belt+suspenders for the cross-backend
|
|
31
|
+
* parity record.
|
|
32
|
+
*
|
|
33
|
+
* `BackendWebsocketTransport` satisfies this interface structurally,
|
|
34
|
+
* so consumers pass their transport instance directly (same shape as
|
|
35
|
+
* `NotificationSender`). The interface stays local so handlers don't
|
|
36
|
+
* couple to the concrete transport, and tests can inject a capturing
|
|
37
|
+
* stub with no WS machinery.
|
|
38
|
+
*
|
|
39
|
+
* @module
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* Narrow capability — three idempotent socket-close methods, each
|
|
43
|
+
* returning the number of sockets actually closed (zero when none
|
|
44
|
+
* matched). Callers typically ignore the return value (used by
|
|
45
|
+
* telemetry / tests).
|
|
46
|
+
*/
|
|
47
|
+
export interface ConnectionCloser {
|
|
48
|
+
/**
|
|
49
|
+
* Close every connection authenticated with a session whose blake3
|
|
50
|
+
* hash matches `session_token_hash`. Idempotent — calling on an
|
|
51
|
+
* already-closed session is a no-op.
|
|
52
|
+
*/
|
|
53
|
+
close_sockets_for_session: (session_token_hash: string) => number;
|
|
54
|
+
/**
|
|
55
|
+
* Close every connection authenticated with the given API token id.
|
|
56
|
+
* Idempotent — calling on an already-revoked token is a no-op.
|
|
57
|
+
*/
|
|
58
|
+
close_sockets_for_token: (api_token_id: string) => number;
|
|
59
|
+
/**
|
|
60
|
+
* Close every connection bound to `account_id`, regardless of
|
|
61
|
+
* credential type (session / api_token / daemon_token). Coarse
|
|
62
|
+
* closure used when every credential on an account is invalidated
|
|
63
|
+
* — password change, session-revoke-all, token-revoke-all, logout.
|
|
64
|
+
* Idempotent.
|
|
65
|
+
*/
|
|
66
|
+
close_sockets_for_account: (account_id: string) => number;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=connection_closer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection_closer.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/connection_closer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAChC;;;;OAIG;IACH,yBAAyB,EAAE,CAAC,kBAAkB,EAAE,MAAM,KAAK,MAAM,CAAC;IAClE;;;OAGG;IACH,uBAAuB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC;IAC1D;;;;;;OAMG;IACH,yBAAyB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;CAC1D"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Narrow structural capability for closing live WebSocket connections
|
|
3
|
+
* tied to a session token hash, API token id, or account id.
|
|
4
|
+
*
|
|
5
|
+
* **Why this exists.** Per-message authorization phase on WebSocket
|
|
6
|
+
* (`actions/perform_action.ts`) reloads role_grants from the DB on every
|
|
7
|
+
* message but does NOT re-query session / token validity — that
|
|
8
|
+
* trade-off keeps chatty connections fast. The cost: revocation
|
|
9
|
+
* doesn't actually disconnect open sockets unless something closes
|
|
10
|
+
* them. `transports_ws_auth_guard.ts` is the listener-based seam
|
|
11
|
+
* (audit-event → close), but it only fires after the audit INSERT
|
|
12
|
+
* succeeds — if the INSERT fails (DB error, pool exhausted, handler
|
|
13
|
+
* dies mid-flight) the listener never runs and the live socket keeps
|
|
14
|
+
* working with a stale `RequestContext` until disconnect.
|
|
15
|
+
*
|
|
16
|
+
* Used by self-service revocation handlers (`account_session_revoke` /
|
|
17
|
+
* `_revoke_all`, `account_token_revoke`, `logout`, `password`) and the
|
|
18
|
+
* admin revoke-all handlers (`admin_session_revoke_all`,
|
|
19
|
+
* `admin_token_revoke_all`) to eagerly drop affected sockets BEFORE
|
|
20
|
+
* emitting the corresponding audit event. The audit listener stays as
|
|
21
|
+
* a fail-safe for out-of-band emit sites (admin tools, scheduled
|
|
22
|
+
* jobs, SSE-driven flows). `close_sockets_for_*` is idempotent so the
|
|
23
|
+
* second pass is a no-op.
|
|
24
|
+
*
|
|
25
|
+
* Mirrors `zzz_server`'s `close_sockets_for_*` calls in
|
|
26
|
+
* `account.rs::logout_inner` / `_password_inner` /
|
|
27
|
+
* `handlers/account.rs::handle_account_session_revoke[_all]` /
|
|
28
|
+
* `_token_revoke` (landed 2026-05-16); see
|
|
29
|
+
* `~/dev/grimoire/lore/fuz_app/TODO_AUTH.md` §Audit-driven WS
|
|
30
|
+
* revocation: handler-side belt+suspenders for the cross-backend
|
|
31
|
+
* parity record.
|
|
32
|
+
*
|
|
33
|
+
* `BackendWebsocketTransport` satisfies this interface structurally,
|
|
34
|
+
* so consumers pass their transport instance directly (same shape as
|
|
35
|
+
* `NotificationSender`). The interface stays local so handlers don't
|
|
36
|
+
* couple to the concrete transport, and tests can inject a capturing
|
|
37
|
+
* stub with no WS machinery.
|
|
38
|
+
*
|
|
39
|
+
* @module
|
|
40
|
+
*/
|
|
41
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"perform_action.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/perform_action.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAGH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AACpD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAEjD,OAAO,EAGN,KAAK,cAAc,EACnB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAC,KAAK,cAAc,EAAC,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAEpC,OAAO,EAEN,KAAK,gBAAgB,EAErB,KAAK,kBAAkB,EACvB,MAAM,oBAAoB,CAAC;AAW5B,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAEpD,OAAO,KAAK,EAA+B,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAE7E;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IAClC,kEAAkE;IAClE,MAAM,EAAE,SAAS,CAAC;IAClB,mGAAmG;IACnG,UAAU,EAAE,OAAO,CAAC;IACpB,sDAAsD;IACtD,UAAU,EAAE,gBAAgB,CAAC;IAC7B,yDAAyD;IACzD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,uEAAuE;IACvE,eAAe,EAAE,cAAc,GAAG,IAAI,CAAC;IACvC,qEAAqE;IACrE,SAAS,EAAE,MAAM,CAAC;IAClB,oGAAoG;IACpG,MAAM,EAAE,WAAW,CAAC;IACpB,sFAAsF;IACtF,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,uDAAuD;IACvD,aAAa,CAAC,EAAE,IAAI,CAAC;IACrB;;;;OAIG;IACH,MAAM,CAAC,EAAE;QAAC,eAAe,EAAE,cAAc,GAAG,IAAI,CAAA;KAAC,CAAC;CAClD;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IACjC,gGAAgG;IAChG,EAAE,EAAE,EAAE,CAAC;IACP;;;OAGG;IACH,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACtC;;;OAGG;IACH,mBAAmB,EAAE,KAAK,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACvD,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,kEAAkE;IAClE,sBAAsB,EAAE,WAAW,GAAG,IAAI,CAAC;IAC3C,uEAAuE;IACvE,2BAA2B,EAAE,WAAW,GAAG,IAAI,CAAC;CAChD;AAED;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAC5B;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAC,GAC7B;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,kBAAkB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,CAAC;AAE9D;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAC1B,OAAO,kBAAkB,EACzB,MAAM,iBAAiB,KACrB,OAAO,CAAC,mBAAmB,
|
|
1
|
+
{"version":3,"file":"perform_action.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/perform_action.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAGH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AACpD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAEjD,OAAO,EAGN,KAAK,cAAc,EACnB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAC,KAAK,cAAc,EAAC,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAEpC,OAAO,EAEN,KAAK,gBAAgB,EAErB,KAAK,kBAAkB,EACvB,MAAM,oBAAoB,CAAC;AAW5B,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAEpD,OAAO,KAAK,EAA+B,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAE7E;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IAClC,kEAAkE;IAClE,MAAM,EAAE,SAAS,CAAC;IAClB,mGAAmG;IACnG,UAAU,EAAE,OAAO,CAAC;IACpB,sDAAsD;IACtD,UAAU,EAAE,gBAAgB,CAAC;IAC7B,yDAAyD;IACzD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,uEAAuE;IACvE,eAAe,EAAE,cAAc,GAAG,IAAI,CAAC;IACvC,qEAAqE;IACrE,SAAS,EAAE,MAAM,CAAC;IAClB,oGAAoG;IACpG,MAAM,EAAE,WAAW,CAAC;IACpB,sFAAsF;IACtF,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,uDAAuD;IACvD,aAAa,CAAC,EAAE,IAAI,CAAC;IACrB;;;;OAIG;IACH,MAAM,CAAC,EAAE;QAAC,eAAe,EAAE,cAAc,GAAG,IAAI,CAAA;KAAC,CAAC;CAClD;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IACjC,gGAAgG;IAChG,EAAE,EAAE,EAAE,CAAC;IACP;;;OAGG;IACH,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACtC;;;OAGG;IACH,mBAAmB,EAAE,KAAK,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACvD,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,kEAAkE;IAClE,sBAAsB,EAAE,WAAW,GAAG,IAAI,CAAC;IAC3C,uEAAuE;IACvE,2BAA2B,EAAE,WAAW,GAAG,IAAI,CAAC;CAChD;AAED;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAC5B;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAC,GAC7B;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,kBAAkB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,CAAC;AAE9D;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAC1B,OAAO,kBAAkB,EACzB,MAAM,iBAAiB,KACrB,OAAO,CAAC,mBAAmB,CAwJ7B,CAAC;AA4EF;;;GAGG;AACH,eAAO,MAAM,iCAAiC,GAC7C,IAAI,gBAAgB,EACpB,QAAQ,mBAAmB,KACzB;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,gBAAgB,CAAA;CAAC,GAAG,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAC,GAAG;IAAC,KAAK,EAAE,kBAAkB,CAAA;CAAC,CAK5F,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register_action_ws.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/register_action_ws.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,MAAM,CAAC;AAC/B,OAAO,KAAK,EAAC,gBAAgB,EAAE,SAAS,EAAC,MAAM,SAAS,CAAC;AAEzD,OAAO,EAAS,KAAK,MAAM,IAAI,UAAU,EAAC,MAAM,yBAAyB,CAAC;AAC1E,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAUjD,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAgBpD,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AACpC,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,mBAAmB,CAAC;AAI9C,OAAO,EAAC,yBAAyB,EAAE,KAAK,kBAAkB,EAAC,MAAM,4BAA4B,CAAC;AAG9F,YAAY,EAAC,MAAM,EAAC,CAAC;AAErB,0EAA0E;AAC1E,eAAO,MAAM,gCAAgC,QAAS,CAAC;AAEvD;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IACjC,qFAAqF;IACrF,EAAE,EAAE,SAAS,CAAC;IACd,4EAA4E;IAC5E,aAAa,EAAE,IAAI,CAAC;IACpB,oDAAoD;IACpD,QAAQ,EAAE,kBAAkB,CAAC;IAC7B;;;OAGG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,wFAAwF;IACxF,MAAM,EAAE,WAAW,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IAClC,+CAA+C;IAC/C,EAAE,EAAE,SAAS,CAAC;IACd,2CAA2C;IAC3C,aAAa,EAAE,IAAI,CAAC;IACpB,kGAAkG;IAClG,QAAQ,EAAE,kBAAkB,CAAC;CAC7B;AAED,MAAM,WAAW,sBAAsB;IACtC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wCAAwC;AACxC,MAAM,WAAW,uBAAuB;IACvC,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,GAAG,EAAE,IAAI,CAAC;IACV,iEAAiE;IACjE,gBAAgB,EAAE,gBAAgB,CAAC;IACnC;;;;;;;OAOG;IACH,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC/B;;;;;;;;;OASG;IACH,EAAE,EAAE,EAAE,CAAC;IACP;;;;OAIG;IACH,SAAS,CAAC,EAAE,yBAAyB,CAAC;IACtC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,sBAAsB,CAAC;IAC7C,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qDAAqD;IACrD,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE;;;;;OAKG;IACH,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,kBAAkB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5C;;;;;OAKG;IACH,2BAA2B,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CACjD;AAED,sCAAsC;AACtC,MAAM,WAAW,sBAAsB;IACtC,yEAAyE;IACzE,SAAS,EAAE,yBAAyB,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,uBAAuB,KAAG,
|
|
1
|
+
{"version":3,"file":"register_action_ws.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/register_action_ws.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,MAAM,CAAC;AAC/B,OAAO,KAAK,EAAC,gBAAgB,EAAE,SAAS,EAAC,MAAM,SAAS,CAAC;AAEzD,OAAO,EAAS,KAAK,MAAM,IAAI,UAAU,EAAC,MAAM,yBAAyB,CAAC;AAC1E,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAUjD,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAgBpD,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AACpC,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,mBAAmB,CAAC;AAI9C,OAAO,EAAC,yBAAyB,EAAE,KAAK,kBAAkB,EAAC,MAAM,4BAA4B,CAAC;AAG9F,YAAY,EAAC,MAAM,EAAC,CAAC;AAErB,0EAA0E;AAC1E,eAAO,MAAM,gCAAgC,QAAS,CAAC;AAEvD;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IACjC,qFAAqF;IACrF,EAAE,EAAE,SAAS,CAAC;IACd,4EAA4E;IAC5E,aAAa,EAAE,IAAI,CAAC;IACpB,oDAAoD;IACpD,QAAQ,EAAE,kBAAkB,CAAC;IAC7B;;;OAGG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,wFAAwF;IACxF,MAAM,EAAE,WAAW,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IAClC,+CAA+C;IAC/C,EAAE,EAAE,SAAS,CAAC;IACd,2CAA2C;IAC3C,aAAa,EAAE,IAAI,CAAC;IACpB,kGAAkG;IAClG,QAAQ,EAAE,kBAAkB,CAAC;CAC7B;AAED,MAAM,WAAW,sBAAsB;IACtC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wCAAwC;AACxC,MAAM,WAAW,uBAAuB;IACvC,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,GAAG,EAAE,IAAI,CAAC;IACV,iEAAiE;IACjE,gBAAgB,EAAE,gBAAgB,CAAC;IACnC;;;;;;;OAOG;IACH,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC/B;;;;;;;;;OASG;IACH,EAAE,EAAE,EAAE,CAAC;IACP;;;;OAIG;IACH,SAAS,CAAC,EAAE,yBAAyB,CAAC;IACtC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,sBAAsB,CAAC;IAC7C,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qDAAqD;IACrD,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE;;;;;OAKG;IACH,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,kBAAkB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5C;;;;;OAKG;IACH,2BAA2B,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CACjD;AAED,sCAAsC;AACtC,MAAM,WAAW,sBAAsB;IACtC,yEAAyE;IACzE,SAAS,EAAE,yBAAyB,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,uBAAuB,KAAG,sBA4VrE,CAAC"}
|
|
@@ -90,6 +90,14 @@ export const register_action_ws = (options) => {
|
|
|
90
90
|
// `credential_type` from this closure; the live request_context is
|
|
91
91
|
// only used by the test-preset escape hatch (perform_action runs
|
|
92
92
|
// the authorization phase fresh on every message in production).
|
|
93
|
+
//
|
|
94
|
+
// Per-message dispatch reloads role_grants via the authorization
|
|
95
|
+
// phase but does NOT re-query session / token validity — those
|
|
96
|
+
// are checked once at upgrade. Revocation enforcement therefore
|
|
97
|
+
// lives outside this dispatcher, in the audit-driven WS auth
|
|
98
|
+
// guard (`transports_ws_auth_guard.ts`). Without that guard wired
|
|
99
|
+
// into the audit chain, `session_revoke` / `token_revoke` are
|
|
100
|
+
// no-ops for existing WS connections.
|
|
93
101
|
const upgrade_context = require_request_context(c);
|
|
94
102
|
const account_id = upgrade_context.account.id;
|
|
95
103
|
const client_ip = get_client_ip(c);
|
|
@@ -263,8 +271,21 @@ export const register_action_ws = (options) => {
|
|
|
263
271
|
// eager fire-and-forget pool writes (audit emits, etc.);
|
|
264
272
|
// `post_commit_effects` collects deferred thunks pushed
|
|
265
273
|
// via `emit_after_commit` (WS notifications). Both flush
|
|
266
|
-
// in the
|
|
267
|
-
//
|
|
274
|
+
// in the `finally` so the next message sees a clean slate.
|
|
275
|
+
//
|
|
276
|
+
// Ordering invariant — reply-before-flush is load-bearing.
|
|
277
|
+
// Handlers that revoke their own credential
|
|
278
|
+
// (`session_revoke_all`, `token_revoke` of the calling
|
|
279
|
+
// bearer) audit-emit events whose listener chain — wired
|
|
280
|
+
// by the WS auth guard in `transports_ws_auth_guard.ts` —
|
|
281
|
+
// closes this socket when the audit row writes. The
|
|
282
|
+
// synchronous `ws.send` on the success path returns
|
|
283
|
+
// before any close can fire (the DB write that triggers
|
|
284
|
+
// the chain is async — even in production with
|
|
285
|
+
// `await_pending_effects: false`, the listener chain only
|
|
286
|
+
// runs after the row lands). Inverting the order —
|
|
287
|
+
// flushing the queues before the send — would silently
|
|
288
|
+
// strand the caller without a reply.
|
|
268
289
|
const pending_effects = [];
|
|
269
290
|
const post_commit_effects = [];
|
|
270
291
|
const notify = notify_socket(ws);
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* and build the `RequestContext` that per-message dispatch reads.
|
|
13
13
|
* Multi-actor accounts must supply `?acting` to pick a persona;
|
|
14
14
|
* single-actor accounts work without it.
|
|
15
|
-
* 4. Optional `require_role(
|
|
16
|
-
*
|
|
15
|
+
* 4. Optional `require_role(required_roles)` — for endpoints gated to a
|
|
16
|
+
* non-empty any-of set of roles.
|
|
17
17
|
*
|
|
18
18
|
* Then delegates to `register_action_ws` for per-message JSON-RPC
|
|
19
19
|
* dispatch.
|
|
@@ -29,15 +29,17 @@ export interface RegisterWsEndpointOptions extends RegisterActionWsOptions {
|
|
|
29
29
|
* env var via `parse_allowed_origins`. Passed straight to
|
|
30
30
|
* `verify_request_source`.
|
|
31
31
|
*/
|
|
32
|
-
allowed_origins:
|
|
32
|
+
allowed_origins: ReadonlyArray<RegExp>;
|
|
33
33
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
34
|
+
* Roles permitted to upgrade — any-of disjunction (matches the
|
|
35
|
+
* underlying `require_role` semantics). Omit (or pass `[]`) for any
|
|
36
|
+
* authenticated account (`require_auth` + actor resolution alone);
|
|
37
|
+
* set to e.g. `[ROLE_ADMIN]` to gate the endpoint behind a single role
|
|
38
|
+
* or `[ROLE_ADMIN, ROLE_KEEPER]` to permit either. The per-action
|
|
39
|
+
* `auth` in each spec still applies at dispatch time — this is a coarse
|
|
40
|
+
* upgrade-time gate.
|
|
39
41
|
*/
|
|
40
|
-
|
|
42
|
+
required_roles?: ReadonlyArray<RoleName>;
|
|
41
43
|
}
|
|
42
44
|
/**
|
|
43
45
|
* Mount a WebSocket endpoint with the standard upgrade stack (origin check
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register_ws_endpoint.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/register_ws_endpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAYH,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,wBAAwB,CAAC;AAGrD,OAAO,EAEN,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,MAAM,yBAAyB,CAAC;AAEjC,0CAA0C;AAC1C,MAAM,WAAW,yBAA0B,SAAQ,uBAAuB;IACzE;;;;OAIG;IACH,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"register_ws_endpoint.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/register_ws_endpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAYH,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,wBAAwB,CAAC;AAGrD,OAAO,EAEN,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,MAAM,yBAAyB,CAAC;AAEjC,0CAA0C;AAC1C,MAAM,WAAW,yBAA0B,SAAQ,uBAAuB;IACzE;;;;OAIG;IACH,eAAe,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACvC;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;CACzC;AAgDD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,GAChC,SAAS,yBAAyB,KAChC,sBAmBF,CAAC"}
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* and build the `RequestContext` that per-message dispatch reads.
|
|
13
13
|
* Multi-actor accounts must supply `?acting` to pick a persona;
|
|
14
14
|
* single-actor accounts work without it.
|
|
15
|
-
* 4. Optional `require_role(
|
|
16
|
-
*
|
|
15
|
+
* 4. Optional `require_role(required_roles)` — for endpoints gated to a
|
|
16
|
+
* non-empty any-of set of roles.
|
|
17
17
|
*
|
|
18
18
|
* Then delegates to `register_action_ws` for per-message JSON-RPC
|
|
19
19
|
* dispatch.
|
|
@@ -77,12 +77,12 @@ const create_ws_authorization_middleware = (db) => {
|
|
|
77
77
|
* then registers the `GET path` route via the inner `register_action_ws`
|
|
78
78
|
*/
|
|
79
79
|
export const register_ws_endpoint = (options) => {
|
|
80
|
-
const { app, path, allowed_origins, db,
|
|
80
|
+
const { app, path, allowed_origins, db, required_roles, log = new Logger('[ws]'), ...rest } = options;
|
|
81
81
|
app.use(path, verify_request_source(allowed_origins));
|
|
82
82
|
app.use(path, require_auth);
|
|
83
83
|
app.use(path, create_ws_authorization_middleware(db));
|
|
84
|
-
if (
|
|
85
|
-
app.use(path, require_role(
|
|
84
|
+
if (required_roles?.length) {
|
|
85
|
+
app.use(path, require_role(required_roles));
|
|
86
86
|
}
|
|
87
87
|
return register_action_ws({ app, path, db, log, ...rest });
|
|
88
88
|
};
|