@fuzdev/fuz_app 0.140.0 → 0.142.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 +72 -22
- 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 +44 -4
- package/dist/actions/connection_closer.d.ts.map +1 -1
- package/dist/actions/connection_closer.js +33 -3
- package/dist/actions/perform_action.d.ts +10 -0
- package/dist/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +23 -9
- 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 +10 -4
- 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 +44 -22
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +12 -1
- 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 +12 -0
- package/dist/auth/account_routes.d.ts +7 -2
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +240 -187
- package/dist/auth/admin_action_specs.d.ts +6 -1
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +6 -1
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +14 -2
- 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/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +85 -76
- package/dist/auth/cell_action_specs.d.ts +7 -3
- package/dist/auth/cell_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_action_specs.js +9 -5
- package/dist/auth/cell_actions.d.ts +3 -3
- package/dist/auth/cell_actions.d.ts.map +1 -1
- package/dist/auth/cell_actions.js +67 -38
- package/dist/auth/cell_authorize.d.ts +69 -8
- package/dist/auth/cell_authorize.d.ts.map +1 -1
- package/dist/auth/cell_authorize.js +89 -9
- package/dist/auth/cell_field_action_specs.d.ts +5 -4
- package/dist/auth/cell_field_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_field_action_specs.js +7 -7
- package/dist/auth/cell_field_actions.d.ts +7 -5
- package/dist/auth/cell_field_actions.d.ts.map +1 -1
- package/dist/auth/cell_field_actions.js +24 -22
- package/dist/auth/cell_grant_action_specs.d.ts +6 -0
- package/dist/auth/cell_grant_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_grant_action_specs.js +6 -0
- package/dist/auth/cell_grant_actions.d.ts +3 -1
- package/dist/auth/cell_grant_actions.d.ts.map +1 -1
- package/dist/auth/cell_grant_actions.js +16 -2
- package/dist/auth/cell_item_action_specs.d.ts +5 -4
- package/dist/auth/cell_item_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_item_action_specs.js +7 -7
- package/dist/auth/cell_item_actions.d.ts +6 -5
- package/dist/auth/cell_item_actions.d.ts.map +1 -1
- package/dist/auth/cell_item_actions.js +23 -22
- package/dist/auth/cell_relation_list_limit.d.ts +20 -0
- package/dist/auth/cell_relation_list_limit.d.ts.map +1 -0
- package/dist/auth/cell_relation_list_limit.js +27 -0
- package/dist/auth/cell_relation_visibility.d.ts +4 -3
- package/dist/auth/cell_relation_visibility.d.ts.map +1 -1
- package/dist/auth/cell_relation_visibility.js +6 -5
- package/dist/auth/cell_steward.d.ts +45 -0
- package/dist/auth/cell_steward.d.ts.map +1 -0
- package/dist/auth/cell_steward.js +53 -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 +8 -2
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +20 -4
- 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/role_grant_offer_action_specs.d.ts +5 -0
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_action_specs.js +6 -1
- package/dist/auth/role_grant_offer_actions.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_actions.js +23 -16
- package/dist/auth/role_grant_offer_queries.d.ts +8 -6
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_queries.js +16 -8
- 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/auth/signup_routes.d.ts +4 -3
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +157 -134
- package/dist/auth/token_scope.d.ts +7 -1
- package/dist/auth/token_scope.d.ts.map +1 -1
- package/dist/auth/token_scope.js +8 -2
- package/dist/db/CLAUDE.md +32 -19
- package/dist/db/cell_ddl.d.ts +1 -2
- package/dist/db/cell_ddl.d.ts.map +1 -1
- package/dist/db/cell_ddl.js +1 -2
- package/dist/db/cell_field_queries.d.ts +20 -18
- package/dist/db/cell_field_queries.d.ts.map +1 -1
- package/dist/db/cell_field_queries.js +28 -22
- package/dist/db/cell_item_queries.d.ts +20 -14
- package/dist/db/cell_item_queries.d.ts.map +1 -1
- package/dist/db/cell_item_queries.js +28 -18
- package/dist/db/cell_moderation.d.ts +67 -0
- package/dist/db/cell_moderation.d.ts.map +1 -0
- package/dist/db/cell_moderation.js +66 -0
- package/dist/db/cell_queries.d.ts +44 -14
- package/dist/db/cell_queries.d.ts.map +1 -1
- package/dist/db/cell_queries.js +68 -45
- package/dist/db/cell_view_sql.d.ts +102 -0
- package/dist/db/cell_view_sql.d.ts.map +1 -0
- package/dist/db/cell_view_sql.js +112 -0
- package/dist/db/db_pg.d.ts +1 -1
- package/dist/db/db_pg.js +1 -1
- 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/auth_shape.d.ts +5 -2
- package/dist/http/auth_shape.d.ts.map +1 -1
- package/dist/http/auth_shape.js +5 -2
- package/dist/http/ip_canonical.d.ts +30 -3
- package/dist/http/ip_canonical.d.ts.map +1 -1
- package/dist/http/ip_canonical.js +50 -3
- 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/primitive_schemas.d.ts +10 -1
- package/dist/primitive_schemas.d.ts.map +1 -1
- package/dist/primitive_schemas.js +11 -2
- package/dist/rate_limiter.d.ts +179 -12
- package/dist/rate_limiter.d.ts.map +1 -1
- package/dist/rate_limiter.js +352 -42
- 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 +8 -4
- package/dist/server/serve_fact_route.d.ts.map +1 -1
- package/dist/server/serve_fact_route.js +16 -6
- package/dist/testing/CLAUDE.md +164 -25
- 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/account_lifecycle.d.ts.map +1 -1
- package/dist/testing/cross_backend/account_lifecycle.js +37 -2
- 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/cell_gated_create.d.ts +38 -0
- package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_gated_create.js +401 -5
- package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_relations.js +145 -2
- 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/expected_schema.json +0 -1
- 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/login_security.d.ts +16 -0
- package/dist/testing/cross_backend/login_security.d.ts.map +1 -1
- package/dist/testing/cross_backend/login_security.js +154 -0
- 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/query_shape.d.ts.map +1 -1
- package/dist/testing/cross_backend/query_shape.js +24 -2
- 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/role_grant_participation.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_participation.js +64 -0
- 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 +20 -1
- package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
- package/dist/testing/cross_backend/spine_surface_constants.js +23 -1
- 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_action_rate_limit.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_action_rate_limit.js +21 -1
- 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/schema_introspect.js +1 -1
- package/dist/testing/schema_parity.d.ts +4 -3
- package/dist/testing/schema_parity.d.ts.map +1 -1
- package/dist/testing/schema_parity.js +4 -3
- package/dist/testing/schema_ready_fixture.d.ts +2 -4
- package/dist/testing/schema_ready_fixture.d.ts.map +1 -1
- package/dist/testing/schema_ready_fixture.js +2 -4
- 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/dist/ui/auth_state.svelte.d.ts +10 -0
- package/dist/ui/auth_state.svelte.d.ts.map +1 -1
- package/dist/ui/auth_state.svelte.js +22 -4
- package/package.json +1 -1
- package/dist/db/cell_history_ddl.d.ts +0 -38
- package/dist/db/cell_history_ddl.d.ts.map +0 -1
- package/dist/db/cell_history_ddl.js +0 -61
package/dist/actions/CLAUDE.md
CHANGED
|
@@ -358,11 +358,12 @@ and `allow_fallback: boolean` (default `true`). Explicit
|
|
|
358
358
|
|
|
359
359
|
### WS close codes (`actions/transports.ts`)
|
|
360
360
|
|
|
361
|
-
- `WS_CLOSE_SESSION_REVOKED = 4001` — server revoked auth (reason `WS_CLOSE_SESSION_REVOKED_REASON`, `Session revoked`); client enters permanent `revoked` state, no reconnect. Also the close for an upgrade refused at admission because its credential was revoked mid-upgrade — indistinguishable on the wire by design.
|
|
361
|
+
- `WS_CLOSE_SESSION_REVOKED = 4001` — server revoked auth (reason `WS_CLOSE_SESSION_REVOKED_REASON`, `Session revoked`); client enters permanent `revoked` state, no reconnect. Also the close for an upgrade refused at admission because its credential was revoked mid-upgrade — indistinguishable on the wire by design — and for a socket whose credential expired while open, with reason `WS_CLOSE_CREDENTIAL_EXPIRED_REASON` (`Credential expired`), which the client treats as the same `revoked` state.
|
|
362
362
|
- `WS_CLOSE_CLIENT_HEARTBEAT_TIMEOUT = 4002` — client observed receive-silence past its receive timeout (`resolve_heartbeat_receive_timeout`).
|
|
363
363
|
- `WS_CLOSE_SERVER_HEARTBEAT_TIMEOUT = 4003` — server observed receive-silence past `DEFAULT_SERVER_HEARTBEAT_TIMEOUT` (60s).
|
|
364
364
|
- `WS_CLOSE_CONNECTION_LIMIT = 4004` — server closed this socket to admit a newer one on the same account, past the per-account connection cap (reason `connection limit`). Not a revocation: the client stays `closed` with `superseded` set and no reconnect, until `connect()` is called. Twin of the Rust spine's constant.
|
|
365
365
|
- `WS_CLOSE_OUTBOUND_OVERFLOW = 4005` — the server had more waiting to be written to this socket than its outbound budget holds (reason `WS_CLOSE_OUTBOUND_OVERFLOW_REASON`, `outbound queue overflow`); what was waiting is dropped. Not a revocation, and nothing took the connection's place: the client reconnects under its ordinary backoff. Twin of the Rust spine's constant.
|
|
366
|
+
- `WS_CLOSE_PERMISSION_REVOKED = 4006` — the socket's account lost a role its endpoint's `required_roles` lists: a revoked global grant (reason `WS_CLOSE_PERMISSION_REVOKED_REASON`, `Permission revoked`), mid-life or at admission. Nothing is sent after it. Not a revocation of the credential: the client stays `closed` until `connect()` is called, without setting `revoked` or `superseded`, and rejects pending and queued requests `forbidden`. Twin of the Rust spine's constant.
|
|
366
367
|
- `WS_CLOSE_GOING_AWAY = 1001` — the server is shutting down (reason `WS_CLOSE_GOING_AWAY_REASON`, `Server shutting down`): `close_all_sockets`, which `AppServer.close` runs. Also the close for an upgrade the shutdown refuses — pending at the close-all, or born closed after it. Not a revocation: the client reconnects under its ordinary backoff.
|
|
367
368
|
- `WS_CLOSE_MESSAGE_TOO_BIG = 1009` — server received a message over its `max_message_bytes` cap (RFC 6455 "Message Too Big").
|
|
368
369
|
- `WS_CLOSE_POLICY_VIOLATION = 1008` — a socket sent too much while it was still opening (before admission and `on_socket_open` completed): past `MAX_PRE_ADMISSION_FRAMES` frames or the `PRE_ADMISSION_QUEUE_BYTES_FACTOR` byte budget.
|
|
@@ -409,8 +410,10 @@ client; `null` skips the client-side measurement.
|
|
|
409
410
|
One map keyed by `connection_id` (branded `Uuid`), in registration order:
|
|
410
411
|
`#connections: Map<Uuid, ConnectionEntry>`, each entry `{ws, identity,
|
|
411
412
|
admitted, abort_controller, writer}`. `identity` is the `ConnectionIdentity`
|
|
412
|
-
`{token_hash, account_id, api_token_id}` (session sets
|
|
413
|
-
sets `api_token_id`, daemon-token sets both null
|
|
413
|
+
`{token_hash, account_id, api_token_id, required_roles}` (session sets
|
|
414
|
+
`token_hash`, bearer sets `api_token_id`, daemon-token sets both null;
|
|
415
|
+
`required_roles` is what the endpoint gated the upgrade on, per registration,
|
|
416
|
+
since endpoints with different gates can share one transport).
|
|
414
417
|
|
|
415
418
|
The id is the connection's only handle — `register_pending` / `add_connection`
|
|
416
419
|
return it and `admit` / `remove_connection` / `is_registered` take it. There is
|
|
@@ -426,7 +429,7 @@ revocation closes nothing and the socket is then admitted on a dead credential.
|
|
|
426
429
|
The transport splits registration to close it (the twin of the Rust
|
|
427
430
|
`fuz_realtime` `ConnectionRegistry`, same method names):
|
|
428
431
|
|
|
429
|
-
- `register_pending(ws, token_hash, account_id, api_token_id?, abort_controller?)`
|
|
432
|
+
- `register_pending(ws, token_hash, account_id, api_token_id?, abort_controller?, required_roles?)`
|
|
430
433
|
inserts the entry **un-admitted**. Every `close_sockets_for_*` matches it
|
|
431
434
|
from that instant. Otherwise it is inert: no `send` broadcast,
|
|
432
435
|
`broadcast_filtered` (the predicate never sees it), `send_to_account`, or
|
|
@@ -434,6 +437,9 @@ The transport splits registration to close it (the twin of the Rust
|
|
|
434
437
|
`is_ready`, and `is_registered` don't see it
|
|
435
438
|
(`get_pending_connection_count` does); the cap neither counts nor evicts it.
|
|
436
439
|
- the caller re-reads the credential (`revalidate_resolved_auth`).
|
|
440
|
+
- `take_pending_close_code(connection_id)` — after a refused `admit`, the
|
|
441
|
+
close code the transport closed the pending entry with (4001, 4006, 1001),
|
|
442
|
+
read once; what `register_action_ws` logs the refusal by.
|
|
437
443
|
- `admit(connection_id)` returns `false` when the entry is gone — a revocation
|
|
438
444
|
closed it while pending — and evicts nothing; otherwise it enforces the cap
|
|
439
445
|
(counting admitted connections only) and marks the entry admitted.
|
|
@@ -555,6 +561,11 @@ each closed connection's controller):
|
|
|
555
561
|
- `close_sockets_for_token(api_token_id)`
|
|
556
562
|
- `close_sockets_for_account(account_id)` — coarse, covers session + bearer + daemon-token
|
|
557
563
|
|
|
564
|
+
`close_sockets_for_role(account_id, role)` closes the account's connections
|
|
565
|
+
whose `required_roles` include `role`, pending included, with
|
|
566
|
+
`WS_CLOSE_PERMISSION_REVOKED` instead — over-closing an any-of gate (the loss
|
|
567
|
+
of any listed role closes, whether or not the account still holds another).
|
|
568
|
+
|
|
558
569
|
`close_all_sockets()` closes every connection, pending and admitted, with
|
|
559
570
|
`WS_CLOSE_GOING_AWAY` instead — the shutdown close, removing and aborting each
|
|
560
571
|
the same way — and leaves the transport **closing** for good (`is_closing()`):
|
|
@@ -610,13 +621,17 @@ interface ConnectionCloser {
|
|
|
610
621
|
close_sockets_for_session: (session_token_hash: string) => number;
|
|
611
622
|
close_sockets_for_token: (api_token_id: string) => number;
|
|
612
623
|
close_sockets_for_account: (account_id: Uuid) => number;
|
|
624
|
+
close_sockets_for_role: (account_id: Uuid, role: string) => number; // a revoked global grant
|
|
613
625
|
close_all_sockets: () => number; // shutdown, never a revocation
|
|
614
626
|
}
|
|
615
627
|
```
|
|
616
628
|
|
|
617
629
|
`BackendWebsocketTransport` satisfies it structurally;
|
|
618
|
-
`create_sse_connection_closer(registry)` (`realtime/sse_auth_guard.ts`) adapts
|
|
619
|
-
a `SubscriberRegistry` (`close_all_sockets` is `SubscriberRegistry.close_all
|
|
630
|
+
`create_sse_connection_closer(registry, required_role)` (`realtime/sse_auth_guard.ts`) adapts
|
|
631
|
+
a `SubscriberRegistry` (`close_all_sockets` is `SubscriberRegistry.close_all`;
|
|
632
|
+
`close_sockets_for_role` closes the account's streams when `role` is the
|
|
633
|
+
registry's gating role). No handler closes for a role: a role revocation's
|
|
634
|
+
closes come from the audit listeners alone.
|
|
620
635
|
Closing is idempotent and synchronous. `close_all_sockets` is the one method no
|
|
621
636
|
revocation calls: `AppServer.close` runs it on `deps.connection_closer` to end
|
|
622
637
|
every live connection of the backend at shutdown, and it has no
|
|
@@ -626,15 +641,21 @@ closed) — so a closed transport or registry hosts nothing more. Its Rust
|
|
|
626
641
|
counterpart is `ShutdownCloser::close_all_sockets`, a supertrait of
|
|
627
642
|
`SocketRevoker`.
|
|
628
643
|
|
|
629
|
-
**Every revocation closes after its commit.** A handler
|
|
630
|
-
inline: it calls
|
|
644
|
+
**Every revocation closes after its commit.** A handler inside a route or
|
|
645
|
+
action transaction never calls a closer inline: it calls
|
|
646
|
+
`queue_connection_close(ctx, deps.connection_closer, target)`,
|
|
631
647
|
which puts the close on the request's post-commit queue (`emit_after_commit`).
|
|
632
648
|
The close runs once the handler's transaction has committed and is discarded
|
|
633
649
|
when the handler throws — a rolled-back revocation closes nothing. `target` is
|
|
634
650
|
a `ConnectionCloseTarget` (`{kind: 'session', session_token_hash}` /
|
|
635
|
-
`{kind: 'token', api_token_id}` / `{kind: 'account', account_id}`
|
|
636
|
-
`
|
|
637
|
-
|
|
651
|
+
`{kind: 'token', api_token_id}` / `{kind: 'account', account_id}` /
|
|
652
|
+
`{kind: 'role', account_id, role}`);
|
|
653
|
+
`close_connections(closer, target)` is the immediate form the queue runs. A
|
|
654
|
+
handler with no wrapping transaction (`/login`, `/password`) opens its own
|
|
655
|
+
around its writes and closes straight after that commit with
|
|
656
|
+
`close_connections_after_commit(closer, target, log)`, which logs a failing
|
|
657
|
+
close as the post-commit flush does. Twin of `queue_socket_close` /
|
|
658
|
+
`SocketCloseTarget`.
|
|
638
659
|
|
|
639
660
|
The order is load-bearing for admission: a connection registers, then re-reads
|
|
640
661
|
its credential, so a close that missed the registration must belong to a
|
|
@@ -702,8 +723,14 @@ guard reads too — one declaration, so the two can't drift:
|
|
|
702
723
|
- `account` (`session_revoke_all`, `token_revoke_all`, `password_change`,
|
|
703
724
|
`logout`, `account_delete`, `account_purge`) →
|
|
704
725
|
`close_sockets_for_account(target_account_id ?? account_id)`
|
|
705
|
-
- `role` (`role_grant_revoke`) →
|
|
706
|
-
|
|
726
|
+
- `role` (`role_grant_revoke`) → `close_sockets_for_role(target_account_id ??
|
|
727
|
+
account_id, metadata.role)` when the revoked grant was global
|
|
728
|
+
(`to_global_role_revocation`); a scoped revoke closes nothing. Per-message
|
|
729
|
+
dispatch re-authorizes each request against its own action, but nothing
|
|
730
|
+
re-checks what a socket admitted by a role gate receives — every push its
|
|
731
|
+
endpoint sends — so the socket closes, with `WS_CLOSE_PERMISSION_REVOKED`.
|
|
732
|
+
The SSE guard closes its streams on the same rows. No handler closes for a
|
|
733
|
+
role, so this guard is the one closer.
|
|
707
734
|
- `none` → nothing — every other event, and any event type fuz_app doesn't
|
|
708
735
|
define
|
|
709
736
|
|
|
@@ -774,9 +801,8 @@ Cross-surface collisions (same `GET <path>` on both `RouteSpec` and
|
|
|
774
801
|
`GET /api/:resource` vs `/api/ws`) is not detected — Hono's specific-before-wildcard routing keeps
|
|
775
802
|
those working but avoid the overlap.
|
|
776
803
|
|
|
777
|
-
`auth_guard
|
|
778
|
-
|
|
779
|
-
`extra_audit_handlers` when needed. When multiple specs share a transport,
|
|
804
|
+
`auth_guard` is also the one closer of a role revocation: with it off for a
|
|
805
|
+
transport, a revoked role closes none of its sockets. When multiple specs share a transport,
|
|
780
806
|
**any** spec with `auth_guard !== false` wires the guard for that
|
|
781
807
|
transport (OR-semantics).
|
|
782
808
|
|
|
@@ -791,7 +817,7 @@ Composes the standard upgrade stack:
|
|
|
791
817
|
1. `verify_request_source(allowed_origins)`
|
|
792
818
|
2. `require_auth`
|
|
793
819
|
3. `require_token_scope('surface:ws_upgrade')` — a narrowed token cannot hold a socket
|
|
794
|
-
4. Upgrade-time authorization phase — resolves the acting actor, seeds `REQUEST_CONTEXT_KEY` for the inner `register_action_ws`
|
|
820
|
+
4. Upgrade-time authorization phase — resolves the acting actor (`?acting=` through `parse_acting`; malformed reads as omitted), seeds `REQUEST_CONTEXT_KEY` for the inner `register_action_ws`
|
|
795
821
|
5. Optional `require_role(required_roles)` — any-of disjunction (coarse upgrade-time gate; per-action `auth` in each spec still applies at dispatch time)
|
|
796
822
|
6. Delegates to `register_action_ws`
|
|
797
823
|
|
|
@@ -829,7 +855,11 @@ audit stream shares, read off the keys the auth middleware set), then
|
|
|
829
855
|
meanwhile, closes the socket with `WS_CLOSE_SESSION_REVOKED` and the reason a
|
|
830
856
|
revoked socket gets; a re-check that throws closes it with
|
|
831
857
|
`WS_CLOSE_INTERNAL_ERROR` (`internal error`) — fail closed, never admitted
|
|
832
|
-
unchecked.
|
|
858
|
+
unchecked. An endpoint with `required_roles` re-reads them beside the
|
|
859
|
+
credential — the acting actor's grants afresh (`refresh_role_grants`), a global
|
|
860
|
+
grant of any listed role admitting, as the gate in front did — and refuses a
|
|
861
|
+
lost role with `WS_CLOSE_PERMISSION_REVOKED`; a failed role read is the same
|
|
862
|
+
1011. During shutdown a refusal is the shutdown's instead: an upgrade
|
|
833
863
|
pending at the close-all was closed by it with `WS_CLOSE_GOING_AWAY` and its
|
|
834
864
|
controller aborted, so the refusal after the re-read — even one the closing
|
|
835
865
|
database failed — writes nothing more; an upgrade that registers after it is
|
|
@@ -881,8 +911,9 @@ client withholds its close frame. So every server-side close removes the
|
|
|
881
911
|
connection from the transport and aborts the socket's controller **at close
|
|
882
912
|
time**: the transport does both for a revocation or a cap eviction, and
|
|
883
913
|
`register_action_ws`'s `end_socket` does both — through
|
|
884
|
-
`transport.close_connection` — for a heartbeat timeout,
|
|
885
|
-
oversized message, a failed `on_socket_open`, and a refused
|
|
914
|
+
`transport.close_connection` — for a heartbeat timeout, a credential
|
|
915
|
+
expiry, an oversized message, a failed `on_socket_open`, and a refused
|
|
916
|
+
admission.
|
|
886
917
|
`onMessage` then drops any frame whose socket has ended — no dispatch, no
|
|
887
918
|
error reply. A connection the transport no longer holds though nothing closed
|
|
888
919
|
its socket (a bare `transport.remove_connection`; `transport.is_registered` is
|
|
@@ -895,7 +926,10 @@ heartbeat activity.
|
|
|
895
926
|
**Per-message authorization phase.** `perform_action` calls
|
|
896
927
|
`apply_authorization_phase` per-message (HTTP and WS uniformly). Role grant
|
|
897
928
|
changes during a connection lifetime are picked up on the next message —
|
|
898
|
-
no in-place refresh
|
|
929
|
+
no in-place refresh. A revoked global grant of a role the endpoint's
|
|
930
|
+
`required_roles` lists closes the socket (`WS_CLOSE_PERMISSION_REVOKED`,
|
|
931
|
+
through `create_ws_auth_guard`), since nothing else re-checks what it
|
|
932
|
+
receives. Authentication
|
|
899
933
|
invalidation (a session or token revoke, `password_change`, a logout, an
|
|
900
934
|
account delete, a cap eviction) still closes the socket — the revoking
|
|
901
935
|
handler through `connection_closer`, `create_ws_auth_guard` again on the
|
|
@@ -953,6 +987,17 @@ Tick interval is `timeout / 2`, so event-loop blockage pauses the timer itself.
|
|
|
953
987
|
A timeout unregisters the connection and aborts its signal along with the
|
|
954
988
|
`WS_CLOSE_SERVER_HEARTBEAT_TIMEOUT` close.
|
|
955
989
|
|
|
990
|
+
**Credential deadline.** From admission, a timer closes the socket when the
|
|
991
|
+
credential it upgraded on expires (`ResolvedAuth.credential_deadline` — the
|
|
992
|
+
earlier of a session's cookie and row expiry, an API token's row; none for an
|
|
993
|
+
eternal token or the daemon token): `WS_CLOSE_SESSION_REVOKED` with
|
|
994
|
+
`WS_CLOSE_CREDENTIAL_EXPIRED_REASON`, through `end_socket`. Expiry writes
|
|
995
|
+
nothing a revocation listener hears, so the socket watches its own deadline.
|
|
996
|
+
The wait is `schedule_credential_deadline`'s chain of timers of at most an
|
|
997
|
+
hour (a 30-day session is past `setTimeout`'s ceiling), each also checking the
|
|
998
|
+
wall clock so a host suspend does not extend the deadline, cancelled when the
|
|
999
|
+
socket ends. Twin of the Rust loop's `WsLoopOptions::credential_deadline`.
|
|
1000
|
+
|
|
956
1001
|
Two abort signals composed via `AbortSignal.any`:
|
|
957
1002
|
|
|
958
1003
|
- `socket_abort_controller` — per-socket, fires when the socket ends: at the server's close (revocation, cap eviction, heartbeat timeout, …) or the client's. Registered with the transport, which aborts it on the closes it makes. Drives every handler's `ctx.signal`.
|
|
@@ -1049,11 +1094,16 @@ recent frame each way — every frame, including heartbeats and the responses
|
|
|
1049
1094
|
Reconnect policy (exponential backoff): `delay = DEFAULT_RECONNECT_DELAY * DEFAULT_BACKOFF_FACTOR ** (attempts-1)`,
|
|
1050
1095
|
capped at `DEFAULT_RECONNECT_DELAY_MAX`. Every other close reconnects —
|
|
1051
1096
|
`WS_CLOSE_OUTBOUND_OVERFLOW` included, the server's notice that this client
|
|
1052
|
-
fell behind and missed messages.
|
|
1097
|
+
fell behind and missed messages. Three close codes never reconnect:
|
|
1053
1098
|
|
|
1054
1099
|
- `WS_CLOSE_SESSION_REVOKED` is **terminal** — sets `revoked`, no reconnect
|
|
1055
1100
|
loop on 401, and `connect()` becomes a no-op. Pending and queued requests
|
|
1056
1101
|
reject `unauthenticated`.
|
|
1102
|
+
- `WS_CLOSE_PERMISSION_REVOKED` is **closed until the user acts** too — status
|
|
1103
|
+
`closed`, no reconnect scheduled (a reconnect would be refused at the role
|
|
1104
|
+
gate), neither `revoked` nor `superseded` set (the credential is still
|
|
1105
|
+
good), pending and queued requests reject `forbidden`. `last_close_code`
|
|
1106
|
+
says why; `connect()` reopens.
|
|
1057
1107
|
- `WS_CLOSE_CONNECTION_LIMIT` is **closed until the user acts** — sets
|
|
1058
1108
|
`superseded` (reactive), status `closed`, no reconnect scheduled: the server
|
|
1059
1109
|
closed this socket to admit a newer one on the same account, and
|
|
@@ -286,7 +286,11 @@ export interface CreateRpcEndpointOptions {
|
|
|
286
286
|
*
|
|
287
287
|
* The RouteSpecs use `auth: {type: 'none'}` because auth is checked per-action
|
|
288
288
|
* inside the dispatcher, and `transaction: false` because transaction scope
|
|
289
|
-
* is per-action (mutations get a transaction, reads get pool).
|
|
289
|
+
* is per-action (mutations get a transaction, reads get pool). They are
|
|
290
|
+
* marked `reads_credential`, since the dispatcher reads the resolved
|
|
291
|
+
* credential, so assembly refuses the pair mounted outside the auth
|
|
292
|
+
* middleware's scope (`assert_credential_routes_in_auth_scope`) whether it
|
|
293
|
+
* arrives as `rpc_endpoints` or through `create_route_specs`.
|
|
290
294
|
*
|
|
291
295
|
* @param options - endpoint path, actions, logger, and the two action rate limiters
|
|
292
296
|
* @returns route specs (GET + POST) ready for `apply_route_specs`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action_rpc.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_rpc.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"action_rpc.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_rpc.ts"],"names":[],"mappings":"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"}
|
|
@@ -114,7 +114,11 @@ const jsonrpc_error_envelope = (id, error) => ({
|
|
|
114
114
|
*
|
|
115
115
|
* The RouteSpecs use `auth: {type: 'none'}` because auth is checked per-action
|
|
116
116
|
* inside the dispatcher, and `transaction: false` because transaction scope
|
|
117
|
-
* is per-action (mutations get a transaction, reads get pool).
|
|
117
|
+
* is per-action (mutations get a transaction, reads get pool). They are
|
|
118
|
+
* marked `reads_credential`, since the dispatcher reads the resolved
|
|
119
|
+
* credential, so assembly refuses the pair mounted outside the auth
|
|
120
|
+
* middleware's scope (`assert_credential_routes_in_auth_scope`) whether it
|
|
121
|
+
* arrives as `rpc_endpoints` or through `create_route_specs`.
|
|
118
122
|
*
|
|
119
123
|
* @param options - endpoint path, actions, logger, and the two action rate limiters
|
|
120
124
|
* @returns route specs (GET + POST) ready for `apply_route_specs`
|
|
@@ -260,6 +264,9 @@ export const create_rpc_endpoint = (options) => {
|
|
|
260
264
|
method: 'POST',
|
|
261
265
|
path: endpoint_path,
|
|
262
266
|
auth: { account: 'none', actor: 'none' }, // per-action auth inside dispatcher
|
|
267
|
+
// the dispatcher reads the resolved credential per action, so the
|
|
268
|
+
// endpoint stays inside the auth middleware's scope however it mounts
|
|
269
|
+
reads_credential: true,
|
|
263
270
|
handler: post_handler,
|
|
264
271
|
description: `JSON-RPC 2.0 endpoint — ${actions.length} method${actions.length === 1 ? '' : 's'}`,
|
|
265
272
|
input: z.null(), // dispatcher owns body parsing; rpc_endpoints surface has the real schemas
|
|
@@ -270,6 +277,7 @@ export const create_rpc_endpoint = (options) => {
|
|
|
270
277
|
method: 'GET',
|
|
271
278
|
path: endpoint_path,
|
|
272
279
|
auth: { account: 'none', actor: 'none' }, // per-action auth inside dispatcher
|
|
280
|
+
reads_credential: true,
|
|
273
281
|
handler: get_handler,
|
|
274
282
|
description: `JSON-RPC 2.0 endpoint (cacheable reads) — ${actions.length} method${actions.length === 1 ? '' : 's'}`,
|
|
275
283
|
input: z.null(), // params from query string, validated by dispatcher
|
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
* something closes it. `ConnectionCloser` is that capability, and every
|
|
10
10
|
* revocation handler holds one: the session and token revokes, the revoke-alls,
|
|
11
11
|
* account delete and purge, `/logout`, `/password`, and the session and token
|
|
12
|
-
* caps for what they evict.
|
|
12
|
+
* caps for what they evict. A role revocation is the exception: it closes
|
|
13
|
+
* only the connections an endpoint gated on that role, and only through the
|
|
14
|
+
* audit listeners (`create_ws_auth_guard`, `create_sse_auth_guard`) — no
|
|
15
|
+
* handler holds a closer for it. The auth cleanup holds one too
|
|
13
16
|
* (`AuthCleanupDeps.connection_closer`): its session sweep closes the
|
|
14
17
|
* connections of each expired session it deletes, after that delete commits.
|
|
15
18
|
*
|
|
@@ -43,6 +46,7 @@
|
|
|
43
46
|
* @module
|
|
44
47
|
*/
|
|
45
48
|
import type { Uuid } from '../primitive_schemas.js';
|
|
49
|
+
import type { Logger } from '@fuzdev/fuz_util/log.ts';
|
|
46
50
|
import { type EmitAfterCommitContext } from '../http/pending_effects.js';
|
|
47
51
|
/**
|
|
48
52
|
* Close live connections by identity, or all of them.
|
|
@@ -70,6 +74,20 @@ export interface ConnectionCloser {
|
|
|
70
74
|
* credentials.
|
|
71
75
|
*/
|
|
72
76
|
close_sockets_for_account: (account_id: Uuid) => number;
|
|
77
|
+
/**
|
|
78
|
+
* Close the connections of `account_id` whose endpoint gated them on
|
|
79
|
+
* `role` — the close for a revoked **global** grant of that role (a scoped
|
|
80
|
+
* grant never admits at a role gate, so revoking one closes nothing; the
|
|
81
|
+
* caller filters, `to_global_role_revocation`). A WebSocket gets
|
|
82
|
+
* `WS_CLOSE_PERMISSION_REVOKED`, not a revocation's code: the credential is
|
|
83
|
+
* still good.
|
|
84
|
+
*
|
|
85
|
+
* **Over-closes an any-of gate**: a connection whose endpoint listed
|
|
86
|
+
* several roles closes on the loss of any of them, whether or not the
|
|
87
|
+
* account still holds another. The client reconnects into the gate if it
|
|
88
|
+
* still qualifies.
|
|
89
|
+
*/
|
|
90
|
+
close_sockets_for_role: (account_id: Uuid, role: string) => number;
|
|
73
91
|
/**
|
|
74
92
|
* Close every connection, whoever opened it — the shutdown close
|
|
75
93
|
* `AppServer.close` runs. Not a revocation: a WebSocket gets
|
|
@@ -82,8 +100,10 @@ export interface ConnectionCloser {
|
|
|
82
100
|
}
|
|
83
101
|
/**
|
|
84
102
|
* Which connections a revocation closes — the argument of
|
|
85
|
-
* `queue_connection_close`, one variant per
|
|
103
|
+
* `queue_connection_close`, one variant per revocation close of
|
|
86
104
|
* `ConnectionCloser` (`close_all_sockets` is shutdown, never a revocation).
|
|
105
|
+
* No handler queues `role` today: the audit listeners are a role's only
|
|
106
|
+
* closer.
|
|
87
107
|
*/
|
|
88
108
|
export type ConnectionCloseTarget = {
|
|
89
109
|
kind: 'session';
|
|
@@ -94,6 +114,10 @@ export type ConnectionCloseTarget = {
|
|
|
94
114
|
} | {
|
|
95
115
|
kind: 'account';
|
|
96
116
|
account_id: Uuid;
|
|
117
|
+
} | {
|
|
118
|
+
kind: 'role';
|
|
119
|
+
account_id: Uuid;
|
|
120
|
+
role: string;
|
|
97
121
|
};
|
|
98
122
|
/**
|
|
99
123
|
* Close the connections `target` names, now.
|
|
@@ -104,6 +128,21 @@ export type ConnectionCloseTarget = {
|
|
|
104
128
|
* @returns how many connections closed
|
|
105
129
|
*/
|
|
106
130
|
export declare const close_connections: (closer: ConnectionCloser, target: ConnectionCloseTarget) => number;
|
|
131
|
+
/**
|
|
132
|
+
* Close the connections `target` names, now, for a revocation that has
|
|
133
|
+
* already committed — logging a close that throws instead of throwing it.
|
|
134
|
+
*
|
|
135
|
+
* For a handler with no route transaction, closing straight after its own
|
|
136
|
+
* commit: a throw there would fail a request whose revocation stands, and skip
|
|
137
|
+
* the work after the close (the response, the audit row). A closer over several
|
|
138
|
+
* transports closes the rest before it rethrows, so catching loses no close.
|
|
139
|
+
* The twin of the post-commit flush, which catches and logs the same way.
|
|
140
|
+
*
|
|
141
|
+
* @param closer - the closer to run, `deps.connection_closer` in a handler
|
|
142
|
+
* @param target - the connections to close
|
|
143
|
+
* @param log - where a failing close is reported
|
|
144
|
+
*/
|
|
145
|
+
export declare const close_connections_after_commit: (closer: ConnectionCloser, target: ConnectionCloseTarget, log: Logger) => void;
|
|
107
146
|
/**
|
|
108
147
|
* Queue a connection close on `ctx`'s post-commit queue, so it runs only after
|
|
109
148
|
* the request's transaction commits and never when the handler throws.
|
|
@@ -112,8 +151,9 @@ export declare const close_connections: (closer: ConnectionCloser, target: Conne
|
|
|
112
151
|
* why an in-transaction revocation must not close inline.
|
|
113
152
|
*
|
|
114
153
|
* In a handler with no wrapping transaction a throw still drops the queued
|
|
115
|
-
* close while an autocommit write stays;
|
|
116
|
-
*
|
|
154
|
+
* close while an autocommit write stays; such a handler closes inline straight
|
|
155
|
+
* after its own commit instead, with `close_connections_after_commit`, which
|
|
156
|
+
* catches and logs a failing close as the post-commit flush does.
|
|
117
157
|
*
|
|
118
158
|
* @param ctx - the route or action context of the request doing the revoking
|
|
119
159
|
* @param closer - the closer to run, `deps.connection_closer` in a handler
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection_closer.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/connection_closer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"connection_closer.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/connection_closer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,EAAqB,KAAK,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAE5F;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,gBAAgB;IAChC,qGAAqG;IACrG,yBAAyB,EAAE,CAAC,kBAAkB,EAAE,MAAM,KAAK,MAAM,CAAC;IAClE,4EAA4E;IAC5E,uBAAuB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC;IAC1D;;;;OAIG;IACH,yBAAyB,EAAE,CAAC,UAAU,EAAE,IAAI,KAAK,MAAM,CAAC;IACxD;;;;;;;;;;;;OAYG;IACH,sBAAsB,EAAE,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACnE;;;;;;;OAOG;IACH,iBAAiB,EAAE,MAAM,MAAM,CAAC;CAChC;AAED;;;;;;GAMG;AACH,MAAM,MAAM,qBAAqB,GAC9B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,UAAU,EAAE,IAAI,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpD;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAC7B,QAAQ,gBAAgB,EACxB,QAAQ,qBAAqB,KAC3B,MAaF,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,8BAA8B,GAC1C,QAAQ,gBAAgB,EACxB,QAAQ,qBAAqB,EAC7B,KAAK,MAAM,KACT,IAMF,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,sBAAsB,GAClC,KAAK,sBAAsB,EAC3B,QAAQ,gBAAgB,EACxB,QAAQ,qBAAqB,KAC3B,IAIF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,gBAMnC,CAAC;AAEH;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,cAAe,SAAQ,gBAAgB;IACvD;;;;;;;;;;;;;;;OAeG;IACH,GAAG,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,MAAM,IAAI,CAAC;IAC9C,2EAA2E;IAC3E,YAAY,EAAE,MAAM,MAAM,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB,QAAO,cA8CzC,CAAC"}
|
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
* something closes it. `ConnectionCloser` is that capability, and every
|
|
10
10
|
* revocation handler holds one: the session and token revokes, the revoke-alls,
|
|
11
11
|
* account delete and purge, `/logout`, `/password`, and the session and token
|
|
12
|
-
* caps for what they evict.
|
|
12
|
+
* caps for what they evict. A role revocation is the exception: it closes
|
|
13
|
+
* only the connections an endpoint gated on that role, and only through the
|
|
14
|
+
* audit listeners (`create_ws_auth_guard`, `create_sse_auth_guard`) — no
|
|
15
|
+
* handler holds a closer for it. The auth cleanup holds one too
|
|
13
16
|
* (`AuthCleanupDeps.connection_closer`): its session sweep closes the
|
|
14
17
|
* connections of each expired session it deletes, after that delete commits.
|
|
15
18
|
*
|
|
@@ -60,10 +63,34 @@ export const close_connections = (closer, target) => {
|
|
|
60
63
|
return closer.close_sockets_for_token(target.api_token_id);
|
|
61
64
|
case 'account':
|
|
62
65
|
return closer.close_sockets_for_account(target.account_id);
|
|
66
|
+
case 'role':
|
|
67
|
+
return closer.close_sockets_for_role(target.account_id, target.role);
|
|
63
68
|
default:
|
|
64
69
|
throw new UnreachableError(target);
|
|
65
70
|
}
|
|
66
71
|
};
|
|
72
|
+
/**
|
|
73
|
+
* Close the connections `target` names, now, for a revocation that has
|
|
74
|
+
* already committed — logging a close that throws instead of throwing it.
|
|
75
|
+
*
|
|
76
|
+
* For a handler with no route transaction, closing straight after its own
|
|
77
|
+
* commit: a throw there would fail a request whose revocation stands, and skip
|
|
78
|
+
* the work after the close (the response, the audit row). A closer over several
|
|
79
|
+
* transports closes the rest before it rethrows, so catching loses no close.
|
|
80
|
+
* The twin of the post-commit flush, which catches and logs the same way.
|
|
81
|
+
*
|
|
82
|
+
* @param closer - the closer to run, `deps.connection_closer` in a handler
|
|
83
|
+
* @param target - the connections to close
|
|
84
|
+
* @param log - where a failing close is reported
|
|
85
|
+
*/
|
|
86
|
+
export const close_connections_after_commit = (closer, target, log) => {
|
|
87
|
+
try {
|
|
88
|
+
close_connections(closer, target);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
log.error('connection close after commit failed:', err);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
67
94
|
/**
|
|
68
95
|
* Queue a connection close on `ctx`'s post-commit queue, so it runs only after
|
|
69
96
|
* the request's transaction commits and never when the handler throws.
|
|
@@ -72,8 +99,9 @@ export const close_connections = (closer, target) => {
|
|
|
72
99
|
* why an in-transaction revocation must not close inline.
|
|
73
100
|
*
|
|
74
101
|
* In a handler with no wrapping transaction a throw still drops the queued
|
|
75
|
-
* close while an autocommit write stays;
|
|
76
|
-
*
|
|
102
|
+
* close while an autocommit write stays; such a handler closes inline straight
|
|
103
|
+
* after its own commit instead, with `close_connections_after_commit`, which
|
|
104
|
+
* catches and logs a failing close as the post-commit flush does.
|
|
77
105
|
*
|
|
78
106
|
* @param ctx - the route or action context of the request doing the revoking
|
|
79
107
|
* @param closer - the closer to run, `deps.connection_closer` in a handler
|
|
@@ -93,6 +121,7 @@ export const noop_connection_closer = Object.freeze({
|
|
|
93
121
|
close_sockets_for_session: () => 0,
|
|
94
122
|
close_sockets_for_token: () => 0,
|
|
95
123
|
close_sockets_for_account: () => 0,
|
|
124
|
+
close_sockets_for_role: () => 0,
|
|
96
125
|
close_all_sockets: () => 0
|
|
97
126
|
});
|
|
98
127
|
/**
|
|
@@ -150,6 +179,7 @@ export const create_realtime_closer = () => {
|
|
|
150
179
|
close_sockets_for_session: (session_token_hash) => fan_out((member) => member.close_sockets_for_session(session_token_hash)),
|
|
151
180
|
close_sockets_for_token: (api_token_id) => fan_out((member) => member.close_sockets_for_token(api_token_id)),
|
|
152
181
|
close_sockets_for_account: (account_id) => fan_out((member) => member.close_sockets_for_account(account_id)),
|
|
182
|
+
close_sockets_for_role: (account_id, role) => fan_out((member) => member.close_sockets_for_role(account_id, role)),
|
|
153
183
|
close_all_sockets: () => fan_out((member) => member.close_all_sockets())
|
|
154
184
|
});
|
|
155
185
|
};
|
|
@@ -163,6 +163,16 @@ export type PerformActionResult = {
|
|
|
163
163
|
* the harness omits `account_id`.
|
|
164
164
|
*/
|
|
165
165
|
export declare const perform_action: (input: PerformActionInput, deps: PerformActionDeps) => Promise<PerformActionResult>;
|
|
166
|
+
/**
|
|
167
|
+
* The credential-type 403 — one builder, so the dispatcher's gate below and a
|
|
168
|
+
* handler that gates a credential channel on its input (`account_delete`'s
|
|
169
|
+
* self path) emit a byte-identical error. Twin of the Rust
|
|
170
|
+
* `fuz_auth::credential_type_required`.
|
|
171
|
+
*
|
|
172
|
+
* @param required_credential_types - the channels that would be admitted
|
|
173
|
+
* @returns the `forbidden` error object with reason `credential_type_required`
|
|
174
|
+
*/
|
|
175
|
+
export declare const credential_type_required_error: (required_credential_types: ReadonlyArray<string>) => JsonrpcErrorObject;
|
|
166
176
|
/**
|
|
167
177
|
* Build a JSON-RPC response envelope from a `PerformActionResult` for
|
|
168
178
|
* transports that wire over the JSON-RPC 2.0 message shape (HTTP RPC + WS).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"perform_action.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/perform_action.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EAGN,KAAK,cAAc,EACnB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"perform_action.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/perform_action.ts"],"names":[],"mappings":"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"}
|
|
@@ -58,6 +58,7 @@ import { DEV } from 'esm-env';
|
|
|
58
58
|
import { apply_authorization_phase, has_any_scoped_role } from '../auth/request_context.js';
|
|
59
59
|
import {} from '../hono_context.js';
|
|
60
60
|
import { to_input_params } from '../http/schema_helpers.js';
|
|
61
|
+
import { rate_limit_ip_key } from '../http/ip_canonical.js';
|
|
61
62
|
import { dispatch_with_post_commit_rollback } from '../http/pending_effects.js';
|
|
62
63
|
import { JSONRPC_VERSION } from '../http/jsonrpc.js';
|
|
63
64
|
import { jsonrpc_error_messages, jsonrpc_error_code_to_http_status, http_status_to_jsonrpc_error_code, JSONRPC_ERROR_CODES, dev_only } from '../http/jsonrpc_errors.js';
|
|
@@ -119,14 +120,17 @@ export const perform_action = async (input, deps) => {
|
|
|
119
120
|
// budget it should, and so a throttled caller pays no schema work.
|
|
120
121
|
const rate_limit = spec.rate_limit;
|
|
121
122
|
if (rate_limit) {
|
|
122
|
-
const
|
|
123
|
+
const ip_limiter = rate_limit === 'ip' || rate_limit === 'both' ? action_ip_rate_limiter : null;
|
|
123
124
|
const account_keyed_context = action_account_rate_limiter &&
|
|
124
125
|
request_context !== null &&
|
|
125
126
|
(rate_limit === 'account' || rate_limit === 'both')
|
|
126
127
|
? request_context
|
|
127
128
|
: null;
|
|
128
|
-
|
|
129
|
-
|
|
129
|
+
// keyed by the address's rate-limit key (an IPv6 client's /64); the
|
|
130
|
+
// audit trail keeps `client_ip` whole
|
|
131
|
+
const ip_key = ip_limiter ? rate_limit_ip_key(client_ip) : '';
|
|
132
|
+
if (ip_limiter) {
|
|
133
|
+
const result = ip_limiter.check(ip_key);
|
|
130
134
|
if (!result.allowed)
|
|
131
135
|
return rate_limited_result(result.retry_after);
|
|
132
136
|
}
|
|
@@ -135,8 +139,8 @@ export const perform_action = async (input, deps) => {
|
|
|
135
139
|
if (!result.allowed)
|
|
136
140
|
return rate_limited_result(result.retry_after);
|
|
137
141
|
}
|
|
138
|
-
if (
|
|
139
|
-
|
|
142
|
+
if (ip_limiter)
|
|
143
|
+
ip_limiter.record(ip_key);
|
|
140
144
|
if (account_keyed_context) {
|
|
141
145
|
action_account_rate_limiter.record(account_keyed_context.account.id);
|
|
142
146
|
}
|
|
@@ -262,6 +266,19 @@ const check_action_auth_pre_authorization = (auth, account_id) => {
|
|
|
262
266
|
}
|
|
263
267
|
return null;
|
|
264
268
|
};
|
|
269
|
+
/**
|
|
270
|
+
* The credential-type 403 — one builder, so the dispatcher's gate below and a
|
|
271
|
+
* handler that gates a credential channel on its input (`account_delete`'s
|
|
272
|
+
* self path) emit a byte-identical error. Twin of the Rust
|
|
273
|
+
* `fuz_auth::credential_type_required`.
|
|
274
|
+
*
|
|
275
|
+
* @param required_credential_types - the channels that would be admitted
|
|
276
|
+
* @returns the `forbidden` error object with reason `credential_type_required`
|
|
277
|
+
*/
|
|
278
|
+
export const credential_type_required_error = (required_credential_types) => jsonrpc_error_messages.forbidden('forbidden', {
|
|
279
|
+
reason: ERROR_CREDENTIAL_TYPE_REQUIRED,
|
|
280
|
+
required_credential_types
|
|
281
|
+
});
|
|
265
282
|
/**
|
|
266
283
|
* Post-authorization auth gate — fires after the authorization phase
|
|
267
284
|
* resolved the actor + role_grants. Enforces `auth.credential_types` and
|
|
@@ -289,10 +306,7 @@ const check_action_auth_pre_authorization = (auth, account_id) => {
|
|
|
289
306
|
const check_action_auth_post_authorization = (auth, request_context, credential_type, method, token_scope) => {
|
|
290
307
|
if (auth.credential_types?.length) {
|
|
291
308
|
if (!credential_type || !auth.credential_types.includes(credential_type)) {
|
|
292
|
-
return
|
|
293
|
-
reason: ERROR_CREDENTIAL_TYPE_REQUIRED,
|
|
294
|
-
required_credential_types: auth.credential_types
|
|
295
|
-
});
|
|
309
|
+
return credential_type_required_error(auth.credential_types);
|
|
296
310
|
}
|
|
297
311
|
}
|
|
298
312
|
// Token-scope gate — second of three. See the doc comment for why the order
|