api #

fullstack app library

341 modules · 1965 declarations

Modules
#

AcceptOfferInput
#

auth/role_grant_offer_queries.ts view source

AcceptOfferInput import type {AcceptOfferInput} from '@fuzdev/fuz_app/auth/role_grant_offer_queries.js';

offer_id

type Uuid

to_account_id

Account of the accepting recipient — IDOR guard against another account accepting the offer.

type Uuid

actor_id

Accepting actor — the actor that will hold the resulting role_grant. Must belong to to_account_id; the query verifies and throws if not (defense-in-depth — the action handler passes auth.actor.id which is session-bound, but the query enforces the invariant for all callers including tests and future direct consumers).

Required because under multi-actor an account may host many actors; the resulting role_grant must bind to the actor that actually accepted, not "an" actor on the account picked by query order.

type Uuid

ip?

Optional IP to stamp on the audit events.

type string | null

AcceptOfferResult
#

auth/role_grant_offer_queries.ts view source

AcceptOfferResult import type {AcceptOfferResult} from '@fuzdev/fuz_app/auth/role_grant_offer_queries.js';

Result of query_accept_offer — the role_grant produced (new or pre-existing on race), plus the (now-accepted) offer.

role_grant

type RoleGrant

offer

type RoleGrantOffer

created

true if this call is the one that accepted the offer (new role_grant inserted); false on a race returning the already-created role_grant.

type boolean

superseded_offers

Sibling offers superseded by this accept — empty on the race-loser path. Each entry carries its grantor's from_account_id so the caller can fan out role_grant_offer_supersede notifications without a second round-trip.

type Array<SupersededOffer>

audit_events

Audit events emitted in-transaction — fed back through audit.notify by the caller, which fans out to the registered listeners. Includes one role_grant_offer_supersede per superseded sibling.

type Array<AuditLogEvent>

Account
#

auth/account_schema.ts view source

Account import type {Account} from '@fuzdev/fuz_app/auth/account_schema.js';

Account — authentication identity. You log in as an account.

id

type Uuid

username

type Username

email

type Email | null

email_verified

type boolean

password_hash

type string

created_at

type string

created_by

type Uuid | null

updated_at

type string

updated_by

type Uuid | null

deleted_at

Soft-delete tombstone. Non-null means the account is deleted (delete = soft); auth resolution treats it as absent. A hard purge removes the row entirely. See auth/account_queries.ts.

type string | null

deleted_by

Actor that performed the soft-delete (initiator: self / admin / keeper). Paired with deleted_at, mirroring role_grant's revoked_at / revoked_by. Plain UUID (no FK, like created_by / updated_by on this table).

type Uuid | null

account_delete_action_spec
#

auth/admin_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodDefault<ZodObject<{ account_id: ZodOptional<ZodNullable<$ZodBranded<ZodUUID, "Uuid", "out">>>; acting: ZodOptional<...>; }, $strict>>; output: ZodObject<...>; async: tru... import {account_delete_action_spec} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Soft-delete an account (reversible tombstone). Self-or-admin: the caller may delete their own account; deleting another requires the admin role (handler-enforced elevation). No admin_ prefix — the privilege lives in the auth check, not the name, so self-service deletion stays open (delete = soft, purge = hard).

ACCOUNT_EMAIL_INDEX
#

auth/auth_ddl.ts view source

"\nCREATE UNIQUE INDEX IF NOT EXISTS idx_account_email ON account (LOWER(email)) WHERE email IS NOT NULL" import {ACCOUNT_EMAIL_INDEX} from '@fuzdev/fuz_app/auth/auth_ddl.js';

ACCOUNT_ID_KEY
#

hono_context.ts view source

"auth_account_id" import {ACCOUNT_ID_KEY} from '@fuzdev/fuz_app/hono_context.js';

Hono context variable name for the authenticated account id.

Set by the auth middleware (session, bearer, or daemon token) on a valid credential. null for unauthenticated requests. The route-spec wrapper / RPC dispatcher's authorization phase reads this when resolving the acting actor; account-grain auth guards (require_auth) and account-grain handlers read it directly.

account_purge_action_spec
#

auth/admin_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; roles: string[]; credential_types: string[]; }; side_effects: true; input: ZodObject<{ account_id: $ZodBranded<...>; confirm: ZodOptional<...>; acting: ZodOptional<...>; }, $strict>; output: ZodObject<.... import {account_purge_action_spec} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Hard-purge an account (keeper-gated, irreversible). Keeper credential (daemon_token) + keeper role + explicit confirm: true. Not admin-reachable and not self-service — the most dangerous operation is the most restricted. purge = hard; the word + gating + WARN flag the danger (fail-loud).

ACCOUNT_SCHEMA
#

auth/auth_ddl.ts view source

"\nCREATE TABLE IF NOT EXISTS account (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n username TEXT UNIQUE NOT NULL,\n email TEXT,\n email_verified BOOLEAN NOT NULL DEFAULT false,\n password_hash TEXT NOT NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n created_by UUID,\n updated_at TIMESTAMPTZ ... import {ACCOUNT_SCHEMA} from '@fuzdev/fuz_app/auth/auth_ddl.js';

account_session_list_action_spec
#

auth/account_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "none"; }; side_effects: false; input: ZodVoid; output: ZodObject<{ sessions: ZodArray<ZodObject<{ id: $ZodBranded<ZodString, "SessionId", "out">; account_id: $ZodBranded<...>; created_at: ZodString; expires_at: Zo... import {account_session_list_action_spec} from '@fuzdev/fuz_app/auth/account_action_specs.js';

account_session_revoke_action_spec
#

auth/account_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "none"; credential_types: string[]; }; side_effects: true; input: ZodObject<{ session_id: $ZodBranded<ZodString, "SessionId", "out">; }, $strict>; output: ZodObject<...>; async: true; description: string; } import {account_session_revoke_action_spec} from '@fuzdev/fuz_app/auth/account_action_specs.js';

account_session_revoke_all_action_spec
#

auth/account_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "none"; credential_types: string[]; }; side_effects: true; input: ZodVoid; output: ZodObject<...>; async: true; description: string; } import {account_session_revoke_all_action_spec} from '@fuzdev/fuz_app/auth/account_action_specs.js';

account_sessions_rpc_context
#

ui/account_sessions_state.svelte.ts view source

{ get: (error_message?: string | undefined) => () => AccountSessionsRpc; get_maybe: () => (() => AccountSessionsRpc) | undefined; set: (value: () => AccountSessionsRpc) => () => AccountSessionsRpc; } import {account_sessions_rpc_context} from '@fuzdev/fuz_app/ui/account_sessions_state.svelte.js';

Svelte context carrying the reactive AccountSessionsRpc accessor. Mirrors the admin-side RPC contexts. get() throws when no provisioner ran above the component — the adapter is required.

account_status_route_shape
#

auth/account_route_schema.ts view source

{ method: "GET"; path: string; auth: { account: "none"; actor: "none"; }; description: string; input: ZodNull; output: ZodObject<{ account: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; username: ZodPipe<...>; email: ZodNullable<...>; email_verified: ZodBoolean; created_at: ZodString; }, $strict>; actor: ZodN... import {account_status_route_shape} from '@fuzdev/fuz_app/auth/account_route_schema.js';

The GET /status route shape minus its handler — pure hono-free data. create_account_status_route_spec spreads this and attaches the live handler (which reads the account id off the request context); surface generation spreads it with a stub handler.

The path is relative like the sibling account shapes (/login, /verify), so it composes under prefix_route_specs('/api/account', …) into /api/account/status. create_account_route_specs bundles it (so every account surface serves /status, matching the Rust account_router); mirror Rust by mounting it as part of the account family, not separately.

account_token_create_action_spec
#

auth/account_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "none"; credential_types: string[]; }; side_effects: true; input: ZodPrefault<ZodObject<{ name: ZodDefault<ZodString>; }, $strict>>; output: ZodObject<...>; async: true; description: string; rate_limit: "account"; } import {account_token_create_action_spec} from '@fuzdev/fuz_app/auth/account_action_specs.js';

credential_types: ['session'] — see docs/security.md §Credential-channel gating.

rate_limit: 'account' bounds the burn rate of API-token creates. The outstanding-token count is already capped by max_tokens (via query_api_token_enforce_limit), but the per-account *rate* of churn is not — without this cap, a caller could rotate tokens in a tight loop to amplify token_create audit churn or attempt to provoke downstream rate-limit hot spots.

account_token_list_action_spec
#

auth/account_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "none"; }; side_effects: false; input: ZodVoid; output: ZodObject<{ tokens: ZodArray<ZodObject<{ id: ZodString; ... 5 more ...; created_at: ZodString; }, $strict>>; }, $strict>; async: true; description: string; } import {account_token_list_action_spec} from '@fuzdev/fuz_app/auth/account_action_specs.js';

account_token_revoke_action_spec
#

auth/account_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "none"; credential_types: string[]; }; side_effects: true; input: ZodObject<{ token_id: ZodString; }, $strict>; output: ZodObject<...>; async: true; description: string; } import {account_token_revoke_action_spec} from '@fuzdev/fuz_app/auth/account_action_specs.js';

account_undelete_action_spec
#

auth/admin_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; roles: string[]; }; side_effects: true; input: ZodObject<{ account_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<...>; }, $strict>; output: ZodObject<...>; async: true; description: strin... import {account_undelete_action_spec} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Reactivate a soft-deleted account (clears the tombstone). Admin-only — there is no self path because a soft-deleted account can't authenticate (auth resolution excludes it and its sessions are revoked), so reactivation is always an admin acting on another account. The inverse of account_delete; does not restore revoked sessions/tokens (delete = soft, purge = hard).

ACCOUNT_USERNAME_CI_INDEX
#

auth/auth_ddl.ts view source

"\nCREATE UNIQUE INDEX IF NOT EXISTS idx_account_username_ci ON account (LOWER(username))" import {ACCOUNT_USERNAME_CI_INDEX} from '@fuzdev/fuz_app/auth/auth_ddl.js';

account_verify_action_spec
#

auth/account_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "none"; }; side_effects: false; input: ZodVoid; output: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; username: ZodPipe<...>; email: ZodNullable<...>; email_verified: ZodBoolean; created_at: ZodString; }, $s... import {account_verify_action_spec} from '@fuzdev/fuz_app/auth/account_action_specs.js';

AccountActionOptions
#

auth/account_actions.ts view source

AccountActionOptions import type {AccountActionOptions} from '@fuzdev/fuz_app/auth/account_actions.js';

max_tokens?

Max API tokens per account. When set, account_token_create enforces the cap via query_api_token_enforce_limit inside the same transaction — oldest tokens are evicted once the cap is exceeded. Default DEFAULT_MAX_TOKENS; pass null to disable the cap.

type number | null

connection_closer?

Live-connection closer — when set, account_session_revoke / _session_revoke_all / account_token_revoke handlers eagerly close affected WebSocket sockets BEFORE emitting the corresponding audit event. Closes the audit-failure-leaks-WS surface: the listener-based close (transports_ws_auth_guard) only fires after the audit INSERT succeeds, so a DB error would leave live sockets stale. BackendWebsocketTransport satisfies this interface structurally; consumers pass their transport instance directly. When absent, only the listener-based close runs. Mirrors zzz_server's handler-side close_sockets_for_* calls.

type ConnectionCloser | null

AccountDeleteInput
#

auth/admin_action_specs.ts view source

ZodDefault<ZodObject<{ account_id: ZodOptional<ZodNullable<$ZodBranded<ZodUUID, "Uuid", "out">>>; acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict>> import type {AccountDeleteInput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Input for account_delete (soft delete). account_id is optional — omitted (or equal to the caller's own account) is a self-delete; a different account requires the admin role (handler-enforced elevation, like role_grant_offer_list).

AccountDeleteOutput
#

auth/admin_action_specs.ts view source

ZodObject<{ ok: ZodLiteral<true>; deleted: ZodBoolean; }, $strict> import type {AccountDeleteOutput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Output for account_delete.

AccountIdentitySnapshot
#

auth/account_queries.ts view source

AccountIdentitySnapshot import type {AccountIdentitySnapshot} from '@fuzdev/fuz_app/auth/account_queries.js';

Identifying values snapshotted into a deletion/purge audit event so the identity behind a now-orphaned audit_log id isn't lost. Mirrors the Rust AccountIdentitySnapshot.

username

type string

email

type string | null

AccountLifecycleCrossTestOptions
#

testing/cross_backend/account_lifecycle.ts view source

RpcPathCrossSuiteOptions import type {AccountLifecycleCrossTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/account_lifecycle.js';

Options for the account-lifecycle parity suite. The standard RPC-dispatched cross-suite shape (setup_test / capabilities / rpc_path); aliases RpcPathCrossSuiteOptions rather than duplicating.

rpc_path?

RPC endpoint path the methods are mounted on. Default /api/rpc.

type string

readonly

setup_test

Per-test fixture-producing function (fresh keeper + db per call).

type (): Promise<TestFixtureBase>

readonly
returns Promise<TestFixtureBase>

capabilities

Backend capability declarations — each suite gates on its own flag.

type BackendCapabilities

readonly

AccountPurgeInput
#

auth/admin_action_specs.ts view source

ZodObject<{ account_id: $ZodBranded<ZodUUID, "Uuid", "out">; confirm: ZodOptional<ZodBoolean>; acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict> import type {AccountPurgeInput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Input for account_purge (hard, irreversible delete). Keeper-only.

AccountPurgeOutput
#

auth/admin_action_specs.ts view source

ZodObject<{ ok: ZodLiteral<true>; purged: ZodBoolean; }, $strict> import type {AccountPurgeOutput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Output for account_purge.

AccountRouteOptions
#

auth/account_routes.ts view source

AccountRouteOptions import type {AccountRouteOptions} from '@fuzdev/fuz_app/auth/account_routes.js';

Per-factory configuration for account route specs.

inheritance

login_account_rate_limiter

Rate limiter for login attempts, keyed by submitted username. Pass null to disable.

type RateLimiter | null

max_sessions?

Max active sessions per account. Evicts oldest on login. Default 5, null disables.

type number | null

login_fail_floor_ms?

Minimum wall-clock time (ms) for login 401 responses. Set to 0 or a negative number to disable (e.g., in tests). Default DEFAULT_LOGIN_FAIL_FLOOR_MS.

type number

login_fail_jitter_ms?

Uniform jitter window (±ms) layered on the floor. Set to 0 to disable jitter while keeping the floor. Default DEFAULT_LOGIN_FAIL_JITTER_MS.

type number

connection_closer?

Live-connection closer — when set, the logout and password handlers eagerly close affected WebSocket sockets for the account BEFORE emitting the corresponding audit event. Mirrors the self-service action surface (see AccountActionOptions.connection_closer). When absent, only the listener-based close (transports_ws_auth_guard registered via audit.add_listener) runs.

type ConnectionCloser | null

bootstrap_status?

Runtime bootstrap status for the bundled GET /status route — when available, its unauthenticated 401 carries bootstrap_available: true so a fresh frontend can route to the bootstrap flow. Pass ctx.bootstrap_status (the live BootstrapStatus ref) so the flag tracks the one-shot bootstrap completing. Omit when no bootstrap flow is wired — /status is still served, just without the flag.

type { available: boolean }

AccountRouteShapeOptions
#

auth/account_route_schema.ts view source

AccountRouteShapeOptions import type {AccountRouteShapeOptions} from '@fuzdev/fuz_app/auth/account_route_schema.js';

Option inputs that shape the account route metadata (not its handlers).

login_account_rate_limited

Whether a per-account login rate limiter is wired — toggles /password's rate_limit.

type boolean

AccountSessions
#

AccountSessionsRpc
#

ui/account_sessions_state.svelte.ts view source

AccountSessionsRpc import type {AccountSessionsRpc} from '@fuzdev/fuz_app/ui/account_sessions_state.svelte.js';

Narrow RPC surface consumed by AccountSessionsState. Consumers adapt their typed RPC client to this shape. Mirrors the other per-domain *Rpc interfaces (AdminAccountsRpc, AuditLogRpc, AdminInvitesRpc).

The three methods wrap the corresponding action specs on auth/account_actions.ts:

  • listaccount_session_list
  • revokeaccount_session_revoke (IDOR-guarded by account_id server-side)
  • revoke_allaccount_session_revoke_all

list

type () => Promise<{ sessions: Array<AuthSessionJson> }>

revoke

type (params: { session_id: string }) => Promise<{ ok: true; revoked: boolean }>

revoke_all

type () => Promise<{ ok: true; count: number }>

AccountSessionsState
#

ui/account_sessions_state.svelte.ts view source

import {AccountSessionsState} from '@fuzdev/fuz_app/ui/account_sessions_state.svelte.js';

list

type AsyncSlot<void, string>

readonly

revoke

type KeyedAsyncSlot<string, void, string>

readonly

revoke_all

type AsyncSlot<void, string>

readonly

sessions

type Array<AuthSessionJson>

$state.raw

active_count

type number

readonly $derived

constructor

type new (options: AccountSessionsStateOptions): AccountSessionsState

options

fetch

type (): Promise<void>

returns Promise<void>

submit_revoke

type (id: string): Promise<void>

id

type string
returns Promise<void>

submit_revoke_all

type (): Promise<void>

returns Promise<void>

AccountSessionsStateOptions
#

ui/account_sessions_state.svelte.ts view source

AccountSessionsStateOptions import type {AccountSessionsStateOptions} from '@fuzdev/fuz_app/ui/account_sessions_state.svelte.js';

get_rpc

Reactive accessor for the RPC adapter. Matches the get_rpc pattern on the admin state classes.

type () => AccountSessionsRpc

AccountStatusInput
#

auth/account_route_schema.ts view source

ZodNull import type {AccountStatusInput} from '@fuzdev/fuz_app/auth/account_route_schema.js';

Input for GET /api/account/status. No parameters — caller is the subject.

AccountStatusOptions
#

auth/account_routes.ts view source

AccountStatusOptions import type {AccountStatusOptions} from '@fuzdev/fuz_app/auth/account_routes.js';

Options for the account status route spec.

path?

Override the default path (/api/account/status).

type string

bootstrap_status?

Runtime bootstrap status — when available, 401 responses include bootstrap_available.

type { available: boolean }

AccountStatusOutput
#

auth/account_route_schema.ts view source

ZodObject<{ account: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; username: ZodPipe<ZodString, ZodTransform<string, string>>; email: ZodNullable<...>; email_verified: ZodBoolean; created_at: ZodString; }, $strict>; actor: ZodNullable<...>; role_grants: ZodArray<...>; }, $strict> import type {AccountStatusOutput} from '@fuzdev/fuz_app/auth/account_route_schema.js';

Output for GET /api/account/status.

AccountStatusUnauthenticatedError
#

auth/account_route_schema.ts view source

ZodObject<{ error: ZodLiteral<"authentication_required">; bootstrap_available: ZodOptional<ZodBoolean>; }, $loose> import type {AccountStatusUnauthenticatedError} from '@fuzdev/fuz_app/auth/account_route_schema.js';

Error body for GET /api/account/status on the unauthenticated path.

AccountUndeleteInput
#

auth/admin_action_specs.ts view source

ZodObject<{ account_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict> import type {AccountUndeleteInput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Input for account_undelete (reactivation). account_id is required — unlike account_delete there is no self path: a soft-deleted account can't authenticate (auth resolution excludes it, sessions are revoked), so reactivation is always an admin acting on another account.

AccountUndeleteOutput
#

auth/admin_action_specs.ts view source

ZodObject<{ ok: ZodLiteral<true>; undeleted: ZodBoolean; }, $strict> import type {AccountUndeleteOutput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Output for account_undelete.

AccountVanishedError
#

http/error_schemas.ts view source

ZodObject<{ error: ZodLiteral<"account_vanished">; }, $loose> import type {AccountVanishedError} from '@fuzdev/fuz_app/http/error_schemas.js';

ActingActor
#

http/auth_shape.ts view source

ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">> import type {ActingActor} from '@fuzdev/fuz_app/http/auth_shape.js';

acting field shared by every input that needs the caller's acting actor. Declaring acting: ActingActor on a route or action input signals to the dispatcher's authorization phase to resolve an actor against the authenticated account: it runs resolve_acting_actor, builds the actor-bound RequestContext, and loads role_grants before auth guards fire.

Resolution rules: omitted + 1 actor → use it; omitted + multiple actors → actor_required with the available list; supplied + on the account → use it; supplied + foreign actor → actor_not_on_account.

Account-grain routes — input doesn't declare acting and auth doesn't require role_grants — skip resolution entirely; their RequestContext.actor is null and the audit envelope's actor_id stays null.

Lives next to RouteAuth because the two are paired by registry-time invariant 2: auth.actor !== 'none' ⟺ input (or query, on REST GETs) declares acting?: ActingActor. Keeping the contract in one module removes the http/ → auth/ import that an earlier split forced.

ActingSlots
#

http/auth_shape.ts view source

ActingSlots import type {ActingSlots} from '@fuzdev/fuz_app/http/auth_shape.js';

Slots where a spec may declare the acting?: ActingActor field — input for both REST + actions; query for REST GETs that bi-locate acting on the query schema (actions have no query shape, so the field is omitted on action call sites).

input

type z.ZodType

query?

type z.ZodType

Action
#

actions/action_types.ts view source

also exported from actions/register_action_ws.ts

Action<TSpec> import type {Action} from '@fuzdev/fuz_app/actions/action_types.js';

A spec paired with its optional handler — the composable unit passed to register_action_ws and create_rpc_client. The server uses both fields; the client reads only spec (the handler is ignored, harmless). Shared fuz_app primitives (e.g. heartbeat_action) export a complete tuple so consumers spread them into both sides' actions arrays without inventing per-repo ping plumbing.

Polymorphic on kind: request_response specs require a handler for dispatch; remote_notification specs may declare a stub handler for symmetry but are dispatcher-handled (e.g. cancel); local_call specs never reach a network dispatcher. The WS dispatcher only invokes handlers on request_response actions; everything else is registry-only.

generics

Action<TSpec extends ActionSpecUnion = ActionSpecUnion>
TSpec
constraint ActionSpecUnion

spec

type TSpec

handler?

Server-side handler — invoked by dispatchers on request_response actions. Ignored for client-only specs and dispatcher-handled notifications.

type ActionHandler

action_event_phase_by_kind
#

actions/action_event_types.ts view source

Record<"request_response" | "remote_notification" | "local_call", readonly ("send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute")[]> import {action_event_phase_by_kind} from '@fuzdev/fuz_app/actions/action_event_types.js';

action_event_phase_transitions
#

actions/action_event_types.ts view source

Record<"send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute", "send_request" | "receive_request" | ... 7 more ... | null> import {action_event_phase_transitions} from '@fuzdev/fuz_app/actions/action_event_types.js';

action_event_step_transitions
#

actions/action_event_types.ts view source

Record<"initial" | "parsed" | "handling" | "handled" | "failed", readonly ("initial" | "parsed" | "handling" | "handled" | "failed")[]> import {action_event_step_transitions} from '@fuzdev/fuz_app/actions/action_event_types.js';

action_manifest_entry
#

testing/cross_backend/action_manifest.ts view source

(spec: { readonly method: string; readonly auth: { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }; readonly side_effects: boolean; }): { ...; } import {action_manifest_entry} from '@fuzdev/fuz_app/testing/cross_backend/action_manifest.js';

Normalize one spec's auth + side-effects into a manifest entry. Pulls the four auth axes off RouteAuth (the same shape the Rust AuthSpec mirrors) and flattens optional roles / credential_types to sorted arrays.

spec

type { readonly method: string; readonly auth: { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }; readonly side_effects: boolean; }

returns

{ method: string; side_effects: boolean; account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles: string[]; credential_types: string[]; }

action_method_enum_kinds_all
#

actions/action_codegen.ts view source

ReadonlySet<ActionMethodEnumKind> import {action_method_enum_kinds_all} from '@fuzdev/fuz_app/actions/action_codegen.js';

Default emit set — every enum kind.

ActionActorContext
#

actions/action_rpc.ts view source

ActionActorContext import type {ActionActorContext} from '@fuzdev/fuz_app/actions/action_rpc.js';

ActionContext narrowed to a resolved acting actor.

Used by handlers whose spec declares auth.actor === 'required' — the dispatcher's authorization phase resolves an actor (per registry-time invariant 2 the input declares acting?: ActingActor), so ctx.auth.actor is non-null. Selected automatically by rpc_action's conditional return type for the actor-implying tier.

inheritance

extends: Omit<ActionContext, 'auth'>

auth

type RequestActorContext

ActionAuthContext
#

actions/action_rpc.ts view source

ActionAuthContext import type {ActionAuthContext} from '@fuzdev/fuz_app/actions/action_rpc.js';

ActionContext narrowed to a non-null RequestContext.

Used by handlers whose spec declares auth.account === 'required' (with auth.actor === 'none') — the dispatcher's pre-validation 401 gate guarantees request_context is populated before the handler runs, but the actor slot stays null because no acting resolution happened. Selected automatically by rpc_action's conditional return type for the account-grain tier.

inheritance

extends: Omit<ActionContext, 'auth'>

auth

type RequestContext

ActionContext
#

actions/action_rpc.ts view source

ActionContext import type {ActionContext} from '@fuzdev/fuz_app/actions/action_rpc.js';

Per-request context provided to action handlers across every transport (HTTP RPC, WebSocket, REST bridge). Built once per dispatched action by perform_action and threaded into the handler.

auth is RequestContext | null — handlers for authenticated actions can narrow via the dispatcher's authorization-phase guarantee.

Single handler context shape across every transport. Consumers inject domain deps via factory closures the same way HTTP RPC factories do.

auth

The authenticated identity, or null for public routes.

type RequestContext | null

request_id

The JSON-RPC request ID from the envelope.

type JsonrpcRequestId

connection_id?

Stable per-socket connection id on WebSocket transport; undefined on HTTP RPC. Consumers key per-connection domain state on this directly; HTTP handlers ignore it.

type Uuid

request_client?

Initiate a JSON-RPC request to the originating client and await its typed reply — the server→client direction of ActionPeer. Present only on the WebSocket transport (it targets the originating socket); undefined on HTTP RPC, where there is no return socket. Handlers that depend on it must handle its absence — e.g. peer/ping surfaces peer_no_transport.

type RequestClient

db

Transaction-scoped when spec.side_effects is true (the dispatcher wraps in db.transaction); pool-level otherwise. Handlers that need rollback-resilient writes call deps.audit.emit(ctx, input), which captures the pool inside its closure.

type Db

pending_effects

Eager fire-and-forget queue — push the in-flight Promise<void> for pool writes already running (audit emits, session touch, api-token usage tracking). Drained via flush_pending_effects after the handler returns.

type Array<Promise<void>>

post_commit_effects

Deferred post-commit thunks — do not push directly; reach for emit_after_commit(ctx, fn) from http/pending_effects.ts. The flush site invokes each thunk after the handler (and any wrapping db.transaction) returns.

type Array<() => void | Promise<void>>

client_ip

Resolved client IP from the trusted-proxy middleware — 'unknown' if the middleware wasn't in the stack (e.g. WS dispatch) or couldn't resolve. Thread into deps.audit.emit as ip: ctx.client_ip for every user-initiated action so RPC audit rows match the REST convention. Pass null only for rows written outside a request (e.g. the role_grant_offer_expire cleanup sweep in auth/cleanup.ts).

type string

credential_type

Credential channel the request arrived on ('session' | 'api_token' | 'daemon_token'), or null for anonymous requests. Same value the dispatcher's credential_types gate consumed at step 4 — exposed here so handlers can record it in audit metadata (defense in depth: the gate may be loosened or bypassed in a future refactor, but the audit row preserves what actually authenticated the request).

type CredentialType | null

log

Logger instance.

type Logger

notify

Send a request-scoped JSON-RPC notification to the originator.

On streaming transports (WebSocket) this routes to the originating connection only. On the HTTP RPC transport this is a no-op with a DEV-mode warn — non-streaming transports have no channel for mid- request notifications. The streams field on an ActionSpec names the notification method this handler is expected to emit.

type (method: string, params: unknown) => void

signal

AbortSignal that fires when the originating request is cancelled (client disconnect on HTTP, socket close or per-request cancel notification on WebSocket). Streaming handlers should check this for early termination.

type AbortSignal

ActionDispatcher
#

actions/action_dispatcher.ts view source

import {ActionDispatcher} from '@fuzdev/fuz_app/actions/action_dispatcher.js';

environment

type ActionEventEnvironment

readonly

transports

type Transports

readonly

default_send_options

type Omit<ActionDispatcherSendOptions, 'signal'>

constructor

type new (options: ActionDispatcherOptions): ActionDispatcher

options

send

Resolve a transport (per-call name → default name → registry default) and forward the message. Catches unexpected throws and converts them to JSON-RPC error responses — this method never throws.

type (message: { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; }, options?: ActionDispatcherSendOptions | undefined): Promise<...>

message

type { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; }

options?

type ActionDispatcherSendOptions | undefined
optional
returns Promise<{ [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<...>); message: string; data?: unknown; }; }>

the response envelope for requests, or null for successful notifications (JsonrpcErrorResponse if the notification's transport send failed)

receive

Dispatch an inbound JSON-RPC message — request, notification, or malformed envelope. Never throws; unexpected failures become JSON-RPC error responses.

type (message: unknown): Promise<{ [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | ... 4 more ... | (number & $brand<...>); message: string; data?: unknown; }; } | { ...; } | null>

message

type unknown
returns Promise<{ [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<...>); message: string; data?: unknown; }; } | { ...; } | n...

response message for requests, null for notifications, or an invalid_request error for malformed input

ActionDispatcherOptions
#

ActionDispatcherSendOptions
#

actions/action_dispatcher.ts view source

ActionDispatcherSendOptions import type {ActionDispatcherSendOptions} from '@fuzdev/fuz_app/actions/action_dispatcher.js';

Per-call options for ActionDispatcher.send. Extends TransportSendOptions with transport_name for per-call transport selection. The peer-wide default for any field lives on ActionDispatcherOptions.default_send_options — set queue: true there once for client-authoritative peers and override per-call for exceptions (e.g. high-frequency position sync where stale replays are wrong).

inheritance

transport_name?

type TransportName

ActionEvent
#

actions/action_event.ts view source

import {ActionEvent} from '@fuzdev/fuz_app/actions/action_event.js';

Action event that manages the lifecycle of an action through its state machine.

generics

ActionEvent<TMethod extends string = string, TPhase extends ActionEventPhase = ActionEventPhase, TStep extends ActionEventStep = ActionEventStep>
TMethod
constraint string
default string
TPhase
constraint ActionEventPhase
TStep
constraint ActionEventStep

environment

type ActionEventEnvironment

readonly

spec

method narrows to TMethod so consumers passing a typed TApi to create_rpc_client get event.spec.method typed as the union of their API's method names rather than plain string. The runtime value comes from lookup_action_spec(method) keyed off the Proxy get trap, so the narrowing matches the dispatched method.

type ActionSpecUnion & { method: TMethod }

readonly

constructor

type new <TMethod extends string = string, TPhase extends ActionEventPhase = "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute", TStep extends ActionEventStep = "initial" | ... 3 more ... | "failed">(environment: ActionEventEnvironment, spec: { ...; } | ... 1 more ... | { ...; }, data: ActionEventDataUnion<...>): ActionEvent<...>

environment

spec

type { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }

data

type ActionEventDataUnion<TMethod>

toJSON

type (): ActionEventDataUnion<TMethod>

returns ActionEventDataUnion<TMethod>

observe

Subscribe a listener fired on every data transition.

type (listener: ActionEventChangeObserver<TMethod>): () => void

listener

called with (new_data, old_data, event) after each mutation

type ActionEventChangeObserver<TMethod>
returns () => void

unsubscribe function

set_data

Replace the event's data and notify observers.

type (new_data: ActionEventDataUnion<TMethod>): void

new_data

type ActionEventDataUnion<TMethod>
returns void

parse

Parse input data according to the action's schema.

type (): this

returns this

this for chaining with handle_async / handle_sync

throws

  • Error - if called from a step other than `initial`

handle_async

Execute the handler for the current phase.

type (): Promise<void>

returns Promise<void>

throws

  • Error - if called from a step other than `parsed` (or `failed`,

handle_sync

Execute handler synchronously (only for sync local_call actions).

type (): void

returns void

throws

  • Error - if the spec is not a sync `local_call`, or if called

transition

Transition to a new phase.

type (phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"): void

phase

the next phase to transition into

type "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"
returns void

throws

  • Error - if called from a step other than `handled` (or

is_complete

type (): boolean

returns boolean

update_progress

type (progress: unknown): void

progress

type unknown
returns void

set_request

type (request: { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; }): void

request

type { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; }
returns void

set_response

type (response: { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<...>); message: string; data?: unknown; }; }): void

response

type { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<...>); message: string; data?: unknown; }; }
returns void

set_notification

type (notification: { [x: string]: unknown; jsonrpc: "2.0"; method: string; params?: { [x: string]: unknown; } | undefined; }): void

notification

type { [x: string]: unknown; jsonrpc: "2.0"; method: string; params?: { [x: string]: unknown; } | undefined; }
returns void

data

type ActionEventDataUnion<TMethod> & { phase: TPhase; step: TStep; }

getter

ActionEventChangeObserver
#

actions/action_event.ts view source

ActionEventChangeObserver<TMethod> import type {ActionEventChangeObserver} from '@fuzdev/fuz_app/actions/action_event.js';

generics

ActionEventChangeObserver<TMethod extends string = string>
TMethod
constraint string
default string

(call)

type (new_data: ActionEventDataUnion<TMethod>, old_data: ActionEventDataUnion<TMethod>, event: ActionEvent<TMethod, "send_request" | ... 7 more ... | "execute", "initial" | ... 3 more ... | "failed">): void

new_data

type ActionEventDataUnion<TMethod>

old_data

type ActionEventDataUnion<TMethod>

event

type ActionEvent<TMethod, "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute", "initial" | "parsed" | "handling" | "handled" | "failed">
returns void

ActionEventData
#

actions/action_event_data.ts view source

ZodObject<{ kind: ZodEnum<{ request_response: "request_response"; remote_notification: "remote_notification"; local_call: "local_call"; }>; phase: ZodEnum<{ send_request: "send_request"; ... 7 more ...; execute: "execute"; }>; ... 9 more ...; notification: ZodNullable<...>; }, $strict> import type {ActionEventData} from '@fuzdev/fuz_app/actions/action_event_data.js';

ActionEventDataUnion
#

actions/action_event_data.ts view source

ActionEventDataUnion<TMethod, TInput, TOutput> import type {ActionEventDataUnion} from '@fuzdev/fuz_app/actions/action_event_data.js';

generics

ActionEventDataUnion<TMethod extends string = string, TInput = unknown, TOutput = unknown>
TMethod
constraint string
default string
TInput
default unknown
TOutput
default unknown

ActionEventEnvironment
#

actions/action_event_types.ts view source

ActionEventEnvironment import type {ActionEventEnvironment} from '@fuzdev/fuz_app/actions/action_event_types.js';

executor

type ActionExecutor

readonly

lookup_action_handler

type ( method: string, phase: ActionEventPhase ) => ((event: any) => any) | undefined

lookup_action_spec

type (method: string) => ActionSpecUnion | undefined

log?

type Logger | null

readonly

ActionEventLocalCallData
#

actions/action_event_data.ts view source

ActionEventLocalCallData<TMethod, TInput, TOutput> import type {ActionEventLocalCallData} from '@fuzdev/fuz_app/actions/action_event_data.js';

generics

ActionEventLocalCallData<TMethod extends string = string, TInput = unknown, TOutput = unknown>
TMethod
constraint string
default string
TInput
default unknown
TOutput
default unknown

ActionEventOptions
#

ActionEventPhase
#

actions/action_spec.ts view source

ZodEnum<{ send_request: "send_request"; receive_request: "receive_request"; send_response: "send_response"; receive_response: "receive_response"; send_error: "send_error"; receive_error: "receive_error"; send: "send"; receive: "receive"; execute: "execute"; }> import type {ActionEventPhase} from '@fuzdev/fuz_app/actions/action_spec.js';

ActionEventRemoteNotificationData
#

actions/action_event_data.ts view source

ActionEventRemoteNotificationData<TMethod, TInput> import type {ActionEventRemoteNotificationData} from '@fuzdev/fuz_app/actions/action_event_data.js';

generics

ActionEventRemoteNotificationData<TMethod extends string = string, TInput = unknown>
TMethod
constraint string
default string
TInput
default unknown

ActionEventRequestResponseData
#

actions/action_event_data.ts view source

ActionEventRequestResponseData<TMethod, TInput, TOutput> import type {ActionEventRequestResponseData} from '@fuzdev/fuz_app/actions/action_event_data.js';

generics

ActionEventRequestResponseData<TMethod extends string = string, TInput = unknown, TOutput = unknown>
TMethod
constraint string
default string
TInput
default unknown
TOutput
default unknown

ActionEventStep
#

actions/action_event_types.ts view source

ZodEnum<{ initial: "initial"; parsed: "parsed"; handling: "handling"; handled: "handled"; failed: "failed"; }> import type {ActionEventStep} from '@fuzdev/fuz_app/actions/action_event_types.js';

ActionExecutor
#

ActionFactoryDeps
#

auth/deps.ts view source

ActionFactoryDeps import type {ActionFactoryDeps} from '@fuzdev/fuz_app/auth/deps.js';

Capabilities for action-spec factories — the "Action caps" shape.

The minimal slice every create_*_actions factory needs: a log for RPC-internal error logging and the bound audit emitter for fire-and-forget audit writes. RouteFactoryDeps (and AppDeps) satisfy it structurally, so consumers pass their fuller deps bundle straight through.

log

Structured logger instance.

type Logger

audit

Bound audit emitter for fire-and-forget audit writes.

type AuditEmitter

ActionHandler
#

actions/action_rpc.ts view source

ActionHandler<TInput, TOutput> import type {ActionHandler} from '@fuzdev/fuz_app/actions/action_rpc.js';

Handler function for an RPC action.

Receives validated input and an ActionContext with per-request deps. Returns the output value (serialized to JSON by the wrapper).

generics

ActionHandler<TInput = any, TOutput = any>
TInput
default any
TOutput
default any

(call)

type (input: TInput, ctx: ActionContext): TOutput | Promise<TOutput>

input

type TInput

ctx

returns TOutput | Promise<TOutput>

ActionInitiator
#

actions/action_spec.ts view source

ZodEnum<{ frontend: "frontend"; backend: "backend"; both: "both"; }> import type {ActionInitiator} from '@fuzdev/fuz_app/actions/action_spec.js';

ActionKind
#

actions/action_spec.ts view source

ZodEnum<{ request_response: "request_response"; remote_notification: "remote_notification"; local_call: "local_call"; }> import type {ActionKind} from '@fuzdev/fuz_app/actions/action_spec.js';

ActionManifest
#

testing/cross_backend/action_manifest.ts view source

ZodObject<{ methods: ZodArray<ZodObject<{ method: ZodString; side_effects: ZodBoolean; account: ZodEnum<{ none: "none"; optional: "optional"; required: "required"; }>; actor: ZodEnum<{ ...; }>; roles: ZodArray<...>; credential_types: ZodArray<...>; }, $strict>>; }, $strict> import type {ActionManifest} from '@fuzdev/fuz_app/testing/cross_backend/action_manifest.js';

The full action manifest — every entry, sorted by method.

ActionManifestDiff
#

ActionManifestDiffLabels
#

testing/cross_backend/action_manifest_parity.ts view source

ActionManifestDiffLabels import type {ActionManifestDiffLabels} from '@fuzdev/fuz_app/testing/cross_backend/action_manifest_parity.js';

Labels used in formatted output — defaults to 'a' and 'b'.

a?

type string

readonly

b?

type string

readonly

ActionManifestEntry
#

testing/cross_backend/action_manifest.ts view source

ZodObject<{ method: ZodString; side_effects: ZodBoolean; account: ZodEnum<{ none: "none"; optional: "optional"; required: "required"; }>; actor: ZodEnum<{ none: "none"; optional: "optional"; required: "required"; }>; roles: ZodArray<...>; credential_types: ZodArray<...>; }, $strict> import type {ActionManifestEntry} from '@fuzdev/fuz_app/testing/cross_backend/action_manifest.js';

One method's normalized RPC metadata — the cross-impl-comparable unit. roles / credential_types are always present + sorted (an absent gate and an empty list both serialize to []) so the diff never trips on a undefined-vs-[] or declaration-order difference between impls; the auth axes reuse the canonical AuthAxisState enum.

ActionMethodEnumKind
#

ActionRegistry
#

actions/action_registry.ts view source

import {ActionRegistry} from '@fuzdev/fuz_app/actions/action_registry.js';

specs

type Array<ActionSpecUnion>

readonly

constructor

type new (specs: ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; })[]): ActionRegistry

specs

type ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; ...

spec_by_method

type Map<string, { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<...>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }>

getter

methods

type string[]

getter

request_response_specs

type { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; }[]

getter

remote_notification_specs

type { method: string; initiator: "frontend" | "backend" | "both"; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; description: string; kind: "remote_notification"; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; }[]

getter

local_call_specs

type { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; }[]

getter

request_response_methods

type string[]

getter

remote_notification_methods

type string[]

getter

local_call_methods

type string[]

getter

specs_relevant_to_frontend

type ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; ...

getter

specs_relevant_to_backend

type ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; ...

getter

methods_relevant_to_frontend

type string[]

getter

methods_relevant_to_backend

type string[]

getter

frontend_handled_specs

type { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; }[]

getter

backend_handled_specs

type { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; }[]

getter

frontend_handled_methods

type string[]

getter

backend_handled_methods

type string[]

getter

broadcast_specs

type { method: string; initiator: "frontend" | "backend" | "both"; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; description: string; kind: "remote_notification"; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; }[]

getter

broadcast_methods

type string[]

getter

backend_initiated_specs

type ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; ...

getter

backend_initiated_methods

type string[]

getter

backend_to_frontend_specs

type ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; ...

getter

frontend_to_backend_specs

type ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; ...

getter

frontend_to_backend_methods

type string[]

getter

backend_to_frontend_methods

type string[]

getter

public_specs

type ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; ...

getter

authenticated_specs

type ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; ...

getter

public_methods

type string[]

getter

authenticated_methods

type string[]

getter

ActionRegistryCompileResult
#

actions/compile_action_registry.ts view source

ActionRegistryCompileResult import type {ActionRegistryCompileResult} from '@fuzdev/fuz_app/actions/compile_action_registry.js';

Result returned by compile_action_registry.

action_map

Method → RpcAction lookup for dispatch. Only request_response specs with a handler land here — kind-polymorphic input arrays (the WebSocket dispatcher's actions: ReadonlyArray<Action>) pass remote_notification / handler-less specs through unchanged.

type Map<string, RpcAction>

ActionRouteOptions
#

actions/action_bridge.ts view source

ActionRouteOptions import type {ActionRouteOptions} from '@fuzdev/fuz_app/actions/action_bridge.js';

Options for deriving a RouteSpec from an ActionSpec.

path

type string

handler

type RouteHandler

params?

URL path parameter schema. Use z.strictObject() with string fields matching :param segments.

type z.ZodObject

query?

URL query parameter schema. Use z.strictObject() with string fields.

type z.ZodObject

http_method?

Override the default HTTP method (default: side_effects → POST, else GET).

type RouteMethod

auth?

Override the route's auth shape — defaults to the action spec's auth (the canonical four-axis shape from http/auth_shape.ts is shared verbatim between action specs and route specs, so no mapping is needed).

type RouteAuth

errors?

Handler-specific error schemas (HTTP status code → Zod schema). Transport-specific — not on ActionSpec.

type RouteErrorSchemas

ActionSideEffects
#

ActionSpec
#

actions/action_spec.ts view source

ZodObject<{ method: ZodString; kind: ZodEnum<{ request_response: "request_response"; remote_notification: "remote_notification"; local_call: "local_call"; }>; initiator: ZodEnum<...>; ... 8 more ...; rate_limit: ZodOptional<...>; }, $strict> import type {ActionSpec} from '@fuzdev/fuz_app/actions/action_spec.js';

ActionSpecUnion
#

actions/action_spec.ts view source

ZodUnion<readonly [ZodObject<{ method: ZodString; initiator: ZodEnum<{ frontend: "frontend"; backend: "backend"; both: "both"; }>; side_effects: ZodBoolean; input: ZodCustom<ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>, ZodType<...>>; ... 7 more ...; async: ZodDefault<...>; }, $strict>, ZodObject<.... import type {ActionSpecUnion} from '@fuzdev/fuz_app/actions/action_spec.js';

Actor
#

auth/account_schema.ts view source

Actor import type {Actor} from '@fuzdev/fuz_app/auth/account_schema.js';

Actor — the entity that acts. Owns cells, holds role_grants, appears in audit trails.

id

type Uuid

account_id

type Uuid

name

type string

created_at

type string

updated_at

type string | null

updated_by

type Uuid | null

deleted_at

Soft-delete tombstone — set alongside the owning account's soft-delete.

type string | null

deleted_by

Actor that performed the soft-delete. Paired with deleted_at.

type Uuid | null

ACTOR_INDEX
#

auth/auth_ddl.ts view source

"\nCREATE INDEX IF NOT EXISTS idx_actor_account ON actor(account_id)" import {ACTOR_INDEX} from '@fuzdev/fuz_app/auth/auth_ddl.js';

actor_lookup_action_spec
#

auth/actor_lookup_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "none"; }; side_effects: false; input: ZodObject<{ ids: ZodArray<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict>; output: ZodObject<...>; async: true; rate_limit: "account"; description: string; } import {actor_lookup_action_spec} from '@fuzdev/fuz_app/auth/actor_lookup_action_specs.js';

ACTOR_LOOKUP_IDS_MAX
#

auth/actor_lookup_action_specs.ts view source

50 import {ACTOR_LOOKUP_IDS_MAX} from '@fuzdev/fuz_app/auth/actor_lookup_action_specs.js';

Hard cap on the number of ids resolvable in one call. Bounds the batched username-enumeration surface.

ACTOR_NAME_LOWER_INDEX
#

auth/auth_ddl.ts view source

"\nCREATE INDEX IF NOT EXISTS idx_actor_name_lower ON actor (LOWER(name) text_pattern_ops)" import {ACTOR_NAME_LOWER_INDEX} from '@fuzdev/fuz_app/auth/auth_ddl.js';

Functional index on LOWER(actor.name) supporting case-insensitive prefix search by actor_search (LOWER(name) LIKE LOWER(query) || '%'). text_pattern_ops keeps the LIKE-prefix pattern index-eligible — without it the planner falls back to a sequential scan once the table grows.

ACTOR_SCHEMA
#

auth/auth_ddl.ts view source

"\nCREATE TABLE IF NOT EXISTS actor (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n account_id UUID NOT NULL REFERENCES account(id) ON DELETE CASCADE,\n name TEXT NOT NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ,\n updated_by UUID REFERENCES actor(id) ON DELETE SET NULL,... import {ACTOR_SCHEMA} from '@fuzdev/fuz_app/auth/auth_ddl.js';

actor_search_action_spec
#

auth/actor_search_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "none"; }; side_effects: false; input: ZodObject<{ query: ZodString; scope_ids: ZodOptional<ZodArray<$ZodBranded<ZodUUID, "Uuid", "out">>>; limit: ZodOptional<...>; }, $strict>; ... 4 more ...; description: string; } import {actor_search_action_spec} from '@fuzdev/fuz_app/auth/actor_search_action_specs.js';

ACTOR_SEARCH_LIMIT_DEFAULT
#

ACTOR_SEARCH_LIMIT_MAX
#

ACTOR_SEARCH_QUERY_LENGTH_MAX
#

auth/actor_search_action_specs.ts view source

50 import {ACTOR_SEARCH_QUERY_LENGTH_MAX} from '@fuzdev/fuz_app/auth/actor_search_action_specs.js';

Hard cap on the query string length. Long inputs offer no extra search value once they exceed actor.name realistic lengths, and a low cap keeps the per-request work bounded for pathological inputs.

ActorActionHandler
#

actions/action_rpc.ts view source

ActorActionHandler<TInput, TOutput> import type {ActorActionHandler} from '@fuzdev/fuz_app/actions/action_rpc.js';

Handler signature for an actor-implying RPC action — auth.actor === 'required'. Mirrors ActionHandler but tightens the ctx.auth slot to the non-null RequestActorContext (with non-null actor).

generics

ActorActionHandler<TInput = any, TOutput = any>
TInput
default any
TOutput
default any

(call)

type (input: TInput, ctx: ActionActorContext): TOutput | Promise<TOutput>

input

type TInput

ctx

returns TOutput | Promise<TOutput>

ActorLookupActionDeps
#

ActorLookupCrossTestOptions
#

testing/cross_backend/actor_lookup.ts view source

RpcPathCrossSuiteOptions import type {ActorLookupCrossTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/actor_lookup.js';

Options for the actor-lookup parity suite. The standard RPC-dispatched cross-suite shape (setup_test / capabilities / rpc_path); aliases the shared RpcPathCrossSuiteOptions rather than minting a duplicate.

rpc_path?

RPC endpoint path the methods are mounted on. Default /api/rpc.

type string

readonly

setup_test

Per-test fixture-producing function (fresh keeper + db per call).

type (): Promise<TestFixtureBase>

readonly
returns Promise<TestFixtureBase>

capabilities

Backend capability declarations — each suite gates on its own flag.

type BackendCapabilities

readonly

ActorLookupEntryJson
#

auth/actor_lookup_action_specs.ts view source

ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; username: ZodString; display_name: ZodOptional<ZodString>; }, $strict> import type {ActorLookupEntryJson} from '@fuzdev/fuz_app/auth/actor_lookup_action_specs.js';

One resolved actor row. display_name omitted when blank.

ActorLookupInput
#

ActorLookupOutput
#

auth/actor_lookup_action_specs.ts view source

ZodObject<{ actors: ZodArray<ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; username: ZodString; display_name: ZodOptional<ZodString>; }, $strict>>; }, $strict> import type {ActorLookupOutput} from '@fuzdev/fuz_app/auth/actor_lookup_action_specs.js';

ActorLookupRow
#

auth/actor_lookup_queries.ts view source

ActorLookupRow import type {ActorLookupRow} from '@fuzdev/fuz_app/auth/actor_lookup_queries.js';

Row shape returned to handlers — wire mapping happens at the action layer.

id

type Uuid

username

type string

display_name

type string | null

ActorNotOnAccountError
#

http/error_schemas.ts view source

ZodObject<{ error: ZodLiteral<"actor_not_on_account">; }, $loose> import type {ActorNotOnAccountError} from '@fuzdev/fuz_app/http/error_schemas.js';

ActorRequiredError
#

http/error_schemas.ts view source

ZodObject<{ error: ZodLiteral<"actor_required">; available: ZodArray<ZodObject<{ id: ZodString; name: ZodString; }, $loose>>; }, $loose> import type {ActorRequiredError} from '@fuzdev/fuz_app/http/error_schemas.js';

Authorization-phase failure shapes. Surfaced when the dispatcher's apply_authorization_phase rejects a request before the handler runs — the route is acting-aware (input declares acting?: ActingActor or auth requires role_grants), but actor resolution failed.

400: actor_required (with available[]) for unspecified-actor on a multi-actor account; actor_not_on_account for a supplied actor id that doesn't belong to the authenticated account.

500: no_actors_on_account for a signup-invariant violation (the actor list enumerated empty); account_vanished for a torn-read race (account/actor row deleted between credential validation and the dispatcher's follow-up read).

Used by derive_error_schemas when auth.actor !== 'none' so the merged error surface matches what the dispatcher actually emits.

ActorSearchActionDeps
#

ActorSearchCrossTestOptions
#

testing/cross_backend/actor_search.ts view source

RpcPathCrossSuiteOptions import type {ActorSearchCrossTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/actor_search.js';

Options for the actor-search parity suite (the standard RPC-dispatched shape).

rpc_path?

RPC endpoint path the methods are mounted on. Default /api/rpc.

type string

readonly

setup_test

Per-test fixture-producing function (fresh keeper + db per call).

type (): Promise<TestFixtureBase>

readonly
returns Promise<TestFixtureBase>

capabilities

Backend capability declarations — each suite gates on its own flag.

type BackendCapabilities

readonly

ActorSearchInput
#

auth/actor_search_action_specs.ts view source

ZodObject<{ query: ZodString; scope_ids: ZodOptional<ZodArray<$ZodBranded<ZodUUID, "Uuid", "out">>>; limit: ZodOptional<ZodNumber>; }, $strict> import type {ActorSearchInput} from '@fuzdev/fuz_app/auth/actor_search_action_specs.js';

ActorSearchOutput
#

auth/actor_search_action_specs.ts view source

ZodObject<{ actors: ZodArray<ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; username: ZodString; display_name: ZodOptional<ZodString>; }, $strict>>; }, $strict> import type {ActorSearchOutput} from '@fuzdev/fuz_app/auth/actor_search_action_specs.js';

ActorSearchQueryInput
#

auth/actor_search_queries.ts view source

ActorSearchQueryInput import type {ActorSearchQueryInput} from '@fuzdev/fuz_app/auth/actor_search_queries.js';

Inputs for query_actor_search.

query

Case-insensitive prefix string. Must be non-empty (action layer enforces min(1)).

type string

scope_ids?

When non-empty, restrict to actors holding an active role_grant on one of these scope ids. When empty / omitted, no scope filter is applied — the handler is responsible for the admin gate.

type ReadonlyArray<Uuid>

limit

Maximum rows to return. The handler clamps to ACTOR_SEARCH_LIMIT_MAX.

type number

ActorSummaryJson
#

auth/account_schema.ts view source

ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; name: ZodString; }, $strict> import type {ActorSummaryJson} from '@fuzdev/fuz_app/auth/account_schema.js';

Zod schema for the actor summary returned in admin account listings.

admin_account_list_action_spec
#

auth/admin_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; roles: string[]; }; side_effects: false; input: ZodDefault<ZodObject<{ acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; limit: ZodOptional<...>; offset: ZodOptional<...>; include_deleted: ZodOp... import {admin_account_list_action_spec} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

rate_limit: 'account' bounds admin-side scraping of the account table via (limit, offset) walking — admin trust is not a substitute for a read-rate cap when the listing is paginated and cross-account (yields every account + actor + active role_grant in the system).

ADMIN_ACCOUNT_LIST_DEFAULT_LIMIT
#

ADMIN_ACCOUNT_LIST_LIMIT_MAX
#

admin_accounts_rpc_context
#

ui/admin_accounts_state.svelte.ts view source

{ get: (error_message?: string | undefined) => () => AdminAccountsRpc; get_maybe: () => (() => AdminAccountsRpc) | undefined; set: (value: () => AdminAccountsRpc) => () => AdminAccountsRpc; } import {admin_accounts_rpc_context} from '@fuzdev/fuz_app/ui/admin_accounts_state.svelte.js';

Svelte context carrying the reactive AdminAccountsRpc accessor. The provisioner (typically the admin route shell) calls set(() => rpc); consumers read with const get_rpc = admin_accounts_rpc_context.get(); and either pass the accessor straight to AdminAccountsState/ AdminSessionsState or wrap it with const rpc = $derived(get_rpc()); for direct RPC calls. get() throws when no provisioner ran above the component — the adapter is required, not optional.

admin_invites_rpc_context
#

ui/admin_invites_state.svelte.ts view source

{ get: (error_message?: string | undefined) => () => AdminInvitesRpc; get_maybe: () => (() => AdminInvitesRpc) | undefined; set: (value: () => AdminInvitesRpc) => () => AdminInvitesRpc; } import {admin_invites_rpc_context} from '@fuzdev/fuz_app/ui/admin_invites_state.svelte.js';

Svelte context carrying the reactive AdminInvitesRpc accessor. Mirrors admin_accounts_rpc_context. get() throws when no provisioner ran above the component — the adapter is required.

admin_only_field_blocklist
#

testing/integration_helpers.ts view source

readonly string[] import {admin_only_field_blocklist} from '@fuzdev/fuz_app/testing/integration_helpers.js';

Field names that must not appear in non-admin HTTP response bodies.

admin_session_list_action_spec
#

auth/admin_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; roles: string[]; }; side_effects: false; input: ZodDefault<ZodObject<{ acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict>>; output: ZodObject<...>; async: true; description: string; r... import {admin_session_list_action_spec} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

rate_limit: 'account' bounds cross-account scraping of every active auth_session row — no pagination, but the read is unbounded across accounts and reveals one row per live cookie globally.

admin_session_revoke_all_action_spec
#

auth/admin_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; roles: string[]; }; side_effects: true; input: ZodObject<{ account_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<...>; }, $strict>; output: ZodObject<...>; async: true; description: strin... import {admin_session_revoke_all_action_spec} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

admin_token_revoke_all_action_spec
#

auth/admin_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; roles: string[]; }; side_effects: true; input: ZodObject<{ account_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<...>; }, $strict>; output: ZodObject<...>; async: true; description: strin... import {admin_token_revoke_all_action_spec} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

AdminAccountEntryJson
#

auth/account_schema.ts view source

ZodObject<{ account: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; username: ZodPipe<ZodString, ZodTransform<string, string>>; ... 5 more ...; deleted_at: ZodNullable<...>; }, $strict>; actor: ZodNullable<...>; role_grants: ZodArray<...>; pending_offers: ZodArray<...>; }, $strict> import type {AdminAccountEntryJson} from '@fuzdev/fuz_app/auth/account_schema.js';

Zod schema for an admin account listing entry (account + actor + role_grants + pending offers).

AdminAccountJson
#

auth/account_schema.ts view source

ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; username: ZodPipe<ZodString, ZodTransform<string, string>>; email: ZodNullable<ZodString>; ... 4 more ...; deleted_at: ZodNullable<...>; }, $strict> import type {AdminAccountJson} from '@fuzdev/fuz_app/auth/account_schema.js';

Zod schema for admin-facing account data — extends SessionAccountJson with audit fields.

AdminAccountListInput
#

auth/admin_action_specs.ts view source

ZodDefault<ZodObject<{ acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; limit: ZodOptional<ZodNullable<ZodNumber>>; offset: ZodOptional<...>; include_deleted: ZodOptional<...>; }, $strict>> import type {AdminAccountListInput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Input for admin_account_list.

AdminAccountListOptions
#

auth/account_queries.ts view source

AdminAccountListOptions import type {AdminAccountListOptions} from '@fuzdev/fuz_app/auth/account_queries.js';

limit?

Max accounts to return. Defaults to ADMIN_ACCOUNT_LIST_DEFAULT_LIMIT when omitted; pass null explicitly to disable the limit (unbounded fetch — for trusted internal callers / scripts; the RPC schema bounds wire callers to [1, ADMIN_ACCOUNT_LIST_LIMIT_MAX]).

type number | null

offset?

Pagination offset. Defaults to 0.

type number | null

include_deleted?

Include soft-deleted (tombstoned) accounts. Defaults to false — the listing shows active accounts only, matching auth resolution. Set true for the admin UI's "show deleted" view, which offers reactivation via account_undelete.

type boolean | null

AdminAccountListOutput
#

auth/admin_action_specs.ts view source

ZodObject<{ accounts: ZodArray<ZodObject<{ account: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; username: ZodPipe<ZodString, ZodTransform<string, string>>; ... 5 more ...; deleted_at: ZodNullable<...>; }, $strict>; actor: ZodNullable<...>; role_grants: ZodArray<...>; pending_offers: ZodArray<...>; }, $stric... import type {AdminAccountListOutput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Output for admin_account_list.

AdminAccounts
#

AdminAccountsRpc
#

ui/admin_accounts_state.svelte.ts view source

AdminAccountsRpc import type {AdminAccountsRpc} from '@fuzdev/fuz_app/ui/admin_accounts_state.svelte.js';

Narrow RPC surface consumed by AdminAccountsState. Consumers adapt their typed RPC client (e.g. a create_rpc_client Proxy) to this shape — the state class stays decoupled from the client's Result return type so tests can inject plain-function stubs. Mirrors the RoleGrantOffersRpc pattern.

Every operation flows through RPC: the listing reuses admin_account_list, grant reuses role_grant_offer_create, revoke and retract have dedicated actions, and the session / token revoke-all mutations reuse admin_session_revoke_all and admin_token_revoke_all.

Method signatures track the underlying action specs — Uuid-branded ids propagate from the wire through the state class to the components. The adapter built by create_admin_rpc_adapters therefore needs zero casts to bridge to the typed throwing Proxy.

list_accounts

type (include_deleted?: boolean) => Promise<AdminAccountListOutput>

delete_account

type (account_id: Uuid) => Promise<AccountDeleteOutput>

undelete_account

type (account_id: Uuid) => Promise<AccountUndeleteOutput>

list_sessions

type () => Promise<AdminSessionListOutput>

create_role_grant

type (params: RoleGrantOfferCreateInput) => Promise<RoleGrantOfferCreateOutput>

revoke_role_grant

type (params: RoleGrantRevokeInput) => Promise<RoleGrantRevokeOutput>

retract_offer

type (offer_id: Uuid) => Promise<RoleGrantOfferOkOutput>

session_revoke_all

type (params: AdminSessionRevokeAllInput) => Promise<AdminSessionRevokeAllOutput>

token_revoke_all

type (params: AdminTokenRevokeAllInput) => Promise<AdminTokenRevokeAllOutput>

AdminAccountsState
#

ui/admin_accounts_state.svelte.ts view source

import {AdminAccountsState} from '@fuzdev/fuz_app/ui/admin_accounts_state.svelte.js';

list

type AsyncSlot<void, string>

readonly

grant

type KeyedAsyncSlot<string, { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; to_actor_id: (string & $brand<...>) | null; ... 11 more ...; resulting_role_grant_id: (string & $brand<...>) | null; }, string>

readonly

revoke

type KeyedAsyncSlot<string & $brand<"Uuid">, void, string>

readonly

retract

type KeyedAsyncSlot<string & $brand<"Uuid">, void, string>

readonly

soft_delete

type KeyedAsyncSlot<string & $brand<"Uuid">, void, string>

readonly

undelete

type KeyedAsyncSlot<string & $brand<"Uuid">, void, string>

readonly

accounts

type Array<AdminAccountEntryJson>

$state.raw

grantable_roles

type Array<RoleName>

$state.raw

show_deleted

When true, fetch() includes soft-deleted (tombstoned) accounts so the admin can reactivate them. Toggled via set_show_deleted.

type boolean

$state

account_count

type number

readonly $derived

constructor

type new (options: AdminAccountsStateOptions): AdminAccountsState

options

fetch

type (): Promise<void>

returns Promise<void>

set_show_deleted

Toggle whether soft-deleted accounts appear in the listing, then re-fetch. Tombstoned rows are surfaced so an admin can reactivate them via submit_undelete.

type (value: boolean): Promise<void>

value

type boolean
returns Promise<void>

submit_delete

Soft-delete an account (reversible tombstone) via account_delete. Keyed by account_id so per-row spinners/errors stay independent. Refreshes the listing on success so the row drops out (active view) or flips to its tombstoned state (show_deleted view).

type (account_id: string & $brand<"Uuid">): Promise<void>

account_id

type string & $brand<"Uuid">
returns Promise<void>

submit_undelete

Reactivate a soft-deleted account via account_undelete (admin-only). Keyed by account_id; refreshes the listing on success so the row returns to active state.

type (account_id: string & $brand<"Uuid">): Promise<void>

account_id

type string & $brand<"Uuid">
returns Promise<void>

submit_grant

Offer the role to the recipient via the role_grant_offer_create RPC. Server returns the pending offer; the recipient must accept before the role_grant materializes. Returns the offer payload on success so callers can drive follow-up UX (e.g. seed RoleGrantOffersState.outgoing).

A re-offer from the same admin to the same (account, role) refreshes the existing pending row — the returned offer id is stable across those calls.

to_actor_id (optional) narrows the offer to a specific actor on account_id; the keyed-slot key stays at account_id:role for the account-grain default (so existing consumers keep working) and becomes account_id:role:to_actor_id when actor-targeted, so the two variants can be in flight without colliding on the per-row spinner.

type (account_id: string & $brand<"Uuid">, role: string, to_actor_id?: (string & $brand<"Uuid">) | null | undefined): Promise<{ id: string & $brand<"Uuid">; ... 14 more ...; resulting_role_grant_id: (string & $brand<...>) | null; } | undefined>

account_id

type string & $brand<"Uuid">

role

type string

to_actor_id?

type (string & $brand<"Uuid">) | null | undefined
optional
returns Promise<{ id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; to_actor_id: (string & $brand<"Uuid">) | null; ... 11 more ...; resulting_role_grant_id: (string & $brand<...>) | null; } | undefined>

submit_revoke

Revoke an active role_grant via the role_grant_revoke RPC.

actor_id is the natural key — role_grants are actor-scoped, and the admin UI reads row.actor.id straight from the listing, so the state class takes it directly rather than deriving it from account_id. The optional reason is stamped on role_grant.revoked_reason and surfaced on the revokee's WS notification.

type (actor_id: string & $brand<"Uuid">, role_grant_id: string & $brand<"Uuid">, reason?: string | null | undefined): Promise<void>

actor_id

type string & $brand<"Uuid">

role_grant_id

type string & $brand<"Uuid">

reason?

type string | null | undefined
optional
returns Promise<void>

submit_retract

Retract a pending offer the admin issued via the role_grant_offer_retract RPC. The action handles auth, audit, and the role_grant_offer_retracted WS notification.

After success, refetches the listing so pending_offers drops the row and the "+ {role}" button un-hides.

type (offer_id: string & $brand<"Uuid">): Promise<void>

offer_id

type string & $brand<"Uuid">
returns Promise<void>

AdminAccountsStateOptions
#

ui/admin_accounts_state.svelte.ts view source

AdminAccountsStateOptions import type {AdminAccountsStateOptions} from '@fuzdev/fuz_app/ui/admin_accounts_state.svelte.js';

get_rpc

Reactive accessor for the RPC adapter. Matches RoleGrantOffersStateOptions.account_id / actor_id pattern — lets the component pass a $props()-sourced rpc without tripping Svelte's state_referenced_locally warning.

type () => AdminAccountsRpc

AdminActionOptions
#

auth/admin_actions.ts view source

AdminActionOptions import type {AdminActionOptions} from '@fuzdev/fuz_app/auth/admin_actions.js';

roles?

Role schema result from create_role_schema(). Defaults to builtin roles only. Used to derive grantable_roles (the subset whose RoleSpec.grant_paths includes 'admin') returned by admin_account_list.

type RoleSchemaResult

connection_closer?

Live-connection closer — when set, admin_session_revoke_all and admin_token_revoke_all handlers eagerly close affected WebSocket sockets for the target account BEFORE emitting the corresponding audit event. Mirrors the self-service surface (see AccountActionOptions.connection_closer). BackendWebsocketTransport satisfies this interface structurally. When absent, only the listener-based close (transports_ws_auth_guard) runs.

type ConnectionCloser | null

AdminAuditLog
#

AdminInvites
#

AdminInvitesRpc
#

ui/admin_invites_state.svelte.ts view source

AdminInvitesRpc import type {AdminInvitesRpc} from '@fuzdev/fuz_app/ui/admin_invites_state.svelte.js';

Narrow RPC surface consumed by AdminInvitesState. Consumers adapt their typed RPC client to this shape. error.data.reason on thrown errors carries the ERROR_INVITE_* constant — handled by the caller when user-friendly messages are needed. Method signatures track the wire spec types directly so the adapter needs no casts.

list

type () => Promise<InviteListOutput>

create

type (params: InviteCreateInput) => Promise<InviteCreateOutput>

delete

type (params: InviteDeleteInput) => Promise<InviteDeleteOutput>

AdminInvitesState
#

ui/admin_invites_state.svelte.ts view source

import {AdminInvitesState} from '@fuzdev/fuz_app/ui/admin_invites_state.svelte.js';

list

type AsyncSlot<void, string>

readonly

create

type AsyncSlot<void, string>

readonly

remove

type KeyedAsyncSlot<string & $brand<"Uuid">, void, string>

readonly

invites

type Array<InviteWithUsernamesJson>

$state.raw

invite_count

type number

readonly $derived

unclaimed_count

type number

readonly $derived

constructor

type new (options: AdminInvitesStateOptions): AdminInvitesState

options

fetch

type (): Promise<void>

returns Promise<void>

submit_create

type (email?: string | undefined, username?: string | undefined): Promise<boolean>

email?

type string | undefined
optional

username?

type string | undefined
optional
returns Promise<boolean>

submit_delete

type (id: string & $brand<"Uuid">): Promise<void>

id

type string & $brand<"Uuid">
returns Promise<void>

AdminInvitesStateOptions
#

ui/admin_invites_state.svelte.ts view source

AdminInvitesStateOptions import type {AdminInvitesStateOptions} from '@fuzdev/fuz_app/ui/admin_invites_state.svelte.js';

get_rpc

Reactive accessor for the RPC adapter.

type () => AdminInvitesRpc

AdminOverview
#

AdminRoleGrantHistory
#

AdminRpcAdapters
#

AdminRpcApi
#

ui/admin_rpc_adapters.ts view source

AdminRpcApi import type {AdminRpcApi} from '@fuzdev/fuz_app/ui/admin_rpc_adapters.js';

The wire-method surface this module needs from the typed throwing RPC client. Every method returns the unwrapped value or throws an Error carrying the JSON-RPC {code, message, data?} shape — i.e. the ThrowingApi<...> view of the corresponding action specs.

Consumers pass the typed throwing Proxy returned by create_frontend_rpc_client directly. Structural typing means any superset (e.g. the consumer's full ThrowingApi<ActionsApi>) is assignable as long as these methods are present at these signatures.

admin_account_list

type (input?: AdminAccountListInput) => Promise<AdminAccountListOutput>

account_delete

type (input: AccountDeleteInput) => Promise<AccountDeleteOutput>

account_undelete

type (input: AccountUndeleteInput) => Promise<AccountUndeleteOutput>

admin_session_list

type () => Promise<AdminSessionListOutput>

admin_session_revoke_all

type ( input: AdminSessionRevokeAllInput ) => Promise<AdminSessionRevokeAllOutput>

admin_token_revoke_all

type (input: AdminTokenRevokeAllInput) => Promise<AdminTokenRevokeAllOutput>

audit_log_list

type (input: AuditLogListInput) => Promise<AuditLogListOutput>

audit_log_role_grant_history

type ( input: AuditLogRoleGrantHistoryInput ) => Promise<AuditLogRoleGrantHistoryOutput>

invite_list

type () => Promise<InviteListOutput>

invite_create

type (input: InviteCreateInput) => Promise<InviteCreateOutput>

invite_delete

type (input: InviteDeleteInput) => Promise<InviteDeleteOutput>

app_settings_get

type () => Promise<AppSettingsGetOutput>

app_settings_update

type (input: AppSettingsUpdateInput) => Promise<AppSettingsUpdateOutput>

role_grant_offer_create

type ( input: RoleGrantOfferCreateInput ) => Promise<RoleGrantOfferCreateOutput>

role_grant_offer_retract

type (input: RoleGrantOfferRetractInput) => Promise<RoleGrantOfferOkOutput>

role_grant_revoke

type (input: RoleGrantRevokeInput) => Promise<RoleGrantRevokeOutput>

AdminSessionJson
#

auth/audit_log_schema.ts view source

ZodObject<{ id: $ZodBranded<ZodString, "SessionId", "out">; account_id: $ZodBranded<ZodUUID, "Uuid", "out">; created_at: ZodString; expires_at: ZodString; last_seen_at: ZodString; username: ZodString; }, $strict> import type {AdminSessionJson} from '@fuzdev/fuz_app/auth/audit_log_schema.js';

Zod schema for admin session listing (session + username).

AdminSessionListInput
#

auth/admin_action_specs.ts view source

ZodDefault<ZodObject<{ acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict>> import type {AdminSessionListInput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Input for admin_session_list.

AdminSessionListOutput
#

auth/admin_action_specs.ts view source

ZodObject<{ sessions: ZodArray<ZodObject<{ id: $ZodBranded<ZodString, "SessionId", "out">; account_id: $ZodBranded<ZodUUID, "Uuid", "out">; created_at: ZodString; expires_at: ZodString; last_seen_at: ZodString; username: ZodString; }, $strict>>; }, $strict> import type {AdminSessionListOutput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Output for admin_session_list. Cross-account listing; fan-out already scoped by role auth.

AdminSessionRevokeAllInput
#

auth/admin_action_specs.ts view source

ZodObject<{ account_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict> import type {AdminSessionRevokeAllInput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Input for admin_session_revoke_all.

AdminSessionRevokeAllOutput
#

auth/admin_action_specs.ts view source

ZodObject<{ ok: ZodLiteral<true>; count: ZodNumber; }, $strict> import type {AdminSessionRevokeAllOutput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Output for admin_session_revoke_all.

AdminSessions
#

AdminSessionsState
#

ui/admin_sessions_state.svelte.ts view source

import {AdminSessionsState} from '@fuzdev/fuz_app/ui/admin_sessions_state.svelte.js';

list

type AsyncSlot<void, string>

readonly

revoke_sessions

type KeyedAsyncSlot<string & $brand<"Uuid">, void, string>

readonly

revoke_tokens

type KeyedAsyncSlot<string & $brand<"Uuid">, void, string>

readonly

sessions

type Array<AdminSessionJson>

$state.raw

active_count

type number

readonly $derived

constructor

type new (options: AdminSessionsStateOptions): AdminSessionsState

options

fetch

type (): Promise<void>

returns Promise<void>

submit_revoke_sessions

type (account_id: string & $brand<"Uuid">): Promise<void>

account_id

type string & $brand<"Uuid">
returns Promise<void>

submit_revoke_tokens

type (account_id: string & $brand<"Uuid">): Promise<void>

account_id

type string & $brand<"Uuid">
returns Promise<void>

AdminSessionsStateOptions
#

ui/admin_sessions_state.svelte.ts view source

AdminSessionsStateOptions import type {AdminSessionsStateOptions} from '@fuzdev/fuz_app/ui/admin_sessions_state.svelte.js';

Options for AdminSessionsState.

The RPC adapter drives every operation (listing + the two revoke-all mutations).

get_rpc

Reactive accessor for the RPC adapter. Mirrors AdminAccountsStateOptions.get_rpc so a single adapter instance backs both states without tripping Svelte's state_referenced_locally warning.

type () => AdminAccountsRpc

AdminSettings
#

AdminSurface
#

AdminTokenRevokeAllInput
#

auth/admin_action_specs.ts view source

ZodObject<{ account_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict> import type {AdminTokenRevokeAllInput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Input for admin_token_revoke_all.

AdminTokenRevokeAllOutput
#

auth/admin_action_specs.ts view source

ZodObject<{ ok: ZodLiteral<true>; count: ZodNumber; }, $strict> import type {AdminTokenRevokeAllOutput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Output for admin_token_revoke_all.

AdversarialHeaderCase
#

testing/adversarial_headers.ts view source

AdversarialHeaderCase import type {AdversarialHeaderCase} from '@fuzdev/fuz_app/testing/adversarial_headers.js';

A header-level attack case for middleware stack testing.

name

type string

headers

type Record<string, string>

expected_status

type number

expected_error?

type string

expected_error_schema?

Zod schema to validate error response body against. Defaults to ApiError when expected_error is set.

type z.ZodType

validate_expectation

Whether the request should reach token validation or be short-circuited by earlier middleware.

type 'called' | 'not_called'

AdversarialTestOptions
#

testing/attack_surface.ts view source

AdversarialTestOptions import type {AdversarialTestOptions} from '@fuzdev/fuz_app/testing/attack_surface.js';

Options for adversarial test runners (auth enforcement and input validation).

build

Build the app surface bundle (surface + route specs + middleware specs).

type () => AppSurfaceSpec

roles

All roles in the app (e.g. ['admin', 'keeper']).

type Array<string>

Alignment
#

all_account_action_specs
#

auth/account_action_specs.ts view source

{ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; }[] import {all_account_action_specs} from '@fuzdev/fuz_app/auth/account_action_specs.js';

All self-service account action specs — a codegen-ready registry. Consumers spread this into their own action-spec array to include account methods in a typed client surface.

all_actor_lookup_action_specs
#

auth/actor_lookup_action_specs.ts view source

readonly [{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "none"; }; side_effects: false; input: ZodObject<{ ids: ZodArray<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict>; output: ZodObject<...>; async: true; rate_limit: "account"; description: string; }] import {all_actor_lookup_action_specs} from '@fuzdev/fuz_app/auth/actor_lookup_action_specs.js';

All actor_lookup action specs — independent opt-in registry. Consumers spread alongside all_standard_action_specs if they want the labels arc; not folded into the standard bundle because consumers without a byline surface can skip it.

all_actor_search_action_specs
#

auth/actor_search_action_specs.ts view source

readonly [{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "none"; }; side_effects: false; input: ZodObject<{ query: ZodString; scope_ids: ZodOptional<ZodArray<$ZodBranded<...>>>; limit: ZodOptional<...>; }, $strict>; ... 4 more ...; description: string; }] import {all_actor_search_action_specs} from '@fuzdev/fuz_app/auth/actor_search_action_specs.js';

All actor_search action specs — independent opt-in registry. Like all_actor_lookup_action_specs, not folded into all_standard_action_specs because consumers without a person-target picker can skip it.

all_admin_action_specs
#

auth/admin_action_specs.ts view source

{ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; }[] import {all_admin_action_specs} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

All admin action specs — a codegen-ready registry. Consumers spread this into their own action-spec array to include admin methods in a typed client surface. Includes the two app-settings specs, whose handlers the runtime factory always wires.

all_cell_action_specs
#

auth/cell_action_specs.ts view source

readonly [{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ data: ZodObject<{ label: ZodOptional<ZodString>; summary: ZodOptional<...>; }, $loose>; ... 4 more ...; acting: ZodOptional<...>; }, $strict>; output:... import {all_cell_action_specs} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

All cell-layer action specs — composed by app registries. Bundles the seven generic verbs (this module), the three cell_grant_* specs, the three cell_field_* specs, the four cell_item_* specs, and the cell_audit_list spec so codegen + UI clients see a single cell namespace.

all_cell_audit_action_specs
#

auth/cell_audit_action_specs.ts view source

readonly [{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: false; input: ZodObject<{ cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<...>; }, $strict>; output: ZodObject<...>; async: true; description: string; }] import {all_cell_audit_action_specs} from '@fuzdev/fuz_app/auth/cell_audit_action_specs.js';

Registry export to compose into all_cell_action_specs.

all_cell_field_action_specs
#

auth/cell_field_action_specs.ts view source

readonly [{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ source_id: $ZodBranded<ZodUUID, "Uuid", "out">; name: $ZodBranded<...>; target_id: $ZodBranded<...>; acting: ZodOptional<...>; }, $strict>; output: Zo... import {all_cell_field_action_specs} from '@fuzdev/fuz_app/auth/cell_field_action_specs.js';

All cell_field action specs — composed into all_cell_action_specs.

all_cell_grant_action_specs
#

auth/cell_grant_action_specs.ts view source

readonly [{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; level: ZodEnum<...>; principal: ZodDiscriminatedUnion<...>; acting: ZodOptional<...>; }, $strict>; outpu... import {all_cell_grant_action_specs} from '@fuzdev/fuz_app/auth/cell_grant_action_specs.js';

All cell_grant action specs — composed into all_cell_action_specs.

all_cell_item_action_specs
#

auth/cell_item_action_specs.ts view source

readonly [{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ parent_id: $ZodBranded<ZodUUID, "Uuid", "out">; child_id: $ZodBranded<...>; position: $ZodBranded<...>; acting: ZodOptional<...>; }, $strict>; output:... import {all_cell_item_action_specs} from '@fuzdev/fuz_app/auth/cell_item_action_specs.js';

All cell_item action specs — composed into all_cell_action_specs.

all_fuz_auth_action_spec_registries
#

auth/all_action_spec_registries.ts view source

readonly FuzAuthActionSpecRegistry[] import {all_fuz_auth_action_spec_registries} from '@fuzdev/fuz_app/auth/all_action_spec_registries.js';

Every fuz_auth action-spec registry, in dependency-stable order.

Update this list when a new fuz_auth registry lands. The walker tests (action_spec_input_invariants.test.ts, all_action_spec_registries.acting_biconditional.test.ts) iterate over it — a missing entry silently skips coverage, which is the failure mode the registry-of-registries shape exists to prevent.

all_role_grant_offer_action_specs
#

auth/role_grant_offer_action_specs.ts view source

{ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; }[] import {all_role_grant_offer_action_specs} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

All role-grant-offer action specs — a codegen-ready registry. Consumers spread this into their own action-spec array to include offer lifecycle + revoke + assign methods in a typed client surface.

all_self_service_role_action_specs
#

auth/self_service_role_action_specs.ts view source

readonly { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; }[] import {all_self_service_role_action_specs} from '@fuzdev/fuz_app/auth/self_service_role_action_specs.js';

All self-service role action specs — a codegen-ready registry. Single-element post-unification, kept for symmetry with the other all_*_action_specs exports so codegen and frontend bundles import the same shape.

all_standard_action_specs
#

auth/standard_action_specs.ts view source

readonly { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; }[] import {all_standard_action_specs} from '@fuzdev/fuz_app/auth/standard_action_specs.js';

Combined spec registry for the standard RPC surface (admin + role_grant_offer + account). Symmetric with create_standard_rpc_actions.

Spec count is the sum of the three sub-registries. Adding a method to any sub-registry surfaces here automatically.

AllCellActionsOptions
#

API_TOKEN_ID_REGEX
#

auth/api_token.ts view source

RegExp import {API_TOKEN_ID_REGEX} from '@fuzdev/fuz_app/auth/api_token.js';

Regex for the public API token id (e.g. tok_abC0_d-3xyzA). Twelve base64url characters after the tok_ prefix. Matches the format produced by generate_api_token.

API_TOKEN_INDEX
#

auth/auth_ddl.ts view source

"\nCREATE INDEX IF NOT EXISTS idx_api_token_account ON api_token(account_id)" import {API_TOKEN_INDEX} from '@fuzdev/fuz_app/auth/auth_ddl.js';

API_TOKEN_PREFIX
#

auth/api_token.ts view source

"secret_fuz_token_" import {API_TOKEN_PREFIX} from '@fuzdev/fuz_app/auth/api_token.js';

Prefix for all fuz API tokens (enables secret scanning).

API_TOKEN_SCHEMA
#

auth/auth_ddl.ts view source

"\nCREATE TABLE IF NOT EXISTS api_token (\n id TEXT PRIMARY KEY,\n account_id UUID NOT NULL REFERENCES account(id) ON DELETE CASCADE,\n name TEXT NOT NULL,\n token_hash TEXT NOT NULL,\n expires_at TIMESTAMPTZ,\n last_used_at TIMESTAMPTZ,\n last_used_ip TEXT,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n)" import {API_TOKEN_SCHEMA} from '@fuzdev/fuz_app/auth/auth_ddl.js';

ApiError
#

http/error_schemas.ts view source

ZodObject<{ error: ZodString; }, $loose> import type {ApiError} from '@fuzdev/fuz_app/http/error_schemas.js';

Base API error — all JSON error responses have at least {error: string}.

ApiToken
#

auth/account_schema.ts view source

ApiToken import type {ApiToken} from '@fuzdev/fuz_app/auth/account_schema.js';

API token for CLI/programmatic access.

id

type string

account_id

type Uuid

name

type string

token_hash

type string

expires_at

type string | null

last_used_at

type string | null

last_used_ip

type string | null

created_at

type string

ApiTokenId
#

auth/api_token.ts view source

ZodString import type {ApiTokenId} from '@fuzdev/fuz_app/auth/api_token.js';

Zod schema for the public API token id.

ApiTokenQueryDeps
#

app_settings_get_action_spec
#

auth/admin_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; roles: string[]; }; side_effects: false; input: ZodDefault<ZodObject<{ acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict>>; output: ZodObject<...>; async: true; description: string; } import {app_settings_get_action_spec} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

app_settings_rpc_context
#

ui/app_settings_state.svelte.ts view source

{ get: (error_message?: string | undefined) => () => AppSettingsRpc; get_maybe: () => (() => AppSettingsRpc) | undefined; set: (value: () => AppSettingsRpc) => () => AppSettingsRpc; } import {app_settings_rpc_context} from '@fuzdev/fuz_app/ui/app_settings_state.svelte.js';

Svelte context carrying the reactive AppSettingsRpc accessor. Mirrors admin_accounts_rpc_context. get() throws when no provisioner ran above the component — the adapter is required.

APP_SETTINGS_SCHEMA
#

auth/auth_ddl.ts view source

"\nCREATE TABLE IF NOT EXISTS app_settings (\n id INTEGER PRIMARY KEY DEFAULT 1 CHECK (id = 1),\n open_signup BOOLEAN NOT NULL DEFAULT false,\n updated_at TIMESTAMPTZ,\n updated_by UUID\n)" import {APP_SETTINGS_SCHEMA} from '@fuzdev/fuz_app/auth/auth_ddl.js';

APP_SETTINGS_SEED
#

auth/auth_ddl.ts view source

"\nINSERT INTO app_settings (id) VALUES (1) ON CONFLICT DO NOTHING" import {APP_SETTINGS_SEED} from '@fuzdev/fuz_app/auth/auth_ddl.js';

app_settings_update_action_spec
#

auth/admin_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; roles: string[]; }; side_effects: true; input: ZodObject<{ open_signup: ZodBoolean; acting: ZodOptional<...>; }, $strict>; output: ZodObject<...>; async: true; description: string; rate_limit: "account"... import {app_settings_update_action_spec} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

AppBackend
#

server/app_backend.ts view source

AppBackend import type {AppBackend} from '@fuzdev/fuz_app/server/app_backend.js';

Result of create_app_backend() — database metadata + deps bundle.

This is the initialized backend, not the HTTP server. Pass it to create_app_server() to assemble the Hono app.

deps

type AppDeps

db_type

type DbType

db_name

type string

migration_results

Migration results from create_app_backend — auth migrations plus any consumer namespaces passed via migration_namespaces.

type ReadonlyArray<MigrationResult>

readonly

close

Close the database connection. Bound to the actual driver.

type () => Promise<void>

AppDeps
#

auth/deps.ts view source

AppDeps import type {AppDeps} from '@fuzdev/fuz_app/auth/deps.js';

Stateless capabilities bundle for fuz_app backends.

Injectable and swappable per environment (production vs test). Does not contain config (static values) or runtime state (mutable refs).

stat

Get file/directory stats, or null if path doesn't exist.

type (path: string) => Promise<StatResult | null>

read_text_file

Read a file as text.

type (path: string) => Promise<string>

delete_file

Delete a file.

type (path: string) => Promise<void>

keyring

HMAC-SHA256 cookie signing keyring.

type Keyring

password

Password hashing operations. Use argon2_password_deps in production.

type PasswordHashDeps

db

Database instance.

type Db

log

Structured logger instance.

type Logger

audit

Bound audit emitter. Closes over the pool, its registered listeners, and the optional AuditLogConfig. Built once at backend assembly via create_audit_emitter so handlers can never accidentally write audits against the request transaction — there is no pool slot on the handler context.

type AuditEmitter

fact_store?

Optional content-addressed byte store. Present only on backends that serve binary content (facts) — minimal consumers leave it unset. The consumer constructs a PgFactStore (db/fact_store.ts) wired to a file_fact_fetcher (server/file_fact_fetcher.ts) at its own backend assembly and assigns it here; create_app_backend stays facts-agnostic.

type FactStore

apply_authorization_phase
#

auth/request_context.ts view source

(deps: QueryDeps, account_id: string | null, auth: { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }, acting_value: string | undefined): Promise<...> import {apply_authorization_phase} from '@fuzdev/fuz_app/auth/request_context.js';

Apply the dispatcher's authorization phase against the flat-record RouteAuth shape. Shared by the route-spec wrapper, the HTTP RPC dispatcher, and the per-message WS dispatcher. Phase order: pre-validation 401 → input validation 400 → authorization phase → post-authorization 403.

Pure data — the function does not touch a Hono context. Each transport passes account_id (extracted from its own credential surface) and binds the returned AuthorizationResult to its wire shape. The REST pipeline additionally writes REQUEST_CONTEXT_KEY on c for downstream require_role / require_credential_types middleware that still reads the resolved context off the Hono context.

Branching by auth.account × auth.actor:

  • Both 'none'{ok: true, request_context: null}. Public actions never see a RequestContext.
  • account_id == null on any non-public route → same null request_context. The 'required' callers were already rejected at the pre-validation gate in the dispatcher; only genuine anonymous access on an 'optional' axis lands here.
  • actor === 'none' → builds account-only context via build_account_context. Null lookup → account_vanished 500 failure.
  • actor === 'required' → resolves the actor from acting_value (or single-actor account); failures map to 400 / 500.
  • actor === 'optional' → same as 'required' except multi-actor accounts without an acting value fall back to account-only context (no actor_required 400). Bad acting ids still 400.

500 branches stay distinct: ERROR_NO_ACTORS_ON_ACCOUNT (signup invariant violation), ERROR_ACCOUNT_VANISHED (torn read after resolve).

deps

account_id

type string | null

auth

type { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }

acting_value

type string | undefined

returns

Promise<AuthorizationResult>

apply_middleware_specs
#

http/route_spec.ts view source

(app: Hono<BlankEnv, BlankSchema, "/">, specs: MiddlewareSpec[]): void import {apply_middleware_specs} from '@fuzdev/fuz_app/http/route_spec.js';

Apply named middleware specs to a Hono app.

app

type Hono<BlankEnv, BlankSchema, "/">

specs

type MiddlewareSpec[]

returns

void

apply_route_specs
#

http/route_spec.ts view source

(app: Hono<BlankEnv, BlankSchema, "/">, specs: RouteSpec[], resolve_auth_guards: AuthGuardResolver, log: Logger, db: Db, authorize?: AuthorizationHandler | undefined): void import {apply_route_specs} from '@fuzdev/fuz_app/http/route_spec.js';

Apply route specs to a Hono app.

For each spec: resolves auth to guards via the provided resolver, adds input validation middleware (for routes with non-null input schemas), runs the optional authorization phase to resolve the acting actor + build the request context, wraps handler with DEV-only output and error validation, wraps with error catch layer (catches ThrownJsonrpcError and generic errors), and registers the route.

Per-route middleware order: params → query → pre-validation auth guards (401) → input validation (400) → authorization phase → post-authorization auth guards (403) → handler. The 401 check runs before any body parsing so unauthenticated callers never see route-shape information from parse failures. Input validation runs before the authorization phase (validate first, authorize after) so the authorization phase reads c.var.validated_input.acting as a typed Zod field instead of pre-parsing the raw body. Role / credential-type denials still surface 403 last; trade-off is that authenticated-but-unauthorized callers can distinguish 400 (validation) from 403 (authorization), a defense-in-depth concession deemed acceptable because the route surface is public via spec/codegen JSON.

Each handler receives a RouteContext with:

  • db: transaction-scoped when RouteSpec.transaction is true; pool-level otherwise
  • pending_effects: eager fire-and-forget pool-write queue
  • post_commit_effects: deferred-thunk queue (push via emit_after_commit)

Also enforces registry-time invariant 2 from the auth-shape design: auth.actor !== 'none' ⟺ input or query declares acting?: ActingActor. REST is bi-located (GETs declare acting on query, mutations on input), so the check passes both slots; the action-dispatcher registries (compile_action_registry) share the same helper with input only — ActionSpec has no query shape.

app

type Hono<BlankEnv, BlankSchema, "/">

specs

type RouteSpec[]

resolve_auth_guards

log

type Logger

db

used for transaction wrapping and RouteContext

type Db

authorize?

optional authorization phase; runs after input validation

type AuthorizationHandler | undefined
optional

returns

void

throws

  • Error - if two specs share the same `method` + `path` (each combination must be unique), or if any spec violates the actor-acting biconditional

AppServer
#

server/app_server.ts view source

AppServer import type {AppServer} from '@fuzdev/fuz_app/server/app_server.js';

Result of create_app_server().

app

type Hono

surface_spec

Surface spec — serializable surface + raw specs that produced it.

type AppSurfaceSpec

bootstrap_status

type BootstrapStatus

migration_results

Migration results from create_app_backend (auth + any migration_namespaces passed there).

type ReadonlyArray<MigrationResult>

audit_sse

Factory-managed audit log SSE. Non-null when the audit_log_sse option was passed to create_app_server, null when omitted. Use require_audit_sse(server) to assert the invariant.

type AuditLogSse | null

ws_endpoints

Path-keyed map of mounted WS endpoints. Each value is the BackendWebsocketTransport create_app_server registered connections against — supplied via WsEndpointSpec.transport or auto-created when omitted. Retain for broadcast / fan-out:

app_server.ws_endpoints['/api/ws'].send_to_account(account_id, msg);

Empty when no ws_endpoints were mounted.

type Readonly<Record<string, BackendWebsocketTransport>>

close

Close the database connection. Propagated from AppBackend.

type () => Promise<void>

AppServerContext
#

server/app_server_context.ts view source

AppServerContext import type {AppServerContext} from '@fuzdev/fuz_app/server/app_server_context.js';

Context passed to create_route_specs.

deps

type AppDeps

backend

type AppBackend

bootstrap_status

type BootstrapStatus

session_options

type SessionOptions<string>

ip_rate_limiter

Shared IP rate limiter (from options). null when not configured.

type RateLimiter | null

login_account_rate_limiter

Per-account login rate limiter (from options). null when not configured.

type RateLimiter | null

signup_account_rate_limiter

Per-account signup rate limiter (from options). null when not configured.

type RateLimiter | null

action_ip_rate_limiter

Per-IP action-dispatcher rate limiter — shared across HTTP RPC + WS. null when not configured.

type RateLimiter | null

action_account_rate_limiter

Per-actor action-dispatcher rate limiter — shared across HTTP RPC + WS. null when not configured.

type RateLimiter | null

audit_sse

Factory-managed audit log SSE. Non-null when the audit_log_sse option was passed to create_app_server, null when omitted. Use require_audit_sse(ctx) to assert the invariant.

type AuditLogSse | null

AppServerOptions
#

server/app_server.ts view source

AppServerOptions import type {AppServerOptions} from '@fuzdev/fuz_app/server/app_server.js';

Configuration for create_app_server().

Requires a pre-initialized AppBackend from create_app_backend(). Two explicit steps: init backend then assemble server.

backend

Pre-initialized backend from create_app_backend().

type AppBackend

session_options

Session options for cookie-based auth.

type SessionOptions<string>

allowed_origins

Parsed allowed origin patterns.

type Array<RegExp>

proxy

Trusted proxy options.

type { trusted_proxies: Array<string>; get_connection_ip: (c: Context) => string | undefined; }

ip_rate_limiter?

Shared IP rate limiter for login, bootstrap, and bearer auth. Omit or undefined to use a default limiter (5 attempts per 15 minutes). Pass null to explicitly disable rate limiting. Also available on AppServerContext for route factory callbacks.

type RateLimiter | null

login_account_rate_limiter?

Per-account rate limiter for login attempts. Omit or undefined to use a default limiter (10 attempts per 30 minutes). Pass null to explicitly disable rate limiting. Also available on AppServerContext for route factory callbacks.

type RateLimiter | null

signup_account_rate_limiter?

Per-account rate limiter for signup attempts, keyed by submitted username. Omit or undefined to use a default limiter (10 attempts per 30 minutes). Pass null to explicitly disable rate limiting. Also available on AppServerContext for route factory callbacks.

type RateLimiter | null

bearer_ip_rate_limiter?

Rate limiter for bearer token auth attempts (per-IP). Omit or undefined to use a default limiter (5 attempts per 15 minutes). Pass null to explicitly disable rate limiting.

type RateLimiter | null

action_ip_rate_limiter?

Per-IP rate limiter for the action dispatchers (HTTP RPC + WebSocket). Consulted for actions whose spec declares rate_limit: 'ip' or 'both'. Same limiter applies across transports — one budget per action. Omit or undefined to use a default limiter (600 attempts per 15 minutes — permissive). Pass null to explicitly disable. Also available on AppServerContext for consumers wiring register_action_ws.

type RateLimiter | null

action_account_rate_limiter?

Per-actor rate limiter for the action dispatchers (HTTP RPC + WebSocket). Consulted for actions whose spec declares rate_limit: 'account' or 'both'. Keyed on request_context.actor.id (post-auth). Omit or undefined to use a default limiter (1200 attempts per 15 minutes — permissive). Pass null to explicitly disable. Also available on AppServerContext for consumers wiring register_action_ws.

type RateLimiter | null

max_body_size?

Maximum allowed request body size in bytes. Omit or undefined to use the default (1 MiB). Pass null to explicitly disable body size limiting.

type number | null

daemon_token_state?

Daemon token state for keeper auth. Omit to disable.

type DaemonTokenState

bootstrap?

Bootstrap options. Omit to skip bootstrap status check and routes.

type BootstrapServerOptions

surface_route?

Set to false to disable the auto-created surface route (GET /api/surface). Default: auto-created (authenticated).

type false

create_route_specs

Build route specs from the initialized backend. Called after all middleware is ready.

type (context: AppServerContext) => Array<RouteSpec>

transform_middleware?

Optional: transform middleware specs before applying.

type (specs: Array<MiddlewareSpec>) => Array<MiddlewareSpec>

audit_log_sse?

Enable factory-managed audit log SSE.

When truthy, creates an AuditLogSse instance internally, registers the SSE listener via backend.deps.audit.add_listener (composing with the consumer's on_audit_event callback rather than rebuilding AppDeps), and auto-includes audit_log_event_specs in the surface. The result is exposed on AppServerContext (for route factories) and AppServer (for the caller), always typed as AuditLogSse | null — when this option is set, the field is non-null. Use require_audit_sse(ctx) to assert the invariant in route factories that depend on it.

Pass true for defaults (admin role), or {role: 'custom'} for a custom role. Omit to wire audit SSE manually.

type true | { role?: string }

event_specs?

SSE event specs for surface generation. Defaults to [] (no SSE events).

type Array<EventSpec>

rpc_endpoints?

RPC endpoint specs — single source of truth for both surface generation *and* live dispatch. Each entry is mounted via create_rpc_endpoint against the assembled Hono app, so consumers no longer call create_rpc_endpoint themselves inside create_route_specs.

Accepts either an array (evaluated eagerly) or a factory (ctx: AppServerContext) => Array<RpcEndpointSpec> (evaluated after the server context is assembled). Use the factory form when action lists depend on ctx.deps — e.g. create_standard_rpc_actions(ctx.deps).

type Array<RpcEndpointSpec> | ((context: AppServerContext) => Array<RpcEndpointSpec>)

upgradeWebSocket?

Hono adapter's upgradeWebSocket helper. Required whenever ws_endpoints resolves to a non-empty array — create_app_server throws at assembly otherwise. Omit (along with ws_endpoints) when the consumer doesn't mount any WS endpoints. The same adapter helper services every WsEndpointSpec mounted from ws_endpoints — one adapter per app.

For Node, import {upgradeWebSocket} from '@hono/node-ws'. For Deno, import {upgradeWebSocket} from 'hono/deno'. Test harnesses use create_stub_upgrade from $lib/testing/ws_round_trip.ts.

type UpgradeWebSocket

ws_endpoints?

WebSocket endpoint specs — single source of truth for both surface generation *and* live dispatch. Each entry is auto-mounted via register_ws_endpoint against the assembled Hono app, so consumers no longer call register_ws_endpoint themselves.

Accepts either an array (evaluated eagerly) or a factory (ctx: AppServerContext) => ReadonlyArray<WsEndpointSpec> (evaluated after the server context is assembled). Use the factory form when action lists depend on ctx.deps / ctx.action_*_rate_limiter — e.g. when spreading create_standard_rpc_actions(ctx.deps, ...) over WS.

When non-empty, upgradeWebSocket must be supplied (throws otherwise). A factory returning [] does NOT trip the check — feature-flag gated WS surfaces stay safe.

Duplicate path values across two WsEndpointSpecs throw at mount time (Hono would silently shadow them otherwise).

Each spec's auth_guard? defaults to true — the factory composes create_ws_auth_guard + create_ws_logout_closer against the mounted transport and registers them via deps.audit.add_listener. Wiring is deduped by transport reference identity so two specs sharing one BackendWebsocketTransport instance get a single pair of listeners; wrapped / proxied transports dedupe as separate entries (set auth_guard: false on duplicates and compose against the underlying transport once).

type ReadonlyArray<WsEndpointSpec> | ((context: AppServerContext) => ReadonlyArray<WsEndpointSpec>)

env_schema?

Env schema for surface generation. Defaults to BaseServerEnv — pass an extended schema (typically BaseServerEnv.extend({...})) when the consumer adds app-specific env vars.

type z.ZodObject

post_route_middleware?

Middleware applied after routes, before static serving. Included in surface.

type Array<MiddlewareSpec>

static_serving?

Static file serving. Omit if not serving static files.

type { serve_static: ServeStaticFactory; /** Root directory for static files. Default `'./build'`. */ root?: string; /** Optional SPA fallback path served for client-side routes. */ spa_fallback?: string; /** * Predicate deciding which paths receive the SPA fallback. * Default: every path that is not under `/api/`. Only consulted * when `spa_fallback` is set. */ is_spa_route?: (path: string) => boolean; }

await_pending_effects?

Await all pending fire-and-forget effects before returning the response. Use in tests so audit log assertions don't need polling. Default false (production: true fire-and-forget).

type boolean

on_effect_error?

Called when a pending effect rejects. Use for monitoring, metrics, or alerting in production. Only called when await_pending_effects is false (production mode).

type (error: unknown, context: EffectErrorContext) => void

env_values?

Env values for startup summary logging.

type Record<string, unknown>

AppSettings
#

auth/app_settings_schema.ts view source

AppSettings import type {AppSettings} from '@fuzdev/fuz_app/auth/app_settings_schema.js';

App settings row from the database.

open_signup

type boolean

updated_at

type string | null

updated_by

type Uuid | null

AppSettingsCrossTestOptions
#

testing/cross_backend/app_settings.ts view source

RpcPathCrossSuiteOptions import type {AppSettingsCrossTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/app_settings.js';

Options for the app-settings effect suite (the standard RPC-dispatched shape).

rpc_path?

RPC endpoint path the methods are mounted on. Default /api/rpc.

type string

readonly

setup_test

Per-test fixture-producing function (fresh keeper + db per call).

type (): Promise<TestFixtureBase>

readonly
returns Promise<TestFixtureBase>

capabilities

Backend capability declarations — each suite gates on its own flag.

type BackendCapabilities

readonly

AppSettingsGetInput
#

auth/admin_action_specs.ts view source

ZodDefault<ZodObject<{ acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict>> import type {AppSettingsGetInput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Input for app_settings_get.

AppSettingsGetOutput
#

auth/admin_action_specs.ts view source

ZodObject<{ settings: ZodObject<{ open_signup: ZodBoolean; updated_at: ZodNullable<ZodString>; updated_by: ZodNullable<$ZodBranded<ZodUUID, "Uuid", "out">>; updated_by_username: ZodNullable<...>; }, $strict>; }, $strict> import type {AppSettingsGetOutput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Output for app_settings_get.

AppSettingsJson
#

auth/app_settings_schema.ts view source

ZodObject<{ open_signup: ZodBoolean; updated_at: ZodNullable<ZodString>; updated_by: ZodNullable<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict> import type {AppSettingsJson} from '@fuzdev/fuz_app/auth/app_settings_schema.js';

Zod schema for client-safe app settings data.

AppSettingsRpc
#

ui/app_settings_state.svelte.ts view source

AppSettingsRpc import type {AppSettingsRpc} from '@fuzdev/fuz_app/ui/app_settings_state.svelte.js';

Narrow RPC surface consumed by AppSettingsState. Consumers adapt their typed RPC client to this shape. Method signatures track the wire spec inputs/outputs directly so the adapter needs no casts.

get

type () => Promise<AppSettingsGetOutput>

update

type (params: AppSettingsUpdateInput) => Promise<AppSettingsUpdateOutput>

AppSettingsState
#

ui/app_settings_state.svelte.ts view source

import {AppSettingsState} from '@fuzdev/fuz_app/ui/app_settings_state.svelte.js';

list

type AsyncSlot<void, string>

readonly

update

type AsyncSlot<void, string>

readonly

settings

type AppSettingsWithUsernameJson | null

$state.raw

constructor

type new (options: AppSettingsStateOptions): AppSettingsState

options

fetch

type (): Promise<void>

returns Promise<void>

update_open_signup

type (value: boolean): Promise<void>

value

type boolean
returns Promise<void>

AppSettingsStateOptions
#

ui/app_settings_state.svelte.ts view source

AppSettingsStateOptions import type {AppSettingsStateOptions} from '@fuzdev/fuz_app/ui/app_settings_state.svelte.js';

get_rpc

Reactive accessor for the RPC adapter.

type () => AppSettingsRpc

AppSettingsUpdateInput
#

auth/admin_action_specs.ts view source

ZodObject<{ open_signup: ZodBoolean; acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict> import type {AppSettingsUpdateInput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Input for app_settings_update.

AppSettingsUpdateOutput
#

auth/admin_action_specs.ts view source

ZodObject<{ ok: ZodLiteral<true>; settings: ZodObject<{ open_signup: ZodBoolean; updated_at: ZodNullable<ZodString>; updated_by: ZodNullable<$ZodBranded<ZodUUID, "Uuid", "out">>; updated_by_username: ZodNullable<...>; }, $strict>; }, $strict> import type {AppSettingsUpdateOutput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Output for app_settings_update.

AppSettingsWithUsernameJson
#

auth/app_settings_schema.ts view source

ZodObject<{ open_signup: ZodBoolean; updated_at: ZodNullable<ZodString>; updated_by: ZodNullable<$ZodBranded<ZodUUID, "Uuid", "out">>; updated_by_username: ZodNullable<...>; }, $strict> import type {AppSettingsWithUsernameJson} from '@fuzdev/fuz_app/auth/app_settings_schema.js';

Zod schema for admin app settings with resolved updater username.

AppShell
#

ui/AppShell.svelte view source

accepts children

import AppShell from '@fuzdev/fuz_app/ui/AppShell.svelte';

children

type Snippet<[]>

sidebar

type Snippet<[]>

sidebar_width?

Sidebar width in pixels when shown.

type number
optional default 180

sidebar_state?

Optional pre-built SidebarState for sharing visibility across shells.

optional

keyboard_shortcut?

Single-key shortcut that toggles the sidebar (e.g. 'b'). false disables.

type string | false
optional default false

show_toggle?

Whether to render the built-in (or custom) toggle button.

type boolean
optional default true

toggle_button?

Custom toggle-button renderer; receives the title, visibility, and toggle callback.

type Snippet<[{ title: string; show_sidebar: boolean; toggle: () => void; }]>
optional
snippet parameters
arg0 { title: string; show_sidebar: boolean; toggle: () => void; }

intersects

SvelteHTMLElements['div']

AppSurface
#

http/surface.ts view source

AppSurface import type {AppSurface} from '@fuzdev/fuz_app/http/surface.js';

Generated attack surface — JSON-serializable.

middleware

type Array<AppSurfaceMiddleware>

routes

type Array<AppSurfaceRoute>

rpc_endpoints

type Array<AppSurfaceRpcEndpoint>

ws_endpoints

type Array<AppSurfaceWsEndpoint>

env

type Array<AppSurfaceEnv>

events

type Array<AppSurfaceEvent>

diagnostics

type Array<AppSurfaceDiagnostic>

AppSurfaceDiagnostic
#

http/surface.ts view source

AppSurfaceDiagnostic import type {AppSurfaceDiagnostic} from '@fuzdev/fuz_app/http/surface.js';

Assembly-time diagnostic collected during surface generation or server assembly.

level

type 'warning' | 'info'

category

type string

message

type string

source?

type string

AppSurfaceEnv
#

http/surface.ts view source

AppSurfaceEnv import type {AppSurfaceEnv} from '@fuzdev/fuz_app/http/surface.js';

An env var in the generated attack surface (JSON-serializable).

name

type string

description

type string

sensitivity

Sensitivity level from .meta({sensitivity}). null when not sensitive.

type Sensitivity | null

has_default

type boolean

optional

type boolean

AppSurfaceEvent
#

http/surface.ts view source

AppSurfaceEvent import type {AppSurfaceEvent} from '@fuzdev/fuz_app/http/surface.js';

An SSE event in the generated attack surface (JSON-serializable).

method

type string

description

type string

channel

type string | null

params_schema

type unknown

AppSurfaceMiddleware
#

http/surface.ts view source

AppSurfaceMiddleware import type {AppSurfaceMiddleware} from '@fuzdev/fuz_app/http/surface.js';

A middleware in the generated attack surface (JSON-serializable).

name

type string

path

type string

error_schemas

JSON Schema representations of error responses, keyed by HTTP status code. null when none.

type Record<string, unknown> | null

AppSurfaceRoute
#

http/surface.ts view source

AppSurfaceRoute import type {AppSurfaceRoute} from '@fuzdev/fuz_app/http/surface.js';

A route in the generated attack surface (JSON-serializable).

method

type string

path

type string

auth

type RouteAuth

applicable_middleware

type Array<string>

description

type string

is_mutation

Whether this route mutates state (POST, PUT, DELETE, PATCH).

type boolean

transaction

Whether this route's handler runs inside a database transaction.

type boolean

raw_body

Whether this route carries raw bytes / a streaming protocol rather than JSON (see RouteSpec.raw_body). When true, input_schema / output_schema being null means "raw bytes", not "no body".

type boolean

rate_limit_key

Rate limit key type declared on the route spec. null when not rate-limited.

type RateLimitKey | null

params_schema

JSON Schema representation of the URL path params schema. null when no params.

type unknown

query_schema

JSON Schema representation of the URL query params schema. null when no query schema.

type unknown

input_schema

JSON Schema representation of the request body schema. null for no-body routes.

type unknown

output_schema

JSON Schema representation of the success response schema.

type unknown

error_schemas

JSON Schema representations of error responses, keyed by HTTP status code. null when none.

type Record<string, unknown> | null

AppSurfaceRpcEndpoint
#

http/surface.ts view source

AppSurfaceRpcEndpoint import type {AppSurfaceRpcEndpoint} from '@fuzdev/fuz_app/http/surface.js';

An RPC endpoint in the generated attack surface (JSON-serializable).

path

type string

methods

type Array<AppSurfaceRpcMethod>

AppSurfaceRpcMethod
#

http/surface.ts view source

AppSurfaceRpcMethod import type {AppSurfaceRpcMethod} from '@fuzdev/fuz_app/http/surface.js';

A method within an RPC endpoint in the generated attack surface (JSON-serializable).

name

type string

auth

type RouteAuth

input_schema

JSON Schema representation of the input schema. null for null-input methods.

type unknown

output_schema

JSON Schema representation of the output schema.

type unknown

side_effects

type boolean

description

type string

rate_limit_key

Rate limit key declared on the action spec. null when not rate-limited.

type RateLimitKey | null

AppSurfaceSpec
#

http/surface.ts view source

AppSurfaceSpec import type {AppSurfaceSpec} from '@fuzdev/fuz_app/http/surface.js';

The surface bundled with the source specs that produced it.

AppSurface is JSON-serializable (snapshots, UI, startup logging) — it's the observability layer, written to disk by gro gen for human inspection + drift detection.

AppSurfaceSpec is runtime-only — tests, introspection, attack surface assertions. Both in-process and cross-process tests construct an AppSurfaceSpec in TS via create_test_app_surface_spec (or a consumer equivalent); the cross-process-ness lives in the transport + per-test fixture, not the schema source.

surface

type AppSurface

route_specs

type Array<RouteSpec>

middleware_specs

type Array<MiddlewareSpec>

rpc_endpoints

type Array<RpcEndpointSpec>

ws_endpoints

type Array<WsEndpointSpec>

AppSurfaceWsEndpoint
#

http/surface.ts view source

AppSurfaceWsEndpoint import type {AppSurfaceWsEndpoint} from '@fuzdev/fuz_app/http/surface.js';

A WebSocket endpoint in the generated attack surface (JSON-serializable).

path

type string

allowed_origins

Upgrade-time origin allowlist, one entry per WsEndpointSpec.allowed_origins regex stringified via RegExp.prototype.toString() ('/<source>/<flags>'). Empty array when no origins were declared (any-origin); reviewers read this as the exact pattern matched at the upgrade gate, not a wildcard approximation. Reconstruct via new RegExp(source, flags) if needed.

type ReadonlyArray<string>

required_roles

Upgrade-time role gate — empty array when no required_roles was declared (any-authenticated). Documents the coarse gate; per-action auth on each method covers per-message authorization.

type ReadonlyArray<string>

methods

type Array<AppSurfaceWsMethod>

AppSurfaceWsMethod
#

http/surface.ts view source

AppSurfaceWsMethod import type {AppSurfaceWsMethod} from '@fuzdev/fuz_app/http/surface.js';

A method within a WebSocket endpoint in the generated attack surface (JSON-serializable).

name

type string

kind

request_response (inbound dispatch) or remote_notification (server → client).

type ActionKind

auth

Per-action auth shape. null for remote_notification (server → client) — notifications have no inbound dispatch and therefore no auth axis. request_response always carries a RouteAuth.

type RouteAuth | null

input_schema

JSON Schema of the input schema. null for nullary inputs.

type unknown

output_schema

JSON Schema of the output schema.

type unknown

description

type string

side_effects

type boolean

rate_limit_key

Rate limit key declared on the action spec. null when not rate-limited.

type RateLimitKey | null

argon2_password_deps
#

assert_404_schemas_use_specific_errors
#

testing/surface_invariants.ts view source

(surface: AppSurface): void import {assert_404_schemas_use_specific_errors} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Routes declaring 404 error schemas should use specific z.literal() or z.enum() error codes, not generic z.string().

A generic 404 schema (ApiError with z.string()) means the error code is unconstrained — the handler could return any string, making client error handling fragile. Routes with params (:id) are the primary 404 producers; their error schemas should use specific constants like ERROR_ACCOUNT_NOT_FOUND.

Only flags routes that have params_schema (param-driven resource lookup) — routes declaring 404 for other reasons (e.g., bootstrap not configured) may legitimately use generic schemas.

surface

returns

void

assert_action_manifests_equal
#

testing/cross_backend/action_manifest_parity.ts view source

(a: { methods: { method: string; side_effects: boolean; account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles: string[]; credential_types: string[]; }[]; }, b: { ...; }, labels?: ActionManifestDiffLabels): void import {assert_action_manifests_equal} from '@fuzdev/fuz_app/testing/cross_backend/action_manifest_parity.js';

Throw if the two manifests disagree. The error message names the impls (via labels) and lists every diff, so the failure is self-diagnosing.

a

type { methods: { method: string; side_effects: boolean; account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles: string[]; credential_types: string[]; }[]; }

b

type { methods: { method: string; side_effects: boolean; account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles: string[]; credential_types: string[]; }[]; }

labels

default {}

returns

void

assert_close_call
#

testing/connection_closer_helpers.ts view source

(call: RecordedClose | undefined, method: "session" | "account" | "token", id: string): void import {assert_close_call} from '@fuzdev/fuz_app/testing/connection_closer_helpers.js';

Pin {method, id} on a single recorded close call without baking in the at: N sequence number. Use at every "did the closer fire?" assertion site; the sequence number is only meaningful for dedicated ordering tests (paired with create_emit_ordering_audit_factory).

Throws via assert.ok if call is undefined — index a recorded calls array directly (calls[0]) and let this helper handle the missing-element case.

call

type RecordedClose | undefined

method

type "session" | "account" | "token"

id

type string

returns

void

assert_descriptions_present
#

assert_error_code_status_consistency
#

testing/surface_invariants.ts view source

(surface: AppSurface): void import {assert_error_code_status_consistency} from '@fuzdev/fuz_app/testing/surface_invariants.js';

The same z.literal() error code should not appear at different HTTP status codes across routes.

Extracts const values from error schema error properties (which correspond to z.literal() in the Zod source). Walks union branches (anyOf from z.union, oneOf from z.discriminatedUnion) so literal codes nested inside merged unions (e.g. validation 400 + actor-resolution 400) are still tracked. Flags when the same literal appears at different status codes — e.g., ERROR_INVALID_CREDENTIALS at both 401 and 403 would be a bug.

Only checks const values (literal schemas). Generic z.string() schemas (which produce {type: 'string'}) and z.enum() schemas are ignored — the literal-only narrow keeps the check unambiguous.

surface

returns

void

assert_error_coverage
#

testing/error_coverage.ts view source

(collector: ErrorCoverageCollector, route_specs: RouteSpec[], options?: ErrorCoverageOptions | undefined): void import {assert_error_coverage} from '@fuzdev/fuz_app/testing/error_coverage.js';

Assert error coverage meets a minimum threshold.

Computes the ratio of exercised error paths to total declared error paths. For routes whose status error schema names specific codes (z.literal or z.enum), each declared code counts as one coverage path; for schemas without declared codes (ApiError/z.string()), the status counts as one path. A status-only observation covers all declared codes for that status (the "any-code" rule).

When min_coverage is 0 (default), logs coverage info without failing. When > 0, fails if coverage is below the threshold.

collector

route_specs

type RouteSpec[]

options?

type ErrorCoverageOptions | undefined
optional

returns

void

throws

  • AssertionError - if `min_coverage > 0` and the covered/total ratio

assert_error_schema_tightness
#

testing/surface_invariants.ts view source

(surface: AppSurface, options?: ErrorSchemaTightnessOptions | undefined): void import {assert_error_schema_tightness} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Assert that all error schemas meet a minimum specificity threshold.

Calls audit_error_schema_tightness and fails on any entry below the configured threshold. Use allowlist and ignore_statuses to exclude known exceptions during progressive tightening.

surface

options?

type ErrorSchemaTightnessOptions | undefined
optional

returns

void

throws

  • AssertionError - listing every route × status combination whose error

assert_error_schema_valid
#

testing/assertions.ts view source

(lookup: Map<string, Partial<Record<number, ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>>>>, route: AppSurfaceRoute, status: number, body: unknown): void import {assert_error_schema_valid} from '@fuzdev/fuz_app/testing/assertions.js';

Assert that an error schema exists for a route+status and validate the body against it.

Protected routes should always have auto-derived error schemas (401 for authenticated, 403 for role-restricted). A missing schema indicates a gap in error schema derivation.

lookup

map from "METHOD /path" to merged error schemas

type Map<string, Partial<Record<number, ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>>>>

route

the surface route to validate against

status

expected HTTP status code

type number

body

the parsed response body to validate

type unknown

returns

void

throws

  • AssertionError - if no schema is declared for the route+status pair.
  • ZodError - if the body does not satisfy the declared schema.

assert_error_schemas_structurally_valid
#

testing/surface_invariants.ts view source

(surface: AppSurface): void import {assert_error_schemas_structurally_valid} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Every route's declared error schemas must have an error field at the top level (conforming to the ApiError base shape {error: string}).

Walks union branches (anyOf from z.union, oneOf from z.discriminatedUnion) so every emit shape inside a merged 400 / 404 is checked, not just the top-level wrapper.

Catches typos in error schema definitions and ensures consumers can always read .error from error responses.

surface

returns

void

assert_expected_headers
#

testing/cross_backend/conformance_table.ts view source

(headers: Record<string, string>, expected: Record<string, string | null>, label: string): void import {assert_expected_headers} from '@fuzdev/fuz_app/testing/cross_backend/conformance_table.js';

Assert each declared header expectation: a string value must be present and equal (header name matched case-insensitively), null must be absent. The negative-space twin for headers — expect.headers pins a header beyond the always-on no-fingerprint floor.

headers

type Record<string, string>

expected

type Record<string, string | null>

label

type string

returns

void

assert_full_middleware_stack
#

testing/assertions.ts view source

(surface: AppSurface, path_prefix: string, expected_middleware: string[]): void import {assert_full_middleware_stack} from '@fuzdev/fuz_app/testing/assertions.js';

Verify every route under a path prefix has the exact expected middleware stack.

surface

the app surface to check

path_prefix

prefix to filter routes (e.g. '/api/')

type string

expected_middleware

the exact middleware names in order

type string[]

returns

void

throws

  • AssertionError - if no routes match `path_prefix`, or if any matching

assert_input_routes_declare_400
#

testing/surface_invariants.ts view source

(surface: AppSurface): void import {assert_input_routes_declare_400} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Every route with non-null input_schema has 400 in error_schemas.

surface

returns

void

assert_jsonrpc_error_response
#

testing/rpc_helpers.ts view source

(body: unknown, expected_code?: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">) | undefined): void import {assert_jsonrpc_error_response} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

Assert that a response body is a valid JSON-RPC error response.

Validates the structure matches JsonrpcErrorResponse and optionally checks the error code.

body

type unknown

expected_code?

optional error code to assert

type -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">) | undefined
optional

returns

void

assert_jsonrpc_success_response
#

testing/rpc_helpers.ts view source

(body: unknown, output_schema?: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>> | undefined): void import {assert_jsonrpc_success_response} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

Assert that a response body is a valid JSON-RPC success response.

Validates the structure matches JsonrpcResponse. When output_schema is provided, also validates the result field against the declared output schema — matching the REST round-trip's assert_response_matches_spec.

body

type unknown

output_schema?

optional Zod schema to validate the result field against

type ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>> | undefined
optional

returns

void

assert_keeper_routes_under_prefix
#

testing/surface_invariants.ts view source

(surface: AppSurface, prefixes?: string[]): void import {assert_keeper_routes_under_prefix} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Keeper-protected routes must be under expected path prefixes.

Catches keeper routes accidentally placed outside the API namespace (e.g., a keeper route at /health or /admin/ instead of /api/...).

surface

prefixes

type string[]
default DEFAULT_KEEPER_ROUTE_PREFIXES

returns

void

assert_middleware_errors_propagated
#

testing/surface_invariants.ts view source

(surface: AppSurface): void import {assert_middleware_errors_propagated} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Every applicable middleware that declares errors must have those status codes present in the route's error_schemas.

surface

returns

void

assert_migration_trackers_equal
#

testing/schema_parity.ts view source

(a: { entries: { namespace: string; name: string; sequence: number; }[]; }, b: { entries: { namespace: string; name: string; sequence: number; }[]; }, labels?: SchemaDiffLabels): void import {assert_migration_trackers_equal} from '@fuzdev/fuz_app/testing/schema_parity.js';

Throw if the two spines' schema_version trackers disagree — the gate for the swap-freely invariant (any consumer can swap TS↔Rust over one DB without re-bootstrapping). This catches what assert_schema_snapshots_equal is blind to by design: the snapshot excludes the tracker, so a migration-name or partitioning divergence that yields an identical *schema* (e.g. cell_v0 vs full_cell_schema, or cell_history bundled vs isolated) passes schema parity but breaks the runner's positional name-prefix check at boot (name-divergence-at-N). The error names the impls and lists every diff.

a

type { entries: { namespace: string; name: string; sequence: number; }[]; }

b

type { entries: { namespace: string; name: string; sequence: number; }[]; }

labels

default {}

returns

void

assert_mutation_routes_use_post
#

testing/surface_invariants.ts view source

(surface: AppSurface): void import {assert_mutation_routes_use_post} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Routes with non-null input schemas should use POST (or other mutation methods), not GET.

GET routes with request bodies are technically allowed by HTTP but semantically suspicious — they bypass browser security assumptions about GET being idempotent. Query-string-driven filtering (audit log, list endpoints) should use params schemas or query string parsing, not input schemas.

Note: RPC endpoints (create_rpc_endpoint) use input: z.null() on their route specs — the dispatcher handles body/query parsing internally. Real input schemas live in rpc_endpoints surface, not on routes; see assert_rpc_ws_surface_invariants for the parallel checks over RPC/WS method shapes.

surface

returns

void

assert_no_duplicate_routes
#

assert_no_error_info_leakage
#

testing/integration_helpers.ts view source

(body: unknown, context: string): void import {assert_no_error_info_leakage} from '@fuzdev/fuz_app/testing/integration_helpers.js';

Assert that an error response contains no leaky field values.

Checks both field names and string values for patterns indicating stack traces, SQL, or internal paths. Accepts unknown so callers pass response bodies / nested envelope fields directly without intermediate as casts; non-object bodies skip the field-name check.

body

type unknown

context

description for error messages

type string

returns

void

assert_no_fingerprint_headers
#

testing/cross_backend/conformance_table.ts view source

(headers: Record<string, string>, label: string): void import {assert_no_fingerprint_headers} from '@fuzdev/fuz_app/testing/cross_backend/conformance_table.js';

Assert a response carries none of the FINGERPRINT_HEADERS. Run on every case unconditionally — the always-on no-fingerprint floor.

headers

type Record<string, string>

label

type string

returns

void

assert_no_sensitive_fields_in_json
#

testing/integration_helpers.ts view source

(body: unknown, blocklist: readonly string[], context: string): void import {assert_no_sensitive_fields_in_json} from '@fuzdev/fuz_app/testing/integration_helpers.js';

Assert that a parsed JSON body contains no fields from the given blocklist.

body

type unknown

blocklist

type readonly string[]

context

description for error messages

type string

returns

void

assert_no_testing_methods
#

testing/surface_invariants.ts view source

(surface: AppSurface): void import {assert_no_testing_methods} from '@fuzdev/fuz_app/testing/surface_invariants.js';

No _testing_* backdoor action ever appears as a method on the declared surface (RPC or WS).

The test-backdoor actions (_testing_reset et al.) are daemon-token-gated privileged actions a consumer's test binary appends to its live RPC endpoint at assembly time — but they are deliberately excluded from surface generation (spine_rpc_endpoints and every consumer's create_*_app_surface_spec omit them) so the published attack surface, the committed *_attack_surface.json snapshot, and codegen never carry a backdoor. This invariant is the structural guard against a future wiring change that folds create_testing_actions(...) into the *declared* registry instead of the live-only append — the surface stays the authoritative "what the server exposes" map only if a backdoor can never hide in it.

Pairs with the wire-level negative-credential check (describe_testing_backdoor_cross_tests, cross-process) and the spec-level gate check: this one pins absence-from-surface, those pin the daemon-token gate and the 401/403 behavior.

surface

returns

void

throws

  • AssertionError - naming the offending endpoint + method.

assert_no_unexpected_public_mutations
#

testing/surface_invariants.ts view source

(surface: AppSurface, allowlist?: string[]): void import {assert_no_unexpected_public_mutations} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Public mutation routes (auth: none + is_mutation) must be in the allowlist.

Catches accidentally unprotected POST/PUT/DELETE routes. Routes like login and bootstrap are public mutations by design — they go in the allowlist.

surface

allowlist

type string[]
default []

returns

void

assert_non_admin_schemas_no_admin_fields
#

testing/data_exposure.ts view source

(surface: AppSurface, admin_only_fields?: readonly string[]): void import {assert_non_admin_schemas_no_admin_fields} from '@fuzdev/fuz_app/testing/data_exposure.js';

Assert that non-admin route output schemas don't contain admin-only fields.

surface

admin_only_fields

type readonly string[]
default admin_only_field_blocklist

returns

void

assert_only_expected_public_routes
#

testing/assertions.ts view source

(surface: AppSurface, expected_public: string[]): void import {assert_only_expected_public_routes} from '@fuzdev/fuz_app/testing/assertions.js';

Bidirectional check: no unexpected public routes, no missing expected ones.

surface

the app surface to check

expected_public

format: ['GET /health', 'POST /api/account/login']

type string[]

returns

void

throws

  • AssertionError - if the live surface has public routes not in

assert_output_schemas_no_sensitive_fields
#

testing/data_exposure.ts view source

(surface: AppSurface, sensitive_fields?: readonly string[]): void import {assert_output_schemas_no_sensitive_fields} from '@fuzdev/fuz_app/testing/data_exposure.js';

Assert that no output schema in the surface contains sensitive field names.

surface

sensitive_fields

type readonly string[]
default sensitive_field_blocklist

returns

void

assert_params_routes_declare_400
#

testing/surface_invariants.ts view source

(surface: AppSurface): void import {assert_params_routes_declare_400} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Every route with non-null params_schema has 400 in error_schemas.

surface

returns

void

assert_protected_routes_declare_401
#

testing/surface_invariants.ts view source

(surface: AppSurface): void import {assert_protected_routes_declare_401} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Every protected route has 401 in error_schemas.

surface

returns

void

assert_query_routes_declare_400
#

testing/surface_invariants.ts view source

(surface: AppSurface): void import {assert_query_routes_declare_400} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Every route with non-null query_schema has 400 in error_schemas.

surface

returns

void

assert_rate_limit_retry_after_header
#

testing/integration_helpers.ts view source

(response: Response, body: { retry_after: number; }): void import {assert_rate_limit_retry_after_header} from '@fuzdev/fuz_app/testing/integration_helpers.js';

Assert that a 429 response includes a valid Retry-After header matching the JSON body's retry_after field.

response

type Response

body

type { retry_after: number; }

returns

void

assert_response_matches_spec
#

testing/integration_helpers.ts view source

(route_specs: RouteSpec[], method: string, path: string, response: Response): Promise<void> import {assert_response_matches_spec} from '@fuzdev/fuz_app/testing/integration_helpers.js';

Validate a response body against the route spec's declared schemas.

For 2xx responses, validates against spec.output. For error responses, validates against the merged error schema for that status code.

route_specs

type RouteSpec[]

method

type string

path

type string

response

type Response

returns

Promise<void>

throws

  • Error - if no route spec matches `method` + `path`, if the response

assert_role_routes_declare_403
#

testing/surface_invariants.ts view source

(surface: AppSurface): void import {assert_role_routes_declare_403} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Every role/keeper route has 403 in error_schemas.

surface

returns

void

assert_route_auth_acting_biconditional
#

http/auth_shape.ts view source

(auth: { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }, slots: ActingSlots, context: string): void import {assert_route_auth_acting_biconditional} from '@fuzdev/fuz_app/http/auth_shape.js';

Registry-time biconditional check: `auth.actor !== 'none' ⟺ some supplied slot declares acting?: ActingActor`. Throws on violation.

The slot set differs by surface: REST passes {input, query} (both locatable, query only set for GETs); action dispatchers pass {input} (no query shape on ActionSpec). The throw message lists the slots that were actually in play, so an actor-required action without acting doesn't point the operator at a query slot that doesn't exist on their spec.

Called by every dispatcher registration loop (apply_route_specs, compile_action_registry) on every spec it accepts.

auth

the route/action's auth shape

type { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }

slots

the spec's acting-bearing schemas; query omitted on action call sites

context

identifier for the throwing message (route key, RPC method, etc.)

type string

returns

void

throws

  • Error - when the biconditional is violated

assert_row
#

db/assert_row.ts view source

<T>(row: T | undefined, context?: string | undefined): T import {assert_row} from '@fuzdev/fuz_app/db/assert_row.js';

Assert that a row is present, throwing a descriptive error if missing.

Use after INSERT ... RETURNING queries where the database guarantees a row is returned on success. Replaces bare row! non-null assertions with an explicit runtime check.

row

the row from query_one (T | undefined) or rows[0] (T | undefined)

type T | undefined

context?

optional context for the error message (e.g. table or operation name)

type string | undefined
optional

returns

T

the row, guaranteed non-undefined

generics

assert_row<T>
T

throws

  • Error - if `row` is `undefined`

assert_rpc_method_coverage
#

testing/cross_backend/method_coverage.ts view source

(input: RpcMethodCoverageInput): void import {assert_rpc_method_coverage} from '@fuzdev/fuz_app/testing/cross_backend/method_coverage.js';

Assert the live RPC method set reconciles exactly with the coverage manifest, and that every manifest entry's tier is internally consistent.

Fails loud on: a live method missing from the manifest (mounted-but-unclaimed), a manifest row naming a method the live mount no longer exposes (stale row), a declared-surface method absent from the live mount (the full mount must be a superset), a duplicate manifest entry, or any tier/declared/backdoor inconsistency (e.g. an off_surface row that is actually on the declared surface, or one missing its suite).

input

returns

void

throws

  • AssertionError - naming the specific divergence.

assert_rpc_method_descriptions_present
#

testing/surface_invariants.ts view source

(surface: AppSurface): void import {assert_rpc_method_descriptions_present} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Every RPC method on every endpoint has a non-empty description.

Parallel of assert_descriptions_present over surface.rpc_endpoints. Empty descriptions on RPC methods leak through library.json codegen and consumer-facing docs without the route-level check catching them.

surface

returns

void

assert_rpc_ws_surface_invariants
#

testing/surface_invariants.ts view source

(surface: AppSurface): void import {assert_rpc_ws_surface_invariants} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Run all RPC / WS structural invariants. Options-free — applies universally to the surface.rpc_endpoints and surface.ws_endpoints slots produced by generate_app_surface.

Parallel of assert_surface_invariants for the non-REST surfaces. Within-endpoint duplicate method names and the auth-shape biconditional are already enforced at startup by compile_action_registry (see actions/CLAUDE.md §Registry compile) — these assertions cover only the contract-surface concerns that a runtime registration check cannot: empty descriptions, missing protocol-action spread on WS endpoints, kind ⇔ auth drift on WS methods, and a _testing_* backdoor action leaking onto the declared surface.

surface

returns

void

throws

  • AssertionError - on the first invariant violation; the message

assert_schema_snapshots_equal
#

testing/schema_parity.ts view source

(a: { tables: Record<string, { columns: Record<string, { data_type: string; udt_name: string; is_nullable: boolean; column_default: string | null; is_identity: boolean; }>; indexes: { name: string; definition: string; }[]; constraints: { ...; }[]; }>; sequences: Record<...>; enums: Record<...>; }, b: { ...; }, labels?: SchemaDiffLabels): void import {assert_schema_snapshots_equal} from '@fuzdev/fuz_app/testing/schema_parity.js';

Throw if the two snapshots disagree. The error message names the impls (via labels) and lists every diff, so the failure is self-diagnosing.

Consumers wire this after bootstrapping each impl against an isolated DB:

await drop_recreate_db('zzz_test'); await spawn_backend(deno_config); const snapshot_deno = await query_schema_snapshot(db, {}); await drop_recreate_db('zzz_test'); await spawn_backend(rust_config); const snapshot_rust = await query_schema_snapshot(db, {}); assert_schema_snapshots_equal(snapshot_deno, snapshot_rust, {a: 'deno', b: 'rust'});

a

type { tables: Record<string, { columns: Record<string, { data_type: string; udt_name: string; is_nullable: boolean; column_default: string | null; is_identity: boolean; }>; indexes: { name: string; definition: string; }[]; constraints: { ...; }[]; }>; sequences: Record<...>; enums: Record<...>; }

b

type { tables: Record<string, { columns: Record<string, { data_type: string; udt_name: string; is_nullable: boolean; column_default: string | null; is_identity: boolean; }>; indexes: { name: string; definition: string; }[]; constraints: { ...; }[]; }>; sequences: Record<...>; enums: Record<...>; }

labels

default {}

returns

void

assert_sensitive_routes_rate_limited
#

testing/surface_invariants.ts view source

(surface: AppSurface, sensitive_patterns?: (string | RegExp)[]): void import {assert_sensitive_routes_rate_limited} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Sensitive routes must declare rate limiting (rate_limit_key is non-null) or have 429 in their error schemas.

Matches routes against sensitive patterns and flags any that lack rate limit declarations. Catches forgotten rate limiting on credential-handling routes.

surface

sensitive_patterns

type (string | RegExp)[]
default DEFAULT_SENSITIVE_PATTERNS

returns

void

assert_surface_deterministic
#

testing/assertions.ts view source

(build_surface: () => AppSurface): void import {assert_surface_deterministic} from '@fuzdev/fuz_app/testing/assertions.js';

Verify surface generation is deterministic (build twice, compare).

build_surface

type () => AppSurface

returns

void

assert_surface_invariants
#

testing/surface_invariants.ts view source

(surface: AppSurface): void import {assert_surface_invariants} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Run all structural invariants. Options-free — applies universally.

Catches schema/surface generation bugs: missing 401/403/400 declarations, empty descriptions, duplicate routes, middleware-injected error codes unpropagated to routes, structurally invalid error schemas, error codes appearing at multiple statuses, and generic 404 schemas on param routes.

surface

returns

void

throws

  • AssertionError - on the first invariant violation; the message names

assert_surface_matches_snapshot
#

testing/assertions.ts view source

(surface: AppSurface, snapshot_path: string): void import {assert_surface_matches_snapshot} from '@fuzdev/fuz_app/testing/assertions.js';

Compare live surface against a committed snapshot JSON file.

Failure message instructs the developer to run gro gen to update the snapshot — every fuz_app consumer wires the snapshot through a *.gen.ts file so regeneration goes through the same pipeline as the rest of the generated artifacts.

surface

the live surface to check

snapshot_path

absolute path to the committed JSON snapshot

type string

returns

void

throws

  • AssertionError - if the live surface does not deep-equal the snapshot,

assert_surface_security_policy
#

testing/surface_invariants.ts view source

(surface: AppSurface, options?: SurfaceSecurityPolicyOptions): void import {assert_surface_security_policy} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Run security policy invariants. Configurable with sensible defaults.

Checks:

  • Sensitive routes are rate-limited
  • No unexpected public mutation routes
  • Input schemas use mutation methods (not GET)
  • Keeper routes under expected prefixes

surface

options

default {}

returns

void

throws

  • AssertionError - on the first policy violation; the message names

assert_valid_sql_identifier
#

db/sql_identifier.ts view source

(name: string): string import {assert_valid_sql_identifier} from '@fuzdev/fuz_app/db/sql_identifier.js';

Assert that a string is a valid SQL identifier.

Use this before interpolating table or column names into DDL queries where parameterized placeholders ($1) are not supported.

name

the identifier to validate

type string

returns

string

the validated identifier

throws

  • Error - if the identifier contains characters outside `[a-zA-Z0-9_]`

assert_ws_endpoints_include_protocol_actions
#

testing/surface_invariants.ts view source

(surface: AppSurface): void import {assert_ws_endpoints_include_protocol_actions} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Every WS endpoint's methods includes every protocol action method (heartbeat, cancel).

Consumers register WS endpoints by spreading protocol_actions from actions/protocol.ts before their own actions:

ws_endpoints: [{path: '/api/ws', actions: [...protocol_actions, ...consumer_actions], ...}]

Forgetting the spread compiles cleanly but breaks at runtime: client-side heartbeats and cancel notifications get method_not_found from the dispatcher, so disconnect detection silently regresses and per-request cancel never aborts the matching handler. Catch the mistake at the surface layer rather than at runtime.

surface

returns

void

assert_ws_method_descriptions_present
#

assert_ws_notifications_have_null_auth
#

testing/surface_invariants.ts view source

(surface: AppSurface): void import {assert_ws_notifications_have_null_auth} from '@fuzdev/fuz_app/testing/surface_invariants.js';

WS methods follow the kind ⇔ auth biconditional emitted by surface generation: kind === 'remote_notification' ⟺ auth === null.

generate_app_surface produces this shape directly from the action spec union (notifications carry auth: null per ActionSpecUnion; request_response carries a RouteAuth). The assertion guards against drift if a future surface emitter, transform, or test fixture violates it — and gives consumers a clear failure message when a hand-built surface mocks the shape incorrectly.

surface

returns

void

AsyncSlot
#

ui/async_slot.svelte.ts view source

import {AsyncSlot} from '@fuzdev/fuz_app/ui/async_slot.svelte.js';

Reactive container for a single async operation.

generics

AsyncSlot<T = void, E = string>
T
default void
E
default string

status

type AsyncStatus

$state.raw

data

type T | undefined

$state.raw

error

type E | null

$state.raw

error_data

The raw caught value from the last failed run(), for programmatic inspection.

type unknown

$state.raw

initial

Convenience derived: status === 'initial'.

type boolean

readonly $derived

loading

Convenience derived: status === 'pending'.

type boolean

readonly $derived

succeeded

Convenience derived: status === 'success'.

type boolean

readonly $derived

failed

Convenience derived: status === 'failure'.

type boolean

readonly $derived

constructor

type new <T = void, E = string>(options?: AsyncSlotOptions<T, E>): AsyncSlot<T, E>

options

type AsyncSlotOptions<T, E>
default {}

run

Run an async operation. The callback receives an AbortSignal it can forward to fetch / RPC clients that support cancellation; the slot also discards superseded results internally even if the callback ignores the signal.

Supersession rule: a second run() aborts the first's signal AND silently drops its commit if it resolves anyway. So back-to-back-to-back run() calls leave only the last call's result in data.

Abort rule: a run() that throws because of its own signal (manual abort(), external options.signal, OR supersession by another run()) does NOT promote to 'failure'. Manual / external aborts revert status to the previous resolved state ('initial' if no run() has ever succeeded, 'success' otherwise). Supersession is handled by the bail-on-mismatch check, leaving the second run's 'pending' standing.

type (fn: (signal: AbortSignal) => Promise<T>, options?: RunOptions): Promise<T | undefined>

fn

type (signal: AbortSignal) => Promise<T>

options

default {}
returns Promise<T | undefined>

the resolved value on success; undefined on failure, abort, or supersession

abort

Manually abort the in-flight run, if any. Reverts status synchronously to the prior resolved state — 'initial' if no run() (or set()) has ever succeeded on this slot, 'success' otherwise. The aborted run's eventual resolution / rejection is dropped without writing to state (the run's Promise resolves to undefined).

type (reason?: unknown): void

reason?

type unknown
optional
returns void

set

Replace data directly and mark the slot 'success'. For post-mutation hydration where the calling RPC already returned the canonical row (parallels CellState.set_cell).

Aborts any in-flight run() first — without this, the in-flight callback could resolve after set() and overwrite the explicit value (the bail-on-mismatch check only fires when #controller was rotated).

type (data: T): void

data

type T
returns void

reset

Reset to 'initial', clear data / error / error_data, and abort any in-flight run. After reset() the slot looks like a fresh instance with no initial option.

type (): void

returns void

AsyncSlotOptions
#

ui/async_slot.svelte.ts view source

AsyncSlotOptions<T, E> import type {AsyncSlotOptions} from '@fuzdev/fuz_app/ui/async_slot.svelte.js';

generics

AsyncSlotOptions<T, E = string>
T
E
default string

initial?

Seed data and put the slot in 'success' before any run(). Useful when the page already has the resource in hand (SSR hydration, a mutation response, hand-off from a parent slot).

type T

map_error?

Convert a caught throw into the error value stored in . Default extracts Error.message (falling back to 'Request failed' for non-Error throws). Pass to_rpc_error_message to unwrap JSON-RPC data.reason codes.

type (e: unknown) => E

preserve_error_on_retry?

When true, the previous error / error_data survive the start of a new run() until the next success (or another failure overwrites them). Useful for retry UX that wants to keep the failure message visible alongside an inline spinner. Default falserun() clears the error at the start so the pending state reads "no current error."

type boolean

audit_error_schema_tightness
#

testing/surface_invariants.ts view source

(surface: AppSurface): ErrorSchemaAuditEntry[] import {audit_error_schema_tightness} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Audit error schema tightness across all routes in a surface.

Reports which route x status code combinations use generic ApiError (z.string()) vs specific z.literal() or z.enum() error codes. Use the output to prioritize progressive tightening of error schemas.

surface

returns

ErrorSchemaAuditEntry[]

audit entries for every route x status combination

AUDIT_EVENT_TYPE_NAME_REGEX
#

auth/audit_log_schema.ts view source

RegExp import {AUDIT_EVENT_TYPE_NAME_REGEX} from '@fuzdev/fuz_app/auth/audit_log_schema.js';

Letter start, then letters, digits, _, ., /, -. Accepts snake_case, dotted, and namespaced consumer conventions; rejects empty strings, leading separators, whitespace, and control characters.

AUDIT_EVENT_TYPES
#

auth/audit_log_schema.ts view source

readonly ["login", "logout", "bootstrap", "signup", "password_change", "session_revoke", "session_revoke_all", "token_create", "token_revoke", "token_revoke_all", "role_grant_create", ... 15 more ..., "app_settings_update"] import {AUDIT_EVENT_TYPES} from '@fuzdev/fuz_app/auth/audit_log_schema.js';

All tracked auth event types. Frozen to convert accidental in-process mutation (test cross-contamination, cast escapes) into loud TypeErrors. Not a security boundary — in-process code has many other paths to subvert audit logging.

AUDIT_LOG_CHANNEL
#

AUDIT_LOG_DEFAULT_LIMIT
#

audit_log_event_specs
#

AUDIT_LOG_INDEXES
#

audit_log_list_action_spec
#

auth/admin_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; roles: string[]; }; side_effects: false; input: ZodDefault<ZodObject<{ event_type: ZodOptional<ZodNullable<ZodString>>; ... 5 more ...; acting: ZodOptional<...>; }, $strict>>; output: ZodObject<...>; as... import {audit_log_list_action_spec} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

rate_limit: 'account' bounds admin-side enumeration of the entire audit log via (limit, offset) walking — same shape as admin_account_list_action_spec. The listing carries cross-account forensic detail (target ids, IPs, metadata), so the read-rate cap is the only check that distinguishes a human reviewer from a scraping script.

AUDIT_LOG_LIST_LIMIT_MAX
#

audit_log_role_grant_history_action_spec
#

auth/admin_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; roles: string[]; }; side_effects: false; input: ZodDefault<ZodObject<{ limit: ZodOptional<ZodNullable<ZodNumber>>; offset: ZodOptional<...>; acting: ZodOptional<...>; }, $strict>>; output: ZodObject<...... import {audit_log_role_grant_history_action_spec} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

rate_limit: 'account' bounds admin-side enumeration of the role_grant history via (limit, offset) walking — same shape as audit_log_list, narrower projection but identical scraping vector.

audit_log_rpc_context
#

ui/audit_log_state.svelte.ts view source

{ get: (error_message?: string | undefined) => () => AuditLogRpc; get_maybe: () => (() => AuditLogRpc) | undefined; set: (value: () => AuditLogRpc) => () => AuditLogRpc; } import {audit_log_rpc_context} from '@fuzdev/fuz_app/ui/audit_log_state.svelte.js';

Svelte context carrying the reactive AuditLogRpc accessor. Mirrors admin_accounts_rpc_context. get() throws when no provisioner ran above the component — the adapter is required.

AUDIT_LOG_SCHEMA
#

auth/audit_log_ddl.ts view source

"\nCREATE TABLE IF NOT EXISTS audit_log (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n seq BIGSERIAL NOT NULL,\n event_type TEXT NOT NULL,\n outcome TEXT NOT NULL DEFAULT 'success',\n actor_id UUID,\n account_id UUID,\n target_account_id UUID,\n target_actor_id UUID,\n ip TEXT,\n created_at TIMESTAMP... import {AUDIT_LOG_SCHEMA} from '@fuzdev/fuz_app/auth/audit_log_ddl.js';

AUDIT_LOG_SSE_MAX_PER_SCOPE
#

realtime/sse_auth_guard.ts view source

10 import {AUDIT_LOG_SSE_MAX_PER_SCOPE} from '@fuzdev/fuz_app/realtime/sse_auth_guard.js';

Default max concurrent SSE subscribers per session scope for the audit log.

The audit log SSE subscribes with scope = session_hash and groups = [account_id]. Only scope is capped — so this limits tabs per session. An account's total streams across all sessions is bounded transitively by max_sessions × AUDIT_LOG_SSE_MAX_PER_SCOPE. 10 tabs per session is a comfortable ceiling for normal use; consumers raising it above ~50 should consider server-side connection limits.

audit_metadata_schemas
#

auth/audit_log_schema.ts view source

Readonly<{ login: ZodNullable<ZodObject<{ username: ZodString; }, $loose>>; logout: ZodNull; bootstrap: ZodNullable<ZodObject<{ error: ZodString; }, $loose>>; ... 23 more ...; app_settings_update: ZodObject<...>; }> import {audit_metadata_schemas} from '@fuzdev/fuz_app/auth/audit_log_schema.js';

Per-event-type metadata Zod schemas. z.looseObject so consumers can add fields while known ones are validated. The record is frozen to catch mutation bugs at the key level (e.g. tests that try to swap in a stub schema); the Zod schemas themselves are reachable and mutable — freeze isn't a security boundary.

audit_unmatched_peer_response
#

actions/peer_request.ts view source

(log: Logger, connection_id: string & $brand<"Uuid">, id: string | number | null): void import {audit_unmatched_peer_response} from '@fuzdev/fuz_app/actions/peer_request.js';

Sampled, bounded audit for an inbound response that matched no pending request on its connection — an unsolicited {id, result}, a cross-connection id echo, or a late/duplicate reply for an already-settled id. Auditing every rejected frame would let a junk flood turn the log into the DoS target, so this warns on the first few then samples 1-in-256. Twin of the Rust fuz_realtime::peer::audit_unmatched_response.

log

the WS dispatcher's logger

type Logger

connection_id

the socket the unmatched reply arrived on

type string & $brand<"Uuid">

id

the reply's echoed id (null when absent)

type string | number | null

returns

void

AuditCompletenessTestOptions
#

testing/audit_completeness.ts view source

AuditCompletenessTestOptions import type {AuditCompletenessTestOptions} from '@fuzdev/fuz_app/testing/audit_completeness.js';

setup_test

Per-test fixture-producing function. The audit suite calls this in every test() body — auth_integration_truncate_tables clears the audit log between tests, so each test re-bootstraps the keeper and the observer admin against a fresh table.

type SetupTest

surface_source

App surface (with route specs). Constructed in TS by the consumer; same shape for in-process and cross-process tests. The audit suite is Tier 2 today (stays in-process per the cross-backend-integration design), but takes the same surface shape as Tier 1 suites for options uniformity.

type AppSurfaceSpec

capabilities

Backend capability declarations.

type BackendCapabilities

session_options

Session config — needed for factory-form rpc_endpoints resolution.

type SessionOptions<string>

rpc_endpoints

RPC endpoint specs — required. The admin role_grant flow is RPC-only and the suite hard-fails without it.

type RpcEndpointsSuiteOption

AuditEmitFn
#

auth/audit_emitter.ts view source

AuditEmitFn import type {AuditEmitFn} from '@fuzdev/fuz_app/auth/audit_emitter.js';

Signature of AuditEmitter.emit — captured by the inner closure so emit_role_grant_target reaches the decorated function rather than a this.emit lookup. Exposed as a type so EmitDecorator can name the inner / outer slot.

(call)

type <T extends string>(ctx: AuditEmitterContext, input: AuditLogInput<T>): void

ctx

input

type AuditLogInput<T>
returns void

AuditEmitMarker
#

AuditEmitRoleGrantContext
#

auth/audit_emitter.ts view source

AuditEmitRoleGrantContext import type {AuditEmitRoleGrantContext} from '@fuzdev/fuz_app/auth/audit_emitter.js';

Context required by AuditEmitter.emit_role_grant_target — adds client_ip so the helper can lift the ip: ctx.client_ip boilerplate every role-grant-shape emit site repeated.

inheritance

client_ip

Resolved client IP from the trusted-proxy middleware — 'unknown' if not resolved.

type string

AuditEmitter
#

auth/audit_emitter.ts view source

AuditEmitter import type {AuditEmitter} from '@fuzdev/fuz_app/auth/audit_emitter.js';

Bound audit-emit capability. Built once at backend assembly via create_audit_emitter; lives on AppDeps.audit so factories never see the pool.

emit

Fire-and-forget audit write via the captured pool.

The in-flight promise is pushed onto ctx.pending_effects so tests with await_pending_effects: true can assert side effects inline. Errors are logged, never thrown. Successful writes fan out to every listener on the chain (notify).

Returns void deliberately — the in-flight promise is already on ctx.pending_effects, and exposing it would tempt callers to await (sequencing audit writes onto the response hot path) or sprinkle void to placate no-floating-promises. For awaitable writes from code paths without pending_effects, use emit_pool.

type <T extends string>(ctx: AuditEmitterContext, input: AuditLogInput<T>): void

ctx

input

type AuditLogInput<T>
returns void

emit_role_grant_target

Emit a role-grant-shape audit event with actor_id / account_id / ip lifted from auth + ctx. Delegates to emit.

Use for any event populating one of the target_*_id columns. Reach for the lower-level emit only when the event is non-role-grant shape (e.g. app_settings_update, bootstrap, signup).

type <T extends string>(ctx: AuditEmitRoleGrantContext, auth: RequestActorContext, input: { event_type: T; target_account_id: (string & $brand<"Uuid">) | null; target_actor_id: (string & $brand<...>) | null; metadata: (T extends "invite_create" | ... 25 more ... | "actor_undelete" ? (AuditMetadataMap[T] & Record<...>) | null : Record<...> | null) | undefined; outcome?: "success" | ... 1 more ... | undefined; }): void

ctx

auth

input

type { event_type: T; target_account_id: (string & $brand<"Uuid">) | null; target_actor_id: (string & $brand<"Uuid">) | null; metadata: (T extends "invite_create" | ... 25 more ... | "actor_undelete" ? (AuditMetadataMap[T] & Record<...>) | null : Record<...> | null) | undefined; outcome?: "success" | ... 1 more ... | und...
returns void

emit_pool

Awaitable pool write for code paths without a pending_effects queue.

Same write-then-notify semantics as emit. Errors are logged and swallowed (resolved void), so callers can sequence sweeps with await audit.emit_pool(...) without try/catch boilerplate. The primary user is auth/cleanup.ts — sweeps have no per-request pending_effects to attach to.

type <T extends string>(input: AuditLogInput<T>): Promise<void>

input

type AuditLogInput<T>
returns Promise<void>

notify

Fan out an already-written audit row to the registered listeners.

Use only when the row was inserted in-transaction by a query helper that returned the AuditLogEvent (e.g. query_accept_offer.audit_events). Per-listener exceptions are caught and logged; one failing listener does not starve siblings.

type (event: AuditLogEvent): void

event

returns void

add_listener

Register an audit-event listener. Append-only — listeners fire in registration order on every successful emit / emit_pool and on every notify.

create_app_server registers the factory-managed audit-log SSE listener and per-endpoint WS auth guards / logout closers here so SSE + WS fan-out compose on top of the consumer's on_audit_event callback without shallow-copying AppDeps. Consumers can also register listeners directly for setups that don't run through create_app_server.

Twin of the Rust fuz_auth AuditEmitter::add_listener.

type (listener: (event: AuditLogEvent) => void): void

listener

type (event: AuditLogEvent) => void
returns void

listener_count

Count of registered listeners — introspection for tests and diagnostics.

type (): number

returns number

AuditEmitterContext
#

auth/audit_emitter.ts view source

AuditEmitterContext import type {AuditEmitterContext} from '@fuzdev/fuz_app/auth/audit_emitter.js';

Per-request context required by AuditEmitter.emit — just the eager pending_effects queue. The bound emitter carries its own log reference inside the closure, so per-call contexts don't need one.

Audit emits are eager-only by design: the bound emitter fires the pool write immediately and pushes the in-flight Promise<void> here. They never go through emit_after_commit — pool-routed audit writes are already rollback-resilient because they run outside the request transaction, so the post-commit timing the deferred queue provides would only delay forensic visibility without any safety benefit.

Both RouteContext and ActionContext structurally satisfy this shape (they each carry pending_effects), so handlers pass route / ctx directly.

pending_effects

type Array<Promise<void>>

AuditEventHandler
#

actions/transports_ws_auth_guard.ts view source

AuditEventHandler import type {AuditEventHandler} from '@fuzdev/fuz_app/actions/transports_ws_auth_guard.js';

Audit-event callback shape — the function CreateAuditEmitterOptions.on_audit_event accepts and that the helpers in this module return.

Exported so consumers composing multiple handlers (typically create_ws_auth_guard + create_ws_logout_closer + their own pre-existing on_audit_event) can annotate their composed callback without reaching for Parameters<typeof create_ws_auth_guard>[0].

(call)

type (event: AuditLogEvent): void

event

returns void

AuditEventType
#

auth/audit_log_schema.ts view source

ZodEnum<{ invite_create: "invite_create"; invite_delete: "invite_delete"; account_delete: "account_delete"; account_purge: "account_purge"; account_undelete: "account_undelete"; app_settings_update: "app_settings_update"; ... 20 more ...; actor_undelete: "actor_undelete"; }> import type {AuditEventType} from '@fuzdev/fuz_app/auth/audit_log_schema.js';

Zod schema for audit event types.

AuditEventTypeName
#

auth/audit_log_schema.ts view source

ZodString import type {AuditEventTypeName} from '@fuzdev/fuz_app/auth/audit_log_schema.js';

Zod schema for valid audit event-type name strings.

AuditFactory
#

server/app_backend.ts view source

AuditFactory import type {AuditFactory} from '@fuzdev/fuz_app/server/app_backend.js';

Callback that builds the bound AuditEmitter after the backend's pool Db and Logger exist. Required on CreateAppBackendOptions so the consumer owns subscriber-chain composition and AuditLogConfig selection without the factory holding a default.

The factory is invoked exactly once during create_app_backend, after create_db resolves and migrations run. The emitter it returns lands on AppDeps.audit and is captured by every query/handler that reaches deps.audit.emit(...).

The canonical body is a one-liner over create_audit_emitter:

audit_factory: ({db, log}) => create_audit_emitter({ db, log, on_audit_event, audit_log_config, })

Returning an emitter built against a different db than the one passed in would route audit writes to a different pool than handlers query — the callback shape exists specifically to make that mistake structurally impossible.

(call)

type (params: { db: Db; log: Logger; }): AuditEmitter

params

type { db: Db; log: Logger; }
returns AuditEmitter

AuditLogConfig
#

auth/audit_log_schema.ts view source

AuditLogConfig import type {AuditLogConfig} from '@fuzdev/fuz_app/auth/audit_log_schema.js';

Configuration bundle for audit-log event types and metadata schemas.

Lets consumers extend the closed AUDIT_EVENT_TYPES enum with their own event strings (and metadata Zod schemas) without forking. Pass to create_audit_emitter (or query_audit_log for in-tx call sites) as the optional config argument; both default to builtin_audit_log_config.

The DB column is TEXT NOT NULL and never enforced an enum, so consumer event types round-trip through query_audit_log_list and SSE identically to builtins.

Constructed configs are deep-frozen (wrapper, event_types, metadata_schemas) to catch accidental mutation bugs early. Not a security boundary against in-process code, which can subvert audit logging through other paths.

event_types

All recognized event-type strings — fuz_app builtins plus consumer extras.

type ReadonlyArray<string>

readonly

metadata_schemas

Per-event-type metadata schemas. Missing entries skip metadata validation for that type (row still written; metadata stored as raw JSONB).

type Readonly<Record<string, z.ZodType>>

readonly

AuditLogEvent
#

auth/audit_log_schema.ts view source

AuditLogEvent import type {AuditLogEvent} from '@fuzdev/fuz_app/auth/audit_log_schema.js';

Audit log row from the database. See AuditLogEventJson for event_type widening rationale.

id

type Uuid

seq

type number

event_type

type AuditEventTypeName

outcome

type AuditOutcome

actor_id

Operator (the actor that initiated the event) — populated when the request resolved an acting actor.

Resolution is driven per-request by the route-spec wrapper / RPC dispatcher; a route gets an acting actor when its input schema declares acting?: ActingActor or its auth requires role_grants (role / keeper). Account-grain operations declare neither, so no actor is resolved and actor_id is null: login (also pre-credential), logout, signup, bootstrap, password_change, session/token revoke, app_settings_update, invite events. Role grant events, admin actions, and actor-targeted offers populate this with the initiator's actor.

type Uuid | null

account_id

type Uuid | null

target_account_id

type Uuid | null

target_actor_id

Actor-grain target — populated when the event subject is bound to a specific actor.

Concretely:

  • Always populated: role_grant_revoke and role_grant_create (admin direct-grant, self-service toggle, and in-tx role_grant_offer_accept all populate both target columns — the role_grant's grantee is the actor-grain subject regardless of who initiated the grant), role_grant_offer_accept on accept (the accept binds the actor deterministically), role_grant_offer_decline (the grantor actor — decline is *to* the offering actor).
  • Conditionally populated: offer-shape events (role_grant_offer_create, _expire, _retract, _supersede) carry the actor when the offer was actor-targeted at create time (role_grant_offer.to_actor_id set), null when the offer was account-grain (any actor on to_account_id may accept).
  • Not populated: admin actions, account-shape events (login, logout, signup, bootstrap, password_change, session/token revoke, app_settings_update, invite events) — subject is the account or no specific resource, not an actor-bound role_grant.
  • Not populated: events whose principal isn't an actor-bound resource (e.g. consumer events that name a non-actor scope in metadata).

Multi-actor invariants this column relies on: when both target_actor_id and target_account_id are populated they refer to the same account (actor.account_id-derivable). The invariant holds uniformly across every populated event including decline (the grantor's account is joined into the decline RETURNING) and the supersede cascade (the recipient account is known on role_grant_offer.to_account_id). target_account_id stays the SSE/WS socket-close key because sessions remain account-grain after multi-actor lands.

type Uuid | null

ip

type string | null

created_at

type string

metadata

type Record<string, unknown> | null

AuditLogEventJson
#

auth/audit_log_schema.ts view source

ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; seq: ZodNumber; event_type: ZodString; outcome: ZodEnum<{ success: "success"; failure: "failure"; }>; ... 6 more ...; metadata: ZodNullable<...>; }, $strict> import type {AuditLogEventJson} from '@fuzdev/fuz_app/auth/audit_log_schema.js';

Zod schema for client-safe audit log event.

event_type is AuditEventTypeName (regex-validated string) — matches the AuditLogEvent row and the DB's TEXT NOT NULL column. Consumer types registered via create_audit_log_config({extra_events}) round-trip through queries, on_audit_event callbacks, and JSON-RPC responses identically to builtins. AuditLogInput<T> stays parameterized on the write side so AuditMetadataMap narrowing via get_audit_metadata works.

AuditLogEventWithUsernamesJson
#

auth/audit_log_schema.ts view source

ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; seq: ZodNumber; event_type: ZodString; outcome: ZodEnum<{ success: "success"; failure: "failure"; }>; ... 8 more ...; target_username: ZodNullable<...>; }, $strict> import type {AuditLogEventWithUsernamesJson} from '@fuzdev/fuz_app/auth/audit_log_schema.js';

Zod schema for audit log events with resolved usernames.

AuditLogInput
#

auth/audit_log_schema.ts view source

AuditLogInput<T> import type {AuditLogInput} from '@fuzdev/fuz_app/auth/audit_log_schema.js';

Input for creating an audit log entry.

generics

AuditLogInput<T extends string = AuditEventType>
T
constraint string

event_type

type T

outcome?

type AuditOutcome

actor_id?

type Uuid | null

account_id?

type Uuid | null

target_account_id?

type Uuid | null

target_actor_id?

type Uuid | null

ip?

type string | null

metadata?

Per-event-type metadata. Builtin T narrows to AuditMetadataMap[T]; consumer strings widen to a generic record (validation runs against AuditLogConfig.metadata_schemas at insert time).

type T extends AuditEventType ? (AuditMetadataMap[T] & Record<string, unknown>) | null : Record<string, unknown> | null

AuditLogListInput
#

auth/admin_action_specs.ts view source

ZodDefault<ZodObject<{ event_type: ZodOptional<ZodNullable<ZodString>>; outcome: ZodOptional<ZodNullable<ZodEnum<{ success: "success"; failure: "failure"; }>>>; ... 4 more ...; acting: ZodOptional<...>; }, $strict>> import type {AuditLogListInput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Input for audit_log_list. All filter fields are optional — omit for the default newest-first page. since_seq exists for SSE reconnection gap fill (caller supplies the highest seq seen; server returns everything after).

AuditLogListOptions
#

auth/audit_log_schema.ts view source

AuditLogListOptions import type {AuditLogListOptions} from '@fuzdev/fuz_app/auth/audit_log_schema.js';

Options for listing audit log entries.

limit?

type number

offset?

type number

event_type?

Event-type filter. Accepts any string — builtins or consumer-registered via create_audit_log_config({extra_events}). The DB column is TEXT NOT NULL with no CHECK, so unknown strings simply match nothing.

type string

event_type_in?

type Array<string>

account_id?

type Uuid

outcome?

type AuditOutcome

since_seq?

When set, only return events with seq greater than this value. Enables SSE reconnection gap fill.

type number

AuditLogListOutput
#

auth/admin_action_specs.ts view source

ZodObject<{ events: ZodArray<ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; seq: ZodNumber; event_type: ZodString; outcome: ZodEnum<{ success: "success"; failure: "failure"; }>; ... 8 more ...; target_username: ZodNullable<...>; }, $strict>>; }, $strict> import type {AuditLogListOutput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Output for audit_log_list.

AuditLogRoleGrantHistoryInput
#

auth/admin_action_specs.ts view source

ZodDefault<ZodObject<{ limit: ZodOptional<ZodNullable<ZodNumber>>; offset: ZodOptional<ZodNullable<ZodNumber>>; acting: ZodOptional<...>; }, $strict>> import type {AuditLogRoleGrantHistoryInput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Input for audit_log_role_grant_history.

AuditLogRoleGrantHistoryOutput
#

auth/admin_action_specs.ts view source

ZodObject<{ events: ZodArray<ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; seq: ZodNumber; event_type: ZodString; outcome: ZodEnum<{ success: "success"; failure: "failure"; }>; ... 8 more ...; target_username: ZodNullable<...>; }, $strict>>; }, $strict> import type {AuditLogRoleGrantHistoryOutput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Output for audit_log_role_grant_history.

AuditLogRouteOptions
#

auth/audit_log_routes.ts view source

AuditLogRouteOptions import type {AuditLogRouteOptions} from '@fuzdev/fuz_app/auth/audit_log_routes.js';

Options for audit log route specs.

required_role?

Role required to access audit routes. Default 'admin'.

type string

stream?

When provided, includes an SSE route at /audit/stream for realtime audit events. The subscribe function receives the stream, channels, and the subscriber's account_id as an identity key — enabling close_by_identity() for auth revocation.

type { subscribe: (stream: SseStream<SseNotification>, options?: SubscribeOptions) => () => void; log: Logger; }

AuditLogRpc
#

ui/audit_log_state.svelte.ts view source

AuditLogRpc import type {AuditLogRpc} from '@fuzdev/fuz_app/ui/audit_log_state.svelte.js';

Narrow RPC surface consumed by AuditLogState. Consumers adapt their typed RPC client to this shape. Mirrors AdminAccountsRpc / AdminInvitesRpc. Method signatures track the wire spec inputs/outputs directly so the adapter needs no casts.

list

type (input?: AuditLogListInput) => Promise<AuditLogListOutput>

role_grant_history

type ( input?: AuditLogRoleGrantHistoryInput ) => Promise<AuditLogRoleGrantHistoryOutput>

AuditLogSse
#

realtime/sse_auth_guard.ts view source

AuditLogSse import type {AuditLogSse} from '@fuzdev/fuz_app/realtime/sse_auth_guard.js';

Convenience factory result for audit log SSE.

Satisfies AuditLogRouteOptions['stream'] and provides the combined on_audit_event callback (broadcast + guard).

subscribe

Subscribe function — pass as part of stream option to create_audit_log_route_specs.

type (stream: SseStream<SseNotification>, options?: SubscribeOptions) => () => void

log

Logger — pass as part of stream option to create_audit_log_route_specs.

type Logger

on_audit_event

Combined broadcast + guard callback. Wired by create_app_server's audit_log_sse option, or compose inside the consumer's audit_factory body.

type (event: AuditLogEvent) => void

registry

The underlying registry — exposed for subscriber count monitoring.

type SubscriberRegistry<SseNotification>

AuditLogState
#

ui/audit_log_state.svelte.ts view source

import {AuditLogState} from '@fuzdev/fuz_app/ui/audit_log_state.svelte.js';

list

type AsyncSlot<void, string>

readonly

role_grant_history

type AsyncSlot<void, string>

readonly

events

type Array<AuditLogEventWithUsernamesJson>

$state.raw

role_grant_history_events

type Array<RoleGrantHistoryEventJson>

$state.raw

count

type number

readonly $derived

connected

Whether the SSE stream is currently connected.

type boolean

$state.raw

constructor

type new (options: AuditLogStateOptions): AuditLogState

options

fetch

type (options?: { event_type?: string | null | undefined; outcome?: "success" | "failure" | null | undefined; account_id?: (string & $brand<"Uuid">) | null | undefined; limit?: number | null | undefined; offset?: number | ... 1 more ... | undefined; since_seq?: number | ... 1 more ... | undefined; acting?: (string & $brand<...>) | undefined; } | undefined): Promise<...>

options?

type { event_type?: string | null | undefined; outcome?: "success" | "failure" | null | undefined; account_id?: (string & $brand<"Uuid">) | null | undefined; limit?: number | null | undefined; offset?: number | ... 1 more ... | undefined; since_seq?: number | ... 1 more ... | undefined; acting?: (string & $brand<...>) | ...
optional
returns Promise<void>

fetch_role_grant_history

type (limit?: number | undefined, offset?: number | undefined): Promise<void>

limit?

type number | undefined
optional

offset?

type number | undefined
optional
returns Promise<void>

subscribe

Connect to the SSE stream for realtime audit events.

New events are prepended to events. EventSource auto-reconnects on transient errors; since_seq fills gaps on reconnection.

type (): () => void

returns () => void

cleanup function that closes the connection

disconnect

Close the SSE connection.

type (): void

returns void

AuditLogStateOptions
#

ui/audit_log_state.svelte.ts view source

AuditLogStateOptions import type {AuditLogStateOptions} from '@fuzdev/fuz_app/ui/audit_log_state.svelte.js';

get_rpc

Reactive accessor for the RPC adapter. Matches the get_rpc pattern on AdminAccountsState. The SSE stream uses EventSource directly and is independent of this adapter.

type () => AuditLogRpc

stream_url?

SSE stream URL. Defaults to the shipped admin audit-log stream route.

type string

AuditMetadataMap
#

auth/audit_log_schema.ts view source

AuditMetadataMap import type {AuditMetadataMap} from '@fuzdev/fuz_app/auth/audit_log_schema.js';

Mapped type of metadata shapes per event type, derived from Zod schemas.

invite_create

type { [x: string]: unknown; invite_id: string & $brand<"Uuid">; email: string | null; username: string | null; }

invite_delete

type { [x: string]: unknown; invite_id: string & $brand<"Uuid">; }

account_delete

type { [x: string]: unknown; username?: string | undefined; email?: string | null | undefined; reason?: string | undefined; attempted_account_id?: (string & $brand<"Uuid">) | undefined; }

account_purge

type { [x: string]: unknown; username?: string | undefined; email?: string | null | undefined; reason?: string | undefined; attempted_account_id?: (string & $brand<"Uuid">) | undefined; }

account_undelete

type { [x: string]: unknown; username?: string | undefined; email?: string | null | undefined; reason?: string | undefined; attempted_account_id?: (string & $brand<"Uuid">) | undefined; }

app_settings_update

type { [x: string]: unknown; setting: string; old_value: unknown; new_value: unknown; }

login

type { [x: string]: unknown; username: string; } | null

logout

type null

bootstrap

type { [x: string]: unknown; error: string; } | null

signup

type { [x: string]: unknown; username: string; invite_id?: (string & $brand<"Uuid">) | undefined; open_signup?: boolean | undefined; reason?: string | undefined; email?: string | undefined; }

password_change

type { [x: string]: unknown; sessions_revoked?: number | undefined; tokens_revoked?: number | undefined; reason?: "concurrent_change" | undefined; credential_type?: "session" | "api_token" | "daemon_token" | undefined; } | null

session_revoke

type { [x: string]: unknown; session_id: string & $brand<"SessionId">; credential_type?: "session" | "api_token" | "daemon_token" | undefined; }

session_revoke_all

type { [x: string]: unknown; count?: number | undefined; reason?: string | undefined; attempted_account_id?: (string & $brand<"Uuid">) | undefined; credential_type?: "session" | "api_token" | "daemon_token" | undefined; }

token_create

type { [x: string]: unknown; token_id: string; name: string; credential_type?: "session" | "api_token" | "daemon_token" | undefined; }

token_revoke

type { [x: string]: unknown; token_id: string; credential_type?: "session" | "api_token" | "daemon_token" | undefined; }

token_revoke_all

type { [x: string]: unknown; count?: number | undefined; reason?: string | undefined; attempted_account_id?: (string & $brand<"Uuid">) | undefined; }

role_grant_create

type { [x: string]: unknown; role: string; role_grant_id?: (string & $brand<"Uuid">) | undefined; scope_id?: (string & $brand<"Uuid">) | null | undefined; source_offer_id?: (string & $brand<...>) | undefined; self_service?: boolean | undefined; }

role_grant_revoke

type { [x: string]: unknown; role: string; role_grant_id: string & $brand<"Uuid">; scope_id?: (string & $brand<"Uuid">) | null | undefined; reason?: string | undefined; self_service?: boolean | undefined; }

role_grant_offer_create

type { [x: string]: unknown; role: string; to_account_id: string & $brand<"Uuid">; offer_id?: (string & $brand<"Uuid">) | undefined; scope_id?: (string & $brand<"Uuid">) | null | undefined; }

role_grant_offer_accept

type { [x: string]: unknown; offer_id: string & $brand<"Uuid">; role_grant_id: string & $brand<"Uuid">; role: string; scope_id?: (string & $brand<"Uuid">) | null | undefined; }

role_grant_offer_decline

type { [x: string]: unknown; offer_id: string & $brand<"Uuid">; role: string; scope_id?: (string & $brand<"Uuid">) | null | undefined; reason?: string | undefined; }

role_grant_offer_retract

type { [x: string]: unknown; offer_id: string & $brand<"Uuid">; role: string; scope_id?: (string & $brand<"Uuid">) | null | undefined; }

role_grant_offer_expire

type { [x: string]: unknown; offer_id: string & $brand<"Uuid">; role: string; scope_id?: (string & $brand<"Uuid">) | null | undefined; }

role_grant_offer_supersede

type { [x: string]: unknown; offer_id: string & $brand<"Uuid">; role: string; reason: "sibling_accepted" | "role_grant_revoked" | "scope_destroyed"; cause_id: string & $brand<"Uuid">; scope_id?: (string & $brand<...>) | ... 1 more ... | undefined; }

actor_delete

type { [x: string]: unknown; name?: string | undefined; }

actor_purge

type { [x: string]: unknown; name?: string | undefined; }

actor_undelete

type { [x: string]: unknown; name?: string | undefined; }

AuditOutcome
#

auth/audit_log_schema.ts view source

ZodEnum<{ success: "success"; failure: "failure"; }> import type {AuditOutcome} from '@fuzdev/fuz_app/auth/audit_log_schema.js';

Zod schema for audit event outcomes.

AuditStreamQuery
#

auth/audit_log_route_schema.ts view source

ZodObject<{ acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict> import type {AuditStreamQuery} from '@fuzdev/fuz_app/auth/audit_log_route_schema.js';

Query schema for the audit-log SSE route — multi-actor admins pass ?acting=<uuid>.

AUTH_API_TOKEN_ID_KEY
#

hono_context.ts view source

"auth_api_token_id" import {AUTH_API_TOKEN_ID_KEY} from '@fuzdev/fuz_app/hono_context.js';

Hono context variable name for the authenticated API token id.

auth_integration_truncate_tables
#

testing/db.ts view source

string[] import {auth_integration_truncate_tables} from '@fuzdev/fuz_app/testing/db.js';

Auth tables including audit_log — for integration tests that exercise the full middleware stack (login, admin, rate limiting).

Separate from auth_truncate_tables because unit-level DB tests that don't touch audit logging don't need to truncate it.

AUTH_MIGRATION_NAMESPACE
#

auth/migrations.ts view source

"fuz_auth" import {AUTH_MIGRATION_NAMESPACE} from '@fuzdev/fuz_app/auth/migrations.js';

Namespace identifier for fuz_app auth migrations.

auth_migration_ns
#

auth/migrations.ts view source

MigrationNamespace import {auth_migration_ns} from '@fuzdev/fuz_app/auth/migrations.js';

Pre-composed migration namespace for auth tables.

auth_migrations
#

auth/migrations.ts view source

Migration[] import {auth_migrations} from '@fuzdev/fuz_app/auth/migrations.js';

Auth schema migrations in order.

  • v0: Full auth schema — account (with email_verified), actor, role_grant, auth_session, api_token, audit_log (with seq), bootstrap_lock, invite, app_settings, plus all indexes and seeds.
  • v1: role_grant_offer table for consentful grants; adds scope_id / scope_kind / source_offer_id / revoked_reason to role_grant and swaps the (actor_id, role) partial unique index for a scope-aware variant using the index-side 'GLOBAL' token + all-zeros sentinel UUID. The (scope_kind, scope_id) pair is enforced paired-null by role_grant_scope_kind_paired / role_grant_offer_scope_kind_paired CHECK constraints — both null for global, both non-null for scoped. The role_grant_offer table carries a superseded_at terminal state; its partial unique index is scoped by (to_account, role, scope_kind, scope, from_actor) so multiple grantors may coexist. scope_kind is informative-only in v1 (registry-membership validation against create_scope_kind_schema); v2 may add INSERT-time (role, scope_kind) enforcement.

AUTH_SESSION_EXTEND_THRESHOLD_MS
#

auth/session_queries.ts view source

number import {AUTH_SESSION_EXTEND_THRESHOLD_MS} from '@fuzdev/fuz_app/auth/session_queries.js';

Extend session when it has less than this remaining (1 day in ms).

AUTH_SESSION_INDEXES
#

AUTH_SESSION_LIFETIME_MS
#

AUTH_SESSION_SCHEMA
#

auth/auth_ddl.ts view source

"\nCREATE TABLE IF NOT EXISTS auth_session (\n id TEXT PRIMARY KEY,\n account_id UUID NOT NULL REFERENCES account(id) ON DELETE CASCADE,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n expires_at TIMESTAMPTZ NOT NULL,\n last_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n)" import {AUTH_SESSION_SCHEMA} from '@fuzdev/fuz_app/auth/auth_ddl.js';

AUTH_SESSION_TOKEN_HASH_KEY
#

auth/request_context.ts view source

"auth_session_token_hash" import {AUTH_SESSION_TOKEN_HASH_KEY} from '@fuzdev/fuz_app/auth/request_context.js';

Hono context variable name for the authenticated session token hash.

Set by create_request_context_middleware after a successful session lookup. null when the request is unauthenticated or authenticated via a non-session credential (bearer token, daemon token). Exposed so handlers can scope per-session resources (e.g., SSE stream identity for targeted disconnection on session_revoke) without re-hashing the token.

auth_state_context
#

ui/auth_state.svelte.ts view source

{ get: (error_message?: string | undefined) => AuthState; get_maybe: () => AuthState | undefined; set: (value: AuthState) => AuthState; } import {auth_state_context} from '@fuzdev/fuz_app/ui/auth_state.svelte.js';

Svelte context for AuthState. Use auth_state_context.set(state) in the provider and auth_state_context.get() to access.

auth_truncate_tables
#

testing/db.ts view source

string[] import {auth_truncate_tables} from '@fuzdev/fuz_app/testing/db.js';

Auth table names in truncation order (children first for FK safety).

Consumer projects can spread this into their own list and append app-specific tables.

AuthActionHandler
#

actions/action_rpc.ts view source

AuthActionHandler<TInput, TOutput> import type {AuthActionHandler} from '@fuzdev/fuz_app/actions/action_rpc.js';

Handler signature for an account-grain RPC action — auth.account === 'required' and auth.actor === 'none'. Mirrors ActionHandler but tightens the ctx.auth slot to the non-null RequestContext (with actor: null).

generics

AuthActionHandler<TInput = any, TOutput = any>
TInput
default any
TOutput
default any

(call)

type (input: TInput, ctx: ActionAuthContext): TOutput | Promise<TOutput>

input

type TInput

ctx

returns TOutput | Promise<TOutput>

AuthAxisState
#

http/auth_shape.ts view source

ZodEnum<{ none: "none"; optional: "optional"; required: "required"; }> import type {AuthAxisState} from '@fuzdev/fuz_app/http/auth_shape.js';

Per-axis auth state — names the dispatcher's behavior on account and actor independently:

  • 'none' — explicitly skipped, even when the credential provides it. Public actions (no auth surface) and notifications declare this.
  • 'optional' — surfaced if the credential provides it, null otherwise. Identity-aware reads with anonymous fallback (cell_get-style) declare this on account / actor.
  • 'required' — must be resolved; the dispatcher rejects requests that fail to provide it (401 for account === 'required' without a credential; the authorization phase 4xx for actor === 'required' without an actor binding).

AuthCleanupDeps
#

auth/cleanup.ts view source

AuthCleanupDeps import type {AuthCleanupDeps} from '@fuzdev/fuz_app/auth/cleanup.js';

Dependencies for the cleanup helpers.

inheritance

extends: QueryDeps

log

type Logger

audit

Bound audit emitter. cleanup_expired_role_grant_offers writes via audit.emit_pool (the captured pool + config + listener chain), so one slot covers both row persistence and SSE/WS fan-out. Required — production wiring always has a bound emitter on AppDeps.audit, and tests that need a no-op pass create_test_audit_emitter().

type AuditEmitter

AuthCleanupResult
#

auth/cleanup.ts view source

AuthCleanupResult import type {AuthCleanupResult} from '@fuzdev/fuz_app/auth/cleanup.js';

Result of run_auth_cleanup.

expired_sessions

Number of expired session rows deleted.

type number

expired_offers

Number of expired role_grant offer rows audit-stamped.

type number

AuthGuardResolver
#

http/route_spec.ts view source

AuthGuardResolver import type {AuthGuardResolver} from '@fuzdev/fuz_app/http/route_spec.js';

Resolves a RouteAuth to middleware guard handlers.

Injected into apply_route_specs to decouple route registration from auth-specific middleware. See fuz_auth_guard_resolver in auth/auth_guard_resolver.ts for the standard implementation.

(call)

type (auth: { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }): AuthGuards

auth

type { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }
returns AuthGuards

AuthGuards
#

http/route_spec.ts view source

AuthGuards import type {AuthGuards} from '@fuzdev/fuz_app/http/route_spec.js';

Two-phase auth guard set returned by AuthGuardResolver.

pre_validation runs before input validation — 401 checks live here so unauthenticated callers never see route-shape information from input parsing failures. post_authorization runs after the authorization phase has populated RequestContext — role / keeper checks live here because they read c.var.request_context.role_grants.

pre_validation

type Array<MiddlewareHandler>

post_authorization

type Array<MiddlewareHandler>

AuthMiddlewareOptions
#

auth/middleware.ts view source

AuthMiddlewareOptions import type {AuthMiddlewareOptions} from '@fuzdev/fuz_app/auth/middleware.js';

Per-factory configuration for the standard auth middleware stack.

allowed_origins

type Array<RegExp>

session_options

type SessionOptions<string>

path?

Path pattern for middleware (default: '/api/*').

type string

daemon_token_state?

Daemon token state for keeper auth. Omit to disable daemon token middleware.

type DaemonTokenState

bearer_ip_rate_limiter

Rate limiter for bearer token auth attempts (per-IP). Pass null to disable.

type RateLimiter | null

AuthorizationFailureBody
#

auth/request_context.ts view source

AuthorizationFailureBody import type {AuthorizationFailureBody} from '@fuzdev/fuz_app/auth/request_context.js';

Resolution-failure shape returned by apply_authorization_phase. Each transport binds this to the appropriate wire shape — REST emits the body directly via c.json(body, status); the RPC dispatcher folds it into a JSON-RPC error envelope {jsonrpc, id, error: {code, message, data}}.

The auth phase deliberately stops short of constructing a Response so the same failure flows through every transport without the auth-domain code knowing about JSON-RPC. See ../../../CLAUDE.md §Cleanest architecture takes priority for the rationale.

AuthorizationHandler
#

http/route_spec.ts view source

AuthorizationHandler import type {AuthorizationHandler} from '@fuzdev/fuz_app/http/route_spec.js';

Per-route authorization phase. Runs after pre-validation auth guards AND input validation; resolves the acting actor (when auth.actor !== 'none') by reading c.var.validated_input.acting and sets the request context on the Hono context. Per-route order in apply_route_specs: params → query → pre-validation auth (401) → input validation (400) → authorization phase → post-authorization auth (403) → handler.

Returns a Response to short-circuit (resolution failure → 400 / 500), or void to continue. The http framework stays auth-agnostic — fuz_app provides the implementation via create_fuz_authorization_handler in auth/request_context.ts.

(call)

type (c: Context<any, any, {}>, spec: RouteSpec): Promise<void | Response>

c

type Context<any, any, {}>

spec

returns Promise<void | Response>

AuthorizationResult
#

auth/request_context.ts view source

AuthorizationResult import type {AuthorizationResult} from '@fuzdev/fuz_app/auth/request_context.js';

Result of the authorization phase. Pure data — the auth domain stops short of touching the Hono context or producing a Response so HTTP RPC, WS, and REST each bind the same shape to their wire surface.

  • {ok: true, request_context}request_context is non-null on resolved (actor-bound or account-only) outcomes; null for public actions ({account: 'none', actor: 'none'}) and for genuine anonymous access on an 'optional' axis. Public and unauthenticated collapse to the same null request_context; every transport already treated them identically.
  • {ok: false, status, body} — 400/500 failure. status is narrowed to the two values the auth phase emits, so Hono's c.json status overload accepts the literals directly. The 500 reasons stay distinct in body: no_actors_on_account (signup invariant violation); account_vanished (torn read after resolve).

AuthSession
#

auth/account_schema.ts view source

AuthSession import type {AuthSession} from '@fuzdev/fuz_app/auth/account_schema.js';

Server-side auth session, keyed by blake3 hash of session token.

id

type SessionId

account_id

type Uuid

created_at

type string

expires_at

type string

last_seen_at

type string

AuthSessionJson
#

auth/account_schema.ts view source

ZodObject<{ id: $ZodBranded<ZodString, "SessionId", "out">; account_id: $ZodBranded<ZodUUID, "Uuid", "out">; created_at: ZodString; expires_at: ZodString; last_seen_at: ZodString; }, $strict> import type {AuthSessionJson} from '@fuzdev/fuz_app/auth/account_schema.js';

Zod schema for AuthSession — id is the blake3 hash, safe for client.

AuthSessionRouteOptions
#

auth/account_routes.ts view source

AuthSessionRouteOptions import type {AuthSessionRouteOptions} from '@fuzdev/fuz_app/auth/account_routes.js';

Shared options for route factories that create sessions and rate-limit by IP.

Extended by AccountRouteOptions and SignupRouteOptions. Consumers can destructure these from AppServerContext once and spread into multiple factories.

session_options

type SessionOptions<string>

ip_rate_limiter

Rate limiter for auth attempts, keyed by client IP. Pass null to disable.

type RateLimiter | null

AuthState
#

ui/auth_state.svelte.ts view source

import {AuthState} from '@fuzdev/fuz_app/ui/auth_state.svelte.js';

verifying

type boolean

$state.raw

verified

type boolean

$state.raw

verify_error

type string | null

$state.raw

account

type SessionAccount | null

$state.raw

actor

type ActorSummaryJson | null

$state.raw

role_grants

type Array<RoleGrantSummaryJson>

$state.raw

active_role_grants

type Array<RoleGrantSummaryJson>

readonly $derived

roles

type Array<string>

readonly $derived

needs_bootstrap

True when bootstrap is available (no accounts exist yet).

type boolean

$state.raw

check_session

Check auth state and bootstrap availability.

Fetches GET /api/account/status — returns account info (200) or 401 with optional bootstrap_available flag. Called on init, and after login/bootstrap to refresh state.

type (): Promise<void>

returns Promise<void>

login

Log in with username and password. Translates 401 / 429 to friendly messages on verify_error; refreshes the session via check_session on success.

type (username: string, password: string): Promise<boolean>

username

type string

password

type string
returns Promise<boolean>

true if login succeeded, false otherwise

bootstrap

Bootstrap the first keeper account using a single-use token.

type (token: string, username: string, password: string): Promise<boolean>

token

type string

username

type string

password

type string
returns Promise<boolean>

true if bootstrap succeeded, false otherwise

signup

Sign up via invite (or open signup, when app_settings.open_signup is true server-side). Translates 403 / 409 / 429 to friendly messages on verify_error.

type (username: string, password: string, email?: string | undefined): Promise<boolean>

username

type string

password

type string

email?

type string | undefined
optional
returns Promise<boolean>

true if signup succeeded, false otherwise

logout

Log out — best-effort POST /api/account/logout to clear the session cookie, then clears local state regardless of the network outcome.

type (): Promise<void>

returns Promise<void>

AuthTestApps
#

testing/auth_apps.ts view source

AuthTestApps import type {AuthTestApps} from '@fuzdev/fuz_app/testing/auth_apps.js';

Pre-built Hono apps for each auth level, shared across adversarial test suites.

public

type Hono

authed

type Hono

keeper

type Hono

by_role

type Map<string, Hono>

BackendBootstrapConfig
#

testing/cross_backend/backend_config.ts view source

BackendBootstrapConfig import type {BackendBootstrapConfig} from '@fuzdev/fuz_app/testing/cross_backend/backend_config.js';

Auth-bootstrap configuration for a spawnable test binary. The runner writes token to token_path before launching the child, then POSTs bootstrap_path (default /api/account/bootstrap) with the token plus the username / password to mint the keeper account and capture the session cookie. After health-probe, the runner reads daemon_token_path to load the binary's deterministic daemon token, which default_cross_process_setup threads onto the per-test TestFixture for _testing_reset calls and other keeper-credential operations.

token_path

Path the binary reads for the bootstrap token (env: *_BOOTSTRAP_TOKEN_PATH).

type string

readonly

token

Token text written to token_path before spawn.

type string

readonly

username

Username for the bootstrapped keeper.

type string

readonly

password

Password for the bootstrapped keeper.

type string

readonly

daemon_token_path

Path the test binary writes its daemon-token JSON to on boot (env: *_DAEMON_TOKEN_PATH). spawn_backend reads this file once after the health probe succeeds and threads the token onto BackendHandle.daemon_token for _testing_reset calls plus any other admin/keeper-gated cross-process tests.

type string

readonly

BackendCapabilities
#

testing/cross_backend/capabilities.ts view source

BackendCapabilities import type {BackendCapabilities} from '@fuzdev/fuz_app/testing/cross_backend/capabilities.js';

Optional behaviors a backend may support. Each flag's TSDoc names the tests that gate on it; add a new flag here before referencing it from a suite body, and document the gating tests inline. Wiring facts that gate nothing belong in BackendShapeNotes, not here.

ws

WebSocket transport is reachable end-to-end. Gates the cross-process WS round-trip suite; the in-process describe_ws_round_trip_tests runs against register_action_ws directly and ignores this flag.

type boolean

readonly

sse

SSE transport is reachable end-to-end. Gates the cross-process SSE suite (describe_cross_process_sse_tests — connect, audit data frame, close-on-revoke); in-process SSE uses the on_audit_event hook and ignores this flag.

type boolean

readonly

cell_crud

Cell CRUD verbs (cell_create / cell_get / cell_update / cell_delete / cell_list) are live-mounted on the backend's RPC path and its DB carries the fuz_cell migration namespace. Gates the dedicated describe_cell_crud_cross_tests suite. Like ws / sse, cells stay off the standard declared surface — only this flag opts a backend into the cell parity coverage.

type boolean

readonly

cell_relations

The relation / ACL / audit cell verbs beyond plain CRUD (cell_grant_* / cell_field_* / cell_item_* / cell_clone / cell_audit_list) are live-mounted on the backend's RPC path. Gates the dedicated describe_cell_relations_cross_tests suite — grant lifecycle, field / item bidirectional relations, clone shallow + deep, manage-tier audit gating, and the now-reachable cell_visibility_manage_only 403 (editor-grant principal). Like cell_crud, these stay off the standard declared surface; a backend mounting only plain CRUD declares cell_crud: true, cell_relations: false.

type boolean

readonly

account_lifecycle

The account-lifecycle admin verbs (account_delete soft-delete, account_undelete reactivation, account_purge keeper hard-delete) are live-mounted on the backend's RPC path. Gates the dedicated describe_account_lifecycle_cross_tests suite.

Unlike cells / fact-serving / ws / sse, these verbs are on the standard declared surface — they live in create_admin_actions, so create_spine_surface_spec carries them and the spec-derived round-trip + attack-surface suites already auto-enumerate their wire shape + auth. This flag gates the *behavioral* parity the generic round-trip can't provide: it can't drive verbs that delete their own subject (soft-delete → undelete round-trip, keeper-confirmed purge, the keeper-guard refusal), so the dedicated cross suite adds them.

type boolean

readonly

fact_serving

The cell-gated fact-serving routes (GET /api/cells/:cell_id/facts/:hash + the admin-only GET /api/facts/:hash) are live-mounted on the backend, its DB carries the fuz_facts migration namespace, and it registers the _testing_put_fact seeder. Gates describe_fact_serving_cross_tests — the per-reference read model (cell-scoped admit via a viewable cell, cross-owner-dedup-no-leak, 404-mask, bare-hash admin-only). Like cells, the serve routes stay off the standard declared surface.

type boolean

readonly

ready

The /ready readiness deploy gate (GET /ready) is live-mounted on the backend — the public column-presence schema-drift probe over the committed expected_schema.json. Gates describe_ready_cross_tests (anonymous GET /ready200 {ready: true} on a clean spine bootstrap). Like ws/sse/cells, the route stays off the standard declared surface (create_spine_surface_spec); this flag opts a backend into the dedicated readiness parity coverage. The drift → 503 path stays per-impl unit tests.

type boolean

readonly

account_status

The account surface serves GET /api/account/status (account info + bootstrap_available flag). Bundled into create_account_route_specs, so any backend mounting the account routes serves it — true for every spine. Gates the account status response body case in describe_standard_integration_tests: when true the case asserts the route is present (fail-loud on 404, no silent skip); when false it skips explicitly (a backend that deliberately omits the route).

type boolean

readonly

oversized_reject_closes_connection

On an oversized-body 413 reject the backend closes the connection without reading the body (the defense-in-depth posture), rather than draining the declared Content-Length and keeping the socket alive. Gates the strong half of describe_body_size_smuggling_cross_tests: when true, the pipelined GET is never reached (at most one response); when false, the suite instead asserts the weaker but still-load-bearing no-desync property (the body is framed on Content-Length, not reparsed as request bytes).

true for the Node / Deno (@hono/node-server graceful close) and Rust (hyper RST) backends; false for Bun — Bun.serve reads the full body and processes the correctly-framed pipelined request even when the 413 carries Connection: close. Bun is not insecure (no desync — it answers the cleanly-delimited GET with a proper 400); the flag records the connection-handling divergence so the suite stays green without losing the smuggle detector. See docs/security.md §"Body Size Limiting".

type boolean

readonly

peer_request

The backend can initiate a JSON-RPC request to a connected client and await its typed reply (the server→client request/response direction ActionPeer adds). Gates describe_peer_ping_ws_tests — the on-demand peer/ping round-trip plus its security negatives (unsolicited-response rejection, per-connection id isolation, never-replying Timeout, wrong-shape reply rejection).

true for both the Rust spine (server-initiated requests landed Rust-first canonical) and the TS spine (BackendWebsocketTransport.request_connection drives the round-trip, correlated by register_action_ws). The conservative ts_default_capabilities keeps it false like sse/ready — a TS backend opts in once it mounts peer/ping on a WS endpoint and the HTTP RPC endpoint (the no-transport refusal). Like ws/sse/cells, peer/ping stays off the standard declared surface (it's a protocol action, manifest-excluded), so this flag is the only opt-in into the peer parity coverage.

type boolean

readonly

cell_gated_create

A test CellCreateAuthorize policy is live-mounted on the backend's cell layer — creating a kind: 'gated' cell requires the participant role or admin; every other kind (and a typeless cell) is open. Gates describe_cell_gated_create_cross_tests, the TS↔Rust cell-creation- authorizer parity proof. The authorizer adds no method / column / wire shape, so schema-snapshot + action-manifest parity are blind to an authorizer divergence — this behavioral cross case is the only gate that catches one. true only on the reference spine binaries that mount the test policy (the TS spine full_spine_mount + the Rust testing_spine_stub); consumers and the in-process default app don't mount it.

type boolean

readonly

BackendConfig
#

testing/cross_backend/backend_config.ts view source

BackendConfig import type {BackendConfig} from '@fuzdev/fuz_app/testing/cross_backend/backend_config.js';

Configuration for one spawnable test backend. Consumer factories (deno_backend_config(), rust_backend_config()) produce these and the runner consumes them through spawn_backend.

Path defaults match the standard fuz_app surface — Deno + Rust spine (zzz_server, fuz_forge_server, testing_spine_stub) all converge on /api/account/{bootstrap,login,logout,password}, /api/rpc, /api/ws, /health. Override only when a backend deliberately diverges (which it shouldn't, per the contract).

name

Diagnostic label ("deno", "rust", "spine_stub"). Surfaces in test output.

type string

readonly

start_command

argv passed to the spawn. The first entry is the binary path.

type ReadonlyArray<string>

readonly

base_url

Base URL for HTTP requests, including port (e.g. http://localhost:8788).

type string

readonly

rpc_path

JSON-RPC endpoint mount point. Default /api/rpc.

type string

readonly

ws_path

WebSocket endpoint mount point. Default /api/ws.

type string

readonly

sse_path?

SSE stream mount point — drives the cross-process SSE suite's stream path. Optional: only backends advertising capabilities.sse serve a stream, and the suite defaults to /api/admin/audit/stream (the standard fuz_app audit-log stream) when omitted. Set it only when a backend mounts its stream elsewhere.

type string

readonly

health_path

Readiness probe path. Default /health.

type string

readonly

bootstrap_path

Bootstrap POST path. Default /api/account/bootstrap.

type string

readonly

cookie_name

Session cookie name the backend issues. Default fuz_session per the ecosystem convergence; consumers using a custom session name (legacy zzz_session, etc.) override. default_cross_process_setup extracts the per-account session value from the transport jar by this name so the cross-process TestAccount.session_cookie matches the in-process shape.

type string

readonly

startup_timeout_ms

How long to wait for the health probe (ms) before giving up.

type number

readonly

env

Env vars merged into the child process. Must include the binary's *_BOOTSTRAP_TOKEN_PATH + *_DAEMON_TOKEN_PATH env var names so the binary reads/writes the right files. Also must include the binary's *_ALLOWED_ORIGINS (typically 'http://localhost:*' for cross-process tests).

type Readonly<Record<string, string>>

readonly

bootstrap

Auth bootstrap details — see BackendBootstrapConfig.

type BackendBootstrapConfig

readonly

capabilities

Capabilities this backend supports — drives test_if(capabilities.X, ...) gating in suite bodies. See testing/cross_backend/capabilities.ts for the vocabulary and existing flags.

type BackendCapabilities

readonly

BackendHandle
#

testing/cross_backend/spawn_backend.ts view source

BackendHandle import type {BackendHandle} from '@fuzdev/fuz_app/testing/cross_backend/spawn_backend.js';

Handle returned by spawn_backend — passed to per-test setup helpers.

config

The config used to spawn this backend. Carried for diagnostic + downstream access.

type BackendConfig

readonly

child

Child process reference — exposed for diagnostic logging only.

type ChildProcess

readonly

daemon_token

Deterministic daemon token captured from config.bootstrap.daemon_token_path after the binary booted. default_cross_process_setup builds keeper-daemon-token headers from this for _testing_reset calls.

type string

readonly

teardown

SIGTERM the child's process group, drain stderr, await exit. Idempotent — calls after the first are no-ops.

type () => Promise<void>

readonly

BackendShapeNotes
#

testing/cross_backend/capabilities.ts view source

BackendShapeNotes import type {BackendShapeNotes} from '@fuzdev/fuz_app/testing/cross_backend/capabilities.js';

Backend wiring facts recorded for documentation — not gating flags.

The companion to BackendCapabilities: where each capability flag has a test_if(capabilities.X, ...) reader that skips a suite the backend doesn't implement, nothing reads these. They record middleware / limiter wiring that differs between the TS and Rust families (a backend-shape record) but gates no cross test today. They live in their own type precisely so BackendCapabilities stops claiming gating power it doesn't have — fold a flag in here the moment it has no test_if reader, and promote it back the day a suite genuinely gates on it.

bearer_auth

Bearer-token auth (Authorization: Bearer <token>) is wired through the backend's middleware stack. true on every spine — the bearer-token cases in describe_standard_integration_tests / describe_rate_limiting_tests run unconditionally.

type boolean

readonly

trusted_proxy

Trusted-proxy XFF parsing (X-Forwarded-For etc.) is wired. Records the proxy-default difference between the TS family (false — the test binary leaves proxy parsing off) and the Rust family (true — the client-IP middleware is always wired; the env-gate only chooses XFF vs the TCP peer).

type boolean

readonly

login_rate_limit

Per-account login rate limiting is wired. false for the TS family (the canonical path leaves the limiter null in test mode), true for the Rust family (env-gated bucket on /login + /password).

type boolean

readonly

BackendWebsocketTransport
#

actions/transports_ws_backend.ts view source

import {BackendWebsocketTransport} from '@fuzdev/fuz_app/actions/transports_ws_backend.js';

inheritance

transport_name

type "backend_websocket_rpc"

readonly

add_connection

Add a new WebSocket connection with auth info. Session connections pass a token hash for targeted revocation. Bearer token connections (api_token) pass the api_token.id so the socket can be closed when that specific token is revoked without tearing down the account's other sockets. Daemon-token connections pass null for both — they're only reachable via close_sockets_for_account.

type (ws: WSContext<unknown>, token_hash: string | null, account_id: string & $brand<"Uuid">, api_token_id?: string | null): string & $brand<"Uuid">

ws

type WSContext<unknown>

token_hash

type string | null

account_id

type string & $brand<"Uuid">

api_token_id

type string | null
default null
returns string & $brand<"Uuid">

the freshly assigned connection_id (branded Uuid)

remove_connection

Remove a WebSocket connection and its auth tracking data. Idempotent — safe to call after revocation has already cleaned up.

type (ws: WSContext<unknown>): void

ws

type WSContext<unknown>
returns void

close_sockets_for_session

Close all sockets associated with a specific session token hash.

type (token_hash: string): number

token_hash

type string
returns number

the number of sockets closed

close_sockets_for_account

Close all sockets associated with a specific account.

type (account_id: string & $brand<"Uuid">): number

account_id

type string & $brand<"Uuid">
returns number

the number of sockets closed

close_sockets_for_token

Close all sockets associated with a specific API token.

Used on token_revoke audit events so revoking one token doesn't tear down the account's session-authenticated sockets or other tokens' sockets.

type (api_token_id: string): number

api_token_id

type string
returns number

the number of sockets closed

send

type (message: { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; }, options?: TransportSendOptions | undefined): Promise<...>

message

type { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; }

options?

type TransportSendOptions | undefined
optional
returns Promise<{ [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<...>); message: string; data?: unknown; }; }>

broadcast_filtered

Broadcast to connections whose identity satisfies a predicate.

Used by the broadcast API when a consumer supplies a subscription ACL hook (e.g. zap's zap_run_created only reaches the account that owns the run). When no ACL is needed, callers should prefer send(message) / #broadcast to skip the per-connection predicate overhead.

type (message: { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<...>); message: string; data?: unknown; }; } | { ...; }, predicate: (identity: ConnectionIdentity) => boolean): number

message

type { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<...>); message: string; data?: unknown; }; } | { ...; }

predicate

type (identity: ConnectionIdentity) => boolean
returns number

the number of sockets the message was sent to

send_to_account

Send a message to every socket bound to a specific account.

Targeted per-account fan-out for any flow where the delivery target is a single known account. Prefer this over broadcast_filtered when the filter is exactly "this account_id"; reach for broadcast_filtered when the ACL is an arbitrary predicate over ConnectionIdentity.

Mirrors close_sockets_for_account on the send side: every connection for the account (session, bearer, and daemon-token) receives the message.

type (account_id: string & $brand<"Uuid">, message: { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { ...; }; } | { ...; }): number

account_id

type string & $brand<"Uuid">

message

type { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<...>); message: string; data?: unknown; }; } | { ...; }
returns number

the number of sockets the message was sent to

request_connection

Initiate a JSON-RPC request to a single connected client and await its reply — the server→client request/response direction (ActionPeer).

Sends {jsonrpc, method, params, id} to exactly the connection_id socket (never a broadcast) and registers a pending entry scoped to that connection. Resolves when the client's matching reply arrives (routed in via resolve_peer_response), the deadline elapses (timeout), the per-connection cap is hit (too_many_in_flight), or the socket closes (connection_gone). Never throws — every failure is a PeerRequestError.

Delegates correlation to #pending (id allocation, deadline, cap, drain); this method owns only the socket lookup + the send. Server-issued ids are s-prefixed so a malicious client echoing a non-s id (or an id it chose for its own request) matches nothing.

type (connection_id: string & $brand<"Uuid">, method: string, params: { [x: string]: unknown; } | undefined, options?: PeerRequestOptions | undefined): Promise<...>

connection_id

type string & $brand<"Uuid">

method

type string

params

type { [x: string]: unknown; } | undefined

options?

type PeerRequestOptions | undefined
optional
returns Promise<PeerRequestOutcome>

the client's success result, or a PeerRequestError

resolve_peer_response

Route an inbound client reply to the matching pending server→client request on connection_id (delegates to #pending.resolve).

Returns false when no entry matches — an unsolicited, cross-connection, or already-settled reply — so the caller drops it. Per-connection scoping means a reply arriving on the wrong socket resolves nothing.

type (connection_id: string & $brand<"Uuid">, response: { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { ...; }; }): boolean

connection_id

type string & $brand<"Uuid">

response

type { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<...>); message: string; data?: unknown; }; }
returns boolean

whether a pending request was resolved

is_ready

type (): boolean

returns boolean

get_connection_count

Number of currently tracked WebSocket connections.

Read-only counter intended for telemetry, logging, and tests. Counts every entry in the connection map — including connections that have been closed by the peer but not yet removed by the WS adapter's onClose callback.

type (): number

returns number

baseline
#

db/migrate.ts view source

(db: Db, ns: MigrationNamespace, names: readonly string[]): Promise<void> import {baseline} from '@fuzdev/fuz_app/db/migrate.js';

Insert tracker rows for the named migrations of a namespace without executing them.

Used to promote an existing schema (e.g. produced by a pre-0.42 build, preserved through a tracker-shape upgrade) into the new identity tracker. baseline() trusts the operator-supplied list — it does not verify that the schema actually matches what the named migrations would have produced. Pair with a schema-assertion script post-baseline before re-enabling traffic.

Contract:

  • Probes for the pre-0.42 tracker shape; throws old-tracker-shape if found (DDL with IF NOT EXISTS would otherwise no-op against the old table and the INSERT would fail with a confusing column-not-found).
  • Creates the new-shape schema_version table if missing — cutover scripts that just dropped the old-shape table can call baseline() directly with no separate DDL step.
  • Acquires the same per-namespace advisory lock as run_migrations (with the same try/catch fallback for environments lacking pg_advisory_lock).
  • Refuses if any tracker rows already exist *for this namespace* — lets multi-call baseline scripts resume after partial failure (completed namespaces guard themselves while remaining ones still run).
  • Verifies the supplied names are a strict prefix of the namespace's current migrations array — a name not in the array, or out of order, errors before any INSERT.
  • Writes sequences 0..N-1 in one transaction.

db

the database instance

type Db

ns

the namespace whose migrations are being baselined

names

prefix of ns.migrations[].name to record as already-applied

type readonly string[]

returns

Promise<void>

throws

  • MigrationError - with `kind` of `old-tracker-shape`,

mutates

  • schema_version — inserts tracker rows for `names` without running

BaseServerEnv
#

server/env.ts view source

ZodObject<{ NODE_ENV: ZodEnum<{ development: "development"; production: "production"; }>; PORT: ZodDefault<ZodCoercedNumber<unknown>>; HOST: ZodDefault<ZodString>; ... 11 more ...; FUZ_FACTS_X_ACCEL_REDIRECT_PREFIX: ZodOptional<...>; }, $strict> import type {BaseServerEnv} from '@fuzdev/fuz_app/server/env.js';

Base Zod schema for server environment variables.

Provides the common fields used by fuz apps: server config, database, auth, security, public URLs, and SMTP.

Apps can use directly or extend with app-specific fields via .extend().

BearerAuthMocks
#

testing/middleware.ts view source

BearerAuthMocks import type {BearerAuthMocks} from '@fuzdev/fuz_app/testing/middleware.js';

Mocks bundle returned by create_bearer_auth_mocks.

mock_validate

type ReturnType<typeof vi.fn>

mock_find_by_id

type ReturnType<typeof vi.fn>

mock_find_actor_by_id

type ReturnType<typeof vi.fn>

mock_find_actors_by_account

type ReturnType<typeof vi.fn>

mock_find_active_for_actor

type ReturnType<typeof vi.fn>

BearerAuthTestCase
#

testing/middleware.ts view source

BearerAuthTestCase import type {BearerAuthTestCase} from '@fuzdev/fuz_app/testing/middleware.js';

A full test case for the table-driven bearer auth runner.

inheritance

validate_expectation

Whether the request should reach token validation or be short-circuited.

type 'called' | 'not_called'

assert_account_set?

If true, assert ACCOUNT_ID_KEY was set and CREDENTIAL_TYPE_KEY is 'api_token'.

type boolean

expected_account_id?

Expected ACCOUNT_ID_KEY value when assert_account_set is true.

type string

expected_api_token_id?

If set, assert AUTH_API_TOKEN_ID_KEY was set to this value after a successful bearer auth.

type string

assert_context_preserved?

If true, assert the pre-existing session ACCOUNT_ID_KEY and credential type are preserved.

type boolean

assert_mocks?

Optional callback for custom spy assertions on the mocks bundle.

type (mocks: BearerAuthMocks) => void

BearerAuthTestOptions
#

testing/middleware.ts view source

BearerAuthTestOptions import type {BearerAuthTestOptions} from '@fuzdev/fuz_app/testing/middleware.js';

Mock configuration for bearer auth middleware test setup.

name

Test description.

type string

headers?

Request headers.

type Record<string, string>

pre_context?

Pre-set request context (simulates session already resolved).

type RequestContext

mock_validate_result?

What query_validate_api_token() returns.

type unknown

mock_find_by_id_result?

What query_account_by_id() returns.

type unknown

mock_find_actor_by_id_result?

What query_actor_by_id() returns.

type unknown

mock_role_grants_result?

What query_role_grant_find_active_for_actor() returns.

type unknown

expected_status

Expected HTTP status, or 'next' if the middleware should call next().

type number | 'next'

expected_error?

Expected error field in JSON response body.

type string

expected_error_schema?

Zod schema to validate error response body against. Defaults to ApiError when expected_error is set.

type z.ZodType

BenchScenario
#

testing/cross_backend/bench/scenario.ts view source

BenchScenario import type {BenchScenario} from '@fuzdev/fuz_app/testing/cross_backend/bench/scenario.js';

One benchmarkable wire scenario. The run body is the Benchmark task fn: it must throw on a non-success response so the benchmark records a failed iteration rather than timing an error path as if it succeeded.

Scenarios should be idempotent — they run thousands of times against a single bootstrapped backend with no reset between iterations. Prefer reads; a mutating scenario must not accumulate unbounded state.

name

Scenario name (groups the per-backend results in the report).

type string

readonly

requires?

Optional capability gate — return false to skip this scenario on a backend that can't serve it (e.g. a WS scenario needs capabilities.ws).

type (capabilities: BackendCapabilities) => boolean

readonly

run

The timed body. Throws on a non-success response.

type (ctx: BenchScenarioContext) => Promise<void>

readonly

BenchScenarioContext
#

testing/cross_backend/bench/scenario.ts view source

BenchScenarioContext import type {BenchScenarioContext} from '@fuzdev/fuz_app/testing/cross_backend/bench/scenario.js';

transport

Pre-authed transport — the bootstrapped keeper's session cookie jar.

type FetchTransport

readonly

rpc_path

RPC endpoint path, e.g. '/api/rpc'.

type string

readonly

capabilities

Declared capabilities of the backend this context targets.

type BackendCapabilities

readonly

BodySizeCrossTestOptions
#

testing/cross_backend/body_size.ts view source

RpcPathCrossSuiteOptions import type {BodySizeCrossTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/body_size.js';

Options for the body-size parity suite — the standard RPC-dispatched cross-suite shape (setup_test / capabilities / rpc_path); aliases the shared RpcPathCrossSuiteOptions rather than minting a duplicate.

rpc_path?

RPC endpoint path the methods are mounted on. Default /api/rpc.

type string

readonly

setup_test

Per-test fixture-producing function (fresh keeper + db per call).

type (): Promise<TestFixtureBase>

readonly
returns Promise<TestFixtureBase>

capabilities

Backend capability declarations — each suite gates on its own flag.

type BackendCapabilities

readonly

BodySizeSmugglingCrossTestOptions
#

testing/cross_backend/body_size_smuggling.ts view source

BodySizeSmugglingCrossTestOptions import type {BodySizeSmugglingCrossTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/body_size_smuggling.js';

Options for the smuggling probe — needs the raw URL, not a transport.

base_url

Base URL the spawned backend is reachable at (e.g. http://localhost:1178).

type string

readonly

rpc_path?

RPC endpoint path to target. Default /api/rpc.

type string

readonly

closes_connection?

Whether the backend closes the connection on an oversized-body reject without reading the body (capabilities.oversized_reject_closes_connection). true (default) demands the strong posture — the pipelined GET is never reached, so at most one response comes back. false (Bun) relaxes to the no-desync property: the body is drained on Content-Length and the pipelined GET is framed correctly, so at most two responses come back and the body bytes are never reparsed as a request. Default true so a consumer that forgets to declare the flag fails loud rather than silently accepting a drain.

type boolean

readonly

bootstrap
#

testing/transports/bootstrap.ts view source

(options: BootstrapOptions): Promise<BootstrapResult> import {bootstrap} from '@fuzdev/fuz_app/testing/transports/bootstrap.js';

Fire POST {config.bootstrap_path} and capture the keeper session.

options

returns

Promise<BootstrapResult>

throws

  • Error - when the binary refuses bootstrap (non-2xx response) or

bootstrap_account
#

auth/bootstrap_account.ts view source

(deps: BootstrapAccountDeps, provided_token: string, input: BootstrapAccountInput): Promise<BootstrapAccountResult> import {bootstrap_account} from '@fuzdev/fuz_app/auth/bootstrap_account.js';

Bootstrap the first account with keeper and admin privileges.

Uses an atomic bootstrap_lock UPDATE to prevent concurrent bootstrap attempts (TOCTOU). The full flow runs in a single transaction:

  1. Read and verify the bootstrap token (before transaction)
  2. Hash the password (CPU-intensive, before transaction)
  3. Acquire the bootstrap lock atomically (inside transaction)
  4. Create account + actor
  5. Grant keeper and admin role_grants (no expiry, granted_by = null)
  6. Delete the token file (after commit, reported via token_file_deleted)

deps

database, token path, filesystem callbacks, and password hashing

provided_token

the bootstrap token from the user

type string

input

username and password

returns

Promise<BootstrapAccountResult>

the created account, actor, and role_grants — or a bootstrap failure

mutates

  • filesystem — deletes the bootstrap token file after commit (reported via `token_file_deleted`)

bootstrap_backend
#

testing/cross_backend/bootstrap_backend.ts view source

(config: BackendConfig): Promise<BootstrappedBackendHandle> import {bootstrap_backend} from '@fuzdev/fuz_app/testing/cross_backend/bootstrap_backend.js';

Spawn the test binary described by config, bootstrap a keeper, and return the enriched handle.

The keeper transport is constructed against config.base_url with no initial cookies; bootstrap() populates its jar with the session cookie returned by POST {config.bootstrap_path}. Subsequent calls against bootstrapped.keeper_transport are authenticated as keeper.

Mirrors the composition default_cross_process_setup's caller would otherwise hand-roll in every consumer's globalSetup.

config

returns

Promise<BootstrappedBackendHandle>

BOOTSTRAP_LOCK_SCHEMA
#

auth/auth_ddl.ts view source

"\nCREATE TABLE IF NOT EXISTS bootstrap_lock (\n id INTEGER PRIMARY KEY DEFAULT 1 CHECK (id = 1),\n bootstrapped BOOLEAN NOT NULL DEFAULT false\n)" import {BOOTSTRAP_LOCK_SCHEMA} from '@fuzdev/fuz_app/auth/auth_ddl.js';

BOOTSTRAP_LOCK_SEED
#

auth/auth_ddl.ts view source

"\nINSERT INTO bootstrap_lock (id, bootstrapped)\n SELECT 1, EXISTS(SELECT 1 FROM account)\n ON CONFLICT DO NOTHING" import {BOOTSTRAP_LOCK_SEED} from '@fuzdev/fuz_app/auth/auth_ddl.js';

Seed the bootstrap_lock table, setting bootstrapped based on whether accounts exist.

bootstrap_route_shape
#

auth/bootstrap_route_schema.ts view source

{ method: "POST"; path: string; auth: { account: "none"; actor: "none"; }; description: string; transaction: false; input: ZodObject<{ token: ZodString; username: ZodPipe<ZodString, ZodTransform<string, string>>; password: ZodString; }, $strict>; output: ZodObject<...>; rate_limit: "ip"; errors: { ...; }; } import {bootstrap_route_shape} from '@fuzdev/fuz_app/auth/bootstrap_route_schema.js';

The POST /bootstrap route shape minus its handler — pure hono-free data. create_bootstrap_route_specs spreads this and attaches the live handler; surface generation spreads it with a stub handler (handlers are never run during surface assembly, only the shape is read).

bootstrap_test_keeper
#

testing/app_server.ts view source

(options: CreateTestAccountWithCredentialsOptions): Promise<{ account: { id: string & $brand<"Uuid">; username: string; }; actor: { ...; }; api_token: string; session_cookie: string; }> import {bootstrap_test_keeper} from '@fuzdev/fuz_app/testing/app_server.js';

Bootstrap the test-DB keeper. Direct-query shortcut for the default create_test_app path — bootstrap is not what most tests exercise, so we skip the real bootstrap_account flow (no audit row, no on_bootstrap callback). Tests that need the full success-path flow use create_test_app_for_bootstrap instead.

Flips bootstrap_lock.bootstrapped = true so the post-insert DB state matches a real bootstrap completion — production code can trust the lock as the single signal without a belt-and-suspenders query_account_has_any defense.

options

returns

Promise<{ account: { id: string & $brand<"Uuid">; username: string; }; actor: { id: string & $brand<"Uuid">; }; api_token: string; session_cookie: string; }>

mutates

  • the — underlying `options.db` — inserts the account/actor/roles/

BootstrapAccountDeps
#

auth/bootstrap_account.ts view source

BootstrapAccountDeps import type {BootstrapAccountDeps} from '@fuzdev/fuz_app/auth/bootstrap_account.js';

Dependencies for bootstrap_account.

db

type Db

token_path

Path to the bootstrap token file on disk.

type string

read_text_file

Read a file's contents as a string.

type (path: string) => Promise<string>

delete_file

Delete a file.

type (path: string) => Promise<void>

password

Only hashing is needed — verification happens separately during login.

type Pick<PasswordHashDeps, 'hash_password'>

log

Structured logger instance.

type Logger

BootstrapAccountFailure
#

BootstrapAccountInput
#

auth/bootstrap_account.ts view source

BootstrapAccountInput import type {BootstrapAccountInput} from '@fuzdev/fuz_app/auth/bootstrap_account.js';

Input for the bootstrap account creation.

username

type string

password

type string

BootstrapAccountResult
#

auth/bootstrap_account.ts view source

BootstrapAccountResult import type {BootstrapAccountResult} from '@fuzdev/fuz_app/auth/bootstrap_account.js';

Bootstrap account result — either success or a bootstrap verification failure.

BootstrapAccountSuccess
#

auth/bootstrap_account.ts view source

BootstrapAccountSuccess import type {BootstrapAccountSuccess} from '@fuzdev/fuz_app/auth/bootstrap_account.js';

Successful bootstrap result with the created entities.

ok

type true

account

type Account

actor

type Actor

role_grants

type { keeper: RoleGrant; admin: RoleGrant }

token_file_deleted

Whether the bootstrap token file was successfully deleted after account creation.

type boolean

BootstrapDisabledOptions
#

server/app_server.ts view source

BootstrapDisabledOptions import type {BootstrapDisabledOptions} from '@fuzdev/fuz_app/server/app_server.js';

mode

type 'disabled'

BootstrapForm
#

ui/BootstrapForm.svelte view source

import BootstrapForm from '@fuzdev/fuz_app/ui/BootstrapForm.svelte';

redirect_on_bootstrap?

Path to navigate to after the first-keeper account is created.

type string
optional default resolve('/')

BootstrapInput
#

auth/bootstrap_route_schema.ts view source

ZodObject<{ token: ZodString; username: ZodPipe<ZodString, ZodTransform<string, string>>; password: ZodString; }, $strict> import type {BootstrapInput} from '@fuzdev/fuz_app/auth/bootstrap_route_schema.js';

Input for POST /bootstrap. token is the one-shot token file contents.

BootstrapLiveOptions
#

server/app_server.ts view source

BootstrapLiveOptions import type {BootstrapLiveOptions} from '@fuzdev/fuz_app/server/app_server.js';

mode

type 'live'

token_path

type string

route_prefix?

Route prefix for bootstrap routes. Default '/api/account'.

type string

on_bootstrap?

Called after successful bootstrap (account + session created). Use for app-specific post-bootstrap work like generating API tokens.

type (result: BootstrapAccountSuccess, c: Context) => Promise<void>

BootstrapOptions
#

testing/transports/bootstrap.ts view source

BootstrapOptions import type {BootstrapOptions} from '@fuzdev/fuz_app/testing/transports/bootstrap.js';

Input for bootstrap().

transport

The cookie-threading HTTP transport pointed at the binary. After bootstrap() resolves, the transport carries the keeper session cookie — every later call against it is authenticated as keeper.

type FetchTransport

readonly

config

Backend config — used for bootstrap_path plus the bootstrap.username / bootstrap.password / bootstrap.token credentials. The runner already wrote bootstrap.token to bootstrap.token_path before spawning, so the binary picks the token up at startup.

type BackendConfig

readonly

BootstrapOutput
#

auth/bootstrap_route_schema.ts view source

ZodObject<{ ok: ZodLiteral<true>; account: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; username: ZodPipe<ZodString, ZodTransform<string, string>>; }, $strict>; actor: ZodObject<...>; }, $strict> import type {BootstrapOutput} from '@fuzdev/fuz_app/auth/bootstrap_route_schema.js';

Output for POST /bootstrap. Session cookie is the operative side effect.

BootstrappedBackendHandle
#

testing/cross_backend/setup.ts view source

BootstrappedBackendHandle import type {BootstrappedBackendHandle} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

Cross-process backend handle enriched with the bootstrapped keeper's captured credentials. Consumers compose this in vitest's globalSetup:

const handle = await spawn_backend(config); const keeper_transport = create_fetch_transport({base_url: config.base_url}); const keeper = await bootstrap({transport: keeper_transport, config}); const bootstrapped: BootstrappedBackendHandle = { ...handle, keeper_transport, keeper_daemon_transport: create_fetch_transport({ base_url: config.base_url, initial_cookies: keeper.cookies, origin: null, }), keeper_account: keeper.account, keeper_actor: keeper.actor, keeper_cookies: keeper.cookies, };

default_cross_process_setup(bootstrapped, options) reads from this shape — the per-test fixture closes over the keeper credentials so cross-process tests can drive admin-RPC / audit-observer flows against the long-lived bootstrapped admin alongside the per-test signup+login account.

inheritance

extends: BackendHandle

keeper_transport

Transport carrying the keeper session cookie + cookie jar.

type FetchTransport

readonly

keeper_daemon_transport

Daemon-token transport for the keeper — same cookie jar as keeper_transport but with origin: null so it sends no Origin header. The daemon-token middleware discards the credential in a browser context (any Origin / Referer), so the _testing_* daemon-token calls (_testing_reset / _testing_schema_snapshot / _testing_action_manifest / _testing_mint_session) must route through this Origin-free transport. CSRF/cookie-session calls (invite_create, role_grant_offer_create) stay on keeper_transport, which keeps its default Origin.

type FetchTransport

readonly

keeper_account

Keeper account JSON captured from POST /bootstrap.

type { readonly id: Uuid; readonly username: string }

readonly

keeper_actor

Keeper actor JSON captured from POST /bootstrap.

type { readonly id: Uuid }

readonly

keeper_cookies

Raw keeper Set-Cookie values — thread into ws_transport for keeper-authenticated WS upgrades.

type ReadonlyArray<string>

readonly

BootstrapResult
#

testing/transports/bootstrap.ts view source

BootstrapResult import type {BootstrapResult} from '@fuzdev/fuz_app/testing/transports/bootstrap.js';

The keeper credentials captured from POST /api/account/bootstrap.

transport

Same transport that came in, now carrying the keeper session cookie in its jar. Returned for call-site clarity (callers don't have to remember the mutation happens in place).

type FetchTransport

readonly

account

Account JSON returned by POST /bootstrap.

type { readonly id: Uuid; readonly username: string }

readonly

actor

Actor JSON returned by POST /bootstrap.

type { readonly id: Uuid }

readonly

cookies

Raw Set-Cookie values for threading into a WS transport.

type ReadonlyArray<string>

readonly

BootstrapRouteOptions
#

auth/bootstrap_routes.ts view source

BootstrapRouteOptions import type {BootstrapRouteOptions} from '@fuzdev/fuz_app/auth/bootstrap_routes.js';

Per-factory configuration for bootstrap route specs.

bootstrap_status is runtime state (a mutable ref), not a dep or options value — it is passed through so the route handler can flip it on success.

session_options

type SessionOptions<string>

bootstrap_status

Shared mutable reference — flipped to false after successful bootstrap.

type BootstrapStatus

on_bootstrap?

Called after successful bootstrap (account + session created). Use for app-specific post-bootstrap work like generating API tokens.

type (result: BootstrapAccountSuccess, c: Context) => Promise<void>

ip_rate_limiter

Rate limiter for bootstrap attempts (per-IP). Pass null to disable.

type RateLimiter | null

BootstrapServerOptions
#

server/app_server.ts view source

BootstrapServerOptions import type {BootstrapServerOptions} from '@fuzdev/fuz_app/server/app_server.js';

Bootstrap configuration for AppServerOptions.bootstrap.

Discriminated union over three deployment intents. Distinct from BootstrapRouteOptions in auth/bootstrap_routes.ts — that one is per-factory runtime state (mutable bootstrap_status ref, rate limiter); this one is the consumer-facing server option that create_app_server reads at startup to decide whether to mount the routes and where.

Three modes:

  • disabled — no route mounted, nothing in /api/surface. Equivalent to omitting bootstrap entirely; the explicit mode is for documentation and reviewable intent at the wiring layer.
  • surface_only — route present, permanent 403 via check_bootstrap_status. For tests asserting on the disabled-but-present wire shape.
  • live — route mounted, real token verification. Success path reachable. token_path is required (non-nullable).

BootstrapStatus
#

auth/bootstrap_routes.ts view source

BootstrapStatus import type {BootstrapStatus} from '@fuzdev/fuz_app/auth/bootstrap_routes.js';

Bootstrap status — runtime state computed once at startup.

available

type boolean

token_path

type string | null

BootstrapSuccessTestOptions
#

testing/bootstrap_success.ts view source

BootstrapSuccessTestOptions import type {BootstrapSuccessTestOptions} from '@fuzdev/fuz_app/testing/bootstrap_success.js';

session_options

type SessionOptions<string>

create_route_specs

Same factory the consumer's production server uses.

type (ctx: AppServerContext) => Array<RouteSpec>

rpc_endpoints?

RPC endpoints — passed through to create_app_server for shape parity.

type RpcEndpointsSuiteOption

bootstrap

Live bootstrap config — the suite drives POST /bootstrap against bootstrap.token_path. The suite does NOT assert on on_bootstrap callback invocation (Hono-coupled signature is in-process only); assertions land on observable DB state.

type BootstrapLiveOptions

bootstrap_token?

Override the synthetic token text. Default deterministic.

type string

BootstrapSurfaceOnlyOptions
#

server/app_server.ts view source

BootstrapSurfaceOnlyOptions import type {BootstrapSurfaceOnlyOptions} from '@fuzdev/fuz_app/server/app_server.js';

mode

type 'surface_only'

route_prefix?

Route prefix for surface generation. Default '/api/account'.

type string

BootstrapTestKeeperOptions
#

testing/app_server.ts view source

CreateTestAccountWithCredentialsOptions import type {BootstrapTestKeeperOptions} from '@fuzdev/fuz_app/testing/app_server.js';

Alias for the keeper-flavored call site. Same shape.

db

type Db

keyring

type Keyring

session_options

type SessionOptions<string>

password

type PasswordHashDeps

username?

type string

password_value?

type string

roles?

type string[]

email?

Optional email stored on the account row — exercises the username-or-email login lookup.

type string

BroadcastApi
#

actions/broadcast_api.ts view source

BroadcastApi import type {BroadcastApi} from '@fuzdev/fuz_app/actions/broadcast_api.js';

Loose base shape for a broadcast API. Consumers typically declare a stricter per-method interface (e.g. BackendActionsApi) and pin it via the type parameter on create_broadcast_api.

[key: string]

type (input: never) => Promise<void>

build_account_context
#

auth/request_context.ts view source

(deps: QueryDeps, account_id: string): Promise<RequestContext | null> import {build_account_context} from '@fuzdev/fuz_app/auth/request_context.js';

Build an account-only RequestContext (no actor, no role_grants) from an account id.

Used by the dispatcher's authorization phase for authenticated routes that don't need an acting actor — account-grain operations (logout, password change, account self-service). Lets handlers read auth.account.id / auth.account.username uniformly with role_grant-bound routes; the cost is one extra query_account_by_id per request.

Returns null when the account row is missing (e.g. deleted between the auth middleware's session lookup and the dispatcher) — caller surfaces that as a 500 since it represents a torn read.

deps

query dependencies

account_id

the account to build context for

type string

returns

Promise<RequestContext | null>

an account-only request context, or null if the account is missing

build_action_manifest
#

testing/cross_backend/action_manifest.ts view source

(specs: readonly Pick<{ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; }, "method" | ... 1 more ... | "side_effects">[]): { ...; } import {build_action_manifest} from '@fuzdev/fuz_app/testing/cross_backend/action_manifest.js';

Build the normalized ActionManifest from a list of request-response specs. Entries are sorted by method so two impls producing the same set serialize identically regardless of mount order. The caller owns the scope it passes (the TS spine passes its full mount; the Rust stub filters PROTOCOL_ACTION_SPECS first) — see the module doc.

specs

type readonly Pick<{ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; }, "metho...

returns

{ methods: { method: string; side_effects: boolean; account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles: string[]; credential_types: string[]; }[]; }

build_broadcast_api
#

testing/ws_round_trip.ts view source

<TApi extends object>(options: { harness: WsTestHarness; specs: readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 7 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; })[]; }): TApi import {build_broadcast_api} from '@fuzdev/fuz_app/testing/ws_round_trip.js';

Wire a typed broadcast API against the harness's transport, matching how a consumer's real backend composes the stack. Returns the typed API so tests can call .zap_run_created(...) / .workspace_changed(...) etc. directly.

const harness = create_ws_test_harness({actions}); const broadcast = build_broadcast_api<MyBackendActionsApi>({ harness, specs: my_broadcast_action_specs, }); const client = await harness.connect(keeper_identity()); await broadcast.zap_run_created({run_id: '...', ...}); await client.wait_for(is_notification('zap_run_created'));

options

type { harness: WsTestHarness; specs: readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 7 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; })[]; }

returns

TApi

generics

build_broadcast_api<TApi extends object>
TApi
constraint object

build_extra_account_fixture
#

testing/cross_backend/setup.ts view source

(seeded: { account: { id: string & $brand<"Uuid">; username: string; }; actor: { id: string & $brand<"Uuid">; }; api_token: string; session_cookie: string; }, cookie_name: string): ExtraAccountFixture import {build_extra_account_fixture} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

Build an ExtraAccountFixture from a seeded `{account, actor, api_token, session_cookie}` bundle and the session cookie name.

Same shape produced by either path that seeds bootstrap-time secondaries: in-process via create_test_account_with_credentials against the live backend's DB, or cross-process via the _testing_reset RPC's extra_accounts output. Both call this helper so the fixture-side header builders + field plumbing stays in one place.

seeded

type { account: { id: string & $brand<"Uuid">; username: string; }; actor: { id: string & $brand<"Uuid">; }; api_token: string; session_cookie: string; }

cookie_name

type string

returns

ExtraAccountFixture

build_full_spine_rpc_actions
#

testing/cross_backend/full_spine_mount.ts view source

(deps: AppDeps, options: FullSpineMountOptions): RpcAction[] import {build_full_spine_rpc_actions} from '@fuzdev/fuz_app/testing/cross_backend/full_spine_mount.js';

Build the complete live RPC action list the spine test binary mounts on its single endpoint: the declared create_standard_rpc_actions bundle plus the off-surface families (_testing_* backdoors, cells, actor resolvers).

Mirrors the previous inline assembly in testing_spine_server.ts exactly — session_options is pinned to spine_session_options (the binary's cookie config) and roles to spine_roles (carrying cell_editor), so the only runtime-varying inputs are the daemon-token state + notification sender.

deps

the backend AppDeps (stub deps suffice for method enumeration)

type AppDeps

options

daemon-token state + optional WS notification sender

returns

RpcAction[]

every RpcAction the binary exposes, in mount order

build_request_context
#

auth/request_context.ts view source

(deps: QueryDeps, account_id: string, actor_id: string): Promise<RequestActorContext | null> import {build_request_context} from '@fuzdev/fuz_app/auth/request_context.js';

Build a full RequestContext from an account id and an explicit actor id (already resolved via resolve_acting_actor).

Loads account + the named actor + the actor's active role_grants. Verifies the actor.account_id === account.id binding so downstream handlers can trust ctx.actor.account_id === ctx.account.id. Returns null when the account is missing, the actor is missing, or the actor doesn't belong to the supplied account.

Called by the route-spec / RPC dispatcher's authorization phase for routes that need an acting actor; account-grain routes use build_account_context instead.

deps

query dependencies

account_id

the account to build context for

type string

actor_id

the actor this request acts as

type string

returns

Promise<RequestActorContext | null>

a request context, or null if account/actor not found or mismatched

build_role_grant_offer_accepted_notification
#

auth/role_grant_offer_notifications.ts view source

(params: { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; to_actor_id: (string & $brand<"Uuid">) | null; ... 11 more ...; resulting_role_grant_id: (string & $brand<...>) | null; }; }): { ...; } import {build_role_grant_offer_accepted_notification} from '@fuzdev/fuz_app/auth/role_grant_offer_notifications.js';

params

type { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; to_actor_id: (string & $brand<"Uuid">) | null; ... 11 more ...; resulting_role_grant_id: (string & $brand<...>) | null; }; }

returns

{ [x: string]: unknown; jsonrpc: "2.0"; method: string; params?: { [x: string]: unknown; } | undefined; }

build_role_grant_offer_declined_notification
#

auth/role_grant_offer_notifications.ts view source

(params: { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; to_actor_id: (string & $brand<"Uuid">) | null; ... 11 more ...; resulting_role_grant_id: (string & $brand<...>) | null; }; }): { ...; } import {build_role_grant_offer_declined_notification} from '@fuzdev/fuz_app/auth/role_grant_offer_notifications.js';

params

type { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; to_actor_id: (string & $brand<"Uuid">) | null; ... 11 more ...; resulting_role_grant_id: (string & $brand<...>) | null; }; }

returns

{ [x: string]: unknown; jsonrpc: "2.0"; method: string; params?: { [x: string]: unknown; } | undefined; }

build_role_grant_offer_received_notification
#

auth/role_grant_offer_notifications.ts view source

(params: { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; to_actor_id: (string & $brand<"Uuid">) | null; ... 11 more ...; resulting_role_grant_id: (string & $brand<...>) | null; }; }): { ...; } import {build_role_grant_offer_received_notification} from '@fuzdev/fuz_app/auth/role_grant_offer_notifications.js';

params

type { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; to_actor_id: (string & $brand<"Uuid">) | null; ... 11 more ...; resulting_role_grant_id: (string & $brand<...>) | null; }; }

returns

{ [x: string]: unknown; jsonrpc: "2.0"; method: string; params?: { [x: string]: unknown; } | undefined; }

build_role_grant_offer_retracted_notification
#

auth/role_grant_offer_notifications.ts view source

(params: { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; to_actor_id: (string & $brand<"Uuid">) | null; ... 11 more ...; resulting_role_grant_id: (string & $brand<...>) | null; }; }): { ...; } import {build_role_grant_offer_retracted_notification} from '@fuzdev/fuz_app/auth/role_grant_offer_notifications.js';

params

type { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; to_actor_id: (string & $brand<"Uuid">) | null; ... 11 more ...; resulting_role_grant_id: (string & $brand<...>) | null; }; }

returns

{ [x: string]: unknown; jsonrpc: "2.0"; method: string; params?: { [x: string]: unknown; } | undefined; }

build_role_grant_offer_supersede_notification
#

auth/role_grant_offer_notifications.ts view source

(params: { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; to_actor_id: (string & $brand<"Uuid">) | null; ... 11 more ...; resulting_role_grant_id: (string & $brand<...>) | null; }; reason: "sibling_accepted" | ... 1 more ... | "scope_destroyed"; cause_id: string & $brand<...>; }): { ...; } import {build_role_grant_offer_supersede_notification} from '@fuzdev/fuz_app/auth/role_grant_offer_notifications.js';

params

type { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; to_actor_id: (string & $brand<"Uuid">) | null; ... 11 more ...; resulting_role_grant_id: (string & $brand<...>) | null; }; reason: "sibling_accepted" | ... 1 more ... | "scope_destroyed"; cause_id:...

returns

{ [x: string]: unknown; jsonrpc: "2.0"; method: string; params?: { [x: string]: unknown; } | undefined; }

build_role_grant_revoke_notification
#

auth/role_grant_offer_notifications.ts view source

(params: { role_grant_id: string & $brand<"Uuid">; role: string; scope_id: (string & $brand<"Uuid">) | null; reason: string | null; }): { [x: string]: unknown; jsonrpc: "2.0"; method: string; params?: { ...; } | undefined; } import {build_role_grant_revoke_notification} from '@fuzdev/fuz_app/auth/role_grant_offer_notifications.js';

params

type { role_grant_id: string & $brand<"Uuid">; role: string; scope_id: (string & $brand<"Uuid">) | null; reason: string | null; }

returns

{ [x: string]: unknown; jsonrpc: "2.0"; method: string; params?: { [x: string]: unknown; } | undefined; }

build_test_backend_paths
#

testing/cross_backend/build_test_backend_paths.ts view source

(prefix: string): TestBackendPaths import {build_test_backend_paths} from '@fuzdev/fuz_app/testing/cross_backend/build_test_backend_paths.js';

Build the generic path layout for a cross-process test backend. prefix is typically the BackendConfig.name (e.g. 'deno', 'rust', 'spine_stub').

prefix

type string

returns

TestBackendPaths

builtin_audit_log_config
#

auth/audit_log_schema.ts view source

AuditLogConfig import {builtin_audit_log_config} from '@fuzdev/fuz_app/auth/audit_log_schema.js';

Builtin fuz_app audit-log config — every existing event type and its metadata schema.

builtin_credential_type_meta
#

auth/credential_type_schema.ts view source

ReadonlyMap<string, CredentialTypeMeta> import {builtin_credential_type_meta} from '@fuzdev/fuz_app/auth/credential_type_schema.js';

Builtin credential-type metadata. Not overridable by consumers.

Typed ReadonlyMap for the contract — but JS Maps don't honor Object.freeze for .set / .delete / .clear (they mutate internal slots, not own properties), so freeze adds no runtime guard here. Read once at startup by create_credential_type_schema; runtime mutation has no effect on already-built schemas.

BUILTIN_CREDENTIAL_TYPES
#

auth/credential_type_schema.ts view source

readonly ["session", "api_token", "daemon_token"] import {BUILTIN_CREDENTIAL_TYPES} from '@fuzdev/fuz_app/auth/credential_type_schema.js';

The builtin credential-type names as a const tuple.

builtin_grant_path_meta
#

auth/grant_path_schema.ts view source

ReadonlyMap<string, GrantPathMeta> import {builtin_grant_path_meta} from '@fuzdev/fuz_app/auth/grant_path_schema.js';

Builtin grant-path metadata. Not overridable by consumers.

Typed ReadonlyMap for the contract — but JS Maps don't honor Object.freeze for .set / .delete / .clear (they mutate internal slots, not own properties), so freeze adds no runtime guard here. Read once at startup by create_grant_path_schema; runtime mutation has no effect on already-built schemas.

BUILTIN_GRANT_PATHS
#

auth/grant_path_schema.ts view source

readonly ["admin", "self_service", "system", "bootstrap"] import {BUILTIN_GRANT_PATHS} from '@fuzdev/fuz_app/auth/grant_path_schema.js';

The builtin grant-path names as a const tuple.

builtin_role_specs_by_name
#

auth/role_schema.ts view source

ReadonlyMap<string, RoleSpec> import {builtin_role_specs_by_name} from '@fuzdev/fuz_app/auth/role_schema.js';

Builtin role specs, keyed by role name. Not overridable by consumers — read once at startup by create_role_schema and the action factories that fall back to builtins when no consumer roles is supplied. ReadonlyMap encodes the contract; runtime mutation has no effect on already-built role schemas (the factory copies entries into a fresh Map).

BUILTIN_ROLES
#

auth/role_schema.ts view source

readonly ["keeper", "admin"] import {BUILTIN_ROLES} from '@fuzdev/fuz_app/auth/role_schema.js';

The builtin role names as a const tuple.

BuiltinCredentialType
#

auth/credential_type_schema.ts view source

ZodEnum<{ session: "session"; api_token: "api_token"; daemon_token: "daemon_token"; }> import type {BuiltinCredentialType} from '@fuzdev/fuz_app/auth/credential_type_schema.js';

Zod enum for builtin credential types only.

BuiltinGrantPath
#

auth/grant_path_schema.ts view source

ZodEnum<{ admin: "admin"; bootstrap: "bootstrap"; self_service: "self_service"; system: "system"; }> import type {BuiltinGrantPath} from '@fuzdev/fuz_app/auth/grant_path_schema.js';

Zod enum for builtin grant paths only.

BuiltinRole
#

auth/role_schema.ts view source

ZodEnum<{ admin: "admin"; keeper: "keeper"; }> import type {BuiltinRole} from '@fuzdev/fuz_app/auth/role_schema.js';

Zod schema for builtin roles only.

BuiltTestingApp
#

testing/cross_backend/testing_server_core.ts view source

BuiltTestingApp import type {BuiltTestingApp} from '@fuzdev/fuz_app/testing/cross_backend/testing_server_core.js';

The assembled app a seam returns.

mount_websocket is invoked by the core after the app exists and the adapter prepared the WS upgrade closure — the closure mounts the WS endpoint(s) (e.g. via register_ws_endpoint) and wires any audit-revocation guards. Omit it for an HTTP-only binary.

app

The assembled Hono app (HTTP routes + RPC already mounted).

type Hono

close

Tear down backend(s) + DB + any rotation on graceful shutdown.

type () => Promise<void>

mount_websocket?

Mount WS endpoint(s) given the runtime-prepared upgrade closure.

type (upgrade_websocket: UpgradeWebSocket) => void

can_edit_cell
#

auth/cell_authorize.ts view source

(auth: RequestContext | null, cell: CellRow, grants: readonly CellGrantRow[] | null): boolean import {can_edit_cell} from '@fuzdev/fuz_app/auth/cell_authorize.js';

Edit authorization for a cell.

Unauthenticated callers can never edit. Admin always allowed.

IMPORTANT: the cell.created_by === null branch is explicit defense-in-depth. NULL created_by means system origin (well-known cells seeded by migration, future daemon/agent cells). Non-admin edits MUST be denied — editor-level grants do NOT bypass this guard, because system cells are policy-controlled at admin level. Do NOT collapse into a single equality check that would silently return false for NULL via JS equality semantics — the explicit branch survives refactors and reads as a load-bearing security property.

auth

request context, or null for unauthenticated callers

type RequestContext | null

cell

the cell row

type CellRow

grants

the cell's grant list, or null to skip the grant branch

type readonly CellGrantRow[] | null

returns

boolean

whether the caller may edit the cell

can_manage_cell
#

auth/cell_authorize.ts view source

(auth: RequestContext | null, cell: CellRow): boolean import {can_manage_cell} from '@fuzdev/fuz_app/auth/cell_authorize.js';

Manage authorization for a cell — admin || owner.

The implicit tier above editor: gates visibility writes and all grant management (cell_grant_create / _list / _revoke). NOT delegable and NOT a grant level — an editor-grant holder is never a manager. Grants are not consulted.

NULL created_by (system origin) has no owner, so manage falls to admin only — the explicit NULL guard lives in is_owner.

auth

request context, or null for unauthenticated callers

type RequestContext | null

cell

the cell row

type CellRow

returns

boolean

whether the caller is in the manage tier for the cell

can_view_cell
#

auth/cell_authorize.ts view source

(auth: RequestContext | null, cell: CellRow, grants: readonly CellGrantRow[] | null): boolean import {can_view_cell} from '@fuzdev/fuz_app/auth/cell_authorize.js';

View authorization for a cell.

  • Admin: always allowed.
  • cell.visibility === 'public': allowed for everyone, including unauthenticated callers (e.g. a public landing cell).
  • Owner (cell.created_by === auth.actor.id): allowed.
  • Any active grant on the cell admits the caller (actor-shaped: match on actor_id; role-shaped: match on (role, scope_id?) against an active role_grant).
  • Otherwise: false.

auth

request context, or null for unauthenticated callers

type RequestContext | null

cell

the cell row

type CellRow

grants

the cell's grant list, or null to skip the grant branch

type readonly CellGrantRow[] | null

returns

boolean

whether the caller may view the cell

cancel_action
#

actions/cancel.ts view source

Action<{ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | {... import {cancel_action} from '@fuzdev/fuz_app/actions/cancel.js';

Protocol-action tuple — spread into the server's actions array (or via protocol_actions from actions/protocol.ts) so the dispatcher registers the spec for input validation and so create_rpc_client codegen sees the method. The client doesn't need to call it directly; FrontendWebsocketClient.request({signal}) sends the cancel notification automatically when the signal fires.

cancel_action_spec
#

actions/cancel.ts view source

{ method: string; kind: "remote_notification"; initiator: "frontend"; auth: null; side_effects: true; input: ZodObject<{ request_id: ZodUnion<readonly [ZodString, ZodNumber]>; }, $strict>; output: ZodVoid; async: true; description: string; } import {cancel_action_spec} from '@fuzdev/fuz_app/actions/cancel.js';

ActionSpec for the shared cancel. auth: null matches every other remote-notification spec — upgrade-time auth has already admitted the socket, so per-action auth on a fire-and-forget notification is moot. The per-connection {request_id → AbortController} map enforces socket-scoped ownership naturally: a different socket's cancel for the same id misses in its own map.

cancel_handler
#

actions/cancel.ts view source

(): void import {cancel_handler} from '@fuzdev/fuz_app/actions/cancel.js';

Placeholder handler — cancel semantics are owned by register_action_ws, not invoked per-handler. Exported for symmetry with the Action tuple shape; the dispatcher short-circuits cancel notifications before any handler lookup happens.

returns

void

CancelNotificationParams
#

actions/cancel.ts view source

ZodObject<{ request_id: ZodUnion<readonly [ZodString, ZodNumber]>; }, $strict> import type {CancelNotificationParams} from '@fuzdev/fuz_app/actions/cancel.js';

Params for the cancel notification. request_id is the id of the pending request to abort. Must match the id of a request sent on the same socket; cancels from other sockets (or for unknown ids) are ignored.

canonicalize_ip
#

http/ip_canonical.ts view source

(ip: string): string import {canonicalize_ip} from '@fuzdev/fuz_app/http/ip_canonical.js';

Canonicalize an IP address string.

Returns the RFC 5952 canonical form for parseable IPv4 or IPv6 input. Returns the input unchanged (only lowercased) when the input is non-IP ('unknown'), malformed ('attacker:controlled', '::1\n'), or any string the strict char-set filter rejects.

Idempotent. canonicalize_ip(canonicalize_ip(x)) === canonicalize_ip(x) for every input.

Order-safe for IPv4-mapped IPv6. The ::ffff: prefix strip runs AFTER the canonical emit because the canonical form of an IPv4-mapped IPv6 address is the dotted form (::ffff:127.0.0.1, not ::ffff:7f00:1). Stripping before canonicalize would miss the full-hex form. Closes the normalize_ipv4_mapped_collapse_is_order_safe test from the Rust port.

ip

type string

returns

string

examples

canonicalize_ip('::0001') // → '::1' canonicalize_ip('0:0:0:0:0:0:0:1') // → '::1' canonicalize_ip('2001:0DB8::0001') // → '2001:db8::1' canonicalize_ip('::ffff:127.0.0.1') // → '127.0.0.1' canonicalize_ip('0:0:0:0:0:ffff:7f00:1') // → '127.0.0.1' canonicalize_ip('::ffff:1') // → '::ffff:1' (NOT IPv4-mapped — group[5] is 0, not ffff) canonicalize_ip('127.0.0.1') // → '127.0.0.1' canonicalize_ip('not-an-ip') // → 'not-an-ip' (passes through) canonicalize_ip('::1\n') // → '::1\n' (fails char-set; passes through) canonicalize_ip('203.0.113.1:8080') // → '203.0.113.1:8080' (passes through; validate_ip_strict rejects)

capture_action_manifest
#

testing/cross_backend/setup.ts view source

(handle: ReconstructedBootstrappedBackendHandle): Promise<{ methods: { method: string; side_effects: boolean; account: "none" | "optional" | "required"; actor: "none" | ... 1 more ... | "required"; roles: string[]; credential_types: string[]; }[]; }> import {capture_action_manifest} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

Capture a backend's live RPC action manifest over the _testing_action_manifest RPC action (keeper daemon-token channel). The action-surface twin of capture_schema_snapshot — pair two calls with assert_action_manifests_equal (testing/cross_backend/action_manifest_parity.ts) to gate that the TS spine and the Rust testing_spine_stub mount the same method set with the same per-method auth shape. Each impl answers from its own live registry (TS via build_action_manifest, Rust via the fuz_testing mirror); the normalized shapes match by design.

handle

returns

Promise<{ methods: { method: string; side_effects: boolean; account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles: string[]; credential_types: string[]; }[]; }>

capture_migration_tracker
#

testing/cross_backend/setup.ts view source

(handle: ReconstructedBootstrappedBackendHandle): Promise<{ entries: { namespace: string; name: string; sequence: number; }[]; }> import {capture_migration_tracker} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

Capture a backend's schema_version migration tracker over the _testing_migration_tracker RPC action (keeper daemon-token channel). The provenance twin of capture_schema_snapshot — where that reads the resulting schema (and excludes the tracker), this reads the tracker rows themselves. Pair two calls with assert_migration_trackers_equal (testing/schema_parity.ts) to gate that the TS spine and the Rust testing_spine_stub record byte-identical migration identity (namespace + name + sequence) — the invariant the schema-snapshot gate is blind to.

handle

returns

Promise<{ entries: { namespace: string; name: string; sequence: number; }[]; }>

capture_schema_snapshot
#

testing/cross_backend/setup.ts view source

(handle: ReconstructedBootstrappedBackendHandle, options?: { exclude_tables?: readonly string[] | undefined; }): Promise<{ tables: Record<string, { columns: Record<...>; indexes: { ...; }[]; constraints: { ...; }[]; }>; sequences: Record<...>; enums: Record<...>; }> import {capture_schema_snapshot} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

Capture a backend's schema snapshot over the _testing_schema_snapshot RPC action (keeper daemon-token channel). The canonical way for a cross-impl parity gate to read each backend's live schema — pair two calls with assert_schema_snapshots_equal (testing/schema_parity.ts).

exclude_tables drops documented divergences from both sides before comparison (e.g. a cell-primary Rust backend lacks tables the TS schema has). Each impl answers from its own introspection — TS via query_schema_snapshot, Rust via fuz_db::query_schema_snapshot — and the snapshot shapes match by design.

handle

options

type { exclude_tables?: readonly string[] | undefined; }
default {}

returns

Promise<{ tables: Record<string, { columns: Record<string, { data_type: string; udt_name: string; is_nullable: boolean; column_default: string | null; is_identity: boolean; }>; indexes: { name: string; definition: string; }[]; constraints: { ...; }[]; }>; sequences: Record<...>; enums: Record<...>; }>

CardinalPosition
#

ui/position_helpers.ts view source

CardinalPosition import type {CardinalPosition} from '@fuzdev/fuz_app/ui/position_helpers.js';

Basic position options for UI elements (cardinal directions).

cell_audit_events
#

auth/cell_audit_events.ts view source

Readonly<Record<string, ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>>> import {cell_audit_events} from '@fuzdev/fuz_app/auth/cell_audit_events.js';

Cell-layer event_type → metadata schema map for extra_events.

Covers the seven generic cell verbs' mutation events plus the grant / field / item relation events. Read-only verbs (cell_get, cell_list, cell_*_list, cell_audit_list) emit nothing and are absent here.

cell_audit_list_action_spec
#

auth/cell_audit_action_specs.ts view source

also exported from auth/cell_action_specs.ts

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: false; input: ZodObject<{ cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<...>; }, $strict>; output: ZodObject<...>; async: true; description: string; } import {cell_audit_list_action_spec} from '@fuzdev/fuz_app/auth/cell_audit_action_specs.js';

CELL_AUDIT_LIST_DEFAULT_LIMIT
#

cell_clone_action_spec
#

auth/cell_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ source_id: $ZodBranded<ZodUUID, "Uuid", "out">; deep: ZodOptional<...>; with_data_patch: ZodOptional<...>; acting: ZodOptional<...>; }, $strict>; output: ZodObj... import {cell_clone_action_spec} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

cell_create_action_spec
#

auth/cell_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ data: ZodObject<{ label: ZodOptional<ZodString>; summary: ZodOptional<...>; }, $loose>; ... 4 more ...; acting: ZodOptional<...>; }, $strict>; output: ZodObject... import {cell_create_action_spec} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

cell_delete_action_spec
#

auth/cell_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<...>; }, $strict>; output: ZodObject<...>; async: true; description: string; } import {cell_delete_action_spec} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

CELL_DROP_TABLES
#

db/cell_ddl.ts view source

readonly ["cell_field", "cell_item", "cell_grant", "cell"] import {CELL_DROP_TABLES} from '@fuzdev/fuz_app/db/cell_ddl.js';

Tables created by CELL_MIGRATION_NS, in drop order (children first).

CELL_EDITOR_ROLE
#

cell_field_delete_action_spec
#

auth/cell_field_action_specs.ts view source

also exported from auth/cell_action_specs.ts

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ source_id: $ZodBranded<ZodUUID, "Uuid", "out">; name: $ZodBranded<...>; acting: ZodOptional<...>; }, $strict>; output: ZodObject<...>; async: true; description:... import {cell_field_delete_action_spec} from '@fuzdev/fuz_app/auth/cell_field_action_specs.js';

CELL_FIELD_INDEXES
#

db/cell_ddl.ts view source

string[] import {CELL_FIELD_INDEXES} from '@fuzdev/fuz_app/db/cell_ddl.js';

cell_field indexes.

  • PK on (source_id, name) covers forward lookup ("what does this cell point to via field X?") and the per-source fields list.
  • idx_cell_field_target covers reverse lookup ("which cells link to this target?").

Soft-delete is filtered by JOIN at the read boundary; no partial indexes here on deleted_at (would force index churn on cell soft-delete toggles, and the join filter is sufficient).

cell_field_list_action_spec
#

auth/cell_field_action_specs.ts view source

also exported from auth/cell_action_specs.ts

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "optional"; actor: "optional"; }; side_effects: false; input: ZodObject<{ source_id: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; target_id: ZodOptional<...>; name_after: ZodOptional<...>; limit: ZodOptional<...>; acting: ZodOpt... import {cell_field_list_action_spec} from '@fuzdev/fuz_app/auth/cell_field_action_specs.js';

CELL_FIELD_NAME_REGEX
#

auth/cell_field_action_specs.ts view source

RegExp import {CELL_FIELD_NAME_REGEX} from '@fuzdev/fuz_app/auth/cell_field_action_specs.js';

Field name grammar — fuz snake_case identifier convention. Anchored ^[a-z][a-z0-9_]{0,63}$: leading letter, alphanumeric + underscore trailing, 64-char cap. No reserved names yet.

CELL_FIELD_SCHEMA
#

db/cell_ddl.ts view source

"\nCREATE TABLE IF NOT EXISTS cell_field (\n\tsource_id UUID NOT NULL REFERENCES cell(id) ON DELETE CASCADE,\n\tname TEXT NOT NULL,\n\ttarget_id UUID NOT NULL REFERENCES cell(id) ON DELETE CASCADE,\n\tcreated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n\tPRIMARY KEY (source_id, name)\n)" import {CELL_FIELD_SCHEMA} from '@fuzdev/fuz_app/db/cell_ddl.js';

cell_field table — named relation ((source_id, name) → target_id). One target per name per source — JSON-object keys are unique. Multiplicity is expressed by composition (foo.tags = collection_cell whose items[] are the tags), not by allowing duplicate (source_id, name) rows.

cell_field_set_action_spec
#

auth/cell_field_action_specs.ts view source

also exported from auth/cell_action_specs.ts

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ source_id: $ZodBranded<ZodUUID, "Uuid", "out">; name: $ZodBranded<...>; target_id: $ZodBranded<...>; acting: ZodOptional<...>; }, $strict>; output: ZodObject<..... import {cell_field_set_action_spec} from '@fuzdev/fuz_app/auth/cell_field_action_specs.js';

cell_get_action_spec
#

auth/cell_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "optional"; actor: "optional"; }; side_effects: false; input: ZodObject<{ id: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; path: ZodOptional<...>; acting: ZodOptional<...>; }, $strict>; output: ZodObject<...>; async: true; rate_... import {cell_get_action_spec} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

cell_grant_create_action_spec
#

auth/cell_grant_action_specs.ts view source

also exported from auth/cell_action_specs.ts

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; level: ZodEnum<...>; principal: ZodDiscriminatedUnion<...>; acting: ZodOptional<...>; }, $strict>; output: ZodObje... import {cell_grant_create_action_spec} from '@fuzdev/fuz_app/auth/cell_grant_action_specs.js';

CELL_GRANT_INDEXES
#

db/cell_ddl.ts view source

string[] import {CELL_GRANT_INDEXES} from '@fuzdev/fuz_app/db/cell_ddl.js';

cell_grant indexes.

  • idx_cell_grant_cell: forward lookup ("who has access to this cell?").
  • idx_cell_grant_actor: reverse lookup ("which cells does this actor have access to?").
  • idx_cell_grant_role_scope: reverse lookup for role-shaped principals.
  • idx_cell_grant_unique_actor: prevents duplicate actor-shaped grants for the same cell. Re-granting updates level via UPSERT on this index.
  • idx_cell_grant_unique_role_scope: same, for role-shaped grants. NULLS NOT DISTINCT so two rows with the same (cell_id, role) and scope_id IS NULL collide — without it, default NULL-distinct semantics would let duplicate null-scope role grants slip past the re-share UPSERT path. Requires PostgreSQL 15+ (pglite tracks PG 16).

cell_grant_list_action_spec
#

auth/cell_grant_action_specs.ts view source

also exported from auth/cell_action_specs.ts

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: false; input: ZodObject<{ cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<...>; }, $strict>; output: ZodObject<...>; async: true; description: string; } import {cell_grant_list_action_spec} from '@fuzdev/fuz_app/auth/cell_grant_action_specs.js';

cell_grant_revoke_action_spec
#

auth/cell_grant_action_specs.ts view source

also exported from auth/cell_action_specs.ts

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ grant_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<...>; }, $strict>; output: ZodObject<...>; async: true; description: string; } import {cell_grant_revoke_action_spec} from '@fuzdev/fuz_app/auth/cell_grant_action_specs.js';

CELL_GRANT_SCHEMA
#

db/cell_ddl.ts view source

"\nCREATE TABLE IF NOT EXISTS cell_grant (\n\tid UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n\tcell_id UUID NOT NULL REFERENCES cell(id) ON DELETE CASCADE,\n\tlevel TEXT NOT NULL CHECK (level IN ('viewer', 'editor')),\n\tactor_id UUID REFERENCES actor(id) ON DELETE CASCADE,\n\trole TEXT,\n\tscope_id UUID,\n\tgrante... import {CELL_GRANT_SCHEMA} from '@fuzdev/fuz_app/db/cell_ddl.js';

cell_grant table — resource-side ACL for cells. Each row admits a principal (actor or (role, scope_id)) at a level (viewer or editor). Owner is implicit (cell.created_by); the table never carries owner rows.

The single-principal arm is actor-grain (actor_id FK); the other arm is role-shaped ((role, scope_id)). The CHECK enforces exactly one arm.

CELL_HISTORY_DROP_TABLES
#

CELL_HISTORY_INDEXES
#

db/cell_history_ddl.ts view source

string[] import {CELL_HISTORY_INDEXES} from '@fuzdev/fuz_app/db/cell_history_ddl.js';

Cell-history indexes.

  • idx_cell_history_cell: per-cell timeline reads (newest first).
  • idx_cell_history_fact: fact → cells reverse lookup for GC liveness and provenance queries.

CELL_HISTORY_MIGRATION_NAMESPACE
#

db/cell_history_ddl.ts view source

"fuz_cell_history" import {CELL_HISTORY_MIGRATION_NAMESPACE} from '@fuzdev/fuz_app/db/cell_history_ddl.js';

Namespace identifier for cell-history migrations.

CELL_HISTORY_MIGRATION_NS
#

CELL_HISTORY_MIGRATIONS
#

CELL_HISTORY_SCHEMA
#

db/cell_history_ddl.ts view source

"\nCREATE TABLE IF NOT EXISTS cell_history (\n\tid BIGSERIAL PRIMARY KEY,\n\tcell_id UUID NOT NULL REFERENCES cell(id) ON DELETE CASCADE,\n\tfact_hash TEXT NOT NULL,\n\taction_id UUID,\n\tcreated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n)" import {CELL_HISTORY_SCHEMA} from '@fuzdev/fuz_app/db/cell_history_ddl.js';

cell_history table — append-only log of cell snapshot references.

CELL_INDEXES
#

db/cell_ddl.ts view source

string[] import {CELL_INDEXES} from '@fuzdev/fuz_app/db/cell_ddl.js';

Cell indexes — all active-only, partial on deleted_at IS NULL.

  • idx_cell_active: active-cell list/scan ordered by creation.
  • idx_cell_path_unique: global path uniqueness + read-side path lookup. Partial on path + active so reused paths after soft delete are allowed.
  • idx_cell_kind: the cell_list kind filter (cell.kind = ?) and kind-scoped scans. Active-only.
  • idx_cell_data: shape-driven queries (`data

cell_item_delete_action_spec
#

auth/cell_item_action_specs.ts view source

also exported from auth/cell_action_specs.ts

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ parent_id: $ZodBranded<ZodUUID, "Uuid", "out">; position: $ZodBranded<...>; acting: ZodOptional<...>; }, $strict>; output: ZodObject<...>; async: true; descript... import {cell_item_delete_action_spec} from '@fuzdev/fuz_app/auth/cell_item_action_specs.js';

CELL_ITEM_INDEXES
#

db/cell_ddl.ts view source

string[] import {CELL_ITEM_INDEXES} from '@fuzdev/fuz_app/db/cell_ddl.js';

cell_item indexes.

  • PK on (parent_id, position) covers ordered scans for the per-parent items list (SELECT ... ORDER BY position).
  • idx_cell_item_child covers reverse lookup ("which parents contain this child?").

cell_item_insert_action_spec
#

auth/cell_item_action_specs.ts view source

also exported from auth/cell_action_specs.ts

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ parent_id: $ZodBranded<ZodUUID, "Uuid", "out">; child_id: $ZodBranded<...>; position: $ZodBranded<...>; acting: ZodOptional<...>; }, $strict>; output: ZodObject... import {cell_item_insert_action_spec} from '@fuzdev/fuz_app/auth/cell_item_action_specs.js';

cell_item_list_action_spec
#

auth/cell_item_action_specs.ts view source

also exported from auth/cell_action_specs.ts

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "optional"; actor: "optional"; }; side_effects: false; input: ZodObject<{ parent_id: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; child_id: ZodOptional<...>; position_after: ZodOptional<...>; limit: ZodOptional<...>; acting: Zod... import {cell_item_list_action_spec} from '@fuzdev/fuz_app/auth/cell_item_action_specs.js';

cell_item_move_action_spec
#

auth/cell_item_action_specs.ts view source

also exported from auth/cell_action_specs.ts

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ parent_id: $ZodBranded<ZodUUID, "Uuid", "out">; position: $ZodBranded<...>; new_position: $ZodBranded<...>; acting: ZodOptional<...>; }, $strict>; output: ZodOb... import {cell_item_move_action_spec} from '@fuzdev/fuz_app/auth/cell_item_action_specs.js';

CELL_ITEM_SCHEMA
#

db/cell_ddl.ts view source

"\nCREATE TABLE IF NOT EXISTS cell_item (\n\tparent_id UUID NOT NULL REFERENCES cell(id) ON DELETE CASCADE,\n\tposition TEXT NOT NULL,\n\tchild_id UUID NOT NULL REFERENCES cell(id) ON DELETE CASCADE,\n\tcreated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n\tPRIMARY KEY (parent_id, position)\n)" import {CELL_ITEM_SCHEMA} from '@fuzdev/fuz_app/db/cell_ddl.js';

cell_item table — ordered child membership keyed by an opaque fractional-indexing string. (parent_id, position) PK enforces one cell per slot; the same child_id may appear at multiple positions (the primitive is JSON-array-shaped — ordered multiset, not set). Domain dedup rules ride on top in helpers.

cell_list_action_spec
#

auth/cell_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "optional"; actor: "optional"; }; side_effects: false; input: ZodDefault<ZodObject<{ ids: ZodOptional<ZodArray<$ZodBranded<ZodUUID, "Uuid", "out">>>; ... 12 more ...; acting: ZodOptional<...>; }, $strict>>; output: ZodObject<...>; as... import {cell_list_action_spec} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

CELL_LIST_LIMIT_DEFAULT
#

CELL_LIST_LIMIT_MAX
#

auth/cell_action_specs.ts view source

200 import {CELL_LIST_LIMIT_MAX} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Soft cap on the size of a cell.list request page. Larger pages chew memory both server- and client-side; combined with the visibility predicate's filter cost, 200 is a safe ceiling.

CELL_MIGRATION_NAMESPACE
#

db/cell_ddl.ts view source

"fuz_cell" import {CELL_MIGRATION_NAMESPACE} from '@fuzdev/fuz_app/db/cell_ddl.js';

Namespace identifier for cell migrations.

CELL_MIGRATION_NS
#

CELL_MIGRATIONS
#

cell_moderate_action_spec
#

auth/cell_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; moderation: ZodEnum<...>; acting: ZodOptional<...>; }, $strict>; output: ZodObject<...>; async: true; rate_limit: ... import {cell_moderate_action_spec} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

CELL_PATH_LENGTH_MAX
#

auth/cell_action_specs.ts view source

256 import {CELL_PATH_LENGTH_MAX} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Wire form for cell.path.

At the spec level we only enforce that the value is a non-empty string with a sane upper bound — the cell layer is generic and doesn't impose a path grammar. App-side curation (well-known names like /map/main, /site/events) is admin-driven.

CELL_RELATIONS_BUNDLE_LIMIT
#

auth/cell_action_specs.ts view source

500 import {CELL_RELATIONS_BUNDLE_LIMIT} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Hard cap on bundled relation arrays in cell_get (per-relation LIMIT). Beyond this the response sets *_truncated: true and the client paginates via cell_item_list({parent_id, position_after}) / cell_field_list({source_id, name_after}).

CELL_ROLE_HOLDER_USERNAME
#

CELL_SCHEMA
#

db/cell_ddl.ts view source

"\nCREATE TABLE IF NOT EXISTS cell (\n\tid UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n\tdata JSONB NOT NULL,\n\tkind TEXT,\n\tvisibility cell_visibility NOT NULL DEFAULT 'private',\n\tpath TEXT,\n\trefs TEXT[],\n\tparent_id UUID REFERENCES cell(id) ON DELETE SET NULL,\n\troot_id UUID REFERENCES cell(id) ON DELETE ... import {CELL_SCHEMA} from '@fuzdev/fuz_app/db/cell_ddl.js';

cell table — universal content primitive: identity + content only. Parent→child membership lives in cell_item; named relations live in cell_field. Includes the created_by / updated_by ownership columns.

visibility is the access-control axis — private (default) is restricted to admin / owner / cell_grant-admitted callers; public admits everyone, including unauthenticated visitors. Lives as a top-level column so the auth predicate reads off the row directly rather than reaching into data.

path is the global namespace axis (no tenant/hub scoping) — globally unique on active rows via idx_cell_path_unique.

kind is the capability / identity axis — a nullable top-level column (peer to visibility / path), not a field inside data. It is the discriminator a creation authorizer gates on (see auth/cell_actions.ts CellCreateAuthorize) and is write-once: set at INSERT and carried on no update path, so a cell's kind is fixed at birth. Content stays duck-typed in data; kind is a capability tag, not a content-type.

parent_id / root_id are the directory tree (containment): parent_id is the immediate container (nullable self-FK; NULL = a root), root_id is the governing root denormalized for flat-subtree queries (`root_id = parent.root_id ?? parent.id, so a root has NULL`). Both are set once at create and immutable in v1 (carried on no update path). moderation (nullable text — pending / approved / rejected; NULL = unmoderated) is the approval-lifecycle marker, peer to visibility (a control field with a non-author writer — see auth/cell_actions.ts), never inside data.

cell_update_action_spec
#

auth/cell_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; data: ZodOptional<...>; visibility: ZodOptional<...>; path: ZodOptional<...>; acting: ZodOptional<...>; }, $strict... import {cell_update_action_spec} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

CELL_VISIBILITY_TYPE
#

db/cell_ddl.ts view source

"\nDO $$ BEGIN\n\tCREATE TYPE cell_visibility AS ENUM ('private', 'public');\nEXCEPTION WHEN duplicate_object THEN NULL;\nEND $$" import {CELL_VISIBILITY_TYPE} from '@fuzdev/fuz_app/db/cell_ddl.js';

cell_visibility enum — access-control axis for a cell. Lives as a top-level column (not inside data) because visibility is access control, not content metadata. cell_grant is the other ACL surface; keeping visibility as a peer column (not a JSON field) co-locates access-control state and lets the planner reason about it directly.

Ships with two states ('private', 'public'); a third (unlisted / public-link) folds in via ALTER TYPE when public-link sharing lands.

Wrapped in a DO block so the migration can replay idempotently — CREATE TYPE has no IF NOT EXISTS variant in PostgreSQL.

CellActionDeps
#

auth/cell_actions.ts view source

CellActionDeps import type {CellActionDeps} from '@fuzdev/fuz_app/auth/cell_actions.js';

Dependencies for create_cell_actions.

validate_data is the optional sub-API hook for per-kind shape validation (e.g., a collection/entry registry). It runs on every incoming data payload (create, update, clone-merged) and may throw a ZodError — the handler converts that into the standard invalid_params JSON-RPC error so per-kind validation failures surface to clients with code -32602 (not -32603 / internal). When omitted, payloads pass through as-is.

authorize_create is the optional creation-gate hook (see CellCreateAuthorize). When omitted, create is open (today's behavior).

log

Structured logger instance.

type Logger

audit

Bound audit emitter for fire-and-forget audit writes.

type AuditEmitter

validate_data?

type (data: { [x: string]: unknown; label?: string | undefined; summary?: string | undefined; }) => { [x: string]: unknown; label?: string | undefined; summary?: string | undefined; }

authorize_create?

type CellCreateAuthorize

CellAuditEventJson
#

auth/cell_audit_action_specs.ts view source

ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; seq: ZodNumber; event_type: ZodString; outcome: ZodEnum<{ success: "success"; failure: "failure"; }>; actor_id: ZodNullable<...>; created_at: ZodString; }, $strict> import type {CellAuditEventJson} from '@fuzdev/fuz_app/auth/cell_audit_action_specs.js';

Wire shape for a single cell-audit row. Narrower than AuditLogEventJsonaccount_id and target_account_id are deliberately omitted so this verb does NOT surface the actor↔account join. target_actor_id and metadata are dropped too: target_actor_id is NULL for every cell-domain event (the grant recipient lives inside metadata.principal on grant rows, not on the audit-log top-level field); metadata is unread by the timeline UI.

ip is also omitted: it is PII about the actors who touched the cell, and even at the manage tier this per-cell timeline has no need for it (admins reach the full audit_log surface, which carries ip, through the admin audit verbs). Keeping it off this wire avoids leaking collaborators' IPs to a cell's owner.

All omitted fields can be re-added under a richer admin-only event-detail view later — keep the wire surface honest about what consumers use.

CellAuditListInput
#

auth/cell_audit_action_specs.ts view source

ZodObject<{ cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict> import type {CellAuditListInput} from '@fuzdev/fuz_app/auth/cell_audit_action_specs.js';

CellAuditListOptions
#

db/cell_audit_queries.ts view source

CellAuditListOptions import type {CellAuditListOptions} from '@fuzdev/fuz_app/db/cell_audit_queries.js';

limit

type number

before?

Cursor — return rows with seq < before.

type number

CellAuditListOutput
#

auth/cell_audit_action_specs.ts view source

ZodObject<{ events: ZodArray<ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; seq: ZodNumber; event_type: ZodString; outcome: ZodEnum<{ success: "success"; failure: "failure"; }>; actor_id: ZodNullable<...>; created_at: ZodString; }, $strict>>; }, $strict> import type {CellAuditListOutput} from '@fuzdev/fuz_app/auth/cell_audit_action_specs.js';

CellAuditMetadata
#

auth/cell_audit_metadata.ts view source

ZodObject<{ cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; kind: ZodOptional<ZodString>; path: ZodOptional<ZodNullable<ZodString>>; }, $loose> import type {CellAuditMetadata} from '@fuzdev/fuz_app/auth/cell_audit_metadata.js';

Shared metadata envelope for cell mutations. kind and path are captured at emit-time so the audit-log viewer can show useful context for soft-deleted rows even after the cell snapshot is gone. Relation membership is tracked independently via the cell_item_* / cell_field_* per-row audit events.

Loose object: per-kind handlers may extend the metadata without spec churn.

CellCloneAuditMetadata
#

auth/cell_audit_metadata.ts view source

ZodObject<{ source_id: $ZodBranded<ZodUUID, "Uuid", "out">; new_id: $ZodBranded<ZodUUID, "Uuid", "out">; deep: ZodBoolean; item_count: ZodNumber; kind: ZodOptional<...>; }, $loose> import type {CellCloneAuditMetadata} from '@fuzdev/fuz_app/auth/cell_audit_metadata.js';

Metadata envelope for cell_clone.

source_id and new_id capture the parent → clone edge. deep flags whether children were walked. item_count reports the number of children actually cloned (post-skip). kind is captured at emit-time so an operator can filter the audit log by source shape (e.g., "every collection clone").

No skipped-child count is recorded: surfacing how many children the caller couldn't view would leak the source's hidden-child count to the cloner (who owns — and can audit — the clone). Non-viewable children are dropped silently (D8).

CellCloneInput
#

auth/cell_action_specs.ts view source

ZodObject<{ source_id: $ZodBranded<ZodUUID, "Uuid", "out">; deep: ZodOptional<ZodBoolean>; with_data_patch: ZodOptional<ZodObject<{ label: ZodOptional<ZodString>; summary: ZodOptional<...>; }, $loose>>; acting: ZodOptional<...>; }, $strict> import type {CellCloneInput} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Input for cell_clone. Source must be view-admitted by can_view_cell (404 otherwise — IDOR mask). The clone is owned by the caller; path is always nulled (admin-only paths can't auto-clone). Provenance lives only in the cell_clone audit row's source_id — no provenance fields are stamped into data.

CellCloneOutput
#

auth/cell_action_specs.ts view source

ZodObject<{ cell: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; path: ZodNullable<$ZodBranded<ZodString, "CellPath", "out">>; ... 12 more ...; grant_count: ZodNumber; }, $strict>; }, $strict> import type {CellCloneOutput} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

CellCreateAuthorize
#

auth/cell_actions.ts view source

CellCreateAuthorize import type {CellCreateAuthorize} from '@fuzdev/fuz_app/auth/cell_actions.js';

Opt-in, parent-aware creation authorizer — the TS twin of the Rust CellCreateAuthorize trait. Gates both roots (parent_id = null) and contributions; answers "may *this actor* create *this kind* here?" and returns a CellCreateVerdict. Runs in cell_create after validate_data and after the handler resolves root_id from the parent (an unviewable parent already 404-masks before this runs). Omitted = today's open create (all consumers untouched). Async-capable (DB / policy calls) — a DB-backed impl closes over its own db (the create handler stays unaware).

(call)

type (auth: RequestActorContext, input: CellCreateAuthorizeInput): CellCreateVerdict | Promise<CellCreateVerdict>

auth

input

returns CellCreateVerdict | Promise<CellCreateVerdict>

CellCreateAuthorizeInput
#

auth/cell_actions.ts view source

CellCreateAuthorizeInput import type {CellCreateAuthorizeInput} from '@fuzdev/fuz_app/auth/cell_actions.js';

Input to a CellCreateAuthorize callback — the TS twin of the Rust CellCreateAuthorizeInput. Parent-aware: it carries the directory context (parent_id / the handler-resolved root_id) so the authorizer can resolve the governing root's policy.

kind

The cell's kind (the top-level cell.kind value); null for a typeless cell.

type string | null

data

The cell data (kind-free — a kind key is rejected upstream). For richer M3-era policies (e.g. content pre-screen).

type CellData

parent_id

The immediate container the create targets. null = a root creation; otherwise a contribution under that parent.

type Uuid | null

root_id

The governing root of the directory subtree, resolved by the handler from the parent (parent.root_id ?? parent.id). null for a root creation.

type Uuid | null

root_data

The governing root's data — the handler reads it in-tx (when an authorizer is mounted) and hands it over, so a directory-aware authorizer resolves root.data.policy[kind] without a DB read of its own (pure predicate; reading in-tx avoids the single-connection PGlite deadlock a separate handle would hit). null for a root creation, or when no authorizer is mounted.

type CellData | null

scope_id

Target scope — designed-in for M2 space-scoping; always null in v1 (cells carry no scope column).

type Uuid | null

CellCreateInput
#

auth/cell_action_specs.ts view source

ZodObject<{ data: ZodObject<{ label: ZodOptional<ZodString>; summary: ZodOptional<ZodString>; }, $loose>; kind: ZodOptional<ZodNullable<ZodString>>; visibility: ZodOptional<...>; path: ZodOptional<...>; parent_id: ZodOptional<...>; acting: ZodOptional<...>; }, $strict> import type {CellCreateInput} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Input for cell_create. created_by is NOT on the wire — the handler stamps it from auth.actor.id. path is admin-only; non-admin callers supplying path get ERROR_CELL_PATH_ADMIN_ONLY (forbidden).

CellCreateOutput
#

auth/cell_action_specs.ts view source

ZodObject<{ cell: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; path: ZodNullable<$ZodBranded<ZodString, "CellPath", "out">>; ... 12 more ...; grant_count: ZodNumber; }, $strict>; }, $strict> import type {CellCreateOutput} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

CellCreateQueryInput
#

db/cell_queries.ts view source

CellCreateQueryInput import type {CellCreateQueryInput} from '@fuzdev/fuz_app/db/cell_queries.js';

Input for query_cell_create. refs is derived from data. kind is the write-once capability axis (cell.kind column) — set here at INSERT and absent from CellUpdatePatch, so it can never change post-create. parent_id / root_id / moderation are the directory-tree + lifecycle columns the handler derives (parent containment, governing root, the create-authorizer verdict) — likewise write-once / control-gated, never on CellUpdatePatch.

data

type Json

kind?

type string | null

visibility?

type CellVisibility

path?

type string | null

parent_id?

type Uuid | null

root_id?

type Uuid | null

moderation?

type string | null

created_by?

type Uuid | null

CellCreateVerdict
#

auth/cell_actions.ts view source

CellCreateVerdict import type {CellCreateVerdict} from '@fuzdev/fuz_app/auth/cell_actions.js';

An authorizer's decision for a cell_create — the TS twin of the Rust Verdict. Folds the moderation outcome into the authority decision (one policy resolution, not two): {allow: false} denies (the handler surfaces a 403 forbidden for a viewable parent / a root creation), `{allow: true, moderation_required} admits — true → born pending + private, false` → born approved at the author's visibility.

CellData
#

auth/cell_data_schema.ts view source

ZodObject<{ label: ZodOptional<ZodString>; summary: ZodOptional<ZodString>; }, $loose> import type {CellData} from '@fuzdev/fuz_app/auth/cell_data_schema.js';

Base cell-data shape. All fields optional; loose mode admits arbitrary additional keys so apps can attach metadata or stage new kinds without touching the wire schema. kind lives on the top-level cell.kind column, not here (see the module doc).

CellDeleteInput
#

auth/cell_action_specs.ts view source

ZodObject<{ cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict> import type {CellDeleteInput} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

CellDeleteOutput
#

auth/cell_action_specs.ts view source

ZodObject<{ ok: ZodLiteral<true>; deleted: ZodBoolean; }, $strict> import type {CellDeleteOutput} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

CellFieldActionDeps
#

auth/cell_field_actions.ts view source

ActionFactoryDeps import type {CellFieldActionDeps} from '@fuzdev/fuz_app/auth/cell_field_actions.js';

log

Structured logger instance.

type Logger

audit

Bound audit emitter for fire-and-forget audit writes.

type AuditEmitter

CellFieldDeleteAuditMetadata
#

auth/cell_field_audit_metadata.ts view source

ZodObject<{ source_id: $ZodBranded<ZodUUID, "Uuid", "out">; name: ZodString; target_id: $ZodBranded<ZodUUID, "Uuid", "out">; }, $loose> import type {CellFieldDeleteAuditMetadata} from '@fuzdev/fuz_app/auth/cell_field_audit_metadata.js';

Metadata envelope for cell_field_delete.

CellFieldDeleteInput
#

auth/cell_field_action_specs.ts view source

ZodObject<{ source_id: $ZodBranded<ZodUUID, "Uuid", "out">; name: $ZodBranded<ZodString, "CellFieldName", "out">; acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict> import type {CellFieldDeleteInput} from '@fuzdev/fuz_app/auth/cell_field_action_specs.js';

Input for cell_field_delete. Idempotent: a successful response is returned even when no row matched.

CellFieldDeleteOutput
#

CellFieldListInput
#

auth/cell_field_action_specs.ts view source

ZodObject<{ source_id: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; target_id: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; name_after: ZodOptional<...>; limit: ZodOptional<...>; acting: ZodOptional<...>; }, $strict> import type {CellFieldListInput} from '@fuzdev/fuz_app/auth/cell_field_action_specs.js';

Input for cell_field_list. Pass source_id for forward fields or target_id for reverse upfields — exactly one (the schema rejects both / neither). Reverse listing has 2-layer authz (target view-check gates the call; per-source view-check filters the rows).

Forward listing supports cursor pagination via name_after (return rows whose name > name_after lex). The reverse listing doesn't paginate (the result set is small in practice — number of sources pointing at a given target).

CellFieldListOutput
#

auth/cell_field_action_specs.ts view source

ZodObject<{ fields: ZodArray<ZodObject<{ source_id: $ZodBranded<ZodUUID, "Uuid", "out">; name: ZodString; target_id: $ZodBranded<ZodUUID, "Uuid", "out">; created_at: ZodString; }, $strict>>; }, $strict> import type {CellFieldListOutput} from '@fuzdev/fuz_app/auth/cell_field_action_specs.js';

CellFieldName
#

CellFieldRow
#

db/cell_field_queries.ts view source

CellFieldRow import type {CellFieldRow} from '@fuzdev/fuz_app/db/cell_field_queries.js';

Row shape returned by cell_field SELECTs.

source_id

type Uuid

name

type string

target_id

type Uuid

created_at

type Date

CellFieldSetAuditMetadata
#

auth/cell_field_audit_metadata.ts view source

ZodObject<{ source_id: $ZodBranded<ZodUUID, "Uuid", "out">; name: ZodString; target_id: $ZodBranded<ZodUUID, "Uuid", "out">; }, $loose> import type {CellFieldSetAuditMetadata} from '@fuzdev/fuz_app/auth/cell_field_audit_metadata.js';

Metadata envelope for cell_field_set. Emitted on every successful create OR update path (UPSERT on (source_id, name)); the audit reader correlates create-vs-update via repeated (source_id, name) if needed.

CellFieldSetInput
#

auth/cell_field_action_specs.ts view source

ZodObject<{ source_id: $ZodBranded<ZodUUID, "Uuid", "out">; name: $ZodBranded<ZodString, "CellFieldName", "out">; target_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<...>; }, $strict> import type {CellFieldSetInput} from '@fuzdev/fuz_app/auth/cell_field_action_specs.js';

Input for cell_field_set. UPSERT on (source_id, name) — re-issuing the same input updates target_id and bumps created_at.

CellFieldSetOutput
#

auth/cell_field_action_specs.ts view source

ZodObject<{ field: ZodObject<{ source_id: $ZodBranded<ZodUUID, "Uuid", "out">; name: ZodString; target_id: $ZodBranded<ZodUUID, "Uuid", "out">; created_at: ZodString; }, $strict>; }, $strict> import type {CellFieldSetOutput} from '@fuzdev/fuz_app/auth/cell_field_action_specs.js';

CellFieldSetQueryInput
#

CellGetInput
#

auth/cell_action_specs.ts view source

ZodObject<{ id: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; path: ZodOptional<$ZodBranded<ZodString, "CellPath", "out">>; acting: ZodOptional<...>; }, $strict> import type {CellGetInput} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Input for cell_get. Pass id OR path (exactly one expected; both accepted, id takes precedence). The handler responds with 404 when no row matches OR when can_view_cell rejects the caller — same code so private-cell existence doesn't leak.

CellGetOutput
#

auth/cell_action_specs.ts view source

ZodObject<{ cell: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; path: ZodNullable<$ZodBranded<ZodString, "CellPath", "out">>; ... 12 more ...; grant_count: ZodNumber; }, $strict>; ... 5 more ...; can_grant: ZodBoolean; }, $strict> import type {CellGetOutput} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Output for cell_get. Bundles relation arrays (fields + items) server-side via JOINs so the common "show this cell with its children" flow needs one round-trip. Targets are filtered to those the caller may view (strict target-visibility). Per-relation `LIMIT CELL_RELATIONS_BUNDLE_LIMIT`; clients paginate via cell_item_list({parent_id, position_after}) / cell_field_list({source_id}) when truncated.

CellGrantActionDeps
#

auth/cell_grant_actions.ts view source

CellGrantActionDeps import type {CellGrantActionDeps} from '@fuzdev/fuz_app/auth/cell_grant_actions.js';

Dependencies for create_cell_grant_actions.

roles is the role schema — read for the role-validity gate on cell_grant_create. The other slots match CellActionDeps so audit-log emit goes through the same fire-and-forget plumbing.

log

Structured logger instance.

type Logger

audit

Bound audit emitter for fire-and-forget audit writes.

type AuditEmitter

roles

type RoleSchemaResult

CellGrantCreateAuditMetadata
#

auth/cell_grant_audit_metadata.ts view source

ZodObject<{ cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; grant_id: $ZodBranded<ZodUUID, "Uuid", "out">; level: ZodEnum<{ viewer: "viewer"; editor: "editor"; }>; principal: ZodUnion<...>; }, $loose> import type {CellGrantCreateAuditMetadata} from '@fuzdev/fuz_app/auth/cell_grant_audit_metadata.js';

Metadata envelope for cell_grant_create.

Emitted on every successful create OR re-share update path (UPSERT-on-unique-index). The audit reader correlates create-vs-update via grant_id if needed; the design doesn't require distinguishing the two at the metadata level.

CellGrantCreateInput
#

auth/cell_grant_action_specs.ts view source

ZodObject<{ cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; level: ZodEnum<{ viewer: "viewer"; editor: "editor"; }>; principal: ZodDiscriminatedUnion<[ZodObject<{ kind: ZodLiteral<"actor">; actor_id: $ZodBranded<...>; }, $strict>, ZodObject<...>], "kind">; acting: ZodOptional<...>; }, $strict> import type {CellGrantCreateInput} from '@fuzdev/fuz_app/auth/cell_grant_action_specs.js';

Input for cell_grant_create. Idempotent on the unique index — re-granting the same (cell_id, principal) pair updates level + granted_by rather than producing a duplicate row.

CellGrantCreateOutput
#

auth/cell_grant_action_specs.ts view source

ZodObject<{ grant: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; level: ZodEnum<{ viewer: "viewer"; editor: "editor"; }>; ... 4 more ...; created_at: ZodString; }, $strict>; }, $strict> import type {CellGrantCreateOutput} from '@fuzdev/fuz_app/auth/cell_grant_action_specs.js';

CellGrantCreateQueryInput
#

CellGrantLevel
#

auth/cell_grant_action_specs.ts view source

ZodEnum<{ viewer: "viewer"; editor: "editor"; }> import type {CellGrantLevel} from '@fuzdev/fuz_app/auth/cell_grant_action_specs.js';

Grant level — view-only or view-plus-edit.

CellGrantListInput
#

auth/cell_grant_action_specs.ts view source

ZodObject<{ cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict> import type {CellGrantListInput} from '@fuzdev/fuz_app/auth/cell_grant_action_specs.js';

CellGrantListOutput
#

auth/cell_grant_action_specs.ts view source

ZodObject<{ grants: ZodArray<ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; level: ZodEnum<{ viewer: "viewer"; editor: "editor"; }>; ... 4 more ...; created_at: ZodString; }, $strict>>; }, $strict> import type {CellGrantListOutput} from '@fuzdev/fuz_app/auth/cell_grant_action_specs.js';

CellGrantPrincipalAuditMetadata
#

auth/cell_grant_audit_metadata.ts view source

ZodUnion<readonly [ZodObject<{ actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; }, $loose>, ZodObject<{ role: ZodString; scope_id: ZodNullable<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $loose>]> import type {CellGrantPrincipalAuditMetadata} from '@fuzdev/fuz_app/auth/cell_grant_audit_metadata.js';

Principal columns as stored on cell_grant. Discriminated by which keys are present: {actor_id} for an actor-shaped grant, {role, scope_id} for a role-shaped grant. Actor-shaped grants carry only the id; names are never persisted in the audit envelope.

CellGrantPrincipalInput
#

auth/cell_grant_action_specs.ts view source

ZodDiscriminatedUnion<[ZodObject<{ kind: ZodLiteral<"actor">; actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; }, $strict>, ZodObject<{ kind: ZodLiteral<"role">; role: ZodString; scope_id: ZodOptional<...>; }, $strict>], "kind"> import type {CellGrantPrincipalInput} from '@fuzdev/fuz_app/auth/cell_grant_action_specs.js';

Wire-input principal. Discriminated by kind. Actor-shaped principals carry a resolved actor_id — the picker UI runs actor_search to convert a typed name to an id before this verb is called.

CellGrantPrincipalQueryInput
#

db/cell_grant_queries.ts view source

CellGrantPrincipalQueryInput import type {CellGrantPrincipalQueryInput} from '@fuzdev/fuz_app/db/cell_grant_queries.js';

Discriminated principal input for query_cell_grant_create. Wire and query shapes are aligned — actor-shaped principals carry a pre-resolved actor_id; pickers run actor_search to convert a typed name to an id upstream of the handler.

CellGrantRevokeAuditMetadata
#

auth/cell_grant_audit_metadata.ts view source

ZodObject<{ cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; grant_id: $ZodBranded<ZodUUID, "Uuid", "out">; level: ZodEnum<{ viewer: "viewer"; editor: "editor"; }>; principal: ZodUnion<...>; self: ZodOptional<...>; }, $loose> import type {CellGrantRevokeAuditMetadata} from '@fuzdev/fuz_app/auth/cell_grant_audit_metadata.js';

Metadata envelope for cell_grant_revoke.

self: true distinguishes the recipient-side "leave shared cell" path (actor-shaped grant where the principal actor === caller actor) from a delegator-side revoke. Single event type for both — the boolean is enough for forensic review and avoids surface- doubling with a parallel cell_grant_leave event.

CellGrantRevokeInput
#

auth/cell_grant_action_specs.ts view source

ZodObject<{ grant_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict> import type {CellGrantRevokeInput} from '@fuzdev/fuz_app/auth/cell_grant_action_specs.js';

CellGrantRevokeOutput
#

auth/cell_grant_action_specs.ts view source

ZodObject<{ ok: ZodLiteral<true>; still_admitted: ZodBoolean; }, $strict> import type {CellGrantRevokeOutput} from '@fuzdev/fuz_app/auth/cell_grant_action_specs.js';

Output for cell_grant_revoke. still_admitted is true when the caller retains some admit path on the cell after the revoke (other grant, ownership, admin). Always true for non-self revokes (the caller didn't admit via this row to begin with).

CellGrantRow
#

db/cell_grant_queries.ts view source

CellGrantRow import type {CellGrantRow} from '@fuzdev/fuz_app/db/cell_grant_queries.js';

Row shape returned by cell_grant SELECTs.

id

type Uuid

cell_id

type Uuid

level

type CellGrantLevel

actor_id

type Uuid | null

role

type string | null

scope_id

type Uuid | null

granted_by

type Uuid | null

created_at

type Date

CellItemActionDeps
#

auth/cell_item_actions.ts view source

ActionFactoryDeps import type {CellItemActionDeps} from '@fuzdev/fuz_app/auth/cell_item_actions.js';

log

Structured logger instance.

type Logger

audit

Bound audit emitter for fire-and-forget audit writes.

type AuditEmitter

CellItemDeleteAuditMetadata
#

auth/cell_item_audit_metadata.ts view source

ZodObject<{ parent_id: $ZodBranded<ZodUUID, "Uuid", "out">; position: ZodString; child_id: $ZodBranded<ZodUUID, "Uuid", "out">; }, $loose> import type {CellItemDeleteAuditMetadata} from '@fuzdev/fuz_app/auth/cell_item_audit_metadata.js';

Metadata envelope for cell_item_delete.

CellItemDeleteInput
#

auth/cell_item_action_specs.ts view source

ZodObject<{ parent_id: $ZodBranded<ZodUUID, "Uuid", "out">; position: $ZodBranded<ZodString, "CellItemPosition", "out">; acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict> import type {CellItemDeleteInput} from '@fuzdev/fuz_app/auth/cell_item_action_specs.js';

Input for cell_item_delete. Idempotent on the slot key.

CellItemDeleteOutput
#

CellItemInsertAuditMetadata
#

auth/cell_item_audit_metadata.ts view source

ZodObject<{ parent_id: $ZodBranded<ZodUUID, "Uuid", "out">; position: ZodString; child_id: $ZodBranded<ZodUUID, "Uuid", "out">; }, $loose> import type {CellItemInsertAuditMetadata} from '@fuzdev/fuz_app/auth/cell_item_audit_metadata.js';

Metadata envelope for cell_item_insert.

CellItemInsertInput
#

auth/cell_item_action_specs.ts view source

ZodObject<{ parent_id: $ZodBranded<ZodUUID, "Uuid", "out">; child_id: $ZodBranded<ZodUUID, "Uuid", "out">; position: $ZodBranded<ZodString, "CellItemPosition", "out">; acting: ZodOptional<...>; }, $strict> import type {CellItemInsertInput} from '@fuzdev/fuz_app/auth/cell_item_action_specs.js';

Input for cell_item_insert. Caller computes position via fractional_index_between(prev, next) (@fuzdev/fuz_util/fractional_index.ts) client-side. Returns cell_item_position_taken on `(parent_id, position)` unique violation; client refreshes bracket and retries.

CellItemInsertOutput
#

auth/cell_item_action_specs.ts view source

ZodObject<{ item: ZodObject<{ parent_id: $ZodBranded<ZodUUID, "Uuid", "out">; position: $ZodBranded<ZodString, "CellItemPosition", "out">; child_id: $ZodBranded<...>; created_at: ZodString; }, $strict>; }, $strict> import type {CellItemInsertOutput} from '@fuzdev/fuz_app/auth/cell_item_action_specs.js';

CellItemInsertQueryInput
#

CellItemListInput
#

auth/cell_item_action_specs.ts view source

ZodObject<{ parent_id: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; child_id: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; position_after: ZodOptional<...>; limit: ZodOptional<...>; acting: ZodOptional<...>; }, $strict> import type {CellItemListInput} from '@fuzdev/fuz_app/auth/cell_item_action_specs.js';

Input for cell_item_list. Pass parent_id for forward items or child_id for reverse lists — exactly one. Reverse listing has 2-layer authz (child view-check gates the call; per-parent view-check filters the rows).

Forward listing supports cursor pagination via position_after (return rows with position > position_after). The reverse listing doesn't paginate (the result set is small in practice — number of parents containing a given child).

CellItemListOutput
#

auth/cell_item_action_specs.ts view source

ZodObject<{ items: ZodArray<ZodObject<{ parent_id: $ZodBranded<ZodUUID, "Uuid", "out">; position: $ZodBranded<ZodString, "CellItemPosition", "out">; child_id: $ZodBranded<...>; created_at: ZodString; }, $strict>>; }, $strict> import type {CellItemListOutput} from '@fuzdev/fuz_app/auth/cell_item_action_specs.js';

CellItemMoveAuditMetadata
#

auth/cell_item_audit_metadata.ts view source

ZodObject<{ parent_id: $ZodBranded<ZodUUID, "Uuid", "out">; position_old: ZodString; position_new: ZodString; }, $loose> import type {CellItemMoveAuditMetadata} from '@fuzdev/fuz_app/auth/cell_item_audit_metadata.js';

Metadata envelope for cell_item_move. Carries both old and new position so the audit trail shows the reorder without a join back to the live row.

CellItemMoveInput
#

auth/cell_item_action_specs.ts view source

ZodObject<{ parent_id: $ZodBranded<ZodUUID, "Uuid", "out">; position: $ZodBranded<ZodString, "CellItemPosition", "out">; new_position: $ZodBranded<...>; acting: ZodOptional<...>; }, $strict> import type {CellItemMoveInput} from '@fuzdev/fuz_app/auth/cell_item_action_specs.js';

Input for cell_item_move. Move within the same parent (cross-parent moves are a future extension).

CellItemMoveOutput
#

auth/cell_item_action_specs.ts view source

ZodObject<{ item: ZodObject<{ parent_id: $ZodBranded<ZodUUID, "Uuid", "out">; position: $ZodBranded<ZodString, "CellItemPosition", "out">; child_id: $ZodBranded<...>; created_at: ZodString; }, $strict>; }, $strict> import type {CellItemMoveOutput} from '@fuzdev/fuz_app/auth/cell_item_action_specs.js';

CellItemPosition
#

auth/cell_item_action_specs.ts view source

$ZodBranded<ZodString, "CellItemPosition", "out"> import type {CellItemPosition} from '@fuzdev/fuz_app/auth/cell_item_action_specs.js';

Position grammar — base62 fractional-indexing key. Wire enforces non-empty, alphabet only, and the helper's FRACTIONAL_INDEX_LENGTH_MAX cap (well above realistic lengths even for hundreds of consecutive front-inserts; set high to avoid arbitrary cliffs). Lex ordering is the contract; the no-trailing-'0' invariant lives in the helper, not the wire.

CellItemRow
#

db/cell_item_queries.ts view source

CellItemRow import type {CellItemRow} from '@fuzdev/fuz_app/db/cell_item_queries.js';

Row shape returned by cell_item SELECTs.

parent_id

type Uuid

position

type string

child_id

type Uuid

created_at

type Date

CellJson
#

auth/cell_action_specs.ts view source

ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; path: ZodNullable<$ZodBranded<ZodString, "CellPath", "out">>; data: ZodObject<...>; ... 11 more ...; grant_count: ZodNumber; }, $strict> import type {CellJson} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Wire form for a cell row. data is the typed-but-permissive CellData shape (kind / label / summary typed-and-optional, additional fields pass through). Per-kind shape validation is sub-API and handled by the app's validate_data deps callback (see auth/cell_actions.ts).

visibility is the access-control axis — a top-level column on the row, not a field inside data. cell_grant and visibility are the two ACL surfaces; both live as peers, not embedded in content.

path is the global namespace axis (no tenant/hub scoping).

Relations (items, fields) are NOT carried on the cell row — they live in the cell_item / cell_field sibling tables. Bundled arrays appear on CellGetOutput; other read verbs (cell_list) do not bundle.

CellListInput
#

auth/cell_action_specs.ts view source

ZodDefault<ZodObject<{ ids: ZodOptional<ZodArray<$ZodBranded<ZodUUID, "Uuid", "out">>>; kind: ZodOptional<ZodString>; ... 11 more ...; acting: ZodOptional<...>; }, $strict>> import type {CellListInput} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Input for cell_list. Filters are optional and combine with AND. The handler applies the SQL-side visibility predicate from query_cell_list so the page-window stays correct under pagination — post-filtering in JS would silently truncate pages.

ids is the batch-read filter — pass a list of cell ids to fetch them in one round-trip (avoids N+1 when rendering a collection's items[]). The visibility predicate still runs, so callers passing ids they can't view simply get fewer rows back. Capped at CELL_LIST_LIMIT_MAX.

shared_with: 'me' narrows to cells that admit the caller via a cell_grant row (actor-shaped or role-shaped principal) AND that the caller does not own. Authenticated only; combine with kind / path_prefix etc. to scope further. Combining with created_by: <my-actor-id> produces an empty result by definition (owner is implicit, never appears as a grant principal); we don't reject the combination at the schema layer because SQL emptiness is correct.

CellListOptions
#

db/cell_queries.ts view source

CellListOptions import type {CellListOptions} from '@fuzdev/fuz_app/db/cell_queries.js';

Common pagination + tombstone-visibility options for list queries.

limit?

type number

offset?

type number

include_deleted?

type boolean

CellListOutput
#

auth/cell_action_specs.ts view source

ZodObject<{ cells: ZodArray<ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; path: ZodNullable<$ZodBranded<ZodString, "CellPath", "out">>; ... 12 more ...; grant_count: ZodNumber; }, $strict>>; cell_grants: ZodOptional<...>; }, $strict> import type {CellListOutput} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

CellListParams
#

db/cell_queries.ts view source

CellListParams import type {CellListParams} from '@fuzdev/fuz_app/db/cell_queries.js';

Parameters for query_cell_list. All filter dimensions are optional.

kind?

Match cell.kind = ? (uses idx_cell_kind).

type string

visibility?

Match cell.visibility = ? directly on the top-level column. Additional narrowing on top of the SQL-side auth visibility predicate — useful for the public discovery feed where authed callers must NOT see their own private entries mixed in.

type CellVisibility

ref?

Match cells whose refs[] contains this hash (uses idx_cell_refs).

type FactHash

created_by?

Filter to cells created by this actor (uses idx_cell_created_by).

type Uuid

path_prefix?

Filter to cells whose path starts with this prefix. Wildcard metachars in the prefix are NOT special — starts_with() does literal matching.

type string

root_id?

Scope to a directory subtree by governing root (cell.root_id = ?, uses idx_cell_root).

type Uuid

moderation?

Filter by moderation lifecycle marker ('pending' drives the mod queue via idx_cell_moderation_pending).

Note: the visibility predicate still applies, so a 'pending' (private) queue surfaces only to viewers it admits — admin / owner / grant. A non-admin container manager won't see pending children here until a manager-scoped visibility branch is added, so for now the queue is an admin surface (cell_moderate of a known id still works regardless).

type string

ids?

Batch-fetch by id. The visibility predicate still runs, so callers passing ids they can't view simply get fewer rows back. Order of the returned rows follows order_by / order_direction, not the input list — callers that need positional output (e.g. preserving a collection's items[] order) should re-index client-side.

type Array<Uuid>

viewer_actor_id

Viewer actor for the visibility predicate. Pass null for unauthenticated callers — only cell.visibility === 'public' rows are admitted then.

type Uuid | null

viewer_is_admin

When true, the visibility predicate is dropped (admin sees all). When false, rows pass when public, owned by the viewer, or admitted by a cell_grant row.

type boolean

caller_actor_id?

Caller's actor_id for the actor-shaped grant branch. NULL = anonymous (actor-grants can never admit). Kept distinct from viewer_actor_id for the predicate's clarity (the visibility branch and the grant branch are independent concerns even when they currently agree).

type Uuid | null

caller_role_grant_roles?

Caller's role_grant roles, parallel-array projection of auth.role_grants (active-only — middleware filters). Pair-wise aligned with caller_role_grant_scope_ids. Empty array (or omitted) admits no role-shaped grants. The two arrays MUST have equal length — unnest(text[], uuid[]) null-pads on length mismatch and would silently widen role-grant admits.

type ReadonlyArray<string>

caller_role_grant_scope_ids?

Caller's role_grant scope ids, parallel-array projection. NULLs in the array mark global (any-scope) role_grants — IS NOT DISTINCT FROM handles them per design.

type ReadonlyArray<Uuid | null>

shared_with_caller_only?

When true, narrow to cells admitting the caller via a cell_grant row AND that the caller does not own. Authenticated only (viewer_actor_id must be set). Combine with kind / path_prefix etc. to scope further.

type boolean

order_by?

Sort column. Default created_at.

type 'created_at' | 'updated_at'

order_direction?

Sort direction. Default desc.

type 'asc' | 'desc'

limit?

Page size.

type number

offset?

Page offset.

type number

include_deleted?

Include soft-deleted rows. Default false.

type boolean

CellModerateAuditMetadata
#

auth/cell_audit_metadata.ts view source

ZodObject<{ cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; root_id: $ZodBranded<ZodUUID, "Uuid", "out">; moderation: ZodEnum<{ approved: "approved"; rejected: "rejected"; }>; }, $loose> import type {CellModerateAuditMetadata} from '@fuzdev/fuz_app/auth/cell_audit_metadata.js';

Metadata envelope for cell_moderate. cell_id is the moderated contribution; root_id is its governing root (the container whose moderation authority gated the call); moderation is the terminal decision applied ('approved' | 'rejected').

CellModerateInput
#

auth/cell_action_specs.ts view source

ZodObject<{ cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; moderation: ZodEnum<{ approved: "approved"; rejected: "rejected"; }>; acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict> import type {CellModerateInput} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Input for cell_moderate — the pending → approved | rejected transition, gated on moderation authority over the governing root (admin / root owner in v1), not the contribution (which the author manages, and could otherwise self-approve). 404 when the target isn't viewable; 403 when it is but the caller isn't a root manager (the author lands here).

CellModerateOutput
#

auth/cell_action_specs.ts view source

ZodObject<{ cell: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; path: ZodNullable<$ZodBranded<ZodString, "CellPath", "out">>; ... 12 more ...; grant_count: ZodNumber; }, $strict>; }, $strict> import type {CellModerateOutput} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

CellModerationDecision
#

auth/cell_action_specs.ts view source

ZodEnum<{ approved: "approved"; rejected: "rejected"; }> import type {CellModerationDecision} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

The terminal transition a cell_moderate call applies to a gated contribution's moderation lifecycle. The born state ('pending') is set by the create authorizer, never on the wire; the verb only moves it to a terminal state. 'approved' publishes (also flips visibility → 'public'); 'rejected' leaves it private.

CellPath
#

auth/cell_action_specs.ts view source

$ZodBranded<ZodString, "CellPath", "out"> import type {CellPath} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Branded so the type system distinguishes a validated path from any other string. Construct via CellPath.parse(s) at external boundaries; the RPC dispatcher does this automatically when the wire schema (CellCreateInput, CellGetInput, CellUpdateInput, CellListInput) is parsed at the entry point. Frontend callers handing a raw string to api.cell_* cast at the callsite (as CellPath) — the runtime check still runs server-side.

CellRow
#

db/cell_queries.ts view source

CellRow import type {CellRow} from '@fuzdev/fuz_app/db/cell_queries.js';

Row shape returned by cell SELECTs. data is typed as CellData — the storage layer trusts the wire validation; the row is what was written, and the wire validates CellData on every write.

Parent↔child membership and named relations live in the cell_item / cell_field sibling tables (see db/cell_item_queries.ts / db/cell_field_queries.ts). The cell row carries identity + content only.

grant_count is a derived projection (correlated subquery against cell_grant keyed by cell_id, served by idx_cell_grant_cell) — not a table column. New cells naturally land at 0.

id

type Uuid

data

type CellData

kind

type string | null

visibility

type CellVisibility

path

type string | null

refs

type Array<FactHash> | null

parent_id

type Uuid | null

root_id

type Uuid | null

moderation

type string | null

created_at

type Date

updated_at

type Date | null

deleted_at

type Date | null

created_by

type Uuid | null

updated_by

type Uuid | null

grant_count

type number

CellUpdateInput
#

auth/cell_action_specs.ts view source

ZodObject<{ cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; data: ZodOptional<ZodObject<{ label: ZodOptional<ZodString>; summary: ZodOptional<ZodString>; }, $loose>>; visibility: ZodOptional<...>; path: ZodOptional<...>; acting: ZodOptional<...>; }, $strict> import type {CellUpdateInput} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Input for cell_update. Fields left undefined keep their existing value. path writes are admin-only (handler-enforced); non-admin callers supplying path get ERROR_CELL_PATH_ADMIN_ONLY even if no other field is changing. visibility writes require the manage tier (can_manage_cell = admin / owner) — editor-grant holders editing data cannot flip visibility (ERROR_CELL_VISIBILITY_MANAGE_ONLY).

CellUpdateOutput
#

auth/cell_action_specs.ts view source

ZodObject<{ cell: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; path: ZodNullable<$ZodBranded<ZodString, "CellPath", "out">>; ... 12 more ...; grant_count: ZodNumber; }, $strict>; }, $strict> import type {CellUpdateOutput} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

CellUpdatePatch
#

db/cell_queries.ts view source

CellUpdatePatch import type {CellUpdatePatch} from '@fuzdev/fuz_app/db/cell_queries.js';

Patch for query_cell_update. Fields left undefined are unchanged; path may be explicitly set to null to clear. refs is re-derived from data whenever data is updated.

data?

type Json

visibility?

type CellVisibility

path?

type string | null

updated_by?

type Uuid | null

CellVisibility
#

auth/cell_action_specs.ts view source

ZodEnum<{ private: "private"; public: "public"; }> import type {CellVisibility} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Cell visibility — the coarse-grained access-control axis for a cell. Sibling to cell_grant (the fine-grained allowlist of actor- / role-shaped principals at viewer / editor levels). Together they form the cell-layer access-control surface:

  • cell.visibility = 'public' admits everyone, including unauthenticated visitors. cell_grant rows still apply for edit- level admit; read is universal.
  • cell.visibility = 'private' (default) restricts read to admin / owner (created_by) / cell_grant-admitted callers.

Stored as a top-level PG enum column (cell.visibility) — NOT inside cell.data, which is content metadata only. can_view_cell reads the column directly.

check_bootstrap_status
#

auth/bootstrap_routes.ts view source

(deps: CheckBootstrapStatusDeps, options: { token_path: string | null; }): Promise<BootstrapStatus> import {check_bootstrap_status} from '@fuzdev/fuz_app/auth/bootstrap_routes.js';

Check bootstrap availability at startup.

Bootstrap is available when:

  1. A token path is configured
  2. The token file exists on disk
  3. The bootstrap_lock table shows bootstrapped = false

deps

filesystem and database access for the check

options

static configuration including token_path

type { token_path: string | null; }

returns

Promise<BootstrapStatus>

an object with available (boolean) and token_path (string | null)

check_daemon_health
#

cli/daemon.ts view source

(deps: FetchDeps, port: number, host?: string, timeout_ms?: number): Promise<boolean> import {check_daemon_health} from '@fuzdev/fuz_app/cli/daemon.js';

Check if a daemon is healthy by probing its /health endpoint.

Complements is_daemon_running (PID check) with an HTTP liveness probe. Requires the daemon to register a /health route (e.g. via create_health_route_spec).

deps

runtime with fetch capability

port

port the daemon should be listening on

type number

host

hostname (default localhost)

type string
default 'localhost'

timeout_ms

request timeout in milliseconds (default 2000)

type number
default 2000

returns

Promise<boolean>

true if the health endpoint responds with 2xx

check_error_response_fields
#

testing/integration_helpers.ts view source

(body: Record<string, unknown>): string[] import {check_error_response_fields} from '@fuzdev/fuz_app/testing/integration_helpers.js';

List the fields in an error response body that are not in the known-safe set.

Error schemas use z.looseObject (intentional — multiple producers), but test responses should be checked for fields that could leak information.

body

type Record<string, unknown>

returns

string[]

array of unexpected field names (empty = clean)

check_schema_drift
#

db/schema_ready.ts view source

(db: Db, expected: ExpectedSchema): Promise<SchemaDriftResult> import {check_schema_drift} from '@fuzdev/fuz_app/db/schema_ready.js';

Compare the live DB's columns against expected. Reports tables and columns the running code expects that the live DB lacks — the drift that breaks queries. Extra live tables / columns are ignored: forward-compatible, and a newer-than-fixture DB shouldn't fail readiness.

db

live database to introspect

type Db

expected

the committed column map a fresh bootstrap produces

returns

Promise<SchemaDriftResult>

CheckBootstrapStatusDeps
#

auth/bootstrap_routes.ts view source

CheckBootstrapStatusDeps import type {CheckBootstrapStatusDeps} from '@fuzdev/fuz_app/auth/bootstrap_routes.js';

Dependencies for checking bootstrap status at startup.

stat

type (path: string) => Promise<StatResult | null>

db

type Db

log

type Logger

cleanup_expired_role_grant_offers
#

auth/cleanup.ts view source

(deps: AuthCleanupDeps): Promise<number> import {cleanup_expired_role_grant_offers} from '@fuzdev/fuz_app/auth/cleanup.js';

Sweep expired role_grant offers and emit one role_grant_offer_expire audit event per row.

Returns the count of offers audit-stamped. The offer rows themselves are preserved — offers carry audit value for the history view even after expiry, and accepted rows are the provenance for the resulting role_grant (deleting expired rows would not threaten that, but keeping them uniform with the retention policy for terminal rows is simpler).

deps

returns

Promise<number>

clear_session_cookie
#

ClientApiTokenJson
#

auth/account_schema.ts view source

ZodObject<{ id: ZodString; account_id: $ZodBranded<ZodUUID, "Uuid", "out">; name: ZodString; expires_at: ZodNullable<ZodString>; last_used_at: ZodNullable<...>; last_used_ip: ZodNullable<...>; created_at: ZodString; }, $strict> import type {ClientApiTokenJson} from '@fuzdev/fuz_app/auth/account_schema.js';

Zod schema for client-safe API token listing (excludes token_hash).

CliLogger
#

cli/logger.ts view source

CliLogger import type {CliLogger} from '@fuzdev/fuz_app/cli/logger.js';

error

Logs an error via Logger (gets Logger's error prefix).

type (...args: Array<unknown>) => void

warn

Logs a warning via Logger (gets Logger's warn prefix).

type (...args: Array<unknown>) => void

info

Logs info via Logger (gets Logger's label prefix).

type (...args: Array<unknown>) => void

debug

Logs debug via Logger (gets Logger's debug prefix).

type (...args: Array<unknown>) => void

raw

Logs raw output via Logger (no prefix, no level filtering).

type (...args: Array<unknown>) => void

success

Logs a success message with [done] prefix at info level.

type (msg: string) => void

skip

Logs a skip message with [skip] prefix at info level.

type (msg: string) => void

step

Logs a step message with ==> prefix at info level.

type (msg: string) => void

header

Logs a header with === title === decoration at info level.

type (title: string) => void

dim

Logs a dimmed message at info level.

type (msg: string) => void

logger

The underlying Logger instance.

type Logger

collect_json_keys_recursive
#

testing/integration_helpers.ts view source

(value: unknown): Set<string> import {collect_json_keys_recursive} from '@fuzdev/fuz_app/testing/integration_helpers.js';

Recursively collect all key names from a parsed JSON value.

Walks objects and arrays to find every property name at any nesting depth.

value

type unknown

returns

Set<string>

collect_json_schema_property_names
#

testing/data_exposure.ts view source

(schema: unknown): Set<string> import {collect_json_schema_property_names} from '@fuzdev/fuz_app/testing/data_exposure.js';

Recursively collect all property names from a JSON Schema.

Walks properties, items, allOf/anyOf/oneOf, and additionalProperties to find every declared field name at any depth.

schema

type unknown

returns

Set<string>

collect_middleware_errors
#

http/surface.ts view source

(middleware: MiddlewareSpec[], route_path: string): Partial<Record<number, ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>>> | null import {collect_middleware_errors} from '@fuzdev/fuz_app/http/surface.js';

Collect error schemas from all middleware that applies to a route path.

middleware

type MiddlewareSpec[]

route_path

type string

returns

Partial<Record<number, ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>>> | null

merged middleware error schemas, or null if none

colors
#

cli/util.ts view source

{ readonly green: "" | "\u001B[32m"; readonly yellow: "" | "\u001B[33m"; readonly blue: "" | "\u001B[34m"; readonly red: "" | "\u001B[31m"; readonly cyan: "" | "\u001B[36m"; readonly dim: "" | "\u001B[2m"; readonly bold: "" | "\u001B[1m"; readonly reset: "" | "\u001B[0m"; } import {colors} from '@fuzdev/fuz_app/cli/util.js';

ColumnInfo
#

http/db_routes.ts view source

ColumnInfo import type {ColumnInfo} from '@fuzdev/fuz_app/http/db_routes.js';

Column metadata from information_schema.

column_name

type string

data_type

type string

is_nullable

type string

ColumnLayout
#

ui/ColumnLayout.svelte view source

accepts children

import ColumnLayout from '@fuzdev/fuz_app/ui/ColumnLayout.svelte';

children

type Snippet<[]>

aside

type Snippet<[]>

column_width?

CSS width of the fixed aside column.

type string
optional default '280px'

intersects

SvelteHTMLElements['div']

ColumnSnapshot
#

testing/schema_introspect.ts view source

ZodObject<{ data_type: ZodString; udt_name: ZodString; is_nullable: ZodBoolean; column_default: ZodNullable<ZodString>; is_identity: ZodBoolean; }, $strip> import type {ColumnSnapshot} from '@fuzdev/fuz_app/testing/schema_introspect.js';

Per-column structural metadata. The Zod schema is the canonical source for the column shape — SchemaSnapshot reuses it as the cross-impl _testing_schema_snapshot RPC action's wire validator, so the introspection type and the wire contract can't drift apart.

CommandDeps
#

runtime/deps.ts view source

CommandDeps import type {CommandDeps} from '@fuzdev/fuz_app/runtime/deps.js';

Command execution.

run_command

Run a command and return the result. Never throws — failures surface as success: false.

options.cwd sets the child's working directory. options.signal aborts the child when the signal fires. options.timeout_ms kills the child after the given duration and returns timed_out: true on the result.

type ( cmd: string, args: Array<string>, options?: RunCommandOptions ) => Promise<CommandResult>

CommandMeta
#

cli/help.ts view source

CommandMeta<TCategory> import type {CommandMeta} from '@fuzdev/fuz_app/cli/help.js';

Command metadata for help generation.

generics

CommandMeta<TCategory extends string = string>
TCategory
constraint string
default string

schema?

type z.ZodType

summary

type string

usage

type string

category

type TCategory

CommandResult
#

runtime/deps.ts view source

CommandResult import type {CommandResult} from '@fuzdev/fuz_app/runtime/deps.js';

Result of executing a command.

timed_out is present only when timeout_ms was passed in RunCommandOptions and the process was killed after exceeding the timeout. Callers that pass timeout_ms should check this flag to distinguish timeout from exit-code failure.

success

type boolean

code

type number

stdout

type string

stderr

type string

timed_out?

type boolean

compare_cross_impl
#

testing/cross_backend/bench/bench_report.ts view source

(result: CrossImplBenchResult, options?: CompareCrossImplOptions | undefined): CrossImplComparisonEntry[] import {compare_cross_impl} from '@fuzdev/fuz_app/testing/cross_backend/bench/bench_report.js';

Welch-test verdict for every non-reference backend vs the reference, per scenario. With deno + node + rust and reference: 'deno' you get node vs deno and rust vs deno for each scenario.

result

options?

type CompareCrossImplOptions | undefined
optional

returns

CrossImplComparisonEntry[]

CompareCrossImplOptions
#

testing/cross_backend/bench/bench_report.ts view source

CompareCrossImplOptions import type {CompareCrossImplOptions} from '@fuzdev/fuz_app/testing/cross_backend/bench/bench_report.js';

reference?

Backend to compare every other backend against. Defaults to result.backends[0].

type string

readonly

compile_action_registry
#

actions/compile_action_registry.ts view source

(actions: readonly Action<{ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<...>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }>[], ctx_label: string): ActionRegistryCompileResult import {compile_action_registry} from '@fuzdev/fuz_app/actions/compile_action_registry.js';

Validate registry-time invariants and build the dispatcher's method → action lookup.

actions

polymorphic action array; HTTP RPC passes RpcAction[] (narrower), WebSocket passes Action[] (kind-polymorphic — handler-less notification specs are accepted)

type readonly Action<{ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ....

ctx_label

per-spec error-message prefix, e.g. 'RPC action' or 'WS action'. Combined with the spec method as ${ctx_label} "${method}".

type string

returns

ActionRegistryCompileResult

throws

  • Error - on biconditional violation, rate-limit/account-axis mismatch, JSON-RPC null-input, or duplicate method.

compose_gen_file
#

actions/action_codegen.ts view source

(input: { origin_path: string; imports: ImportBuilder; blocks: readonly string[]; }): string import {compose_gen_file} from '@fuzdev/fuz_app/actions/action_codegen.js';

Wrap the per-*.gen.ts boilerplate (banner + imports.build() + blocks join + template literal) into one call. Returns the full file body as a string ready to return from a Gen function.

Each consumer producer collapses to one compose_gen_file call wrapping the helper invocations.

input

type { origin_path: string; imports: ImportBuilder; blocks: readonly string[]; }

returns

string

examples

export const gen: Gen = ({origin_path}) => { const imports = new ImportBuilder(); return compose_gen_file({ origin_path, imports, blocks: [ generate_action_specs_record(all_action_specs, imports), generate_action_inputs_outputs(all_action_specs, imports), generate_action_event_datas(all_action_specs, imports), ], }); };

Empty blocks ('') are filtered out so helpers that short-circuit on empty spec sets don't introduce stray double blank lines.

confirm
#

cli/util.ts view source

(runtime: TerminalDeps, message: string): Promise<boolean> import {confirm} from '@fuzdev/fuz_app/cli/util.js';

Prompt for yes/no confirmation.

runtime

runtime with stdout_write and stdin_read capabilities

message

message to display

type string

returns

Promise<boolean>

true if user confirms, false otherwise

ConfirmButton
#

ui/ConfirmButton.svelte view source

accepts children

import ConfirmButton from '@fuzdev/fuz_app/ui/ConfirmButton.svelte';

onconfirm

type (popover: Popover) => void

popover_button_attrs?

type HTMLButtonAttributes
optional

hide_on_confirm?

type boolean
optional default true

popover_content?

Unlike on PopoverButton this is optional and has a confirm arg

type Snippet<[popover: Popover, confirm: () => void]>
optional
snippet parameters
popover Popover
confirm () => void

popover_button_content?

Content for the popover button

type Snippet<[popover: Popover, confirm: () => void]>
optional
snippet parameters
popover Popover
confirm () => void

children?

Unlike on PopoverButton this has a confirm arg

type Snippet<[popover: Popover, confirm: () => void]>
optional
snippet parameters
popover Popover
confirm () => void

label?

Simple string content for the trigger. Mutually exclusive with children.

type string
optional

pending?

When true, the trigger is disabled and a spinner overlays the content (mirrors PendingButton). The label / children stay rendered underneath so the button keeps its size.

type boolean
optional default false

intersects

OmitStrict<SvelteHTMLElements['button'], 'children'>

ConformanceCase
#

testing/cross_backend/conformance_case.ts view source

ZodObject<{ name: ZodString; request: ZodObject<{ method: ZodString; params: ZodOptional<ZodUnknown>; as: ZodEnum<{ token: "token"; keeper: "keeper"; daemon: "daemon"; ... 7 more ...; expired_session: "expired_session"; }>; verb: ZodOptional<...>; }, $strict>; expect: ZodObject<...>; note: ZodOptional<...>; xfail: Z... import type {ConformanceCase} from '@fuzdev/fuz_app/testing/cross_backend/conformance_case.js';

A single conformance case. name is the assertion; the optional free-text note is printed in the test label / failure output. A security case's note should reference a public fuz_app doc property (security.md / architecture.md / module TSDoc), since the table ships in a public package — not an internal planning doc. The note is documentation, not a gate: it stays free-text by design because a non-empty-string check never catches a *wrong* citation — the citation is verified in review.

ConformanceCaseExpectation
#

testing/cross_backend/conformance_case.ts view source

ZodObject<{ status: ZodNumber; error_reason: ZodOptional<ZodString>; fields: ZodOptional<ZodRecord<ZodString, ZodUnknown>>; absent_fields: ZodOptional<...>; headers: ZodOptional<...>; equivalence_group: ZodOptional<...>; }, $strict> import type {ConformanceCaseExpectation} from '@fuzdev/fuz_app/testing/cross_backend/conformance_case.js';

The expected response shape a conformance case asserts.

ConformanceCaseRequest
#

testing/cross_backend/conformance_case.ts view source

ZodObject<{ method: ZodString; params: ZodOptional<ZodUnknown>; as: ZodEnum<{ token: "token"; keeper: "keeper"; daemon: "daemon"; invalid_daemon: "invalid_daemon"; ... 6 more ...; expired_session: "expired_session"; }>; verb: ZodOptional<...>; }, $strict> import type {ConformanceCaseRequest} from '@fuzdev/fuz_app/testing/cross_backend/conformance_case.js';

The request a conformance case issues.

ConformanceCaseXfail
#

testing/cross_backend/conformance_case.ts view source

ZodObject<{ tracking_id: ZodString; reason: ZodString; }, $strict> import type {ConformanceCaseXfail} from '@fuzdev/fuz_app/testing/cross_backend/conformance_case.js';

Marks a case as a deferred-by-design gap. The runner routes it through xfail_until instead of a normal test — visible (distinct from pass) and self-cleaning (flips red when the impl starts passing, forcing the marker's removal). Use for declared gaps (e.g. facts), never for in-scope gaps (those fail loud as a red test).

ConformancePrincipal
#

testing/cross_backend/conformance_case.ts view source

ZodEnum<{ token: "token"; keeper: "keeper"; daemon: "daemon"; invalid_daemon: "invalid_daemon"; daemon_browser: "daemon_browser"; bearer_browser: "bearer_browser"; anonymous: "anonymous"; fresh_non_admin: "fresh_non_admin"; role_holder: "role_holder"; wrong_role: "wrong_role"; expired_session: "expired_session"; }> import type {ConformancePrincipal} from '@fuzdev/fuz_app/testing/cross_backend/conformance_case.js';

Closed enum of fixture-provisioned principals a case runs as. Each value maps to a TestFixture accessor (or a seeded extra_accounts entry) in the runner's resolve_principal — there is no inline credential minting in a case (that would be the setup-DSL trap).

  • keeper — the per-test bootstrapped keeper (holds ROLE_KEEPER + ROLE_ADMIN), session credential.
  • daemon — the keeper authenticated via the daemon-token header.
  • invalid_daemon — a *malformed/invalid* X-Daemon-Token carried alongside the keeper's session cookie, over a non-browser (no-Origin) transport. The middleware soft-fail-discards the invalid daemon token (matching the Rust spine's None), so auth falls through to the session leg: the request authenticates as the keeper-via-session and a daemon-gated action then refuses the session credential with credential_type_required — not a hard invalid_daemon_token 401. The no-Origin transport keeps the daemon token on the invalid-token path rather than the browser-context discard; the session base credential is what makes the credential-type gate (not the auth gate) the refusing layer (without it the discard would 401 anonymous).
  • daemon_browser — a *valid* X-Daemon-Token carried in a browser context (default Origin present) alongside the keeper's session cookie. Browsers attach Origin automatically; the daemon-token middleware discards a header-bearing daemon token as browser context (mirroring the bearer guard and the Rust spine's is_browser_context), so the *valid* token is dropped and auth falls through to the session leg → a daemon-gated action then refuses the session credential with credential_type_required. Distinct from invalid_daemon: here the token is well-formed and current, so a 403 (not a 400 confirm-guard hit like daemon) proves the browser-context discard fired — a valid daemon token does NOT authenticate when an Origin is present. Origin is deliberately NOT suppressed; its presence is the signal under test.
  • token — the keeper authenticated via a bearer api-token (non-browser context; the runner suppresses Origin so the token isn't discarded).
  • bearer_browser — a *valid* bearer api-token carried in a browser context (default Origin present), fresh jar so NO session rides alongside. The bearer middleware discards the token as browser context (mirroring the daemon guard + the Rust spine's is_browser_context), so the request arrives anonymous and an authed action 401s. Proves a stolen bearer cannot be replayed from a browser — wire-indistinguishable from sending no credential (the token principal is the honored counterpart: it suppresses Origin, so the same token authenticates).
  • anonymous — no credential, fresh cookie jar.
  • fresh_non_admin — a freshly minted account with no roles, session credential (via the production invite → signup → login flow).
  • role_holder — a seeded extra_accounts principal holding a specific role; the runner reads it by the username named in ConformanceTableOptions.principals.role_holder.
  • wrong_role — a seeded extra_accounts principal holding a role other than the one a route requires; named via ConformanceTableOptions.principals.wrong_role.
  • expired_session — the keeper account presented via an *expired server-side session* cookie (minted by fixture.mint_expired_session(): a backdated auth_session row behind a still-valid signed cookie payload, so the authoritative DB-row expiry gate is what refuses it).

ConformancePrincipalConfig
#

testing/cross_backend/conformance_table.ts view source

ConformancePrincipalConfig import type {ConformancePrincipalConfig} from '@fuzdev/fuz_app/testing/cross_backend/conformance_table.js';

Names a seeded extra_accounts username for the role_holder / wrong_role principals — the only two that aren't backed by an always-available fixture accessor. Suites exercising those principals declare the matching extra_accounts at setup and name them here.

role_holder?

extra_accounts username for the role_holder principal.

type string

readonly

wrong_role?

extra_accounts username for the wrong_role principal.

type string

readonly

ConformanceTableOptions
#

testing/cross_backend/conformance_table.ts view source

ConformanceTableOptions import type {ConformanceTableOptions} from '@fuzdev/fuz_app/testing/cross_backend/conformance_table.js';

cases

The conformance cases to run, in order.

type ReadonlyArray<ConformanceCase>

readonly

setup_test

Per-test fixture producer (in-process or cross-process).

type SetupTest

readonly

surface_source

Surface spec — supplies the RouteSpecs for the REST branch.

type AppSurfaceSpec

readonly

capabilities

Declared backend capabilities (reserved for capability-gated rows).

type BackendCapabilities

readonly

rpc_endpoints

RPC endpoints — resolved to find each method's action spec.

type RpcEndpointsSuiteOption

readonly

session_options

Session options — needed to resolve the rpc_endpoints factory form.

type SessionOptions<string>

readonly

principals?

Maps the role_holder / wrong_role principals to seeded usernames.

type ConformancePrincipalConfig

readonly

suite_name?

describe block label. Defaults to 'conformance table'.

type string

readonly

ConnectionCloser
#

actions/connection_closer.ts view source

ConnectionCloser import type {ConnectionCloser} from '@fuzdev/fuz_app/actions/connection_closer.js';

Narrow capability — three idempotent socket-close methods, each returning the number of sockets actually closed (zero when none matched). Callers typically ignore the return value (used by telemetry / tests).

close_sockets_for_session

Close every connection authenticated with a session whose blake3 hash matches session_token_hash. Idempotent — calling on an already-closed session is a no-op.

type (session_token_hash: string) => number

close_sockets_for_token

Close every connection authenticated with the given API token id. Idempotent — calling on an already-revoked token is a no-op.

type (api_token_id: string) => number

close_sockets_for_account

Close every connection bound to account_id, regardless of credential type (session / api_token / daemon_token). Coarse closure used when every credential on an account is invalidated — password change, session-revoke-all, token-revoke-all, logout. Idempotent.

type (account_id: string) => number

ConnectionIdentity
#

actions/transports_ws_backend.ts view source

ConnectionIdentity import type {ConnectionIdentity} from '@fuzdev/fuz_app/actions/transports_ws_backend.js';

Auth identity attached to a single WebSocket connection.

One record per connection. token_hash is set for cookie-session connections, api_token_id for bearer (api_token) connections, and both are null for daemon-token connections (reachable only via BackendWebsocketTransport.close_sockets_for_account).

token_hash

Blake3 session token hash, or null for non-session credentials.

type string | null

account_id

Authenticated account id. Always set.

type Uuid

api_token_id

api_token.id for bearer-authenticated connections, else null.

type string | null

ContributionRule
#

CookieAttributesCrossTestOptions
#

testing/cross_backend/cookie_attributes.ts view source

CookieAttributesCrossTestOptions import type {CookieAttributesCrossTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/cookie_attributes.js';

Options for the session-cookie-attribute parity suite.

setup_test

Per-test fixture producer (cross-process only — see the module doc).

type SetupTest

readonly

cookie_name

The spine's session cookie name (handle.config.cookie_name, e.g. fuz_session). The Set-Cookie under test is matched by this name so a future cookie-name change surfaces here as "no session cookie set" rather than a silent miss.

type string

readonly

login_path?

REST login route path. Default /api/account/login.

type string

readonly

logout_path?

REST logout route path. Default /api/account/logout.

type string

readonly

CoverageFilterOptions
#

testing/error_coverage.ts view source

CoverageFilterOptions import type {CoverageFilterOptions} from '@fuzdev/fuz_app/testing/error_coverage.js';

Options controlling which routes/statuses are considered for coverage.

ignore_routes?

Routes to skip, in 'METHOD /path' format.

type Array<string>

ignore_statuses?

HTTP status codes to skip.

type Array<number>

create_account_actions
#

auth/account_actions.ts view source

(deps: ActionFactoryDeps, options?: AccountActionOptions): RpcAction[] import {create_account_actions} from '@fuzdev/fuz_app/auth/account_actions.js';

Create the self-service account RPC actions.

deps

ActionFactoryDeps (log, audit). audit.emit writes audit rows via the captured pool; the bound emitter encapsulates on_audit_event fan-out and the optional AuditLogConfig.

options

per-factory configuration

default {}

returns

RpcAction[]

the RpcAction array to spread into a create_rpc_endpoint call

create_account_route_shapes
#

auth/account_route_schema.ts view source

(options: AccountRouteShapeOptions): [Omit<RouteSpec, "handler">, Omit<RouteSpec, "handler">, Omit<RouteSpec, "handler">, Omit<...>] import {create_account_route_shapes} from '@fuzdev/fuz_app/auth/account_route_schema.js';

The four account route shapes (/verify, /login, /logout, /password) minus their handlers — pure hono-free data. create_account_route_specs spreads each and attaches the live handler; cross-process surface builders spread them with stub handlers. Single source of truth — the shapes can't drift between the live routes and the surface.

Returns a fixed 4-tuple [verify, login, logout, password] so destructuring yields non-optional shapes under noUncheckedIndexedAccess.

options

returns

[Omit<RouteSpec, "handler">, Omit<RouteSpec, "handler">, Omit<RouteSpec, "handler">, Omit<RouteSpec, "handler">]

create_account_route_specs
#

auth/account_routes.ts view source

(deps: RouteFactoryDeps, options: AccountRouteOptions): RouteSpec[] import {create_account_route_specs} from '@fuzdev/fuz_app/auth/account_routes.js';

Create account route specs for session-based auth.

The returned specs cover the REST flows that stay after the RPC migration: /status (account info + bootstrap availability), /verify (nginx auth_request shim), /login, /logout, /password. /status is bundled here (relative path, prefixed to /api/account/status by the caller) so every account surface serves it, matching the Rust account_router. Self-service session/token management is on auth/account_actions.ts.

deps

stateless capabilities (keyring, password, log)

options

per-factory configuration (session_options, ip_rate_limiter, login_account_rate_limiter, bootstrap_status)

returns

RouteSpec[]

route specs (not yet applied to Hono)

create_account_status_route_spec
#

auth/account_routes.ts view source

(options?: AccountStatusOptions | undefined): RouteSpec import {create_account_status_route_spec} from '@fuzdev/fuz_app/auth/account_routes.js';

Create the account status route spec.

Handles both authenticated and unauthenticated requests:

  • Authenticated: returns {account} with 200
  • Unauthenticated: returns 401 with optional bootstrap_available flag

This eliminates the need for a separate /health fetch on page load — the frontend gets both session state and bootstrap availability in one request.

options?

optional configuration (bootstrap_status for bootstrap detection)

type AccountStatusOptions | undefined
optional

returns

RouteSpec

a single account status route spec

create_action_event
#

actions/action_event.ts view source

<TMethod extends string = string>(environment: ActionEventEnvironment, spec: { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<...>>; ... 7 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }, input: unknown, initial_phase?: "send_request" | ... 8 more ... | undefined): ActionEvent<...> import {create_action_event} from '@fuzdev/fuz_app/actions/action_event.js';

Create an action event from a spec and initial input.

environment

spec

type { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }

input

type unknown

initial_phase?

type "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute" | undefined
optional

returns

ActionEvent<TMethod, "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute", "initial" | "parsed" | "handling" | "handled" | "failed">

generics

create_action_event<TMethod extends string = string>
TMethod
constraint string
default string

throws

  • Error - if `initial_phase` is omitted and the executor cannot

create_action_event_from_json
#

actions/action_event.ts view source

<TMethod extends string = string>(json: ActionEventDataUnion<TMethod>, environment: ActionEventEnvironment): ActionEvent<TMethod, "send_request" | ... 7 more ... | "execute", "initial" | ... 3 more ... | "failed"> import {create_action_event_from_json} from '@fuzdev/fuz_app/actions/action_event.js';

Reconstruct an action event from serialized JSON data.

json

type ActionEventDataUnion<TMethod>

environment

returns

ActionEvent<TMethod, "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute", "initial" | "parsed" | "handling" | "handled" | "failed">

generics

create_action_event_from_json<TMethod extends string = string>
TMethod
constraint string
default string

throws

  • Error - if the JSON's `method` field has no spec registered in `environment`

create_action_event_spec
#

actions/action_bridge.ts view source

(spec: { method: string; kind: "request_response" | "remote_notification" | "local_call"; initiator: "frontend" | "backend" | "both"; auth: { account: "none" | "optional" | "required"; actor: "none" | ... 1 more ... | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; } | null; ... 7 more ...; rate_limit?: "both" | ... 2 more ... | undefined; }, options?: ActionEventOptions | undefined): EventSpec import {create_action_event_spec} from '@fuzdev/fuz_app/actions/action_bridge.js';

Derive an EventSpec from an ActionSpec.

Only remote_notification actions can become push events.

spec

the action spec (must have kind: 'remote_notification')

type { method: string; kind: "request_response" | "remote_notification" | "local_call"; initiator: "frontend" | "backend" | "both"; auth: { account: "none" | "optional" | "required"; actor: "none" | ... 1 more ... | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; } | n...

options?

optional event-specific options (channel)

type ActionEventOptions | undefined
optional

returns

EventSpec

throws

  • Error - if `spec.kind` is not `'remote_notification'`

create_action_route_spec
#

actions/action_bridge.ts view source

(spec: { method: string; kind: "request_response" | "remote_notification" | "local_call"; initiator: "frontend" | "backend" | "both"; auth: { account: "none" | "optional" | "required"; actor: "none" | ... 1 more ... | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; } | null; ... 7 more ...; rate_limit?: "both" | ... 2 more ... | undefined; }, options: ActionRouteOptions): RouteSpec import {create_action_route_spec} from '@fuzdev/fuz_app/actions/action_bridge.js';

Derive a RouteSpec from an ActionSpec and options.

Only request_response actions (which require non-null auth) can become routes. remote_notification actions (auth null) should use create_action_event_spec. local_call actions are not for HTTP transport.

Error schemas are transport-specific (keyed by HTTP status codes) and belong on the options, not the action spec. Action specs define the contract; transport concerns like HTTP error codes are added at the bridge layer.

spec

the action spec (must have non-null auth)

type { method: string; kind: "request_response" | "remote_notification" | "local_call"; initiator: "frontend" | "backend" | "both"; auth: { account: "none" | "optional" | "required"; actor: "none" | ... 1 more ... | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; } | n...

options

HTTP-specific options (path, handler, optional overrides)

returns

RouteSpec

throws

  • Error - if `spec.auth` is null (only `request_response` actions can

create_actor_lookup_actions
#

create_actor_search_actions
#

create_admin_actions
#

auth/admin_actions.ts view source

(deps: ActionFactoryDeps, options?: AdminActionOptions): RpcAction[] import {create_admin_actions} from '@fuzdev/fuz_app/auth/admin_actions.js';

Create the admin-only RPC actions.

deps

ActionFactoryDeps (log, audit). log drives RPC- internal error logging; audit.emit writes audit rows via the captured pool. The bound emitter encapsulates listener fan-out and the optional AuditLogConfig.

options

role schema for grantable_roles derivation

default {}

returns

RpcAction[]

the RpcAction array to spread into a create_rpc_endpoint call

create_admin_rpc_adapters
#

ui/admin_rpc_adapters.ts view source

(api: AdminRpcApi): AdminRpcAdapters import {create_admin_rpc_adapters} from '@fuzdev/fuz_app/ui/admin_rpc_adapters.js';

Build the four admin RPC adapters from a typed throwing RPC client.

Method-name mapping:

Narrow RPC methodAction spec method
admin_accounts.list_accountsadmin_account_list
admin_accounts.delete_accountaccount_delete (soft)
admin_accounts.undelete_accountaccount_undelete
admin_accounts.list_sessionsadmin_session_list
admin_accounts.create_role_grantrole_grant_offer_create
admin_accounts.revoke_role_grantrole_grant_revoke
admin_accounts.retract_offerrole_grant_offer_retract
admin_accounts.session_revoke_alladmin_session_revoke_all
admin_accounts.token_revoke_alladmin_token_revoke_all
admin_invites.listinvite_list
admin_invites.createinvite_create
admin_invites.deleteinvite_delete
audit_log.listaudit_log_list
audit_log.role_grant_historyaudit_log_role_grant_history
app_settings.getapp_settings_get
app_settings.updateapp_settings_update

All four adapter factories call through the same api — consumers pass the typed throwing Proxy from create_frontend_rpc_client once, regardless of how many admin surfaces they mount.

api

returns

AdminRpcAdapters

create_all_cell_actions
#

auth/all_cell_actions.ts view source

(deps: CellActionDeps, options: AllCellActionsOptions): RpcAction[] import {create_all_cell_actions} from '@fuzdev/fuz_app/auth/all_cell_actions.js';

Build the full cell RPC action set — CRUD (create_cell_actions, which also carries cell_clone) + grant ACL + field + item relations + per-cell audit — as a single handler-bound bundle.

The handler-side twin of the all_cell_action_specs spec bundle and the sibling of create_standard_rpc_actions. Assembling the five cell factories here means an HTTP-RPC mount and a WS mount (or two different backends) can't silently diverge on which cell verbs they expose — the spine_method_coverage reconciliation gate enforces that the spine's live mount matches its coverage manifest, and this aggregator is the single list every mount draws from.

Distinct from create_cell_actions (the CRUD-only factory this bundles) — reach for this whenever a backend mounts the complete cell layer.

deps

CellActionDeps (log, audit, optional validate_data)

options

the role schema for grant validation

returns

RpcAction[]

every cell RpcAction, in mount order

create_app_backend
#

server/app_backend.ts view source

(options: CreateAppBackendOptions): Promise<AppBackend> import {create_app_backend} from '@fuzdev/fuz_app/server/app_backend.js';

Initialize the backend: database + auth migrations + deps.

Calls create_dbrun_migrations (auth namespace, then any migration_namespaces from options in order) → audit_factory({db, log}) and bundles the result with the provided keyring and password deps.

options

keyring, password deps, audit_factory, optional database URL, and optional migration_namespaces

returns

Promise<AppBackend>

app backend with deps, database metadata, and combined migration results

throws

  • Error - if `migration_namespaces` contains a namespace in `reserved_migration_namespaces`

create_app_server
#

server/app_server.ts view source

(options: AppServerOptions): Promise<AppServer> import {create_app_server} from '@fuzdev/fuz_app/server/app_server.js';

Create a fully assembled Hono app with auth, middleware, and routes.

Handles the assembly lifecycle: proxy middleware → auth middleware → bootstrap status → route specs → surface generation → Hono app assembly → static serving. Database migrations belong to the backend lifecycle — pass migration_namespaces to create_app_backend.

When audit_log_sse is set, the SSE registry's listener is registered via backend.deps.audit.add_listener — no shallow-copy of AppDeps. The audit_sse field on the returned AppServer (and the AppServerContext passed to create_route_specs) is non-null in that case; consumers can call require_audit_sse(ctx) / require_audit_sse(server) to assert the invariant.

options

returns

Promise<AppServer>

assembled Hono app, backend, surface build, and bootstrap status

create_app_surface_spec
#

create_audit_emitter
#

auth/audit_emitter.ts view source

(options: CreateAuditEmitterOptions): AuditEmitter import {create_audit_emitter} from '@fuzdev/fuz_app/auth/audit_emitter.js';

Build a bound AuditEmitter. Typical caller is the consumer's audit_factory callback on CreateAppBackendOptionscreate_app_backend invokes that callback with its constructed {db, log} and lands the result on AppDeps.audit.

options

pool, logger, optional initial subscriber, optional config

returns

AuditEmitter

the bound emitter; closes over the pool + config + listener chain

create_audit_log_config
#

auth/audit_log_schema.ts view source

(options?: CreateAuditLogConfigOptions | undefined): AuditLogConfig import {create_audit_log_config} from '@fuzdev/fuz_app/auth/audit_log_schema.js';

Build an AuditLogConfig by merging fuz_app builtins with consumer extras.

Throws when an extra_events key collides with a builtin event type, or fails AuditEventTypeName format validation.

Call once at startup; pass the result into the consumer's audit_factory body — typically `({db, log}) => create_audit_emitter({db, log, audit_log_config, ...})` — so it gets captured inside the bound AppDeps.audit emitter. Builtin handlers omit the audit_log_config slot and pick up builtin_audit_log_config.

options?

type CreateAuditLogConfigOptions | undefined
optional

returns

AuditLogConfig

throws

  • Error - when an `extra_events` key collides with a builtin event type or fails `AuditEventTypeName` format validation

create_audit_log_route_shape
#

auth/audit_log_route_schema.ts view source

(required_role?: string): Omit<RouteSpec, "handler"> import {create_audit_log_route_shape} from '@fuzdev/fuz_app/auth/audit_log_route_schema.js';

The GET /audit/stream SSE route shape minus its handler — pure hono-free data. create_audit_log_route_specs spreads this and attaches the live SSE handler; cross-process surface builders spread it with a stub handler. The output is z.null() because SSE streams have no JSON response body.

required_role

role gating the stream (default DEFAULT_AUDIT_STREAM_ROLE)

type string
default DEFAULT_AUDIT_STREAM_ROLE

returns

Omit<RouteSpec, "handler">

the SSE route shape minus its handler

create_audit_log_route_specs
#

auth/audit_log_routes.ts view source

(options?: AuditLogRouteOptions | undefined): RouteSpec[] import {create_audit_log_route_specs} from '@fuzdev/fuz_app/auth/audit_log_routes.js';

Create the optional audit-log SSE route spec.

Returns an empty array when options.stream is not set — no REST routes live here apart from the stream.

options?

optional stream wiring + role override

type AuditLogRouteOptions | undefined
optional

returns

RouteSpec[]

the SSE route spec (when options.stream is provided) or an empty array

create_audit_log_sse
#

realtime/sse_auth_guard.ts view source

(options: { role?: string | undefined; log: Logger; max_per_scope?: number | null | undefined; }): AuditLogSse import {create_audit_log_sse} from '@fuzdev/fuz_app/realtime/sse_auth_guard.js';

Create a complete audit log SSE setup with broadcasting and auth guard.

Combines SubscriberRegistry, create_sse_auth_guard, and the broadcast call into a single object. The result satisfies AuditLogRouteOptions['stream'] and provides the on_audit_event listener for the audit emitter.

Most consumers pass audit_log_sse: true to create_app_server and never touch this directly — the factory builds an AuditLogSse, registers audit_sse.on_audit_event via backend.deps.audit.add_listener, and exposes it via AppServerContext.audit_sse. Reach for the manual path (compose inside audit_factory body, or audit.add_listener(audit_sse.on_audit_event) post-assembly) only when wiring outside create_app_server.

options

factory options

type { role?: string | undefined; log: Logger; max_per_scope?: number | null | undefined; }

returns

AuditLogSse

audit log SSE setup (stream options + on_audit_event + registry)

examples

const audit_sse = create_audit_log_sse({log}); // Inside the audit_factory body on CreateAppBackendOptions: audit_factory: ({db, log}) => create_audit_emitter({ db, log, on_audit_event: audit_sse.on_audit_event, }), // In create_route_specs: create_audit_log_route_specs({stream: audit_sse}); // In create_app_server options: event_specs: audit_log_event_specs,

create_auth_middleware_specs
#

auth/middleware.ts view source

(deps: AppDeps, options: AuthMiddlewareOptions): Promise<MiddlewareSpec[]> import {create_auth_middleware_specs} from '@fuzdev/fuz_app/auth/middleware.js';

Create the auth middleware stack.

Returns [origin, session, request_context, bearer_auth] middleware specs for the given path pattern. When daemon_token_state is provided, appends a 5th daemon_token layer. Apps can append extra entries for non-standard paths (e.g., tx's /tx binary endpoint).

deps

stateless capabilities (keyring, db)

type AppDeps

options

middleware configuration (allowed_origins, session_options, path, daemon_token_state)

returns

Promise<MiddlewareSpec[]>

the middleware spec array

create_auth_test_apps
#

testing/auth_apps.ts view source

(route_specs: RouteSpec[], roles: string[]): AuthTestApps import {create_auth_test_apps} from '@fuzdev/fuz_app/testing/auth_apps.js';

Create one Hono test app per auth level.

route_specs

the route specs to register

type RouteSpec[]

roles

all roles in the app

type string[]

returns

AuthTestApps

create_banner
#

actions/action_codegen.ts view source

(origin_path: string): string import {create_banner} from '@fuzdev/fuz_app/actions/action_codegen.js';

"DO NOT EDIT" banner naming the gen producer.

origin_path

type string

returns

string

create_bearer_auth_middleware
#

auth/bearer_auth.ts view source

(deps: QueryDeps, ip_rate_limiter: RateLimiter | null, log: Logger): MiddlewareHandler import {create_bearer_auth_middleware} from '@fuzdev/fuz_app/auth/bearer_auth.js';

Create middleware that authenticates via bearer token.

Soft-fails for invalid, expired, or empty tokens — calls next() without setting account identity, letting downstream auth enforcement (the RPC dispatcher's pre-validation / post-authorization auth gates or require_auth) return a consistent JSON-RPC or route-level error. This avoids leaking token-specific diagnostics (invalid_token, account_not_found) that could aid enumeration attacks, and ensures public actions are not blocked by bad credentials.

Rejects bearer tokens when an Origin or Referer header is present — browsers must use cookie auth to reduce attack surface. Auth scheme matching is case-insensitive per RFC 7235. On success, sets c.var.auth_account_id, CREDENTIAL_TYPE_KEY = 'api_token', and AUTH_API_TOKEN_ID_KEY. Skips when an account is already authenticated (e.g. by session middleware). Acting-actor resolution + RequestContext construction are deferred to the dispatcher's authorization phase.

Rate limiting (429) is the only hard-fail — it's a throttling concern independent of auth identity.

deps

query dependencies (pool-level db for middleware)

ip_rate_limiter

per-IP rate limiter for bearer token attempts (null to disable)

type RateLimiter | null

log

the logger instance

type Logger

returns

MiddlewareHandler

mutates

  • Hono — context - sets `ACCOUNT_ID_KEY`, `CREDENTIAL_TYPE_KEY`, and `AUTH_API_TOKEN_ID_KEY` on success

create_bearer_auth_mocks
#

testing/middleware.ts view source

(tc: BearerAuthTestOptions): BearerAuthMocks import {create_bearer_auth_mocks} from '@fuzdev/fuz_app/testing/middleware.js';

Create mock dependencies for create_bearer_auth_middleware, configured per test case.

Configures the module-level mocks for query_validate_api_token, query_account_by_id, query_actor_by_id, and query_role_grant_find_active_for_actor so each test case controls return values independently.

tc

returns

BearerAuthMocks

mocks bundle with spy references

create_bearer_auth_test_app
#

testing/middleware.ts view source

(tc: BearerAuthTestOptions, ip_rate_limiter?: RateLimiter | null): { app: Hono<BlankEnv, BlankSchema, "/">; mocks: BearerAuthMocks; } import {create_bearer_auth_test_app} from '@fuzdev/fuz_app/testing/middleware.js';

Create a Hono app wired with create_bearer_auth_middleware using mocked deps.

The route handler at /api/test returns the resolved context in the response body, enabling assertions on REQUEST_CONTEXT_KEY and CREDENTIAL_TYPE_KEY.

tc

ip_rate_limiter

type RateLimiter | null
default null

returns

{ app: Hono<BlankEnv, BlankSchema, "/">; mocks: BearerAuthMocks; }

create_bootstrap_route_specs
#

auth/bootstrap_routes.ts view source

(deps: RouteFactoryDeps, options: BootstrapRouteOptions): RouteSpec[] import {create_bootstrap_route_specs} from '@fuzdev/fuz_app/auth/bootstrap_routes.js';

Create bootstrap route specs for first-time account creation.

deps

stateless capabilities including filesystem access

options

per-factory configuration (session, token path, bootstrap status)

returns

RouteSpec[]

route specs (not yet applied to Hono)

create_broadcast_api
#

actions/broadcast_api.ts view source

<TApi extends object>(options: CreateBroadcastApiOptions): TApi import {create_broadcast_api} from '@fuzdev/fuz_app/actions/broadcast_api.js';

Builds a typed broadcast API from a set of action specs.

For each spec, adds a method keyed by spec.method that:

  • Validates input against the spec's Zod schema (logs and returns on failure)
  • Creates a JSON-RPC notification from the validated input
  • Broadcasts via the peer (filtered by should_deliver when supplied)

Silently returns when no transport is ready (e.g. before any clients connect). Errors during send are logged but never thrown — broadcasts are fire-and-forget from the handler's perspective.

Typed consumer surface

Consumers declare an explicit interface and pin it via the type parameter:

export interface BackendActionsApi { filer_change: (input: ActionInputs['filer_change']) => Promise<void>; workspace_changed: (input: ActionInputs['workspace_changed']) => Promise<void>; } const api = create_broadcast_api<BackendActionsApi>({ peer: backend.peer, specs: [filer_change_action_spec, workspace_changed_action_spec], });

The cast is unchecked — callers must keep the interface and the specs array in sync. Codegen (action_collections.gen.ts) is a natural fit if the consumer already generates per-method type maps.

options

returns

TApi

generics

create_broadcast_api<TApi extends object>
TApi
constraint object

create_bun_testing_adapter
#

create_cell_actions
#

auth/cell_actions.ts view source

(deps: CellActionDeps): RpcAction[] import {create_cell_actions} from '@fuzdev/fuz_app/auth/cell_actions.js';

Create the six generic cell RPC actions.

deps

returns

RpcAction[]

create_cell_audit_actions
#

create_cell_field_actions
#

create_cell_grant_actions
#

create_cell_item_actions
#

create_cli_logger
#

cli/logger.ts view source

(logger: Logger): CliLogger import {create_cli_logger} from '@fuzdev/fuz_app/cli/logger.js';

Creates a CLI logger wrapping a Logger with semantic output methods.

logger

the Logger instance to wrap

type Logger

returns

CliLogger

a CliLogger with CLI semantic methods mapped to Logger levels

create_credential_type_schema
#

auth/credential_type_schema.ts view source

(consumer_types?: Record<string, CredentialTypeMeta>): CredentialTypeSchemaResult import {create_credential_type_schema} from '@fuzdev/fuz_app/auth/credential_type_schema.js';

Create a credential-type schema from the builtin set plus optional consumer-declared additions.

Builtins (session, api_token, daemon_token) are always present; consumer entries that collide with a builtin name throw at construction. Pass the result into create_role_schema's optional credential_types parameter so each role's required_credential_types entries are validated against this set at construction time.

consumer_types

optional consumer-declared credential-type set with optional metadata

type Record<string, CredentialTypeMeta>
default {}

returns

CredentialTypeSchemaResult

{CredentialType, credential_types} — Zod schema and metadata map

throws

  • Error - if any `consumer_types` key fails the `CredentialTypeName` regex, collides with a builtin name, or appears more than once

examples

// simple — builtins only const {CredentialType, credential_types} = create_credential_type_schema(); // with consumer extensions const {CredentialType} = create_credential_type_schema({ sso_assertion: {description: 'OIDC SSO assertion bound to an IdP-asserted account.'}, });

create_cross_backend_global_setup
#

testing/cross_backend/create_cross_backend_global_setup.ts view source

({ configs, derive_name, provide_key }: CrossBackendGlobalSetupOptions): (project: TestProject) => Promise<() => Promise<void>> import {create_cross_backend_global_setup} from '@fuzdev/fuz_app/testing/cross_backend/create_cross_backend_global_setup.js';

Build a vitest globalSetup default export. Returns the (project) => teardown function vitest 4 expects.

__0

returns

(project: TestProject) => Promise<() => Promise<void>>

create_daemon_token_middleware
#

auth/daemon_token_middleware.ts view source

(state: DaemonTokenState, deps: QueryDeps, log: Logger): MiddlewareHandler import {create_daemon_token_middleware} from '@fuzdev/fuz_app/auth/daemon_token_middleware.js';

Create middleware that authenticates via daemon token.

Checks the X-Daemon-Token header. Behavior:

  • No header: pass through (don't touch existing context).
  • Header present + Origin / Referer present: discard the credential (browser context) and pass through — daemon tokens are loopback-only and never carry an Origin in production, so a header-bearing request is not a legitimate daemon caller. Mirrors the bearer guard: next() rather than 401, so downstream auth enforcement returns credential_type_required (not a hard fail). Silent on the wire (anti-enumeration); in DEV only, sets X-Fuz-Auth-Debug: daemon_token_discarded_browser_context.
  • Header present + Zod-invalid (malformed): soft-fail discard (pass through, not 401) — mirrors the bearer guard and the Rust spine's resolve.rs (None). Downstream a daemon-gated action returns credential_type_required; a public action proceeds anonymous.
  • Header present + invalid value (not the current/previous token): soft-fail discard (pass through, not 401) — same downstream behavior.
  • Header present + valid + keeper_account_id null (still pre-bootstrap after the lazy refresh): soft-fail discard (pass through, not 503) — mirrors the Rust spine's resolve.rs (None), so the request falls through to anonymous and a daemon-gated action returns credential_type_required downstream.
  • Header present + valid + ok: set `c.var.auth_account_id = state.keeper_account_id, CREDENTIAL_TYPE_KEY = 'daemon_token'` (overrides any existing session / bearer identity).

Acting-actor resolution + RequestContext construction are deferred to the dispatcher's authorization phase. Multi-actor keeper accounts surface actor_required from there if a daemon caller doesn't pass an explicit acting value.

state

the daemon token runtime state

deps

query dependencies (pool-level db for keeper-account resolution)

log

the logger instance

type Logger

returns

MiddlewareHandler

mutates

  • Hono — context - sets `ACCOUNT_ID_KEY`, `CREDENTIAL_TYPE_KEY`, and `AUTH_API_TOKEN_ID_KEY` on a valid token

create_database
#

dev/setup.ts view source

(deps: CommandDeps, db_name: string, options?: CreateDatabaseOptions | undefined): Promise<CommandResult> import {create_database} from '@fuzdev/fuz_app/dev/setup.js';

Create a PostgreSQL database if createdb is available.

Does not throw — returns the underlying command result so callers can decide how to react to a missing createdb or an "already exists" failure.

deps

command execution capability

db_name

database name to create

type string

options?

logger

type CreateDatabaseOptions | undefined
optional

returns

Promise<CommandResult>

the command result

mutates

  • external — database - invokes `createdb` to create `db_name` when available

create_db
#

db/create_db.ts view source

(database_url: string): Promise<CreateDbResult> import {create_db} from '@fuzdev/fuz_app/db/create_db.js';

Create a database connection based on a URL.

The close callback is bound to the actual driver — callers never need to know which driver is in use.

For direct driver construction without URL routing, import create_pg_db from db/db_pg.ts or create_pglite_db from db/db_pglite.ts.

database_url

connection URL (postgres://, postgresql://, file://, or memory://)

type string

returns

Promise<CreateDbResult>

database instance, close callback, type, and display name

throws

  • Error - if `database_url` uses an unsupported scheme. Driver

create_db_route_specs
#

http/db_routes.ts view source

(options: DbRouteOptions): RouteSpec[] import {create_db_route_specs} from '@fuzdev/fuz_app/http/db_routes.js';

Create the db API route specs.

options

returns

RouteSpec[]

create_default_fetcher
#

db/fact_store.ts view source

(): FactExternalFetcher import {create_default_fetcher} from '@fuzdev/fuz_app/db/fact_store.js';

Default fetcher backed by globalThis.fetch.

returns

FactExternalFetcher

create_deno_runtime
#

runtime/deno.ts view source

(args: readonly string[]): RuntimeDeps import {create_deno_runtime} from '@fuzdev/fuz_app/runtime/deno.js';

Create a runtime backed by Deno APIs.

Returns an object satisfying all *Deps interfaces from runtime/deps.ts. Pass to shared functions that accept EnvDeps, FsReadDeps, etc.

args

CLI arguments (typically Deno.args)

type readonly string[]

returns

RuntimeDeps

runtime implementation using Deno APIs

create_deno_testing_adapter
#

create_describe_db
#

testing/db.ts view source

(factories: DbFactory | DbFactory[], truncate_tables: string[]): (name: string, fn: (get_db: () => Db) => void) => void import {create_describe_db} from '@fuzdev/fuz_app/testing/db.js';

Create a describe_db function bound to specific factories and truncate tables.

Returns a 2-arg (name, fn) function that runs the test suite against each factory. Each factory gets its own describe block with a shared database instance, automatic beforeEach truncation, and afterAll cleanup. Skipped factories use describe.skip.

factories

one or more database factories to run suites against

type DbFactory | DbFactory[]

truncate_tables

tables to truncate between tests (children first for FK safety)

type string[]

returns

(name: string, fn: (get_db: () => Db) => void) => void

mutates

  • the — underlying database between tests — `beforeEach` issues

create_disk_fact_fetcher
#

db/fact_disk_storage.ts view source

(deps: Pick<FactDiskStorageDeps, "read_file" | "read_file_stream">, facts_dir: string): FactExternalFetcher import {create_disk_fact_fetcher} from '@fuzdev/fuz_app/db/fact_disk_storage.js';

FactExternalFetcher reading from the <facts_dir>/<shard>/<rest> layout the writers above produce, over the injected *Deps. Does NOT verify hash content — PgFactStore.get calls fact_hash_verify(hash, bytes) after the fetch and returns null on mismatch.

Defense at the read seam is the FILE_FACT_URL_PATTERN regex (via parse_file_fact_url) — .. segments, foreign schemes, and non-hex chars fail before any disk access.

deps

type Pick<FactDiskStorageDeps, "read_file" | "read_file_stream">

facts_dir

type string

returns

FactExternalFetcher

create_dual_spawn_global_setup
#

testing/cross_backend/create_dual_spawn_global_setup.ts view source

({ configs, provide_keys }: DualSpawnGlobalSetupOptions): (project: TestProject) => Promise<() => Promise<void>> import {create_dual_spawn_global_setup} from '@fuzdev/fuz_app/testing/cross_backend/create_dual_spawn_global_setup.js';

Build a vitest globalSetup default export that spawns both backends.

__0

returns

(project: TestProject) => Promise<() => Promise<void>>

create_emit_ordering_audit_factory
#

testing/audit_drift_guard.ts view source

<E extends { kind: string; at: number; }>(seq_ref: { value: number; }, events_ref: (AuditEmitMarker | E)[], extra_options?: Omit<CreateAuditEmitterOptions, "db" | ... 1 more ... | "emit_decorator"> | undefined): AuditFactory import {create_emit_ordering_audit_factory} from '@fuzdev/fuz_app/testing/audit_drift_guard.js';

Build an audit_factory that produces a real create_audit_emitter with its emit decorated to push a {kind: 'emit', at: seq.value++} marker into a shared sequence + events array. Used by the close-vs-emit ordering test to compose against a shared sequence counter (typically create_recording_closer(seq_ref) capturing eager-close calls).

Pass the returned factory through create_test_app({audit_factory: …}) — the test backend invokes it with its constructed {db, log} and lands the decorated emitter on backend.deps.audit. Production handlers dereference deps.audit.emit at call time, so the decorator sees every subsequent handler invocation. The underlying emit still runs — the decorator records the call, it does not suppress side effects.

Scope — both emit and emit_role_grant_target. The decorator is captured by emit_role_grant_target's closure inside create_audit_emitter (and re-exposed as the outer emit slot), so role-grant-shape emissions land in events_ref alongside bare emit calls. emit_pool and notify are not decorated — they take AuditLogInput / AuditLogEvent directly without going through emit, so handler-side emit_pool writes (today only auth/cleanup.ts) skip capture. Close-firing handlers all reach for emit or emit_role_grant_target, so the ordering test sees them regardless of which entry point a future refactor picks.

Optionally accept extra_options to thread on_audit_event / audit_log_config into the inner emitter — useful when a test wants both ordering capture and a real SSE/WS guard wired into the same emitter chain.

seq_ref

type { value: number; }

events_ref

type (AuditEmitMarker | E)[]

extra_options?

type Omit<CreateAuditEmitterOptions, "db" | "log" | "emit_decorator"> | undefined
optional

returns

AuditFactory

generics

create_emit_ordering_audit_factory<E extends { kind: string; at: number }>
E
constraint { kind: string; at: number }

create_expired_test_cookie
#

create_extract_global_flags
#

cli/args.ts view source

<T extends Record<string, unknown>>(schema: ZodType<T, unknown, $ZodTypeInternals<T, unknown>>, fallback: T): (unparsed: ParsedArgs) => { flags: T; remaining: ParsedArgs; } import {create_extract_global_flags} from '@fuzdev/fuz_app/cli/args.js';

Create a project-specific global flag extractor.

Returns a function that separates global flags from command-specific args. The schema defines which flags are global (with aliases via .meta({aliases})), and the fallback provides defaults when parsing fails.

schema

Zod schema for global flags

type ZodType<T, unknown, $ZodTypeInternals<T, unknown>>

fallback

default values when parsing fails

type T

returns

(unparsed: ParsedArgs) => { flags: T; remaining: ParsedArgs; }

extractor function (unparsed) => {flags, remaining}

generics

create_extract_global_flags<T extends Record<string, unknown>>
T
constraint Record<string, unknown>

create_fake_hono_context
#

testing/ws_round_trip.ts view source

(opts: FakeHonoContextOptions): Context<any, any, {}> import {create_fake_hono_context} from '@fuzdev/fuz_app/testing/ws_round_trip.js';

Build a fake Hono Context exposing the auth keys the dispatcher reads via c.get(...). Only .get() is populated — no other Hono context surface is simulated.

opts

returns

Context<any, any, {}>

create_fake_ws
#

testing/ws_round_trip.ts view source

(): FakeWs import {create_fake_ws} from '@fuzdev/fuz_app/testing/ws_round_trip.js';

Build a real WSContext backed by in-memory send/close capture. Parsing of outgoing frames is left to the caller — sends holds the raw strings as the dispatcher wrote them.

returns

FakeWs

create_fetch_transport
#

testing/transports/fetch_transport.ts view source

(options: FetchTransportOptions): FetchTransport import {create_fetch_transport} from '@fuzdev/fuz_app/testing/transports/fetch_transport.js';

Build a cookie-threading transport pinned to options.base_url. The returned function carries a private Map<name, cookie-head> jar that updates on every response's Set-Cookie and re-sends on every subsequent request.

Request rewriting:

  • Absolute URLs (http://other.example/...) pass through verbatim — handy for cross-origin negative tests that target a deliberately different host.
  • Relative URLs are resolved against base_url.
  • Origin is set to options.origin ?? base_url unless the caller already provided one.
  • Cookie is set from the jar unless the caller already provided one.

options

returns

FetchTransport

create_file_fact_fetcher
#

server/file_fact_fetcher.ts view source

(options: FileFactFetcherOptions): FactExternalFetcher import {create_file_fact_fetcher} from '@fuzdev/fuz_app/server/file_fact_fetcher.js';

Build a FactExternalFetcher that resolves file: URLs against the filesystem. Throws on a malformed URL before touching the disk so PgFactStore.get logs the warning + returns null without an I/O round-trip on bad data.

options

returns

FactExternalFetcher

create_frontend_rpc_client
#

actions/frontend_rpc_client.ts view source

<TApi extends object>(options: CreateFrontendRpcClientOptions<TApi>): FrontendRpcClient<TApi> import {create_frontend_rpc_client} from '@fuzdev/fuz_app/actions/frontend_rpc_client.js';

Build a frontend-only typed RPC client. See module doc for the bundle's design.

options

type CreateFrontendRpcClientOptions<TApi>

returns

FrontendRpcClient<TApi>

generics

create_frontend_rpc_client<TApi extends object>
TApi
constraint object

create_fuz_authorization_handler
#

auth/request_context.ts view source

(deps: QueryDeps): (c: Context<any, any, {}>, spec: RouteSpec) => Promise<void | Response> import {create_fuz_authorization_handler} from '@fuzdev/fuz_app/auth/request_context.js';

Create the route-spec authorization handler used by apply_route_specs.

Reads acting off c.var.validated_input (or c.var.validated_query for GET routes) — input validation runs first, so the authorization phase consumes the typed Zod field instead of pre-parsing the body. Public routes (auth.account === 'none' && auth.actor === 'none') skip the phase entirely.

Per registry-time invariant 2, auth.actor !== 'none' ⟺ the input (or query) schema declares acting?: ActingActor — so reading from c.var.validated_input.acting / c.var.validated_query.acting is type-safe.

Resolved contexts land on REQUEST_CONTEXT_KEY so the post-authorization REST middleware (require_role, require_credential_types) reads the actor-bound context off c.var. The HTTP RPC and WS dispatchers consume the apply_authorization_phase outcome directly without round-tripping through c.var.

deps

returns

(c: Context<any, any, {}>, spec: RouteSpec) => Promise<void | Response>

create_grant_path_schema
#

auth/grant_path_schema.ts view source

(consumer_paths?: Record<string, GrantPathMeta>): GrantPathSchemaResult import {create_grant_path_schema} from '@fuzdev/fuz_app/auth/grant_path_schema.js';

Create a grant-path schema from the builtin set plus optional consumer-declared additions.

Builtins (admin, self_service, system, bootstrap) are always present; consumer entries that collide with a builtin name throw at construction. Pass the result into create_role_schema's optional grant_paths parameter so each role's grant_paths entries are validated against this set at construction time.

consumer_paths

optional consumer-declared grant-path set with optional metadata

type Record<string, GrantPathMeta>
default {}

returns

GrantPathSchemaResult

{GrantPath, grant_paths} — Zod schema and metadata map

throws

  • Error - if any `consumer_paths` key fails the `GrantPathName` regex, collides with a builtin name, or appears more than once

examples

// simple — builtins only const {GrantPath, grant_paths} = create_grant_path_schema(); // with consumer extensions const {GrantPath} = create_grant_path_schema({ invite_only: {description: 'Granted by claiming a consumer-issued invite.'}, });

create_health_route_spec
#

http/common_routes.ts view source

(): RouteSpec import {create_health_route_spec} from '@fuzdev/fuz_app/http/common_routes.js';

Create a public health check route spec.

Infrastructure endpoint for uptime monitors and load balancers. Bootstrap availability is exposed via /api/account/status instead.

returns

RouteSpec

create_help
#

cli/help.ts view source

<TCategory extends string>(options: HelpOptions<TCategory>): HelpGenerator import {create_help} from '@fuzdev/fuz_app/cli/help.js';

Create a help generator configured for an application.

options

help configuration

type HelpOptions<TCategory>

returns

HelpGenerator

help generator with generate_main_help, generate_command_help, and get_help_text

generics

create_help<TCategory extends string>
TCategory
constraint string

create_initial_data
#

actions/action_event_helpers.ts view source

(kind: "request_response" | "remote_notification" | "local_call", phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute", method: string, executor: "frontend" | "backend", input: unknown): { ...; } import {create_initial_data} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

kind

type "request_response" | "remote_notification" | "local_call"

phase

type "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"

method

type string

executor

type "frontend" | "backend"

input

type unknown

returns

{ kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }

create_jsonrpc_error_response
#

http/jsonrpc_helpers.ts view source

(id: string | number | null, error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">); message: string; data?: unknown; }): { ...; } import {create_jsonrpc_error_response} from '@fuzdev/fuz_app/http/jsonrpc_helpers.js';

Creates a JSON-RPC error response message.

id

type string | number | null

error

type { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">); message: string; data?: unknown; }

returns

{ [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">); message: string; data?: unknown; }; }

create_jsonrpc_error_response_from_thrown
#

http/jsonrpc_helpers.ts view source

(id: string | number | null, error: unknown): { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<...>); message: string; data?: unknown; }; } import {create_jsonrpc_error_response_from_thrown} from '@fuzdev/fuz_app/http/jsonrpc_helpers.js';

Creates a JSON-RPC error response from any error. Handles ThrownJsonrpcError (preserves code/message/data) and regular Error objects (maps to internal_error, includes stack in DEV).

id

type string | number | null

error

type unknown

returns

{ [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">); message: string; data?: unknown; }; }

create_jsonrpc_notification
#

http/jsonrpc_helpers.ts view source

(method: string, params: { [x: string]: unknown; } | undefined): { [x: string]: unknown; jsonrpc: "2.0"; method: string; params?: { [x: string]: unknown; } | undefined; } import {create_jsonrpc_notification} from '@fuzdev/fuz_app/http/jsonrpc_helpers.js';

Creates a JSON-RPC notification message (no id, no response expected).

method

type string

params

type { [x: string]: unknown; } | undefined

returns

{ [x: string]: unknown; jsonrpc: "2.0"; method: string; params?: { [x: string]: unknown; } | undefined; }

create_jsonrpc_request
#

http/jsonrpc_helpers.ts view source

(method: string, params: { [x: string]: unknown; } | undefined, id: string | number): { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; } import {create_jsonrpc_request} from '@fuzdev/fuz_app/http/jsonrpc_helpers.js';

Creates a JSON-RPC request message.

method

type string

params

type { [x: string]: unknown; } | undefined

id

type string | number

returns

{ [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; }

create_jsonrpc_response
#

http/jsonrpc_helpers.ts view source

(id: string | number, result: JSONType): { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } import {create_jsonrpc_response} from '@fuzdev/fuz_app/http/jsonrpc_helpers.js';

Creates a JSON-RPC success response message.

id

type string | number

result

type JSONType

returns

{ [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; }

create_keyring
#

auth/keyring.ts view source

(env_value: string | undefined): Keyring | null import {create_keyring} from '@fuzdev/fuz_app/auth/keyring.js';

Create a keyring from environment variable.

Keys are separated by __ for rotation support. First key is used for signing, all keys are tried for verification.

CryptoKeys are cached on first use for performance.

Security: key rotation is an operational concern. Old keys remain valid for verification indefinitely — a leaked old key can forge session cookies until it is removed from SECRET_FUZ_COOKIE_KEYS. After rotating to a new signing key, remove the old key within a grace period (e.g. 24–48 hours, long enough for active sessions to re-sign with the new key via cookie refresh). Treat SECRET_FUZ_COOKIE_KEYS changes as security-critical deploys.

env_value

the SECRET_FUZ_COOKIE_KEYS environment variable

type string | undefined

returns

Keyring | null

keyring or null if no keys configured

create_mock_fs
#

testing/mock_fs.ts view source

(initial_files?: Record<string, string>): MockFs import {create_mock_fs} from '@fuzdev/fuz_app/testing/mock_fs.js';

Creates an in-memory file system for tests.

read_file throws an ENOENT-tagged error for missing paths so callers can exercise the same "file doesn't exist" code path as node:fs.

initial_files

type Record<string, string>
default {}

returns

MockFs

create_mock_runtime
#

runtime/mock.ts view source

(args?: string[]): MockRuntime import {create_mock_runtime} from '@fuzdev/fuz_app/runtime/mock.js';

Create a mock RuntimeDeps for testing.

The mock exit records the code on exit_calls and throws MockExitError (so the never-returning contract holds in tests). fetch throws TypeError when no mock_fetch_responses pattern matches the request URL.

args

type string[]
default []

returns

MockRuntime

MockRuntime with controllable state

examples

const runtime = create_mock_runtime(['apply', 'zap.ts']); runtime.mock_env.set('HOME', '/home/test'); runtime.mock_fs.set('/home/test/.app/config.json', '{}'); await some_function(runtime); assert.strictEqual(runtime.command_calls.length, 1); assert.deepStrictEqual(runtime.exit_calls, [0]);

create_namespace_qualifier
#

actions/action_codegen.ts view source

(sources: readonly SpecSource[], imports: ImportBuilder): { qualify_spec: (spec: { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; ... 8 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }) => string; all_specs: readonly ({ ...; } | ... 1 more ... | { ...; })[]; } import {create_namespace_qualifier} from '@fuzdev/fuz_app/actions/action_codegen.js';

Multi-source consumer helper. Takes a list of {ns, module, specs} rows, registers import * as ns from module for each on imports, builds the method_to_ns lookup with duplicate-method detection, and returns {qualify_spec, all_specs} ready to thread through the high-level helpers.

Closes the per-file boilerplate gap that kept tx + visiones on hand-rolled template strings even after qualify_spec? landed in API review II — the per-call callback wasn't enough; the import dance + dup-check was the real boilerplate.

sources

type readonly SpecSource[]

imports

returns

{ qualify_spec: (spec: { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<...>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }) => string; all_specs: rea...

throws

  • Error - if two sources contain the same method name (same-method

examples

const sources = [ {ns: 'tx_specs', module: './action_specs.ts', specs: all_zap_action_specs}, {ns: 'admin_specs', module: '@fuzdev/fuz_app/auth/admin_action_specs.ts', specs: all_admin_action_specs}, ]; export const gen: Gen = ({origin_path}) => { const imports = new ImportBuilder(); const {qualify_spec, all_specs} = create_namespace_qualifier(sources, imports); return compose_gen_file({ origin_path, imports, blocks: [ generate_action_specs_record(all_specs, imports, {qualify_spec}), generate_action_inputs_outputs(all_specs, imports, {qualify_spec}), ], }); };

create_node_runtime
#

runtime/node.ts view source

(args?: readonly string[]): RuntimeDeps import {create_node_runtime} from '@fuzdev/fuz_app/runtime/node.js';

Create a RuntimeDeps backed by Node.js APIs.

args

CLI arguments (typically process.argv.slice(2))

type readonly string[]
default process.argv.slice(2)

returns

RuntimeDeps

RuntimeDeps implementation using Node.js runtime

create_node_testing_adapter
#

create_noop_stub
#

testing/stubs.ts view source

<T = any>(_label: string, overrides?: Record<string, unknown> | undefined): T import {create_noop_stub} from '@fuzdev/fuz_app/testing/stubs.js';

Create a Proxy where every method access returns a no-op async function.

Use for deps that may be reached during "correct auth passes guard" tests but whose return values don't matter. Unlike the explicit method listing, this auto-updates when interfaces change.

_label

type string

overrides?

explicit properties to set (e.g. {db: stub_db})

type Record<string, unknown> | undefined
optional

returns

T

generics

create_noop_stub<T = any>
T
default any

create_pg_db
#

db/db_pg.ts view source

(pool: Pool): DbDriverResult import {create_pg_db} from '@fuzdev/fuz_app/db/db_pg.js';

Create a Db backed by a pg.Pool.

Owns the transaction implementation, acquiring a dedicated pool client per transaction.

pool

an already-constructed pg.Pool

type Pool

returns

DbDriverResult

the Db instance and a close callback bound to pool.end()

create_pg_factory
#

testing/db.ts view source

(init_schema: (db: Db) => Promise<void>, test_url?: string | undefined): DbFactory import {create_pg_factory} from '@fuzdev/fuz_app/testing/db.js';

Create a pg (PostgreSQL) database factory for tests.

Skipped when test_url is not provided. Drops schema_version before running init_schema, forcing migrations to re-evaluate against the actual tables. Prevents stale tracker rows from skipping migrations when DDL changes between test sessions.

For full clean-slate behavior (recommended), call drop_auth_schema(db) at the start of init_schema before running migrations. This handles upstream schema changes that go beyond adding new tables/columns.

init_schema

callback to initialize the database schema

type (db: Db) => Promise<void>

test_url?

PostgreSQL connection URL (e.g. from TEST_DATABASE_URL)

type string | undefined
optional

returns

DbFactory

a factory that creates pg databases. The returned create() throws when test_url is unset (despite the skip: true flag — defense against direct invocation), and rewrites Postgres "database does not exist" errors into a createdb hint message.

create_pglite_db
#

db/db_pglite.ts view source

(pglite: PGlite): DbDriverResult import {create_pglite_db} from '@fuzdev/fuz_app/db/db_pglite.js';

Create a Db backed by a PGlite instance.

Delegates transactions to PGlite's native transaction() method.

pglite

an already-constructed PGlite instance

type PGlite

returns

DbDriverResult

the Db instance and a close callback bound to pglite.close()

create_pglite_factory
#

testing/db.ts view source

(init_schema: (db: Db) => Promise<void>): DbFactory import {create_pglite_factory} from '@fuzdev/fuz_app/testing/db.js';

Create a pglite (in-memory) database factory for tests.

Always enabled — no external dependencies required. Shares a single PGlite WASM instance across all factories in the same vitest worker thread (one test file). Subsequent create() calls reset the schema via DROP SCHEMA public CASCADE instead of paying the WASM cold-start cost again.

init_schema

callback to initialize the database schema

type (db: Db) => Promise<void>

returns

DbFactory

create_proxy_middleware
#

http/proxy.ts view source

(options: ProxyOptions): MiddlewareHandler import {create_proxy_middleware} from '@fuzdev/fuz_app/http/proxy.js';

Create a Hono middleware that resolves the client IP from trusted proxies.

Sets client_ip on the Hono context for downstream use by get_client_ip. All client IPs are normalized (lowercase, IPv4-mapped IPv6 stripped).

Resolution logic:

  1. No X-Forwarded-For → use connection IP directly.
  2. X-Forwarded-For present but connection is untrusted → ignore header (spoofed by a direct attacker), use connection IP.
  3. X-Forwarded-For present and connection is trusted → walk header right-to-left, strip trusted entries, use first untrusted entry.

options

trusted proxy configuration

returns

MiddlewareHandler

throws

  • Error - if any entry in `options.trusted_proxies` is invalid (parsed eagerly via `parse_proxy_entry`)

create_proxy_middleware_spec
#

http/proxy.ts view source

(options: ProxyOptions): MiddlewareSpec import {create_proxy_middleware_spec} from '@fuzdev/fuz_app/http/proxy.js';

Create a middleware spec for trusted proxy resolution.

Apply before auth middleware so client_ip is available for rate limiting.

options

trusted proxy configuration

returns

MiddlewareSpec

create_rate_limiter
#

rate_limiter.ts view source

(options?: Partial<RateLimiterOptions> | undefined): RateLimiter import {create_rate_limiter} from '@fuzdev/fuz_app/rate_limiter.js';

Create a RateLimiter with sensible defaults for per-IP login protection.

options?

override individual options; unset fields use default_login_ip_rate_limit

type Partial<RateLimiterOptions> | undefined
optional

returns

RateLimiter

create_ready_route_spec
#

http/common_routes.ts view source

(options: ReadyRouteOptions): RouteSpec import {create_ready_route_spec} from '@fuzdev/fuz_app/http/common_routes.js';

Create the /ready readiness route spec — the deploy gate.

Returns 200 {ready: true} when the live DB's columns cover expected, else 503 {error} (schema_drift when columns are missing, db_unreachable when the introspection query throws). The detailed drift goes to the server log only — the public body stays a minimal code so the endpoint doesn't leak schema structure (mirrors why /api/surface is authenticated). A deploy poll treats 503 as a failed release and rolls back, turning a silent schema-drift auth outage into a loud blocked deploy. See db/schema_ready.ts for the column-presence rationale and auth/migrations.ts for the frozen-append discipline that prevents the drift in the first place.

options

returns

RouteSpec

create_recording_audit_emitter
#

testing/audit_drift_guard.ts view source

(calls_ref?: AuditLogInput<"invite_create" | "invite_delete" | "account_delete" | "account_purge" | "account_undelete" | "app_settings_update" | "login" | "logout" | "bootstrap" | "signup" | ... 16 more ... | "actor_undelete">[] | undefined): RecordingAuditEmitter import {create_recording_audit_emitter} from '@fuzdev/fuz_app/testing/audit_drift_guard.js';

Build a no-op AuditEmitter that records every emit, emit_pool, and emit_role_grant_target call into calls as an AuditLogInput. Use to capture audit metadata shapes in unit tests (e.g. password change failure outcome, role-grant create denial) without standing up the full PGlite + query_audit_log pipeline.

Capture scope — all four production fan-out shapes. emit_role_grant_target mirrors create_audit_emitter's lift logic in place — actor_id / account_id / ip are populated from auth + ctx and the event_type / outcome / target_*_id / metadata fields forward from the input envelope. Tests asserting on role-grant-shape emissions read out of the same homogeneous calls array. notify is a no-op; add_listener records into a local array that listener_count reports (registered listeners never fire — this emitter captures emit shapes, not fan-out).

emit AND emit_pool both append to calls so cleanup-sweep tests (which use emit_pool exclusively — see auth/cleanup.ts) can also read assertions off the same array.

Pass calls_ref to write into a caller-owned array (callers that declared const events: Array<AuditLogInput> = [] and want to keep the reference). Omit to let the helper allocate a fresh array and return it on the calls field of the result.

The returned emitter is deliberately NOT frozen — slots stay mutable so a test can override one when it needs bespoke shape (e.g. an emit_pool that throws on the first call). The production create_audit_emitter freeze invariant exists to catch the patch_audit_emit_capture hot-patch footgun against the closure-captured emit; the recording emitter has no inner closure, so the freeze isn't load-bearing here.

calls_ref?

type AuditLogInput<"invite_create" | "invite_delete" | "account_delete" | "account_purge" | "account_undelete" | "app_settings_update" | "login" | "logout" | "bootstrap" | "signup" | "password_change" | ... 15 more ... | "actor_undelete">[] | undefined
optional

returns

RecordingAuditEmitter

create_recording_closer
#

testing/connection_closer_helpers.ts view source

(seq_ref?: { value: number; } | undefined): RecordingCloser import {create_recording_closer} from '@fuzdev/fuz_app/testing/connection_closer_helpers.js';

Build a ConnectionCloser that records every call into calls rather than touching real transports. Each method returns 1 ("one socket closed") regardless of whether a real socket exists — handlers typically ignore the return value.

Pass seq_ref to share the sequence counter with a sibling create_emit_ordering_audit_factory so tests can pin close-vs-emit ordering at the handler call site. Without seq_ref, the closer uses a fresh internal counter — at: N values within a single test are meaningful, but cannot be compared against audit emit ordering.

seq_ref?

type { value: number; } | undefined
optional

returns

RecordingCloser

create_request_context_middleware
#

auth/request_context.ts view source

(deps: QueryDeps, log: Logger, session_context_key?: string): MiddlewareHandler import {create_request_context_middleware} from '@fuzdev/fuz_app/auth/request_context.js';

Create middleware that authenticates the account from a session cookie.

Reads the session identity (set by session middleware), looks up the auth_session, and on a valid session sets c.var.auth_account_id, CREDENTIAL_TYPE_KEY = 'session', and AUTH_SESSION_TOKEN_HASH_KEY. Touches the session (fire-and-forget). Does not load actor or role_grants; REQUEST_CONTEXT_KEY is left null — the route-spec / RPC dispatcher authorization phase resolves the acting actor and builds the full RequestContext when the route needs one.

Invalid / missing session leaves all keys null and calls next()require_auth / require_role enforce.

deps

query dependencies (pool-level db for middleware)

log

the logger instance

type Logger

session_context_key

the Hono context key where session middleware stored the session token

type string
default 'auth_session_id'

returns

MiddlewareHandler

mutates

  • Hono — context - sets `ACCOUNT_ID_KEY`, `CREDENTIAL_TYPE_KEY`, `AUTH_SESSION_TOKEN_HASH_KEY`, and `AUTH_API_TOKEN_ID_KEY`

create_role_grant_offer_actions
#

auth/role_grant_offer_actions.ts view source

(deps: ActionFactoryDeps & { notification_sender?: NotificationSender | null | undefined; }, options?: RoleGrantOfferActionOptions): RpcAction[] import {create_role_grant_offer_actions} from '@fuzdev/fuz_app/auth/role_grant_offer_actions.js';

Create the eight role-grant-offer RPC actions (six offer-lifecycle methods plus role_grant_revoke and the immediate role_grant_assign).

deps

ActionFactoryDeps (log, audit) plus optional notification_sender for WS fan-out — when absent, WS fan-out is silently skipped (DB-only side effects still happen). Consumers wiring BackendWebsocketTransport assign its instance directly (the transport's send_to_account signature accepts the broader JsonrpcMessageFromServerToClient, which is contravariantly compatible)

type ActionFactoryDeps & { notification_sender?: NotificationSender | null | undefined; }

options

role schema, default TTL, authorization override

default {}

returns

RpcAction[]

the RpcAction array to spread into a create_rpc_endpoint call

create_role_schema
#

auth/role_schema.ts view source

(consumer_roles: readonly RoleSpec[], options?: CreateRoleSchemaOptions): RoleSchemaResult import {create_role_schema} from '@fuzdev/fuz_app/auth/role_schema.js';

Create a role schema and spec map that extends the builtins with app-defined roles.

Call once at server init. The returned Role schema validates role strings at I/O boundaries (grant endpoint, role_grant queries). The role_specs map is read by middleware for required_credential_types checks and by admin / self-service factories to derive their default eligibility filters from RoleSpec.grant_paths.

Construction-time guards (all fire on misconfiguration):

  1. Every consumer_roles[i].name matches RoleName regex.
  2. No two consumer roles share a name.
  3. No consumer role collides with a builtin (keeper / admin).
  4. When options.credential_types is supplied, every entry in required_credential_types is registered in that map.
  5. When options.scope_kinds is supplied, every entry in applicable_scope_kinds is registered in that map. (Builtins declare empty applicable_scope_kinds, so they pass any registry.)
  6. When options.grant_paths is supplied, every entry in grant_paths is registered in that map. (Builtins use only 'admin' and 'bootstrap', both of which are builtin grant paths, so they pass the default registry from create_grant_path_schema().)

consumer_roles

app-defined role specs

type readonly RoleSpec[]

options

optional registries for cross-axis validation

default {}

returns

RoleSchemaResult

{Role, role_specs} — Zod schema and full spec map

throws

  • Error - if any `consumer_roles` entry fails any of the construction-time guards above

examples

// visiones — opt into all four registries for full construction-time validation const credential_types = create_credential_type_schema(); const scope_kinds = create_scope_kind_schema({ classroom: {description: 'A classroom — teacher and student role_grants scope here.'}, }); const grant_paths = create_grant_path_schema(); const {Role, role_specs} = create_role_schema( [ { name: 'teacher', description: 'Educator role. Web-grantable; applies at classroom scope.', grant_paths: ['admin'], applicable_scope_kinds: ['classroom'], }, ], {credential_types, scope_kinds, grant_paths}, );

create_rpc_client
#

actions/rpc_client.ts view source

<TApi extends object>(options: CreateRpcClientOptions<TApi>): TApi import {create_rpc_client} from '@fuzdev/fuz_app/actions/rpc_client.js';

Creates a Proxy-based API from action specs.

Method calls are dynamically dispatched based on the action spec's kind:

  • request_response → send request, await response, return Result
  • remote_notification → send notification, return Result
  • local_call → execute locally (sync or async), return Result or throw

Generic TApi is the consumer's typed Proxy interface (typically a codegen-derived ActionsApi). Required — no default, so forgetting it is a type error rather than a silent slide into any. The `as unknown as TApi` coercion lives inside this function so call sites get a typed return without a cast at the seam. TApi is a type-layer promise about what the Proxy responds to; the runtime walks specs (kept in sync by the consumer, codegen recommended).

const api_result = create_rpc_client<MyActionsApi>({peer, environment});

options

type CreateRpcClientOptions<TApi>

returns

TApi

a Proxy typed as TApi that responds to any method name found in the environment's specs

generics

create_rpc_client<TApi extends object>
TApi
constraint object

create_rpc_endpoint
#

actions/action_rpc.ts view source

(options: CreateRpcEndpointOptions): RouteSpec[] import {create_rpc_endpoint} from '@fuzdev/fuz_app/actions/action_rpc.js';

Single JSON-RPC 2.0 endpoint — the canonical RPC transport binding.

Returns two RouteSpec entries (GET + POST on the same path) for apply_route_specs. The internal dispatcher handles:

  1. Parse envelope — POST: JSON body as JsonrpcRequest. GET: method and params from query string.
  2. Lookup method — find the RpcAction by method name.
  3. Pre-validation auth — short-circuit unauthenticated when no account is on the request, before input validation runs.
  4. Authorization phase — resolve the acting actor (when the action's auth requires role_grants or its input declares acting?: ActingActor) and build the request context. Runs before input validation so role-grant-grain auth checks return 403 before 400 invalid_params; acting is read from raw params via a string typeguard.
  5. Post-authorization auth — enforce role / keeper requirements against the request context.
  6. Validate params — parse input against the action's input schema.
  7. Rate limit — per-action IP / account throttling.
  8. Dispatch — acquire DB handle (transaction for mutations, pool for reads), construct ActionContext, call handler, return JSON-RPC response.

GET is restricted to side_effects: false actions (cacheable reads). All errors use JSON-RPC format: {jsonrpc, id, error: {code, message, data?}}.

The RouteSpecs use auth: {type: 'none'} because auth is checked per-action inside the dispatcher, and transaction: false because transaction scope is per-action (mutations get a transaction, reads get pool).

options

endpoint path, actions, and logger

returns

RouteSpec[]

route specs (GET + POST) ready for apply_route_specs

throws

  • Error - if two actions share the same `spec.method` (registration-time

create_rpc_get_url
#

testing/rpc_helpers.ts view source

(endpoint_path: string, method: string, params?: unknown, id?: string | number): string import {create_rpc_get_url} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

Build a GET URL with JSON-RPC query parameters.

endpoint_path

the RPC endpoint path (e.g., /api/rpc)

type string

method

type string

params?

params (omit for parameterless methods)

type unknown
optional

id

request id (default 'test')

type string | number
default 'test'

returns

string

create_rpc_post_init
#

testing/rpc_helpers.ts view source

(method: string, params?: unknown, id?: string | number): RequestInit import {create_rpc_post_init} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

Create a RequestInit for a JSON-RPC POST request.

method

type string

params?

params (omit for parameterless methods; null is also stripped for ergonomic call sites — JSON-RPC 2.0 §4.2 forbids "params": null on the wire, and create_rpc_endpoint rejects z.null() action input schemas at registration). Tests that need to construct a literal "params": null envelope (e.g. asserting envelope-level rejection) should build the body inline rather than route through this helper.

type unknown
optional

id

request id (default 'test')

type string | number
default 'test'

returns

RequestInit

create_scope_kind_schema
#

auth/scope_kind_schema.ts view source

(consumer_kinds: Record<string, ScopeKindMeta>): ScopeKindSchemaResult import {create_scope_kind_schema} from '@fuzdev/fuz_app/auth/scope_kind_schema.js';

Create a scope-kind schema from a consumer-declared registry.

Open registry — no builtins. The 'GLOBAL' token used inside the partial unique indexes on role_grant and role_grant_offer is not a registry entry (it's an index expression only) and cannot collide with consumer-declared kinds because the regex rejects uppercase.

Call once at server init. Pass the result into create_role_schema's optional scope_kinds parameter so each role's applicable_scope_kinds entries are validated against this set at construction time. v1 keeps applicable_scope_kinds informative-only (registry-membership validation only); v2 may add INSERT-time (role, scope_kind) enforcement once the shape is clear from real consumer usage.

consumer_kinds

the consumer-declared scope-kind set with optional metadata

type Record<string, ScopeKindMeta>

returns

ScopeKindSchemaResult

{ScopeKind, scope_kinds} — Zod schema and metadata map

throws

  • Error - if any `consumer_kinds` key fails the `ScopeKindName` regex or appears more than once

examples

// visiones const {ScopeKind, scope_kinds} = create_scope_kind_schema({ classroom: {description: 'A classroom — teacher and student role_grants scope here.'}, });

create_self_service_role_actions
#

auth/self_service_role_actions.ts view source

(deps: ActionFactoryDeps, options?: SelfServiceRoleActionsOptions): RpcAction[] import {create_self_service_role_actions} from '@fuzdev/fuz_app/auth/self_service_role_actions.js';

Build the unified self-service role toggle RPC action.

deps

ActionFactoryDeps (log, audit); audit.emit writes audit rows via the captured pool. The bound emitter encapsulates on_audit_event fan-out and the optional AuditLogConfig.

options

optional eligible-role override plus optional role schema for default-eligibility derivation

default {}

returns

RpcAction[]

the RpcAction array to spread into a create_rpc_endpoint call

throws

  • Error - at factory time if any `eligible_roles` entry is missing from `options.roles.role_specs`

create_serve_cell_fact_route_spec
#

server/serve_fact_route.ts view source

(options: CreateServeFactRouteSpecOptions): RouteSpec import {create_serve_cell_fact_route_spec} from '@fuzdev/fuz_app/server/serve_fact_route.js';

Build the cell-scoped GET /api/cells/:cell_id/facts/:hash RouteSpec — the per-reference read.

Resolves the named cell (404 if missing / soft-deleted), requires can_view_cell(caller, cell) AND cell.refs to include the hash (else 404, masked), then serves the bytes. Authz is scoped to this one (cell, hash) edge — never unioned across the fact's other referrers.

Pure-public auth — the handler builds the per-request RequestContext from c.var.account_id and enforces visibility per-reference.

options

returns

RouteSpec

create_serve_fact_route_spec
#

server/serve_fact_route.ts view source

(options: CreateServeFactRouteSpecOptions): RouteSpec import {create_serve_fact_route_spec} from '@fuzdev/fuz_app/server/serve_fact_route.js';

Build the admin-only bare-hash GET /api/facts/:hash RouteSpec.

An admin's reach already spans every cell, so serving by bare hash grants no escalation — the union concern that made this route a cross-owner leak for non-admins is vacuous for an admin. Non-admin callers are rejected at the auth phase (403) and never reach the handler. Confidential non-admin reads always go through the cell-scoped route above.

Auth is {account: 'required', actor: 'required', roles: ['admin']} — the dispatcher's authorization phase resolves the acting actor and the post-authorization guard enforces the admin role before the handler runs. The handler re-checks has_role(_, admin) as defense-in-depth so a future mounting/auth-shape regression fails closed rather than serving by bare hash to a non-admin.

options

returns

RouteSpec

create_server_status_route_spec
#

http/common_routes.ts view source

(options: ServerStatusOptions): RouteSpec import {create_server_status_route_spec} from '@fuzdev/fuz_app/http/common_routes.js';

Create an authenticated server status route spec.

Returns version and uptime. Unlike the public health check, this requires authentication.

options

returns

RouteSpec

create_session_and_set_cookie
#

create_session_config
#

auth/session_cookie.ts view source

(cookie_name: string): SessionOptions<string> import {create_session_config} from '@fuzdev/fuz_app/auth/session_cookie.js';

Create a session config for raw session token identity.

The standard pattern: cookie stores the raw session token, server hashes it (blake3) to look up the auth_session row. Only the cookie_name varies per app.

cookie_name

cookie name (e.g. 'zap_session', 'visiones_session')

type string

returns

SessionOptions<string>

a SessionOptions<string> ready for use with session middleware

create_session_cookie_value
#

create_session_middleware
#

auth/session_middleware.ts view source

<TIdentity>(keyring: Keyring, options: SessionOptions<TIdentity>): MiddlewareHandler import {create_session_middleware} from '@fuzdev/fuz_app/auth/session_middleware.js';

Create session middleware that parses cookies and sets identity on context.

Always sets the identity on context (null when invalid/missing) for type-safe reads. Uses options.context_key as the Hono context variable name.

keyring

key ring for cookie verification

type Keyring

options

session configuration

type SessionOptions<TIdentity>

returns

MiddlewareHandler

generics

create_session_middleware<TIdentity>
TIdentity

mutates

  • Hono — context - sets `options.context_key` and may refresh or clear the session cookie

create_signup_route_shape
#

auth/signup_route_schema.ts view source

(options: SignupRouteShapeOptions): Omit<RouteSpec, "handler"> import {create_signup_route_shape} from '@fuzdev/fuz_app/auth/signup_route_schema.js';

The POST /signup route shape minus its handler — pure hono-free data. create_signup_route_specs spreads this and attaches the live handler; cross-process surface builders spread it with a stub handler. Single source of truth — the shape can't drift between the live route and the surface.

options

returns

Omit<RouteSpec, "handler">

create_signup_route_specs
#

auth/signup_routes.ts view source

(deps: RouteFactoryDeps, options: SignupRouteOptions): RouteSpec[] import {create_signup_route_specs} from '@fuzdev/fuz_app/auth/signup_routes.js';

Create signup route specs for account creation.

deps

stateless capabilities

options

per-factory configuration

returns

RouteSpec[]

route specs (not yet applied to Hono)

create_spine_ready_route_spec
#

testing/cross_backend/default_spine_surface.ts view source

(log?: Logger | undefined): RouteSpec import {create_spine_ready_route_spec} from '@fuzdev/fuz_app/testing/cross_backend/default_spine_surface.js';

The spine's /ready route spec — the column-presence schema-drift deploy gate, reading . Mounted live by the TS spine binary (in build_spine_app) and the in-process readiness parity leg, but kept off the declared surface (create_spine_surface_spec) like the fact-serving / ws / sse behaviors — describe_ready_cross_tests (gated on capabilities.ready) is its explicit coverage, not the generic round-trip.

log?

optional logger for server-side drift diagnostics

type Logger | undefined
optional

returns

RouteSpec

create_spine_route_specs
#

testing/cross_backend/default_spine_surface.ts view source

(ctx: AppServerContext): RouteSpec[] import {create_spine_route_specs} from '@fuzdev/fuz_app/testing/cross_backend/default_spine_surface.js';

Account REST + signup route specs under /api/account (bootstrap auto-mounted by the surface builder / create_app_server), plus the audit-log SSE stream under /api/admin only when ctx.audit_sse is set (the TS spine binary passes audit_log_sse: true).

The shared create_spine_surface_spec() builds its ctx with audit_sse: null, so the declared surface snapshot stays SSE-free and the Rust spine_stub cross test is unaffected — only the live TS binary mounts the stream at SPINE_SSE_PATH.

ctx

returns

RouteSpec[]

create_spine_surface_spec
#

testing/cross_backend/default_spine_surface.ts view source

(): AppSurfaceSpec import {create_spine_surface_spec} from '@fuzdev/fuz_app/testing/cross_backend/default_spine_surface.js';

The AppSurfaceSpec for the standard spine surface — the wire-shape source the cross-process round-trip + RPC-round-trip suites validate against. bootstrap: {mode: 'surface_only'} mounts POST /api/account/bootstrap's shape to match the binary (which wires bootstrap for real); the harness's globalSetup already consumed the live bootstrap, so the cross-process round-trip validates the binary's 409 against the route's declared error schema.

returns

AppSurfaceSpec

create_sse_auth_guard
#

realtime/sse_auth_guard.ts view source

<T>(registry: SubscriberRegistry<T>, required_role: string | null, log: Logger): (event: AuditLogEvent) => void import {create_sse_auth_guard} from '@fuzdev/fuz_app/realtime/sse_auth_guard.js';

Create an audit event handler that closes SSE streams on auth changes.

Closes streams when:

  • role_grant_revoke fires for the required_role targeting a connected subscriber
  • session_revoke targets the specific revoked session (session-hash-scoped)
  • session_revoke_all / token_revoke_all / password_change / logout target a connected subscriber (account-wide)

The registry must use account_id as the identity key when subscribing (passed as the third argument to registry.subscribe()).

registry

the subscriber registry to guard

type SubscriberRegistry<T>

required_role

the role that grants access to the SSE endpoint, or null to skip role_grant_revoke handling entirely (for streams not gated by a specific role_grant)

type string | null

log

logger for disconnect events

type Logger

returns

(event: AuditLogEvent) => void

an on_audit_event callback

generics

create_sse_auth_guard<T>
T

create_sse_frame_reader
#

testing/transports/sse_frame_reader.ts view source

(reader: ReadableStreamDefaultReader<Uint8Array<ArrayBufferLike>>, default_timeout_ms?: number): SseFrameReader import {create_sse_frame_reader} from '@fuzdev/fuz_app/testing/transports/sse_frame_reader.js';

Wrap a byte-stream reader in \n\n-delimited SSE frame parsing.

Preserves bytes past a frame terminator in an internal buffer for the next read_frame. read_frame and wait_for_close both race each underlying read against timeout_ms so a misbehaving stream surfaces as a failure rather than a vitest hang.

reader

type ReadableStreamDefaultReader<Uint8Array<ArrayBufferLike>>

default_timeout_ms

type number
default SSE_FRAME_READ_TIMEOUT_MS

returns

SseFrameReader

create_sse_response
#

realtime/sse.ts view source

<T = unknown>(c: Context<any, any, {}>, log: Logger): { response: Response; stream: SseStream<T>; } import {create_sse_response} from '@fuzdev/fuz_app/realtime/sse.js';

Create an SSE response for a Hono context.

Wraps Hono's streamSSE to provide a {response, stream} API compatible with SubscriberRegistry push-based broadcasting. The callback suspends via a promise that resolves on client disconnect or explicit close(), keeping the stream alive for external sends.

Uses hono_stream.write() directly (not writeSSE) to avoid Hono's HTML callback resolution — keeps the same data: JSON\n\n format.

c

type Context<any, any, {}>

log

logger for serialization and on_close listener errors

type Logger

returns

{ response: Response; stream: SseStream<T>; }

object with the streaming Response and an SseStream controller

generics

create_sse_response<T = unknown>
T
default unknown

create_sse_transport
#

testing/transports/sse_transport.ts view source

(options: SseTransportOptions): Promise<SseTransport> import {create_sse_transport} from '@fuzdev/fuz_app/testing/transports/sse_transport.js';

Open a real-HTTP SSE stream pinned to options.base_url + sse_path.

Resolves once the response headers arrive and the body is a text/event-stream; rejects if the connect is refused (non-2xx status, wrong content type, missing body) so the test surfaces the real cause rather than hanging.

options

returns

Promise<SseTransport>

throws

  • Error - if the connect fails (status, content type, or no body).

create_standard_adversarial_cases
#

testing/adversarial_headers.ts view source

(allowed_origin: string): AdversarialHeaderCase[] import {create_standard_adversarial_cases} from '@fuzdev/fuz_app/testing/adversarial_headers.js';

7 standard adversarial header cases applicable to any middleware stack.

Origin verification is Origin-only — fuz_app's verify_request_source no longer falls back to Referer (matches zzz_server::auth::is_request_origin_allowed). Bearer auth still treats a Referer header as a browser-context indicator and silently discards the bearer token — so Referer-bearing requests reach the route as unauthenticated rather than 403.

allowed_origin

an origin that passes the origin check

type string

returns

AdversarialHeaderCase[]

create_standard_rpc_actions
#

auth/standard_rpc_actions.ts view source

(deps: StandardRpcActionsDeps, options?: StandardRpcActionsOptions): RpcAction[] import {create_standard_rpc_actions} from '@fuzdev/fuz_app/auth/standard_rpc_actions.js';

Build the combined admin + role-grant-offer + account RPC action set.

Spreads create_admin_actions(deps, {roles}), create_role_grant_offer_actions(deps, {roles, default_ttl_ms, authorize}), and create_account_actions(deps, {max_tokens}). The shared roles option flows to admin + role-grant-offer.

deps

StandardRpcActionsDeps (log, audit from ActionFactoryDeps; optional notification_sender for WS fan-out)

options

role schema, role-grant-offer config, account config

default {}

returns

RpcAction[]

RPC actions to pass as rpc_endpoints or spread into create_rpc_endpoint

create_static_middleware
#

server/static.ts view source

(serve_static: ServeStaticFactory, options?: { root?: string | undefined; spa_fallback?: string | undefined; is_spa_route?: ((path: string) => boolean) | undefined; } | undefined): MiddlewareHandler[] import {create_static_middleware} from '@fuzdev/fuz_app/server/static.js';

Create static file serving middleware for SvelteKit static builds.

Returns an array of middleware handlers to register on '/*'.

serve_static

runtime-specific serveStatic factory

options?

optional root directory and SPA fallback path

type { root?: string | undefined; spa_fallback?: string | undefined; is_spa_route?: ((path: string) => boolean) | undefined; } | undefined
optional

returns

MiddlewareHandler[]

create_stub_api_middleware
#

testing/stubs.ts view source

(options?: { include_daemon_token?: boolean | undefined; } | undefined): MiddlewareSpec[] import {create_stub_api_middleware} from '@fuzdev/fuz_app/testing/stubs.js';

Create the API middleware stub array matching create_auth_middleware_specs output.

options?

type { include_daemon_token?: boolean | undefined; } | undefined
optional

returns

MiddlewareSpec[]

create_stub_app_deps
#

testing/stubs.ts view source

(): AppDeps import {create_stub_app_deps} from '@fuzdev/fuz_app/testing/stubs.js';

Create no-op AppDeps for auth surface testing.

returns

AppDeps

create_stub_app_server_context
#

testing/stubs.ts view source

(session_options: SessionOptions<string>): AppServerContext import {create_stub_app_server_context} from '@fuzdev/fuz_app/testing/stubs.js';

Create a stub AppServerContext for attack surface testing.

Provides sensible defaults for all fields. Pass session_options since it varies per consumer; other fields use stubs/nulls.

session_options

consumer's session config (required — varies per app)

type SessionOptions<string>

returns

AppServerContext

create_stub_audit_sse
#

testing/stubs.ts view source

(): AuditLogSse import {create_stub_audit_sse} from '@fuzdev/fuz_app/testing/stubs.js';

Build a no-op AuditLogSse for tests that wire audit_sse into the surface helper but don't assert on SSE fan-out or subscriber state.

subscribe returns a no-op cleanup; on_audit_event is a no-op; the registry is a fresh SubscriberRegistry instance (call sites that inspect .size or call .close_* see a real registry, so writes are isolated per test). Tests that need real SSE plumbing build it via create_audit_log_sse against create_test_app.

returns

AuditLogSse

create_stub_db
#

testing/stubs.ts view source

(): Db import {create_stub_db} from '@fuzdev/fuz_app/testing/stubs.js';

Create a stub Db for handler tests that use apply_route_specs with declarative transactions.

Returns a real Db instance with:

  • query returns empty rows (safety net for unmocked query functions)
  • query_one returns undefined
  • transaction(fn) calls fn(db) synchronously (no real transaction)

returns

Db

create_stub_upgrade
#

testing/ws_round_trip.ts view source

(): StubUpgrade import {create_stub_upgrade} from '@fuzdev/fuz_app/testing/ws_round_trip.js';

Build a fake upgradeWebSocket that captures the createEvents callback. The returned middleware is inert — tests drive createEvents directly.

returns

StubUpgrade

create_surface_route_spec
#

http/common_routes.ts view source

(options: SurfaceRouteOptions): RouteSpec import {create_surface_route_spec} from '@fuzdev/fuz_app/http/common_routes.js';

Create an authenticated route spec that serves the AppSurface as JSON.

Surface data reveals API structure (routes, auth, schemas), so this requires authentication like the server status route.

options

returns

RouteSpec

create_test_account
#

testing/entities.ts view source

(overrides?: TestAccountOverrides | undefined): Account import {create_test_account} from '@fuzdev/fuz_app/testing/entities.js';

Create a test Account with sensible defaults.

overrides?

type TestAccountOverrides | undefined
optional

returns

Account

create_test_account_with_actor
#

testing/db_entities.ts view source

(db: Db, options: { username: string; password_hash?: string | undefined; }): Promise<TestAccountWithActor> import {create_test_account_with_actor} from '@fuzdev/fuz_app/testing/db_entities.js';

Create an account + actor row pair in the database for tests.

Wraps query_create_account_with_actor with a default password_hash so suites that don't exercise password verification can stay terse. Replaces the per-file create_user / create_test_actor / create_test_account helpers that had accumulated across the auth test suite.

db

type Db

options

type { username: string; password_hash?: string | undefined; }

returns

Promise<TestAccountWithActor>

create_test_account_with_credentials
#

testing/app_server.ts view source

(options: CreateTestAccountWithCredentialsOptions): Promise<{ account: { id: string & $brand<"Uuid">; username: string; }; actor: { ...; }; api_token: string; session_cookie: string; }> import {create_test_account_with_credentials} from '@fuzdev/fuz_app/testing/app_server.js';

Create a test account with credentials. Use for additional accounts minted alongside the keeper (e.g. TestApp.create_account for cross-account / multi-user tests). Does NOT flip bootstrap_lock — non-keeper accounts should not appear to the system as bootstrap having happened.

Creates an account with actor, grants roles, creates an API token, creates a session, and signs a session cookie.

options

returns

Promise<{ account: { id: string & $brand<"Uuid">; username: string; }; actor: { id: string & $brand<"Uuid">; }; api_token: string; session_cookie: string; }>

mutates

  • the — underlying `options.db` — inserts rows into `account`, `actor`,

create_test_actor
#

testing/entities.ts view source

(overrides?: TestActorOverrides | undefined): Actor import {create_test_actor} from '@fuzdev/fuz_app/testing/entities.js';

Create a test Actor with sensible defaults.

overrides?

type TestActorOverrides | undefined
optional

returns

Actor

create_test_app
#

testing/app_server.ts view source

(options: CreateTestAppOptions): Promise<TestApp> import {create_test_app} from '@fuzdev/fuz_app/testing/app_server.js';

Create a fully assembled test app with a Hono server, middleware, and routes.

Combines create_test_app_server + create_app_server into a single call. Disables rate limiters and logging by default (test-friendly).

A fresh Hono app is created each call — middleware closures bind to the server's deps (db, keyring), so reuse across servers is unsafe. The expensive resource (PGlite WASM) is cached separately in test_db.ts.

options

test app configuration

returns

Promise<TestApp>

a TestApp ready for HTTP testing

create_test_app_for_bootstrap
#

testing/app_server.ts view source

(options: CreateTestAppForBootstrapOptions): Promise<TestAppForBootstrap> import {create_test_app_for_bootstrap} from '@fuzdev/fuz_app/testing/app_server.js';

Create a test app in the pre-bootstrap state for exercising the bootstrap success path end-to-end.

Skips the keeper pre-creation create_test_app does by default — bootstrap_lock.bootstrapped stays at false and the DB has no accounts. The fs stubs return options.bootstrap_token when the bootstrap handler reads bootstrap.token_path, so a POST /bootstrap with {token: bootstrap_token, username, password} reaches the success branch.

Pair with describe_bootstrap_success_tests for the consumer-runnable suite that drives the full happy path + adjacent assertions on observable state (account exists, audit row emitted, on_bootstrap callback fired).

options

bootstrap config + factory inputs

returns

Promise<TestAppForBootstrap>

a TestAppForBootstrap ready for the test to drive bootstrap

create_test_app_from_specs
#

testing/auth_apps.ts view source

(route_specs: RouteSpec[], auth_ctx?: RequestContext | undefined, credential_type?: "session" | "api_token" | "daemon_token" | undefined): Hono<BlankEnv, BlankSchema, "/"> import {create_test_app_from_specs} from '@fuzdev/fuz_app/testing/auth_apps.js';

Create a Hono test app from route specs with optional auth context.

route_specs

the route specs to register

type RouteSpec[]

auth_ctx?

optional request context to inject via middleware

type RequestContext | undefined
optional

credential_type?

optional credential type (default: 'session' when auth_ctx provided)

type "session" | "api_token" | "daemon_token" | undefined
optional

returns

Hono<BlankEnv, BlankSchema, "/">

create_test_app_server
#

create_test_app_surface_spec
#

testing/stubs.ts view source

(options: CreateTestAppSurfaceSpecOptions): AppSurfaceSpec import {create_test_app_surface_spec} from '@fuzdev/fuz_app/testing/stubs.js';

Create an AppSurfaceSpec for the standard testing suites.

Used by both in-process and cross-process tests as the schema source — the cross-process-ness lives in the transport + per-test fixture, not here. The on-disk *_attack_surface.json snapshot is observability (gen-time drift detection via assert_surface_matches_snapshot); the suites consume the spec object this function returns, not the JSON file.

Mirrors create_app_server's route assembly: consumer routes + factory-managed bootstrap routes + surface generation. If create_app_server changes how it wires routes, update this helper to stay in sync (single source of truth for all consumers).

options

surface spec options

returns

AppSurfaceSpec

the surface spec for the standard suites

create_test_audit_emitter
#

testing/stubs.ts view source

(): AuditEmitter import {create_test_audit_emitter} from '@fuzdev/fuz_app/testing/stubs.js';

Build a no-op AuditEmitter for tests that don't assert on audit fan-out.

emit / emit_role_grant_target are no-ops; emit_pool resolves immediately; notify is a no-op; add_listener throws, so a test that wires a listener fails loudly instead of silently never firing (create_recording_audit_emitter accepts listeners); listener_count returns 0. Tests asserting on real audit-row persistence (or on listener fan-out) build a real emitter via create_audit_emitter against a stub or real DB — create_test_app already does this on the test backend.

returns

AuditEmitter

create_test_audit_event
#

testing/entities.ts view source

(overrides?: TestAuditEventOverrides | undefined): AuditLogEvent import {create_test_audit_event} from '@fuzdev/fuz_app/testing/entities.js';

Create a test AuditLogEvent with sensible defaults.

overrides?

type TestAuditEventOverrides | undefined
optional

returns

AuditLogEvent

create_test_context
#

testing/entities.ts view source

(role_grants?: TestRoleGrantOverrides[]): RequestContext import {create_test_context} from '@fuzdev/fuz_app/testing/entities.js';

Create a test RequestContext with role_grants from partial overrides.

role_grants

type TestRoleGrantOverrides[]
default [{}]

returns

RequestContext

create_test_extra_actor
#

testing/db_entities.ts view source

(db: Db, account_id: string, name: string): Promise<Actor> import {create_test_extra_actor} from '@fuzdev/fuz_app/testing/db_entities.js';

Add a second actor to an existing test account — the multi-actor edge.

Wraps the production query_create_actor; the TS twin of the Rust tests/common seed_extra_actor helper. Lets a .db.test.ts suite build a multi-actor account without reimplementing the raw insert.

db

type Db

account_id

type string

name

type string

returns

Promise<Actor>

create_test_middleware_stack_app
#

testing/middleware.ts view source

(options?: TestMiddlewareStackOptions | undefined): TestMiddlewareStackApp import {create_test_middleware_stack_app} from '@fuzdev/fuz_app/testing/middleware.js';

Create a Hono app with real proxy + origin + bearer middleware for integration testing.

All DB queries return undefined (no real database needed). The echo route at TEST_MIDDLEWARE_PATH returns {ok, client_ip, has_context}.

options?

type TestMiddlewareStackOptions | undefined
optional

returns

TestMiddlewareStackApp

the app and mock spies (reconfigure via mockImplementation for valid-token paths)

create_test_request_context
#

testing/auth_apps.ts view source

(role?: string | undefined): RequestContext import {create_test_request_context} from '@fuzdev/fuz_app/testing/auth_apps.js';

Create a mock RequestContext with optional role role_grant.

role?

type string | undefined
optional

returns

RequestContext

create_test_role_grant
#

testing/entities.ts view source

(overrides?: TestRoleGrantOverrides | undefined): RoleGrant import {create_test_role_grant} from '@fuzdev/fuz_app/testing/entities.js';

Create a test RoleGrant with sensible defaults.

overrides?

type TestRoleGrantOverrides | undefined
optional

returns

RoleGrant

create_test_role_grant_direct
#

testing/db_entities.ts view source

(db: Db, input: CreateRoleGrantInput): Promise<RoleGrant> import {create_test_role_grant_direct} from '@fuzdev/fuz_app/testing/db_entities.js';

Materialize a role_grant directly via query_create_role_grant, bypassing the production offer/accept consent flow.

In-process only. This helper takes a raw Db handle and seeds rows without firing audit fan-out, WebSocket broadcasts, or the _supersede notification chain a real grant emits. Cross-process suites must instead drive role_grant_offer_create_action_spec + role_grant_offer_accept_action_spec via testing/role_grant_helpers.ts's role_grant_offer_and_accept so the fixture observes the full post-commit fan-out the way production does — otherwise tests would mask real divergence between the TS and Rust spines.

Use this helper for query-level (*.db.test.ts) tests that exercise revoke or isolation semantics — not the consent path itself. The schema's source_offer_id = null shape is an intentional admin-direct escape; this helper exposes it so suites don't reimplement the same direct-seed wrapper.

db

type Db

input

returns

Promise<RoleGrant>

create_testing_action_manifest_action
#

create_testing_actions
#

testing/cross_backend/testing_reset_actions.ts view source

(deps: AppDeps, options: CreateTestingActionsOptions): RpcAction[] import {create_testing_actions} from '@fuzdev/fuz_app/testing/cross_backend/testing_reset_actions.js';

Build the testing RPC actions for a test binary's registry.

Returns _testing_reset — the single privileged action test binaries register. The test binary calls this at server-assembly time and registers the result on its dispatcher.

The reset action's table-wipe list mirrors auth_integration_truncate_tables from testing/db.ts — the canonical "auth tables a between-test reset must clear" set. testing_reset_actions.coverage.test.ts enforces the set-equality invariant so a future auth migration that adds a table to that list without updating this handler fails CI.

deps

type AppDeps

options

returns

RpcAction[]

create_testing_drain_effects_action
#

testing/cross_backend/testing_reset_actions.ts view source

(): RpcAction import {create_testing_drain_effects_action} from '@fuzdev/fuz_app/testing/cross_backend/testing_reset_actions.js';

Build the standalone _testing_drain_effects action. No deps — on TS the barrier is satisfied by await_pending_effects (see the spec doc), so the handler just returns {ok: true}. Mount it on any test endpoint whose suite asserts on audit rows (the spine binary bundles it via create_testing_actions; in-process suites mount it directly).

returns

RpcAction

create_testing_migration_tracker_action
#

create_testing_schema_snapshot_action
#

create_throwing_api
#

actions/rpc_client.ts view source

<TApi extends object>(api_result: TApi): ThrowingApi<TApi> import {create_throwing_api} from '@fuzdev/fuz_app/actions/rpc_client.js';

Wrap a typed RPC client so every call resolves to its unwrapped value or throws an Error carrying the JSON-RPC {code, message, data?} shape.

Implementation is a Proxy because the underlying create_rpc_client return is itself a Proxy with no concrete keys — a key-by-key wrap would need to enumerate the typed surface, which only the consumer's generated ActionsApi interface knows.

Pass-through on non-Result returns is deliberate: sync local_call Proxy methods return values directly (see create_sync_local_call_method above). The Proxy can't distinguish those at get-time, so the wrapper inspects result shape at call-time and only unwraps when it sees a Result. Non-object returns pass through unchanged.

Only {code, data} cross onto the thrown Error — name / stack are left as the Error's own properties so attacker-shaped result.error payloads cannot overwrite them.

Recommended consumer convention: create_frontend_rpc_client ships both shapes by default — api (throwing) for hot-path call sites and api_result (Result) for sites that inspect error.data.reason without try/catch. Result is the protocol primitive; this wrapper is the ergonomic layer over it. Picking is per call site — both Proxies share the same underlying transport.

Catch blocks read err.data?.reason — optional chaining required because JSON-RPC data is spec-level optional.

On unknown string-keyed methods, the get trap returns a function that throws "rpc method not found: <prop>" on invocation — clearer than the JS default "api.foo is not a function". Symbol props and then stay undefined so the Proxy isn't accidentally treated as a thenable (await api would otherwise probe then and trip the thrower).

api_result

typed Result-returning RPC client from create_rpc_client<ActionsApi>(...). The "_result" suffix names what the underlying calls return (Result<{value}, {error}>).

type TApi

returns

ThrowingApi<TApi>

generics

create_throwing_api<TApi extends object>
TApi
constraint object

create_throwing_stub
#

testing/stubs.ts view source

<T = any>(label: string): T import {create_throwing_stub} from '@fuzdev/fuz_app/testing/stubs.js';

Create a Proxy that throws descriptive errors on any property access or method call.

Use for deps that should never be reached during a test. If a test accidentally calls through to a throwing stub, the error message identifies exactly which stub was hit, catching test bugs that would silently pass with {} as any.

JS-internal probes (Symbol, then, constructor, $$typeof) return undefined so the proxy doesn't crash framework-level identity checks; toJSON returns "[throwing_stub:label]" so accidental serialization surfaces the stub's identity in console output rather than silent "{}".

label

descriptive name for error messages (e.g. 'keyring', 'db')

type string

returns

T

generics

create_throwing_stub<T = any>
T
default any

throws

  • Error - on any non-internal property access, labeled with the stub

create_validated_broadcaster
#

realtime/sse.ts view source

<T extends SseNotification>(broadcaster: { broadcast: (channel: string, data: T) => void; }, event_specs: EventSpec[], log: Logger): { broadcast: (channel: string, data: T) => void; } import {create_validated_broadcaster} from '@fuzdev/fuz_app/realtime/sse.js';

Create a broadcaster that validates events in DEV mode.

In DEV: warns on unknown methods and invalid params. In production: passes through with zero overhead.

broadcaster

duck-typed broadcaster (e.g. SubscriberRegistry)

type { broadcast: (channel: string, data: T) => void; }

event_specs

event specs to validate against

type EventSpec[]

log

logger used to emit DEV warnings on unknown methods or param mismatches

type Logger

returns

{ broadcast: (channel: string, data: T) => void; }

validated broadcaster wrapper (passthrough in production)

generics

create_validated_broadcaster<T extends SseNotification>
T
constraint SseNotification

create_validated_keyring
#

auth/keyring.ts view source

(env_value: string | undefined): ValidatedKeyringResult import {create_validated_keyring} from '@fuzdev/fuz_app/auth/keyring.js';

Validate and create a keyring in one step.

Returns a discriminated union so callers handle exit/logging their own way (e.g. Deno.exit(1) vs runtime.exit(1)).

env_value

the SECRET_FUZ_COOKIE_KEYS environment variable

type string | undefined

returns

ValidatedKeyringResult

{ok: true, keyring} or {ok: false, errors}

create_ws_auth_guard
#

actions/transports_ws_auth_guard.ts view source

(transport: BackendWebsocketTransport, log: Logger): AuditEventHandler import {create_ws_auth_guard} from '@fuzdev/fuz_app/actions/transports_ws_auth_guard.js';

Create an audit event handler that closes WebSocket connections on auth changes.

Ignores outcome === 'failure' events — they carry attacker-controlled identifiers (e.g. a session_revoke that the DB rejected still records the submitted session_id), so reacting to them would let any authenticated user close another user's socket by guessing a session hash or token id.

transport

log

logger for disconnect events (info level on non-zero closures)

type Logger

returns

AuditEventHandler

an on_audit_event callback suitable for create_audit_emitter's on_audit_event slot, or for registering via audit.add_listener post-assembly. The returned callback mutates transport (closing matching sockets via close_sockets_for_session / _token / _account) on every relevant event.

create_ws_logout_closer
#

actions/transports_ws_auth_guard.ts view source

(transport: BackendWebsocketTransport, log: Logger): AuditEventHandler import {create_ws_logout_closer} from '@fuzdev/fuz_app/actions/transports_ws_auth_guard.js';

Create an audit event handler that closes WebSocket connections on user-initiated logout.

Sibling helper to create_ws_auth_guard — kept separate because ws_disconnect_event_types deliberately omits logout (admin-initiated revocations use session_revoke, while logout is the user-initiated case). Multiple consumers hand-rolled this same branch before extraction.

Compose with create_ws_auth_guard to handle both kinds of disconnect:

const ws_guard = create_ws_auth_guard(transport, log); const ws_logout_closer = create_ws_logout_closer(transport, log); const on_audit_event = (event: AuditLogEvent): void => { ws_guard(event); ws_logout_closer(event); };

Ignores outcome === 'failure' events — failed logouts carry unauthenticated identifiers (no session to close anyway), and reacting to them would let an unauthenticated probe close the targeted account's sockets by submitting a logout for an arbitrary account_id.

transport

log

logger for disconnect events (info level on non-zero closures)

type Logger

returns

AuditEventHandler

an on_audit_event callback wireable alongside create_ws_auth_guard. The returned callback mutates transport via close_sockets_for_account on every successful logout event with a non-empty account_id.

create_ws_test_harness
#

testing/ws_round_trip.ts view source

(options: CreateWsTestHarnessOptions): WsTestHarness import {create_ws_test_harness} from '@fuzdev/fuz_app/testing/ws_round_trip.js';

Create a WebSocket test harness for the given specs + handlers.

Registers against a throwaway Hono app with a fake upgradeWebSocket; the captured events factory is invoked per connect() with a synthesized Hono context carrying the requested auth identity. Returned clients drive the real onOpen/onMessage/onClose path against a real WSContext.

options

returns

WsTestHarness

create_ws_transport
#

testing/transports/ws_transport.ts view source

(options: WsTransportOptions): Promise<WsClient> import {create_ws_transport} from '@fuzdev/fuz_app/testing/transports/ws_transport.js';

Build a real-upgrade WS client pinned to options.base_url + ws_path.

Resolves once the upgrade succeeds and the socket is in OPEN state; rejects if the upgrade is refused (401, allowlist rejection, network failure). Incoming messages are JSON-parsed and pushed onto the messages array; wait_for checks already-received messages first before waiting for new arrivals.

options

returns

Promise<WsClient>

throws

  • Error - if the upgrade fails (status, network) — the rejection

create_x_accel_config
#

server/x_accel.ts view source

(redirect_prefix: string, nginx_config: string): { redirect_prefix: string; } & $brand<"XAccelConfig"> import {create_x_accel_config} from '@fuzdev/fuz_app/server/x_accel.js';

Build a validated XAccelConfig, asserting the nginx location serving redirect_prefix is internal;.

The make-impossible-states gate: serving can only emit X-Accel-Redirect into a location proven internal; at boot.

redirect_prefix

the X-Accel redirect prefix (e.g. /_facts/)

type string

nginx_config

the nginx config template string to validate against

type string

returns

{ redirect_prefix: string; } & $brand<"XAccelConfig">

the validated XAccelConfig carrying redirect_prefix

throws

  • `XAccelConfigError` when `nginx_config` has no matching `location` for `redirect_prefix`, or the matching block is not marked `internal;` — either is a public facts location that bypasses every cell-visibility check.

CreateAccountInput
#

auth/account_schema.ts view source

CreateAccountInput import type {CreateAccountInput} from '@fuzdev/fuz_app/auth/account_schema.js';

username

type Username

password_hash

type string

email?

type Email | null

CreateAppBackendOptions
#

server/app_backend.ts view source

CreateAppBackendOptions import type {CreateAppBackendOptions} from '@fuzdev/fuz_app/server/app_backend.js';

Input for create_app_backend().

keyring is passed pre-validated — callers handle their own error reporting (e.g., tx uses runtime.exit(1) on invalid keys).

stat

Get file/directory stats, or null if path doesn't exist.

type (path: string) => Promise<StatResult | null>

read_text_file

Read a file as text.

type (path: string) => Promise<string>

delete_file

Delete a file.

type (path: string) => Promise<void>

database_url

Database connection URL (postgres://, file://, or memory://).

type string

keyring

Validated cookie signing keyring.

type Keyring

password

Password hashing implementation. Use argon2_password_deps in production.

type PasswordHashDeps

log?

Structured logger instance. Omit for default (new Logger('server')).

type Logger

audit_factory

Build the bound AuditEmitter once the backend's pool Db + Logger exist. Required — the factory owns listener composition and AuditLogConfig selection without create_app_backend holding a default. Typical body:

audit_factory: ({db, log}) => create_audit_emitter({ db, log, on_audit_event, audit_log_config, })

Additional listeners (factory-managed audit SSE, per-endpoint WS auth guards) are registered at create_app_server time via audit.add_listener(...).

type AuditFactory

migration_namespaces?

Additional migration namespaces to run after the builtin auth namespace. The shared schema_version table records one row per applied migration (namespace, name, sequence); order is append-only so forward-only guarantees hold per-namespace.

Names in reserved_migration_namespaces (currently ['fuz_auth']) are rejected at startup. Omit for no extra namespaces. This is the only place to splice consumer migrations — DB init belongs to the backend lifecycle, not server assembly.

type ReadonlyArray<MigrationNamespace>

CreateAuditEmitterOptions
#

auth/audit_emitter.ts view source

CreateAuditEmitterOptions import type {CreateAuditEmitterOptions} from '@fuzdev/fuz_app/auth/audit_emitter.js';

db

Pool-level Db. Captured by every emit call.

type Db

log

Logger for write + listener-callback failures.

type Logger

on_audit_event?

Initial listener — registered as the first listener when set. Omit for backends that compose listeners post-assembly (e.g. via audit_log_sse).

type ((event: AuditLogEvent) => void) | null

audit_log_config?

Audit-log config. Defaults to builtin_audit_log_config. Consumer- extended configs from create_audit_log_config({extra_events}) get registered here once at backend assembly.

type AuditLogConfig

emit_decorator?

Test-only hook to wrap emit at construction time. The decorated function is captured by emit_role_grant_target's closure and is the function exposed on the returned AuditEmitter, so both call shapes route through it — see EmitDecorator for the rationale.

Leave unset in production. The intended caller is create_emit_ordering_audit_factory in testing/audit_drift_guard.ts.

type EmitDecorator

CreateAuditLogConfigOptions
#

auth/audit_log_schema.ts view source

CreateAuditLogConfigOptions import type {CreateAuditLogConfigOptions} from '@fuzdev/fuz_app/auth/audit_log_schema.js';

extra_events?

Extra event types keyed by event-type string. Value is a Zod metadata schema, or null to register the type without validation (row still written, metadata stored as raw JSONB).

Collisions with builtin event-type strings throw at construction. Schemas are run via safeParse at insert time; mismatches log + count but never throw (fail-open — see the drift counters in auth/audit_log_queries.ts).

type Readonly<Record<string, z.ZodType | null>>

CreateBroadcastApiOptions
#

actions/broadcast_api.ts view source

CreateBroadcastApiOptions import type {CreateBroadcastApiOptions} from '@fuzdev/fuz_app/actions/broadcast_api.js';

peer

The peer holding the transport registry used for sends.

type ActionDispatcher

specs

Notification specs to expose as broadcast methods. Typically the remote_notification specs whose initiator is backend (or both). Other kinds are accepted — the helper only uses spec.method and spec.input — but the typical use is notifications.

type ReadonlyArray<ActionSpecUnion>

log?

Logger for validation/send errors. Defaults to a [broadcast] namespace.

type LoggerType | null

should_deliver?

Optional per-connection ACL predicate. When set, the broadcast fans out via the transport's broadcast_filtered (feature-detected) — each connection's identity is checked before the message is sent. When unset, the transport broadcasts unfiltered via transport.send.

Requires a transport that implements FilterableBroadcastTransport (today: only BackendWebsocketTransport). If set and the active transport is not filterable, the send is skipped and an error logged.

type ShouldDeliverFn

CreateDatabaseOptions
#

CreateDbResult
#

db/create_db.ts view source

CreateDbResult import type {CreateDbResult} from '@fuzdev/fuz_app/db/create_db.js';

Result of database initialization.

db

type Db

close

Close the database connection. Bound to the actual driver at construction.

type () => Promise<void>

db_type

type DbType

db_name

type string

CreateFrontendRpcClientOptions
#

actions/frontend_rpc_client.ts view source

CreateFrontendRpcClientOptions<TApi> import type {CreateFrontendRpcClientOptions} from '@fuzdev/fuz_app/actions/frontend_rpc_client.js';

generics

CreateFrontendRpcClientOptions<TApi extends object = object>
TApi
constraint object
default object

specs

Action specs the typed Proxy can dispatch. Methods absent from this list silently return undefined from the Proxy — the generic TApi cannot constrain runtime membership, so consumers must keep this list in sync with the typed surface (codegen recommended).

Protocol actions (heartbeat, cancel) are not auto-spread — they're filtered out of generated action_specs by codegen's include_protocol_actions: false default and consumers spread them in explicitly so the contract stays visible at every registration site. For WS-using consumers, spread protocol_action_specs from actions/protocol.ts here: specs: [...protocol_action_specs, ...action_specs]. HTTP-only consumers can omit them.

type ReadonlyArray<ActionSpecUnion>

path?

HTTP RPC endpoint path for the default FrontendHttpTransport. Defaults to /api/rpc. Ignored when transports is provided.

type string

transports?

Optional explicit transport list. When provided, the default FrontendHttpTransport(path) is not registered — the caller is responsible for at least one ready transport. Use for WS-first or WS+HTTP mixed setups.

type ReadonlyArray<Transport>

transport_for_method?

Optional per-method transport selector — pure pass-through to create_rpc_client. Return the transport name to use for a given method, or undefined to fall back to the peer's default selection.

Useful when methods are registered on different backend dispatchers (e.g. streaming actions on WS, REST RPC on HTTP) — a tx-style mixed setup. Per-call RpcClientCallOptions.transport_name overrides this for individual dispatches.

type TransportForMethod

on_action_event?

Optional callback fired once per dispatched action — pure pass-through to create_rpc_client. Used by zzz-style consumers that thread the ActionEvent into a reactive cell (add_from_json + listen_to_action_event) for pending / failed / value derivations.

event.spec.method and event.data.method narrow to keyof TApi & string — drop the as ActionMethod cast at the call site when TApi is a generated ActionsApi interface.

type (event: ActionEvent<keyof TApi & string>) => void

lookup_action_handler?

Optional handler resolver. Wired onto environment.lookup_action_handler — the registry the dispatcher uses to find handlers for inbound messages and lifecycle phases. Defaults to () => undefined, which is fine for HTTP-only frontends that never receive a server-pushed notification or register a receive_error recovery hook.

Common reasons to provide this:

  • Server-pushed notifications over WS — return a handler for (method, 'receive') so a remote_notification arriving on the socket dispatches to your subscriber bus (tx-style).
  • Per-method retry / telemetry on errors — return a handler for (method, 'receive_error'). Note that as of the extract_action_result fix, a missing handler already produces {ok: false, error} — the stub is no longer required just to surface server errors.

type ActionEventEnvironment['lookup_action_handler']

CreateInviteInput
#

auth/invite_schema.ts view source

CreateInviteInput import type {CreateInviteInput} from '@fuzdev/fuz_app/auth/invite_schema.js';

Input for creating an invite.

email?

type Email | null

username?

type Username | null

created_by

type Uuid | null

CreateRoleGrantInput
#

auth/account_schema.ts view source

CreateRoleGrantInput import type {CreateRoleGrantInput} from '@fuzdev/fuz_app/auth/account_schema.js';

actor_id

type Uuid

role

type string

scope_kind?

Machine-readable kind for the scope_id. Required iff scope_id is set; must be null/omitted when scope_id is null. The DB-level role_grant_scope_kind_paired CHECK rejects mismatched pairs.

type string | null

scope_id?

Scope the grant applies to. null / omitted grants a global role_grant.

type Uuid | null

expires_at?

type Date | null

granted_by

type Uuid | null

source_offer_id?

Offer id that produced this role_grant. Set by query_accept_offer; leave unset for direct grants.

type Uuid | null

CreateRoleGrantOfferInput
#

auth/role_grant_offer_schema.ts view source

CreateRoleGrantOfferInput import type {CreateRoleGrantOfferInput} from '@fuzdev/fuz_app/auth/role_grant_offer_schema.js';

Input for query_role_grant_offer_create.

expires_at must be supplied — the query layer does not apply a default, so callers can thread their own TTL (typically ROLE_GRANT_OFFER_DEFAULT_TTL_MS).

from_actor_id

type Uuid

to_account_id

type Uuid

to_actor_id?

Optional actor-grain target on the recipient account. When set, query_role_grant_offer_create validates that the actor belongs to to_account_id and stamps the column; accept then matches against this specific actor. Omit (or pass null) for the account-grain default — any actor on to_account_id may accept.

type Uuid | null

role

type string

scope_kind?

Machine-readable kind for the scope_id. Required iff scope_id is set; must be null when scope_id is null (DB-level CHECK rejects the mismatch). Consumer-declared via create_scope_kind_schema(...).

type string | null

scope_id?

type Uuid | null

message?

type string | null

expires_at

type Date

CreateRoleSchemaOptions
#

auth/role_schema.ts view source

CreateRoleSchemaOptions import type {CreateRoleSchemaOptions} from '@fuzdev/fuz_app/auth/role_schema.js';

Optional registries to validate RoleSpec cross-axis fields against at construction time.

credential_types?

Pass create_credential_type_schema() to validate RoleSpec.required_credential_types entries.

type CredentialTypeSchemaResult

scope_kinds?

Pass create_scope_kind_schema() to validate RoleSpec.applicable_scope_kinds entries.

type ScopeKindSchemaResult

grant_paths?

Pass create_grant_path_schema() to validate RoleSpec.grant_paths entries.

type GrantPathSchemaResult

CreateRpcClientOptions
#

actions/rpc_client.ts view source

CreateRpcClientOptions<TApi> import type {CreateRpcClientOptions} from '@fuzdev/fuz_app/actions/rpc_client.js';

Options for create_rpc_client.

generics

CreateRpcClientOptions<TApi extends object = object>
TApi
constraint object
default object

peer

type ActionDispatcher

environment

type ActionEventEnvironment

on_action_event?

Optional callback fired once per dispatched action with the live ActionEvent. Consumers wire reactive state here — e.g. zzz's Actions cell calls add_from_json + listen_to_action_event inside the callback so its history stays decoupled from the rpc_client surface.

event.spec.method and event.data.method narrow to keyof TApi & string — drop the as ActionMethod cast at the call site when TApi is a generated ActionsApi interface.

type (event: ActionEvent<keyof TApi & string>) => void

transport_for_method?

Optional per-method transport selector. When provided, the client calls peer.send(msg, {transport_name}) with the returned transport for each request_response / remote_notification dispatch. Returning undefined falls back to the peer's default selection.

type TransportForMethod

CreateRpcEndpointOptions
#

actions/action_rpc.ts view source

CreateRpcEndpointOptions import type {CreateRpcEndpointOptions} from '@fuzdev/fuz_app/actions/action_rpc.js';

Options for create_rpc_endpoint.

path

Mount path for the endpoint (e.g., /api/rpc).

type string

actions

RPC actions to serve.

type Array<RpcAction>

log

Logger instance for handler context.

type Logger

action_ip_rate_limiter?

Per-IP rate limiter consulted for actions whose spec declares rate_limit: 'ip' or 'both'. null disables the IP check. Per-action gate via action.spec.rate_limit. Same limiter is shared with the WebSocket action dispatcher — one budget per action, not per transport.

type RateLimiter | null

action_account_rate_limiter?

Per-account rate limiter consulted for actions whose spec declares rate_limit: 'account' or 'both'. Keyed on request_context.account.id (account-grain — billed to the authenticated account regardless of which actor was resolved). null disables the account check. Same limiter is shared with the WebSocket action dispatcher.

type RateLimiter | null

CreateServeFactRouteSpecOptions
#

server/serve_fact_route.ts view source

CreateServeFactRouteSpecOptions import type {CreateServeFactRouteSpecOptions} from '@fuzdev/fuz_app/server/serve_fact_route.js';

deps

App deps reference. Currently unused at handler time (cell + fact tables are read via RouteContext.db); kept on the factory signature for symmetry with sibling route factories and to give future role_grant-scoped viewer extensions somewhere to read other deps without changing the public shape.

type AppDeps

facts_dir

Absolute path of the facts directory. Used for the dev/test streaming path.

type string

x_accel?

When set, external facts return an X-Accel-Redirect pointing at ${x_accel.redirect_prefix}<shard>/<rest> — nginx's internal facts location serves the bytes. When unset, external facts stream from <facts_dir>/<shard>/<rest> directly. Production sets a validated XAccelConfig (built via create_x_accel_config, which proves the facts location is internal;); tests + dev leave it unset.

type XAccelConfig

log

type Logger

CreateSessionAndSetCookieOptions
#

auth/session_middleware.ts view source

CreateSessionAndSetCookieOptions import type {CreateSessionAndSetCookieOptions} from '@fuzdev/fuz_app/auth/session_middleware.js';

keyring

Keyring for cookie signing.

type Keyring

deps

Query deps (needs db for session creation).

type QueryDeps

c

Hono context for setting the cookie.

type Context

account_id

The account to create a session for.

type string

session_options

Session cookie configuration.

type SessionOptions<string>

max_sessions?

Per-account session cap (null to skip enforcement).

type number | null

CreateTestAccountOptions
#

testing/cross_backend/setup.ts view source

CreateTestAccountOptions import type {CreateTestAccountOptions} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

Options for TestFixture.create_account — mints an additional bootstrapped account alongside the keeper. Matches the existing TestApp.create_account signature so the migration to fixture-style reads is a one-site call rewrite per use.

username?

type string

readonly

password_value?

type string

readonly

roles?

type Array<string>

readonly

email?

Optional email to store on the account, exercising the username-or-email login lookup. Cross-process this rides the production signup body (claimed against the username-scoped invite, which matches regardless of the email); in-process it lands directly on the inserted row. Must be a valid Email (the cross-process signup validates it) and unique per test (the LOWER(email) partial-unique index rejects collisions).

type string

readonly

CreateTestAccountWithCredentialsOptions
#

testing/app_server.ts view source

CreateTestAccountWithCredentialsOptions import type {CreateTestAccountWithCredentialsOptions} from '@fuzdev/fuz_app/testing/app_server.js';

Options for bootstrap_test_keeper and create_test_account_with_credentials.

Same shape for both — the data inserted is identical; the only behavioral difference is the lock flip on the keeper path.

db

type Db

keyring

type Keyring

session_options

type SessionOptions<string>

password

type PasswordHashDeps

username?

type string

password_value?

type string

roles?

type Array<string>

email?

Optional email stored on the account row — exercises the username-or-email login lookup.

type string

CreateTestAppAccountArgs
#

testing/app_server.ts view source

CreateTestAppAccountArgs import type {CreateTestAppAccountArgs} from '@fuzdev/fuz_app/testing/app_server.js';

Args for TestApp.create_account — mint an additional account alongside the keeper. Mirrors the cross-process CreateTestAccountOptions (the two create_account paths share a shape so fixture call sites read identically), kept local to avoid a cross-module cycle with cross_backend/setup.ts.

username?

type string

password_value?

type string

roles?

type Array<string>

email?

Optional email stored on the account row — exercises the username-or-email login lookup.

type string

CreateTestAppForBootstrapOptions
#

testing/app_server.ts view source

CreateTestAppForBootstrapOptions import type {CreateTestAppForBootstrapOptions} from '@fuzdev/fuz_app/testing/app_server.js';

Configuration for create_test_app_for_bootstrap. Like CreateTestAppOptions but the keeper-related fields drop (no pre-bootstrap keeper) and bootstrap is required + narrowed to live mode (the helper exists specifically to drive the success path).

session_options

type SessionOptions<string>

create_route_specs

type (context: AppServerContext) => Array<RouteSpec>

rpc_endpoints?

type RpcEndpointsSuiteOption

app_options?

type SuiteAppOptions

bootstrap

Live bootstrap config — the test drives POST /bootstrap against this.

type BootstrapLiveOptions

bootstrap_token

Token contents the stub fs returns when reading bootstrap.token_path. The test posts a body containing this same value as token to satisfy the timing-safe equality check inside bootstrap_account.

type string

db?

type Db

db_type?

type DbType

password?

type PasswordHashDeps

audit_factory?

type AuditFactory

CreateTestAppOptions
#

testing/app_server.ts view source

CreateTestAppOptions import type {CreateTestAppOptions} from '@fuzdev/fuz_app/testing/app_server.js';

Configuration for create_test_app.

inheritance

create_route_specs

Route spec factory — called with the assembled AppServerContext.

type (context: AppServerContext) => Array<RouteSpec>

rpc_endpoints?

RPC endpoints mounted by create_app_server — eager array or (ctx: AppServerContext) => Array<RpcEndpointSpec> factory. Single source of truth; the equivalent slot under app_options is Omit'd so setup-time path lookup and runtime dispatch read from one place. Symmetric with the suite-level rpc_endpoints option on describe_standard_admin_integration_tests etc.

type RpcEndpointsSuiteOption

bootstrap?

Bootstrap config — symmetric with AppServerOptions.bootstrap. Same single-source-of-truth precedent as rpc_endpoints: setup-time surface generation and runtime dispatch both read this slot, so the equivalent field under app_options is Omit'd. Discriminated union over {mode: 'disabled' | 'surface_only' | 'live'}. Omit (or pass {mode: 'disabled'}) for the default — no bootstrap route mounted.

For tests that exercise the bootstrap success path against a real token + empty DB, use create_test_app_for_bootstrap instead — it skips the keeper pre-creation that blocks the success branch.

type BootstrapServerOptions

app_options?

Optional overrides for AppServerOptions. Excludes fields create_test_app manages directly: backend, session_options, create_route_specs, rpc_endpoints, bootstrap (top-level slots above).

type SuiteAppOptions

CreateTestAppSurfaceSpecOptions
#

testing/stubs.ts view source

CreateTestAppSurfaceSpecOptions import type {CreateTestAppSurfaceSpecOptions} from '@fuzdev/fuz_app/testing/stubs.js';

session_options

Consumer's session config (required — varies per app).

type SessionOptions<string>

create_route_specs

Consumer's route factory — receives the same AppServerContext as production.

type (ctx: AppServerContext) => Array<RouteSpec>

env_schema?

Env schema for surface generation (default: BaseServerEnv).

type z.ZodObject

event_specs?

SSE event specs for surface generation.

type Array<EventSpec>

rpc_endpoints?

RPC endpoint specs for surface generation.

Accepts either an array (eager) or a factory (ctx: AppServerContext) => Array<RpcEndpointSpec> — symmetric with create_app_server's rpc_endpoints option, so consumers can pass the same factory to both entry points. The factory runs once against the stub AppServerContext this helper already builds.

type Array<RpcEndpointSpec> | ((ctx: AppServerContext) => Array<RpcEndpointSpec>)

ws_endpoints?

WebSocket endpoint specs for surface generation. Symmetric with create_app_server's ws_endpoints option — pass the same value to both entry points so the attack surface tests see the same WS endpoints production auto-mounts. The factory runs once against the stub AppServerContext this helper already builds. No upgradeWebSocket needed — this helper produces an AppSurfaceSpec only, never mounts.

type ReadonlyArray<WsEndpointSpec> | ((ctx: AppServerContext) => ReadonlyArray<WsEndpointSpec>)

transform_middleware?

Transform middleware array (e.g., zap's extend_middleware_for_zap_binary).

type (specs: Array<MiddlewareSpec>) => Array<MiddlewareSpec>

bootstrap?

Bootstrap config — symmetric with AppServerOptions.bootstrap. Discriminated by mode: 'disabled' skips the route (same as omission), 'surface_only' mounts the route shape, 'live' accepts a token_path for production symmetry (surface assembly only uses it for shape symmetry; the value is a live-execution concern handled by create_test_appcreate_app_server).

Surface assembly only reads route_prefix (default '/api/account').

type BootstrapServerOptions

CreateTestingActionsOptions
#

testing/cross_backend/testing_reset_actions.ts view source

CreateTestingActionsOptions import type {CreateTestingActionsOptions} from '@fuzdev/fuz_app/testing/cross_backend/testing_reset_actions.js';

session_options

Session cookie options — the reset action uses these when signing the fresh keeper's (and any extra accounts') session cookies. Pass the same SessionOptions the live create_app_server call was wired with.

type SessionOptions<string>

readonly

daemon_token_state

Daemon-token runtime state — the reset action mutates state.keeper_account_id to point at the freshly seeded keeper after the old row is wiped. Pass the same DaemonTokenState instance the daemon-token middleware reads.

type DaemonTokenState

readonly

reset_state?

Consumer-supplied callback invoked after the auth-table reset, passed the same transactional Db the auth wipes ran on. DB-domain consumers (e.g. fuz_forge truncating its cell / fact / file tables) MUST use this db rather than a separately-pooled connection — under PGlite's single connection a second connection deadlocks against this still-open transaction. testing_zzz_server clears in-memory workspace registry + terminals + scoped-FS scratch (ignores db); testing_spine_stub has no domain layer and omits the option. Runs inside the same RPC dispatch as the auth-table writes, so a throw surfaces to the caller as a JSON-RPC error and the per-test fixture short-circuits.

type (db: Db) => Promise<void> | void

readonly

CreateWsTestHarnessOptions
#

testing/ws_round_trip.ts view source

CreateWsTestHarnessOptions import type {CreateWsTestHarnessOptions} from '@fuzdev/fuz_app/testing/ws_round_trip.js';

actions

The actions registered on this endpoint — matches the shape register_action_ws accepts. Each entry is a {spec, handler?} tuple; shared fuz_app primitives (like heartbeat_action) can be spread in alongside consumer-specific actions.

type ReadonlyArray<Action>

transport?

Pass a pre-created transport to share with a broadcast API.

type BackendWebsocketTransport

heartbeat?

Threaded through to register_action_ws. Defaults to false in tests — fake timers + receive-silence detection need explicit opt-in and per- test tuning to avoid spurious closes.

type RegisterActionWsOptions['heartbeat']

log?

Optional logger. Defaults to a silent [ws-test] logger.

type Logger

on_socket_open?

Threaded straight through to register_action_ws.

type RegisterActionWsOptions['on_socket_open']

on_socket_close?

Threaded straight through to register_action_ws.

type RegisterActionWsOptions['on_socket_close']

on_request?

Optional responder for server-initiated requests, applied to every connect(). Purely additive (and unexercised until the TS server transport gains server-initiated requests — the deferred BackendWebsocketTransport.send() convergence): with it set, an inbound server→client request is answered rather than surfaced as a message; without it, behavior is unchanged. Mirrors the cross-process create_ws_transport seam so suite bodies share one shape.

type WsRequestResponder

CREDENTIAL_TYPE_API_TOKEN
#

auth/credential_type_schema.ts view source

"api_token" import {CREDENTIAL_TYPE_API_TOKEN} from '@fuzdev/fuz_app/auth/credential_type_schema.js';

HTTP Authorization: Bearer API token credential. The wire literal 'api_token' aligns with the api_token storage table name; the constant is named _API_TOKEN (not _BEARER) to keep wire and storage nomenclature in lockstep.

CREDENTIAL_TYPE_DAEMON_TOKEN
#

auth/credential_type_schema.ts view source

"daemon_token" import {CREDENTIAL_TYPE_DAEMON_TOKEN} from '@fuzdev/fuz_app/auth/credential_type_schema.js';

Daemon-token credential — filesystem proof for the keeper account.

CREDENTIAL_TYPE_KEY
#

hono_context.ts view source

"credential_type" import {CREDENTIAL_TYPE_KEY} from '@fuzdev/fuz_app/hono_context.js';

Hono context variable name for the credential type.

CREDENTIAL_TYPE_NAME_REGEX
#

CREDENTIAL_TYPE_SESSION
#

CREDENTIAL_TYPES
#

hono_context.ts view source

readonly ["session", "api_token", "daemon_token"] import {CREDENTIAL_TYPES} from '@fuzdev/fuz_app/hono_context.js';

The credential types that can authenticate a request — the closed set of fuz_app builtins. The open registry on top (create_credential_type_schema(consumer_types)) is consulted at registry time by create_role_schema for RoleSpec.required_credential_types validation; the wire-validated CredentialType enum here stays narrow because middleware only ever sets one of the three builtins.

CredentialHeaderRobustnessCrossTestOptions
#

testing/cross_backend/credential_header_robustness.ts view source

CredentialHeaderRobustnessCrossTestOptions import type {CredentialHeaderRobustnessCrossTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/credential_header_robustness.js';

Options for the credential-header robustness probe — needs the raw URL + a valid daemon token.

base_url

Base URL the spawned backend is reachable at (e.g. http://localhost:1178).

type string

readonly

rpc_path?

RPC endpoint path to target. Default /api/rpc.

type string

readonly

daemon_token

A valid daemon token (keeper) — handle.daemon_token.

type string

readonly

CredentialType
#

hono_context.ts view source

ZodEnum<{ session: "session"; api_token: "api_token"; daemon_token: "daemon_token"; }> import type {CredentialType} from '@fuzdev/fuz_app/hono_context.js';

Credential type — how a request was authenticated.

CredentialTypeMeta
#

auth/credential_type_schema.ts view source

CredentialTypeMeta import type {CredentialTypeMeta} from '@fuzdev/fuz_app/auth/credential_type_schema.js';

Per-credential-type metadata. description is admin-UI-facing copy (mirrors RoleSpec.description and ScopeKindMeta.description). Open shape so v2 can extend without a breaking change.

description?

type string

CredentialTypeName
#

CredentialTypeRequiredError
#

http/error_schemas.ts view source

ZodObject<{ error: ZodLiteral<"credential_type_required">; required_credential_types: ZodReadonly<ZodArray<ZodString>>; }, $loose> import type {CredentialTypeRequiredError} from '@fuzdev/fuz_app/http/error_schemas.js';

Credential-type error — returned by the dispatcher's post-authorization credential gate (and the require_credential_types REST middleware) when the request's credential type isn't in the route's auth.credential_types allowlist.

required_credential_types carries what the route declared (['daemon_token'] for keeper; future gates carry their own labels). Symmetric with PermissionError's required_roles: clients see what the route demanded, not what their credential is.

CredentialTypeSchemaResult
#

auth/credential_type_schema.ts view source

CredentialTypeSchemaResult import type {CredentialTypeSchemaResult} from '@fuzdev/fuz_app/auth/credential_type_schema.js';

The result of create_credential_type_schema — a Zod schema and metadata map.

CredentialType

Zod schema that validates credential-type name strings against the registered set (builtins + consumer-declared). Use at I/O boundaries (admin UIs, codegen) and as the construction-time check inside create_role_schema for every RoleSpec.required_credential_types entry.

type z.ZodType<string>

credential_types

Map of every registered credential-type to its metadata. Keyed by name. Read at startup by admin / codegen surfaces.

type ReadonlyMap<string, CredentialTypeMeta>

cross_rpc_call
#

testing/cross_backend/cell_cross_helpers.ts view source

(transport: FetchTransport, path: string, method: string, params: unknown, headers: Record<string, string>): Promise<RpcResult> import {cross_rpc_call} from '@fuzdev/fuz_app/testing/cross_backend/cell_cross_helpers.js';

POST a JSON-RPC call over a cross-process FetchTransport with the given auth headers. Distinct from testing/rpc_helpers.ts's app-based rpc_call: this variant drives the cookie-jar FetchTransport the cross-backend harness spawns against, and returns the slim RpcResult the cell suites read.

transport

path

type string

method

type string

params

type unknown

headers

type Record<string, string>

returns

Promise<RpcResult>

CrossBackendGlobalSetupOptions
#

testing/cross_backend/create_cross_backend_global_setup.ts view source

CrossBackendGlobalSetupOptions import type {CrossBackendGlobalSetupOptions} from '@fuzdev/fuz_app/testing/cross_backend/create_cross_backend_global_setup.js';

configs

Map of derived backend name → BackendConfig factory. The derived name (see derive_name) selects the factory; unknown names throw with the full supported list so a misnamed project surfaces clearly.

type Readonly<Record<string, () => BackendConfig>>

readonly

derive_name?

Derive the backend name from the vitest project name. Default strips cross_backend_(ts_)?.

type (project_name: string) => string

readonly

provide_key?

Key passed to project.provide (and read by inject in test files). Default 'backend_handle'. Augment vitest's ProvidedContext for it.

type string

readonly

CrossBackendProjectOptions
#

testing/cross_backend/make_cross_backend_project.ts view source

CrossBackendProjectOptions import type {CrossBackendProjectOptions} from '@fuzdev/fuz_app/testing/cross_backend/make_cross_backend_project.js';

name

vitest project name. create_cross_backend_global_setup derives the backend name from it (by default stripping a cross_backend_(ts_)? prefix), so name projects cross_backend_<backend> (e.g. cross_backend_rust, cross_backend_ts_deno).

type string

readonly

global_setup

Path to the consumer's vitest globalSetup module, relative to the consumer repo root (e.g. './src/test/cross_backend/global_setup.ts'). That module is expected to export a create_cross_backend_global_setup result as its default.

type string

readonly

include?

Test-file globs. Default: ['src/test/cross_backend/*.cross.test.ts'].

type ReadonlyArray<string>

readonly

exclude?

Globs to exclude from include (e.g. a backend-specific variant file). Default: [].

type ReadonlyArray<string>

readonly

group_order?

vitest sequence.groupOrder. Default: 3 (runs after unit + db).

type number

readonly

CrossImplBenchEntry
#

testing/cross_backend/bench/run_cross_impl_bench.ts view source

CrossImplBenchEntry import type {CrossImplBenchEntry} from '@fuzdev/fuz_app/testing/cross_backend/bench/run_cross_impl_bench.js';

One backend × one scenario, with its timing result.

backend

type string

readonly

scenario

type string

readonly

result

type BenchmarkResult

readonly

CrossImplBenchResult
#

testing/cross_backend/bench/run_cross_impl_bench.ts view source

CrossImplBenchResult import type {CrossImplBenchResult} from '@fuzdev/fuz_app/testing/cross_backend/bench/run_cross_impl_bench.js';

Full sweep across the supplied backends and scenarios.

backends

Backend names, in the order they were run.

type ReadonlyArray<string>

readonly

scenarios

Scenario names that ran on at least one backend.

type ReadonlyArray<string>

readonly

entries

type ReadonlyArray<CrossImplBenchEntry>

readonly

CrossImplComparisonEntry
#

testing/cross_backend/bench/bench_report.ts view source

CrossImplComparisonEntry import type {CrossImplComparisonEntry} from '@fuzdev/fuz_app/testing/cross_backend/bench/bench_report.js';

One backend's result compared against the reference backend, per scenario.

scenario

type string

readonly

reference

The reference backend (a side of the comparison).

type string

readonly

backend

The compared backend (b side).

type string

readonly

comparison

type BenchmarkComparison

readonly

CrossProcessSetupOptions
#

testing/cross_backend/setup.ts view source

CrossProcessSetupOptions import type {CrossProcessSetupOptions} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

extra_keeper_roles?

Additional roles to grant the fresh keeper on every per-test reset, *in addition to* the [ROLE_KEEPER, ROLE_ADMIN] defaults the _testing_reset action seeds. Cross-process mirror of in-process extra_keeper_roles on default_in_process_suite_options.

Costs nothing extra per test — the _testing_reset action seeds the keeper in a single transaction regardless of how many roles are in the list.

ROLE_ADMIN is already in the default set, so admin-suite consumers usually pass an empty / omitted array. Consumer-defined roles (e.g. teacher) are passed here when the keeper-acting test needs them.

Keeper ≠ admin. Tests that need a *non-admin* secondary account with ROLE_KEEPER declare it via extra_accountsROLE_KEEPER's RoleSpec.grant_paths is bootstrap-only, so it can only be granted at the test-binary bootstrap-equivalent step.

type ReadonlyArray<string>

readonly

extra_accounts?

Bootstrap-time secondary accounts seeded alongside the keeper on every per-test reset. See ExtraAccountSpec for why this is a cradle-only bypass. The reset action seeds them in the same transaction as the keeper.

type ReadonlyArray<ExtraAccountSpec>

readonly

extra_actors?

Additional actor names to seed on the keeper account, beyond its single bootstrap actor — exposed on fixture.extra_actors. Declare to put the keeper into a multi-actor state so the actor_required / explicit-acting branches are reachable cross-process. See TestFixtureBase.extra_actors.

type ReadonlyArray<string>

readonly

CrossProcessSseTestOptions
#

testing/cross_backend/sse_round_trip.ts view source

CrossProcessSseTestOptions import type {CrossProcessSseTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/sse_round_trip.js';

Configuration for .

setup_test

Per-test fixture producer (default_cross_process_setup(handle)). Each case reads the fresh-per-test keeper's session cookies from fixture.transport.cookies() to thread onto the stream. The keeper holds ROLE_ADMIN by default, so it can subscribe to the admin-gated audit stream and drive admin_session_revoke_all.

type SetupTest

readonly

capabilities

Backend capability flags; every case gates on capabilities.sse.

type BackendCapabilities

readonly

base_url

Base URL the backend is reachable at (e.g. http://localhost:1178).

type string

readonly

sse_path?

SSE stream path on the backend. Defaults to /api/admin/audit/stream.

type string

readonly

rpc_path?

RPC endpoint path (e.g. /api/rpc) used by the data-frame and close-on-revoke cases to fire admin_session_revoke_all / account_session_revoke_all over the keeper's session channel. When omitted, those cases are skipped — they depend on the standard account + admin actions being mounted on the RPC endpoint.

type string

readonly

origin?

Origin for the stream request. Defaults to base_url.

type string

readonly

CrossProcessWsTestOptions
#

testing/cross_backend/ws_round_trip.ts view source

CrossProcessWsTestOptions import type {CrossProcessWsTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/ws_round_trip.js';

Configuration for .

setup_test

Per-test fixture producer (default_cross_process_setup(handle)). The authenticated case reads the fresh-per-test keeper's session cookies from fixture.transport.cookies() to thread onto the upgrade.

type SetupTest

readonly

capabilities

Backend capability flags; every case gates on capabilities.ws.

type BackendCapabilities

readonly

base_url

Base URL the backend is reachable at (e.g. http://localhost:1178).

type string

readonly

ws_path

WebSocket endpoint path on the backend (e.g. /api/ws).

type string

readonly

origin?

Origin for the authenticated upgrade. Defaults to base_url.

type string

readonly

rpc_path?

RPC endpoint path (e.g. /api/rpc) used by the close-on-revoke case to fire account_session_revoke_all over the keeper's session channel. When omitted, that case is skipped — it depends on the standard account actions being mounted on the RPC endpoint.

type string

readonly

CrossSuiteOptions
#

testing/cross_backend/setup.ts view source

CrossSuiteOptions import type {CrossSuiteOptions} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

Base options shared by every imperative cross-backend parity suite (origin / ready / body_size / cell_* / actor_* / account_lifecycle / app_settings / testing_backdoor / …). The {setup_test, capabilities} core is identical across them; each suite extends this with its own path field (rpc_path for RPC-dispatched suites via RpcPathCrossSuiteOptions, ready_path for the readiness probe, etc.). Lives here in the neutral fixture-types home rather than in any one domain helper, so a non-cell suite no longer reaches into the cell helpers for its option shape.

setup_test

Per-test fixture-producing function (fresh keeper + db per call).

type SetupTest

readonly

capabilities

Backend capability declarations — each suite gates on its own flag.

type BackendCapabilities

readonly

DAEMON_TOKEN_HEADER
#

auth/daemon_token.ts view source

"X-Daemon-Token" import {DAEMON_TOKEN_HEADER} from '@fuzdev/fuz_app/auth/daemon_token.js';

The X-Daemon-Token header name.

DaemonInfo
#

cli/daemon.ts view source

ZodObject<{ version: ZodNumber; pid: ZodNumber; port: ZodNumber; started: ZodString; app_version: ZodString; }, $strict> import type {DaemonInfo} from '@fuzdev/fuz_app/cli/daemon.js';

Daemon info schema for ~/.{name}/run/daemon.json.

DaemonToken
#

auth/daemon_token.ts view source

ZodString import type {DaemonToken} from '@fuzdev/fuz_app/auth/daemon_token.js';

Daemon token format: 43 base64url characters (256 bits).

DaemonTokenRotation
#

DaemonTokenRotationOptions
#

auth/daemon_token_middleware.ts view source

DaemonTokenRotationOptions import type {DaemonTokenRotationOptions} from '@fuzdev/fuz_app/auth/daemon_token_middleware.js';

Options for daemon token rotation.

token_path

Absolute path the token file is written to. Caller computes from its own conventions — e.g. get_daemon_token_path(runtime, app_name) for the standard ~/.{name}/run/daemon_token layout, or a path derived from PUBLIC_<APP>_DIR for cross-process test setups that isolate the app dir to a tmpdir.

type string

rotation_interval_ms?

Rotation interval in ms. Default: 30000 (30s).

type number

DaemonTokenState
#

auth/daemon_token.ts view source

DaemonTokenState import type {DaemonTokenState} from '@fuzdev/fuz_app/auth/daemon_token.js';

Mutable runtime state for daemon token rotation.

This is runtime state (not AppDeps or *Options) — it changes during operation. Created at server startup, passed to the middleware factory.

current_token

Current valid token.

type string

previous_token

Previous token, still valid during the race window. null before first rotation.

type string | null

rotated_at

When the last rotation occurred.

type Date

keeper_account_id

The account ID of the keeper (resolved at startup, set by on_bootstrap).

type string | null

DaemonTokenWriteDeps
#

auth/daemon_token_middleware.ts view source

DaemonTokenWriteDeps import type {DaemonTokenWriteDeps} from '@fuzdev/fuz_app/auth/daemon_token_middleware.js';

Deps for writing the daemon token to disk.

env_get

Get an environment variable value.

type (name: string): string | undefined

name

type string
returns string | undefined

mkdir

Create a directory.

type (path: string, options?: { recursive?: boolean | undefined; } | undefined): Promise<void>

path

type string

options?

type { recursive?: boolean | undefined; } | undefined
optional
returns Promise<void>

write_text_file

Write text to a file.

type (path: string, content: string): Promise<void>

path

type string

content

type string
returns Promise<void>

rename

Rename (move) a file.

type (old_path: string, new_path: string): Promise<void>

old_path

type string

new_path

type string
returns Promise<void>

chmod?

Set file permissions. Optional — consumers provide when available (e.g. Deno.chmod).

type (path: string, mode: number) => Promise<void>

DataExposureTestOptions
#

testing/data_exposure.ts view source

DataExposureTestOptions import type {DataExposureTestOptions} from '@fuzdev/fuz_app/testing/data_exposure.js';

setup_test

Per-test fixture-producing function (per-describe cadence).

type SetupTest

surface_source

App surface for schema-level + route-iteration checks. Constructed in TS by the consumer; same shape for in-process and cross-process tests.

type AppSurfaceSpec

capabilities

Backend capability declarations.

type BackendCapabilities

sensitive_fields?

Fields that must never appear in any response. Default: sensitive_field_blocklist.

type ReadonlyArray<string>

admin_only_fields?

Fields that must not appear in non-admin responses. Default: admin_only_field_blocklist.

type ReadonlyArray<string>

skip_routes?

Routes to skip, in 'METHOD /path' format.

type Array<string>

Datatable
#

ui/Datatable.svelte view source

accepts children

import Datatable from '@fuzdev/fuz_app/ui/Datatable.svelte';

columns

type DatatableColumn<T>[]

rows

type T[]

row_key?

Row property used as the keyed-each key.

type string & keyof T
optional default 'id' as string & keyof T

height?

CSS height for the scrollable region (e.g. '400px'). Omit to size to content.

type string
optional

header?

Override default header-cell rendering. Receives the column.

type Snippet<[column: DatatableColumn<T>]>
optional
snippet parameters
column DatatableColumn<T>

cell?

Override default cell rendering. Receives column, row, and the cell value.

type Snippet<[column: DatatableColumn<T>, row: T, value: T[keyof T]]>
optional
snippet parameters
column DatatableColumn<T>
row T
value T[keyof T]

empty?

Rendered when rows is empty. Defaults to a no data text.

type Snippet<[]>
optional

intersects

SvelteHTMLElements['div']

generics

Datatable<T extends Record<string, any> = Record<string, any>>
T
constraint Record<string, any>
default Record<string, any>

DATATABLE_COLUMN_WIDTH_DEFAULT
#

ui/datatable.ts view source

120 import {DATATABLE_COLUMN_WIDTH_DEFAULT} from '@fuzdev/fuz_app/ui/datatable.js';

Default initial column width in pixels.

DATATABLE_MIN_COLUMN_WIDTH
#

ui/datatable.ts view source

50 import {DATATABLE_MIN_COLUMN_WIDTH} from '@fuzdev/fuz_app/ui/datatable.js';

Default minimum column width in pixels.

DatatableColumn
#

ui/datatable.ts view source

DatatableColumn<T> import type {DatatableColumn} from '@fuzdev/fuz_app/ui/datatable.js';

Column definition for a Datatable.

generics

DatatableColumn<T = unknown>
T
default unknown

key

Row data accessor key.

type string & keyof T

label

Header label text.

type string

width?

Initial column width in pixels.

type number

min_width?

Minimum column width in pixels.

type number

format?

Format a cell value for display. Falls back to format_value when absent.

type (value: T[keyof T], row: T) => string

Db
#

db/db.ts view source

import {Db} from '@fuzdev/fuz_app/db/db.js';

Database wrapper providing a consistent query and transaction interface.

Construct via create_pg_db() from db/db_pg.ts or create_pglite_db() from db/db_pglite.ts for proper transaction support, or via create_db() for URL-based auto-detection.

examples

const {db, close} = await create_db('postgres://...'); const users = await db.query<User>('SELECT * FROM users WHERE active = $1', [true]); await db.transaction(async (tx) => { await tx.query('INSERT INTO users ...'); await tx.query('INSERT INTO audit_log ...'); }); await close();

client

type DbClient

readonly

constructor

type new (options: DbDeps): Db

options

type DbDeps

query

Execute a query and return all rows.

type <T>(text: string, values?: unknown[] | undefined): Promise<T[]>

text

SQL text with $1, $2, ... parameter placeholders

type string

values?

parameter values bound to the placeholders in text

type unknown[] | undefined
optional
returns Promise<T[]>

the result rows, typed as T

query_one

Execute a query and return the first row, or undefined if no rows.

type <T>(text: string, values?: unknown[] | undefined): Promise<T | undefined>

text

SQL text with $1, $2, ... parameter placeholders

type string

values?

parameter values bound to the placeholders in text

type unknown[] | undefined
optional
returns Promise<T | undefined>

the first row, or undefined when the result set is empty

transaction

Run a function inside a database transaction.

The callback receives a transaction-scoped Db. Queries inside the callback go through the transaction connection; queries outside use the pool normally. Commits on success, rolls back on error.

type <T>(fn: (tx_db: Db) => Promise<T>): Promise<T>

fn

async function receiving a transaction-scoped Db

type (tx_db: Db) => Promise<T>
returns Promise<T>

the value returned by fn

throws

  • Error - propagated from `fn` after `ROLLBACK`, or from the driver

DB_TABLE_ROWS_DEFAULT_LIMIT
#

http/db_routes.ts view source

100 import {DB_TABLE_ROWS_DEFAULT_LIMIT} from '@fuzdev/fuz_app/http/db_routes.js';

Default page size for GET /tables/:name rows.

DB_TABLE_ROWS_LIMIT_MAX
#

http/db_routes.ts view source

1000 import {DB_TABLE_ROWS_LIMIT_MAX} from '@fuzdev/fuz_app/http/db_routes.js';

Maximum page size for GET /tables/:name rows.

DbClient
#

db/db.ts view source

DbClient import type {DbClient} from '@fuzdev/fuz_app/db/db.js';

Minimal interface that both pg and pglite satisfy.

query

type <T = unknown>(text: string, values?: Array<unknown>) => Promise<{ rows: Array<T> }>

DbDeps
#

db/db.ts view source

DbDeps import type {DbDeps} from '@fuzdev/fuz_app/db/db.js';

Configuration for constructing a Db with transaction support.

transaction is injected by create_db which knows the driver. For pg: acquires a dedicated pool client per transaction. For PGlite: delegates to pglite.transaction().

client

type DbClient

transaction

type <T>(fn: (tx_db: Db) => Promise<T>) => Promise<T>

DbDriverResult
#

db/db.ts view source

DbDriverResult import type {DbDriverResult} from '@fuzdev/fuz_app/db/db.js';

Result of constructing a driver-specific Db.

Returned by create_pg_db() and create_pglite_db(). The close callback is bound to the actual driver — callers never need to know which driver is in use.

db

type Db

close

Close the database connection. Bound to the actual driver at construction.

type () => Promise<void>

DbFactory
#

testing/db.ts view source

DbFactory import type {DbFactory} from '@fuzdev/fuz_app/testing/db.js';

Factory interface for creating test database instances.

name

type string

create

type () => Promise<Db>

close

type (db: Db) => Promise<void>

skip

type boolean

skip_reason?

type string

DbRouteOptions
#

http/db_routes.ts view source

DbRouteOptions import type {DbRouteOptions} from '@fuzdev/fuz_app/http/db_routes.js';

Per-factory configuration for db routes.

db_type

type DbType

db_name

type string

extra_stats?

Optional callback to provide app-specific stats in the health response.

type (db: Db) => Promise<Record<string, unknown>>

log?

Optional logger for server-side diagnostics (e.g. FK violation details).

type Logger

DbStatus
#

db/status.ts view source

DbStatus import type {DbStatus} from '@fuzdev/fuz_app/db/status.js';

Full database status snapshot.

connected

Whether the database is reachable.

type boolean

error?

Error message if connection failed.

type string

table_count

Number of public tables.

type number

tables

Per-table row counts.

type Array<TableStatus>

migrations

Per-namespace migration status.

type Array<MigrationStatus>

old_tracker_shape?

True if the pre-0.42 schema_version shape (with a version column) was detected. The runner refuses to start in this state — operators see this flag as their cue to drop the table or call baseline().

type boolean

DbType
#

db/db.ts view source

DbType import type {DbType} from '@fuzdev/fuz_app/db/db.js';

Database driver type.

DeclinedOffer
#

auth/role_grant_offer_queries.ts view source

DeclinedOffer import type {DeclinedOffer} from '@fuzdev/fuz_app/auth/role_grant_offer_queries.js';

Result of query_role_grant_offer_decline — the declined offer plus the grantor's account_id.

inheritance

from_account_id

Grantor's account_id, resolved via a join on actor so the audit envelope's target_account_id (decline is *to* the grantor) and the post-commit notification target are both addressable without a second round-trip.

type Uuid

default_action_account_rate_limit
#

rate_limiter.ts view source

RateLimiterOptions import {default_action_account_rate_limit} from '@fuzdev/fuz_app/rate_limiter.js';

Default options for per-actor action-dispatcher rate limiting: 1200 attempts per 15 minutes. Shared by the HTTP RPC and WebSocket action dispatchers. Permissive — sustained ~80/min is well above any human admin workflow; an oracle probing 10k addresses still finishes in ~2 hours, slow enough to surface in audit. Tighten downstream.

default_action_ip_rate_limit
#

rate_limiter.ts view source

RateLimiterOptions import {default_action_ip_rate_limit} from '@fuzdev/fuz_app/rate_limiter.js';

Default options for per-IP action-dispatcher rate limiting: 600 attempts per 15 minutes. Shared by the HTTP RPC and WebSocket action dispatchers (one budget per action, not per transport). Permissive — catches runaway scripts and egregious oracle probes, but well above human or normal automation pace. Tighten downstream for stricter deployments.

default_audit_factory
#

server/app_backend.ts view source

(params: { db: Db; log: Logger; }): AuditEmitter import {default_audit_factory} from '@fuzdev/fuz_app/server/app_backend.js';

Trivial AuditFactory for consumers that don't compose on_audit_event or audit_log_config. Equivalent to ({db, log}) => create_audit_emitter({db, log}) — exported so the default case stays a single-symbol reference rather than five tokens of boilerplate at every consumer.

Use the inline form when you need to thread on_audit_event / audit_log_config / emit_decorator; the factory composes those three fields itself so there's nothing this constant can pass through.

params

type { db: Db; log: Logger; }

returns

AuditEmitter

DEFAULT_AUDIT_STREAM_ROLE
#

DEFAULT_BACKOFF_FACTOR
#

actions/socket.svelte.ts view source

1.5 import {DEFAULT_BACKOFF_FACTOR} from '@fuzdev/fuz_app/actions/socket.svelte.js';

Exponential backoff factor: delay = base * factor^(attempt-1).

default_bench_scenarios
#

testing/cross_backend/bench/scenario.ts view source

readonly BenchScenario[] import {default_bench_scenarios} from '@fuzdev/fuz_app/testing/cross_backend/bench/scenario.js';

Starter cross-impl scenarios — all on the standard spine surface, so they run on every backend (TS Hono, Rust spine), and all reads, so they're safe to repeat against one bootstrapped keeper without state accumulation.

  • account_verify — the dispatch + auth-resolve floor (no real query work).
  • account_session_list — an authed DB read.
  • audit_log_list — an admin paginated read (the keeper holds ROLE_ADMIN).

login is deliberately omitted: the cross-process test binaries wire a fast TestingArgon2idHasher, so a login scenario would measure dispatch rather than real Argon2 cost — misleading without its own clearly-labeled tier.

DEFAULT_CLOSE_CODE
#

DEFAULT_COLLECTIONS_PATH
#

actions/action_codegen.ts view source

"./action_collections.ts" import {DEFAULT_COLLECTIONS_PATH} from '@fuzdev/fuz_app/actions/action_codegen.js';

Default collections_path — every consumer's gen producers point at the sibling action_collections.ts.

default_cross_process_setup
#

testing/cross_backend/setup.ts view source

(handle: ReconstructedBootstrappedBackendHandle, options?: CrossProcessSetupOptions | undefined): SetupTest import {default_cross_process_setup} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

Build a SetupTest against a spawned + bootstrapped backend.

Per-test body (unconditional reset — fresh keeper every test):

  1. Fire _testing_reset via the keeper's daemon-token channel. The action wipes auth tables, seeds a fresh keeper (with extra_keeper_roles applied), seeds any extra_accounts, and returns the new credentials.
  2. Build the TestFixture closing over the new keeper as the fixture's primary account / actor (matching in-process semantics). fixture.extra_accounts[username] exposes any bootstrap-time secondaries.
  3. fixture.create_account() mints additional *post-bootstrap* accounts via the production signup + login flow (invite → signup → login → token). Roles go through offer/accept (production consent path).

No reset: boolean opt-in — every test runs against a freshly bootstrapped keeper. This converges in-process and cross-process keeper lifetimes; mutation-cascade tests (password change, revoke-all) and hardcoded-username signup tests work uniformly.

handle

options?

type CrossProcessSetupOptions | undefined
optional

returns

SetupTest

default_error_schema_tightness
#

testing/surface_invariants.ts view source

ErrorSchemaTightnessOptions import {default_error_schema_tightness} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Baseline error schema tightness applied by describe_standard_attack_surface_tests when no config is passed.

Uses min_specificity: 'enum' (the assertion default) with ignore_statuses for middleware-derived status codes that are commonly generic (auth middleware produces multiple error codes at 401/403, and 429 comes from rate limiters), and allowlist seeded with fuz_app_stock_route_tightness_allowlist so fuz_app-shipped routes with heterogeneous generic schemas don't force every consumer to hand-maintain an identical allowlist. Consumers can pass a narrower config with project-specific allowlist entries, or pass null to skip the assertion entirely.

default_format_scope
#

ui/format_scope.ts view source

(args: { scope_id: string | null; role: string; }): string | null import {default_format_scope} from '@fuzdev/fuz_app/ui/format_scope.js';

Default FormatScope — always returns null so callers fall back to the raw uuid.

args

type { scope_id: string | null; role: string; }

returns

string | null

DEFAULT_HEARTBEAT_INTERVAL
#

actions/socket.svelte.ts view source

30000 import {DEFAULT_HEARTBEAT_INTERVAL} from '@fuzdev/fuz_app/actions/socket.svelte.js';

Idle interval before sending a heartbeat (ms).

DEFAULT_HEARTBEAT_RECEIVE_TIMEOUT
#

default_in_process_setup
#

testing/cross_backend/in_process_setup.ts view source

(options: InProcessSetupOptions): SetupTest import {default_in_process_setup} from '@fuzdev/fuz_app/testing/cross_backend/in_process_setup.js';

Build a SetupTest that creates a fresh TestApp per call via create_test_app and projects it into the TestFixture shape.

Same factory inputs create_test_app already takes — this helper is a projection layer, not a new lifecycle. fuz_app's own src/test/ and consumer suites pass default_in_process_setup({...factory_inputs}) in place of the old per-suite factory-input bundle. The extra_accounts slot (see InProcessSetupOptions) seeds bootstrap-time secondaries directly via create_test_account_with_credentials against the same DB the keeper just landed on — mirrors the cross-process _testing_reset cradle so suite bodies read fixture.extra_accounts[username] uniformly regardless of transport.

The describe-level auth_integration_truncate_tables / pglite WASM cache lifecycle stays in create_pglite_factory / create_describe_db (testing/db.ts) — default_in_process_setup doesn't manage db state beyond what create_test_app already does.

options

returns

SetupTest

default_in_process_suite_options
#

testing/cross_backend/in_process_setup.ts view source

<const O extends DefaultInProcessSuiteOptions>(options: O): { setup_test: SetupTest; surface_source: AppSurfaceSpec; capabilities: BackendCapabilities; session_options: O["session_options"]; create_route_specs: O["create_route_specs"]; rpc_endpoints: O["rpc_endpoints"]; } import {default_in_process_suite_options} from '@fuzdev/fuz_app/testing/cross_backend/in_process_setup.js';

Build the full in-process suite bundle in a single helper invocation. Output covers {setup_test, surface_source, capabilities} plus every factory input the Tier 1 suites read at their top level (session_options, create_route_specs, rpc_endpoints) — so the call site spreads once and adds only suite-specific extras (roles, skip_routes, input_overrides, db_factories, ...).

// Suite-extras-free call: helper output is the entire options bag. describe_round_trip_validation(default_in_process_suite_options({ session_options, create_route_specs, rpc_endpoints: [rpc_endpoint_spec], })); // With suite-specific extras: spread and add. describe_standard_admin_integration_tests({ ...default_in_process_suite_options({ session_options, create_route_specs, rpc_endpoints, extra_keeper_roles: [ROLE_ADMIN], }), roles, });

Suites that don't read session_options / rpc_endpoints at their top level (round_trip, data_exposure) accept the spread anyway — excess properties on spread sources aren't checked by TS, and the uniform shape keeps consumer call sites mechanical.

options

type O

returns

{ setup_test: SetupTest; surface_source: AppSurfaceSpec; capabilities: BackendCapabilities; session_options: O["session_options"]; create_route_specs: O["create_route_specs"]; rpc_endpoints: O["rpc_endpoints"]; }

generics

default_in_process_suite_options<O extends DefaultInProcessSuiteOptions>
O

DEFAULT_INTEGRATION_ERROR_COVERAGE
#

testing/error_coverage.ts view source

0.2 import {DEFAULT_INTEGRATION_ERROR_COVERAGE} from '@fuzdev/fuz_app/testing/error_coverage.js';

Default minimum error coverage threshold for the standard integration and admin test suites. Conservative — not all error paths are exercisable in the composable suites. Consumers should increase as their test suites mature.

default_login_account_rate_limit
#

rate_limiter.ts view source

RateLimiterOptions import {default_login_account_rate_limit} from '@fuzdev/fuz_app/rate_limiter.js';

Default options for per-account login rate limiting: 10 attempts per 30 minutes.

DEFAULT_LOGIN_FAIL_FLOOR_MS
#

auth/account_routes.ts view source

250 import {DEFAULT_LOGIN_FAIL_FLOOR_MS} from '@fuzdev/fuz_app/auth/account_routes.js';

Default minimum wall-clock time (ms) for a login failure (401) response.

Picked to exceed the p99 of every 401 code path (Argon2id dominates at ~100ms, plus DB + overhead). The handler races failure work against sleep(floor + jitter) via await, so observed response time = max(work, delay). Found-vs-not-found and rate-limit-skipped-vs-not paths converge. Only 401 is padded — 429 stays fast by design to keep rate-limit DoS handling cheap.

DEFAULT_LOGIN_FAIL_JITTER_MS
#

auth/account_routes.ts view source

25 import {DEFAULT_LOGIN_FAIL_JITTER_MS} from '@fuzdev/fuz_app/auth/account_routes.js';

Default uniform jitter window (±ms) layered on the floor.

Random jitter prevents a stable clamp point from leaking whenever a path occasionally exceeds the floor. Math.random is sufficient — we only need unpredictability of the exact delay, not cryptographic guarantees.

default_login_ip_rate_limit
#

rate_limiter.ts view source

RateLimiterOptions import {default_login_ip_rate_limit} from '@fuzdev/fuz_app/rate_limiter.js';

Default options for per-IP login rate limiting: 5 attempts per 15 minutes.

DEFAULT_MAX_BODY_SIZE
#

server/app_server.ts view source

number import {DEFAULT_MAX_BODY_SIZE} from '@fuzdev/fuz_app/server/app_server.js';

Default maximum request body size: 1 MiB.

DEFAULT_MAX_SESSIONS
#

DEFAULT_MAX_TOKENS
#

DEFAULT_METATYPES_PATH
#

actions/action_codegen.ts view source

"./action_metatypes.ts" import {DEFAULT_METATYPES_PATH} from '@fuzdev/fuz_app/actions/action_codegen.js';

Default metatypes_path — sibling action_metatypes.ts carrying the generated ActionMethod.

DEFAULT_PEER_REQUEST_TIMEOUT
#

actions/peer_request.ts view source

10000 import {DEFAULT_PEER_REQUEST_TIMEOUT} from '@fuzdev/fuz_app/actions/peer_request.js';

Default deadline (ms) for a server→client peer request before it resolves timeout. Twin of the Rust spine's DEFAULT_PEER_TIMEOUT.

DEFAULT_QUEUE_MAX_SIZE
#

actions/socket.svelte.ts view source

100 import {DEFAULT_QUEUE_MAX_SIZE} from '@fuzdev/fuz_app/actions/socket.svelte.js';

Default bound on buffered requests while disconnected. Overflow rejects.

DEFAULT_RATE_LIMITER_MAX_KEYS
#

rate_limiter.ts view source

100000 import {DEFAULT_RATE_LIMITER_MAX_KEYS} from '@fuzdev/fuz_app/rate_limiter.js';

Default tracked-key cap: bounds worst-case memory under key-enumeration attacks (an attacker rotating source IPs cannot grow the backing map indefinitely between cleanup ticks). Tuned to comfortably fit real traffic for a single-instance deployment while capping memory at a few MB in the worst case.

DEFAULT_RECONNECT_DELAY
#

DEFAULT_RECONNECT_DELAY_MAX
#

actions/socket.svelte.ts view source

10000 import {DEFAULT_RECONNECT_DELAY_MAX} from '@fuzdev/fuz_app/actions/socket.svelte.js';

Max reconnect delay in ms (cap on exponential backoff).

DEFAULT_ROTATION_INTERVAL_MS
#

DEFAULT_SERVER_HEARTBEAT_TIMEOUT
#

actions/register_action_ws.ts view source

60000 import {DEFAULT_SERVER_HEARTBEAT_TIMEOUT} from '@fuzdev/fuz_app/actions/register_action_ws.js';

Default inactivity window before the server closes a silent socket.

default_setup_logger
#

dev/setup.ts view source

SetupLogger import {default_setup_logger} from '@fuzdev/fuz_app/dev/setup.js';

Default logger using bracket format.

DEFAULT_SIGNUP_FAIL_FLOOR_MS
#

auth/signup_routes.ts view source

250 import {DEFAULT_SIGNUP_FAIL_FLOOR_MS} from '@fuzdev/fuz_app/auth/signup_routes.js';

Default minimum wall-clock time (ms) for a signup denial (403 / 409) response.

Parallel to login's DEFAULT_LOGIN_FAIL_FLOOR_MS. Without a floor, an attacker can distinguish ERROR_NO_MATCHING_INVITE (cheap — bails before Argon2 + tx) from ERROR_SIGNUP_CONFLICT (Argon2 + tx + rollback) via response time and use the gap as a username-enumeration oracle. Picked to exceed the p99 of every denial code path (Argon2id dominates at ~100ms, plus DB + overhead). 429 stays fast by design (same precedent as login) so rate-limit DoS handling stays cheap.

DEFAULT_SIGNUP_FAIL_JITTER_MS
#

auth/signup_routes.ts view source

25 import {DEFAULT_SIGNUP_FAIL_JITTER_MS} from '@fuzdev/fuz_app/auth/signup_routes.js';

Default uniform jitter window (±ms) layered on the floor.

Random jitter prevents a stable clamp point from leaking whenever a path occasionally exceeds the floor. Math.random is sufficient — we only need unpredictability of the exact delay, not cryptographic guarantees.

DEFAULT_SPECS_MODULE
#

actions/action_codegen.ts view source

"./action_specs.ts" import {DEFAULT_SPECS_MODULE} from '@fuzdev/fuz_app/actions/action_codegen.js';

Default specs_module — sibling action_specs.ts namespace bundled by the consumer.

default_test_bootstrap_token
#

testing/cross_backend/default_secrets.ts view source

"test_bootstrap_token_for_cross_be" import {default_test_bootstrap_token} from '@fuzdev/fuz_app/testing/cross_backend/default_secrets.js';

Fixed bootstrap token written to each backend's token_path before spawn. The test binary reads + consumes this via its *_BOOTSTRAP_TOKEN_PATH env var; the harness POSTs the same token to /api/account/bootstrap to mint the keeper account. Any 32+ char string works — the binary just compares bytes, no entropy required for tests.

default_test_cookie_keys
#

default_test_keeper_password
#

default_test_keeper_username
#

DEFAULT_TEST_PASSWORD
#

testing/test_credentials.ts view source

"test-password-123" import {DEFAULT_TEST_PASSWORD} from '@fuzdev/fuz_app/testing/test_credentials.js';

Default password for test-bootstrapped accounts.

DefaultInProcessSuiteOptions
#

testing/cross_backend/in_process_setup.ts view source

DefaultInProcessSuiteOptions import type {DefaultInProcessSuiteOptions} from '@fuzdev/fuz_app/testing/cross_backend/in_process_setup.js';

Consumer-facing options for default_in_process_suite_options — the minimal factory inputs both default_in_process_setup and create_test_app_surface_spec consume to produce the {setup_test, surface_source, capabilities} bundle.

session_options

type SessionOptions<string>

create_route_specs

type (ctx: AppServerContext) => Array<RouteSpec>

rpc_endpoints?

type RpcEndpointsSuiteOption

bootstrap?

Bootstrap config — top-level slot, single source of truth for both surface generation and live dispatch. Same precedent as rpc_endpoints. Discriminated by mode; omit for the default (no bootstrap route mounted).

type BootstrapServerOptions

app_options?

type SuiteAppOptions

extra_keeper_roles?

Additional roles to grant the bootstrapped keeper alongside ROLE_KEEPER — additive, never replaces. The keeper account always holds ROLE_KEEPER (otherwise daemon-token auth breaks); pass extras here for suites that need additional role coverage.

Admin-suite consumers pass [ROLE_ADMIN] so the default keeper can hit admin-gated RPC methods. describe_standard_admin_integration_tests and describe_audit_completeness_tests need this.

type Array<string>

extra_accounts?

Bootstrap-time secondary accounts seeded alongside the keeper. See ExtraAccountSpec for the cradle-only-bypass rationale. Same shape as the cross-process extra_accounts option — suites read seeded accounts from fixture.extra_accounts[username] regardless of transport.

type ReadonlyArray<ExtraAccountSpec>

extra_actors?

Additional actor names to seed on the bootstrapped keeper — exposed on fixture.extra_actors. See TestFixtureBase.extra_actors.

type ReadonlyArray<string>

surface_source?

Pre-built AppSurfaceSpec — overrides the default which calls create_test_app_surface_spec against the same factory inputs. Pass when surface assembly needs fields outside the shared subset (e.g. env_schema, event_specs, ws_endpoints, transform_middleware).

type AppSurfaceSpec

deliver_inbound
#

testing/transports/ws_client.ts view source

(parsed: unknown, received: unknown[], waiters: WsWaiter[], on_request: WsRequestResponder | undefined, reply: (message: unknown) => void): void import {deliver_inbound} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

Deliver one parsed inbound frame to a client's receive sink — the shared parse→(respond|push)→resolve core.

A server-initiated request ({method, id}) is answered by on_request when supplied (replying via reply) and is not surfaced as a normal message; everything else — notifications, replies to the client's own requests, and non-JSON frames — is pushed onto received and resolves any matching waiters, exactly as before. With no on_request, every frame (including a server-initiated request) flows to the sink unchanged, so the seam is purely additive.

parsed

type unknown

received

type unknown[]

waiters

type WsWaiter[]

on_request

type WsRequestResponder | undefined

reply

type (message: unknown) => void

returns

void

derive_error_schemas
#

http/error_schemas.ts view source

({ auth, has_input, has_params, has_query, rate_limit }: DeriveErrorSchemasOptions): Partial<Record<number, ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>>> import {derive_error_schemas} from '@fuzdev/fuz_app/http/error_schemas.js';

__0

returns

Partial<Record<number, ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>>>

derive_http_method
#

actions/action_bridge.ts view source

(side_effects: boolean): RouteMethod import {derive_http_method} from '@fuzdev/fuz_app/actions/action_bridge.js';

Derive the default HTTP method from side effects.

side_effects

type boolean

returns

RouteMethod

DeriveErrorSchemasOptions
#

http/error_schemas.ts view source

DeriveErrorSchemasOptions import type {DeriveErrorSchemasOptions} from '@fuzdev/fuz_app/http/error_schemas.js';

Derive error schemas from a route's auth requirement, input schema, and rate limit config.

Returns the error schemas that middleware will auto-produce for this route. Route handlers can declare additional error schemas via RouteSpec.errors; explicit entries override auto-derived ones for the same status code.

Derivation rules under the new flat-record auth shape:

auth

type RouteAuth

has_input?

type boolean

has_params?

type boolean

has_query?

type boolean

rate_limit?

type RateLimitKey

describe_account_lifecycle_cross_tests
#

describe_actor_lookup_cross_tests
#

describe_actor_search_cross_tests
#

describe_adversarial_404
#

testing/adversarial_404.ts view source

(options: AdversarialTestOptions): void import {describe_adversarial_404} from '@fuzdev/fuz_app/testing/adversarial_404.js';

Generate adversarial 404 response validation tests.

For each route with params + 404 in error_schemas:

  1. Creates a stub handler returning 404 with the declared error code
  2. Fires a request with valid-format params (nil UUIDs for UUID params)
  3. Validates response status is 404
  4. Validates response body matches the declared 404 Zod schema

options

returns

void

describe_adversarial_auth
#

testing/attack_surface.ts view source

(options: AdversarialTestOptions): void import {describe_adversarial_auth} from '@fuzdev/fuz_app/testing/attack_surface.js';

Generate adversarial HTTP auth enforcement test suites.

Describe blocks:

  • unauthenticated → 401 — every protected route
  • wrong role → 403 — every role route, tested with all non-matching roles
  • authenticated without role → 403 — every role route, no-role context
  • correct auth passes guard — every protected route, assert not 401/403

options

returns

void

describe_adversarial_input
#

testing/adversarial_input.ts view source

(options: AdversarialTestOptions): void import {describe_adversarial_input} from '@fuzdev/fuz_app/testing/adversarial_input.js';

Generate adversarial input validation test suites.

Tests input body validation and params validation for all routes. Uses correct auth credentials so auth guards pass and validation middleware is actually exercised.

options

returns

void

describe_app_settings_cross_tests
#

describe_audit_completeness_tests
#

testing/audit_completeness.ts view source

(options: AuditCompletenessTestOptions): void import {describe_audit_completeness_tests} from '@fuzdev/fuz_app/testing/audit_completeness.js';

Composable audit log completeness test suite.

Verifies that every auth mutation route produces the correct audit log event type. Exercises routes via HTTP requests against a real PGlite database, then reads events back through the audit_log_list RPC (the production observation path the admin UI consumes).

options

returns

void

throws

  • Error - at setup time when `options.rpc_endpoints` is empty — the

describe_bearer_auth_cases
#

testing/middleware.ts view source

(suite_name: string, cases: BearerAuthTestCase[], ip_rate_limiter?: RateLimiter | null): void import {describe_bearer_auth_cases} from '@fuzdev/fuz_app/testing/middleware.js';

Run a table of bearer auth middleware test cases.

Generates one test() per case inside a describe() block.

suite_name

type string

cases

type BearerAuthTestCase[]

ip_rate_limiter

type RateLimiter | null
default null

returns

void

describe_body_size_cross_tests
#

describe_body_size_smuggling_cross_tests
#

describe_bootstrap_success_tests
#

describe_cell_crud_cross_tests
#

describe_cell_gated_create_cross_tests
#

describe_cell_grant_role_cross_tests
#

describe_cell_moderate_cross_tests
#

testing/cross_backend/cell_gated_create.ts view source

(options: RpcPathCrossSuiteOptions): void import {describe_cell_moderate_cross_tests} from '@fuzdev/fuz_app/testing/cross_backend/cell_gated_create.js';

Cross-backend parity for the cell_moderate verb (the `pending → approved | rejected` transition) — root-authority-gated.

Builds a moderated public space (admin) + a participant's post (born pending + private under the moderation_required: true policy), then proves both spines agree:

  • a root manager (admin) approvesmoderation: 'approved' + visibility: 'public' (the contribution goes live).
  • the author cannot self-approve403 cell_moderate_forbidden (the author can *view* their own pending cell, so they reach the gate, and are denied — moderation authority is over the governing root, not the contribution).
  • a non-viewer is 404-masked → a stranger who can't see the pending contribution gets cell_not_found, never learns it exists.
  • rejectmoderation: 'rejected', visibility stays private.

Gated on capabilities.cell_gated_create — a pending contribution only exists when the directory authorizer is mounted (reference spine binaries).

$lib-free by contract (relative specifiers only).

options

returns

void

describe_cell_relations_cross_tests
#

describe_conformance_table_tests
#

describe_cookie_attributes_cross_tests
#

describe_credential_header_robustness_cross_tests
#

describe_cross_process_sse_tests
#

testing/cross_backend/sse_round_trip.ts view source

(options: CrossProcessSseTestOptions): void import {describe_cross_process_sse_tests} from '@fuzdev/fuz_app/testing/cross_backend/sse_round_trip.js';

Register the cross-process SSE round-trip suite. Up to four cases over a real streaming fetch: connected-comment, audit data frame, account-wide close-on-revoke, and session-scoped close-on-revoke.

options

returns

void

describe_cross_process_ws_tests
#

testing/cross_backend/ws_round_trip.ts view source

(options: CrossProcessWsTestOptions): void import {describe_cross_process_ws_tests} from '@fuzdev/fuz_app/testing/cross_backend/ws_round_trip.js';

Register the cross-process WS round-trip suite. Up to four cases over a real upgrade: authed heartbeat round-trip, anonymous-upgrade refusal, disallowed-origin refusal, and — when rpc_path is supplied — session-revocation closing the live socket.

options

returns

void

describe_data_exposure_tests
#

testing/data_exposure.ts view source

(options: DataExposureTestOptions): void import {describe_data_exposure_tests} from '@fuzdev/fuz_app/testing/data_exposure.js';

Composable data exposure test suite.

Three test groups:

  1. Schema-level — walk JSON Schema output/error schemas for sensitive field names
  2. Runtime — fire real requests and check response bodies against blocklists
  3. Cross-privilege — admin routes return 403 for non-admin, error responses contain no sensitive fields

options

returns

void

describe_fact_serving_cross_tests
#

describe_identity_parity_cross_tests
#

describe_login_security_cross_tests
#

describe_origin_cross_tests
#

describe_peer_ping_ws_tests
#

testing/cross_backend/peer_ping_ws.ts view source

(options: PeerPingWsTestOptions): void import {describe_peer_ping_ws_tests} from '@fuzdev/fuz_app/testing/cross_backend/peer_ping_ws.js';

Register the server-initiated peer/ping suite — a positive round-trip plus the security negatives the design doc's §Security surface mandates (unsolicited-response rejection, per-connection id isolation, never-reply Timeout, wrong-shape reply rejection, plus client-error forwarding and the HTTP no-transport path). Gated on capabilities.peer_request.

options

returns

void

describe_rate_limiting_tests
#

testing/rate_limiting.ts view source

(options: RateLimitingTestOptions): void import {describe_rate_limiting_tests} from '@fuzdev/fuz_app/testing/rate_limiting.js';

Standard rate limiting integration test suite.

Creates 3 test groups:

  1. IP rate limiting on login — fires max_attempts + 1 login requests, verifies the last returns 429 with a valid RateLimitError body.
  2. Per-account rate limiting on login — fires max_attempts + 1 login requests with the same username, verifies the last returns 429.
  3. Bearer auth IP rate limiting — fires max_attempts + 1 bearer requests with an invalid token, verifies the last returns 429.

Each test group asserts that required routes exist, failing with a descriptive message if the consumer's route specs are misconfigured.

options

returns

void

throws

  • Error - at setup time when `options.rpc_endpoints` is empty — the

describe_ready_cross_tests
#

describe_role_grant_offer_enumeration_cross_tests
#

describe_role_grant_offer_notification_ws_tests
#

testing/cross_backend/role_grant_offer_notification_ws.ts view source

(options: RoleGrantOfferNotificationWsTestOptions): void import {describe_role_grant_offer_notification_ws_tests} from '@fuzdev/fuz_app/testing/cross_backend/role_grant_offer_notification_ws.js';

Register the role-grant-offer WS notification suite — seven cases over a real upgrade, one per server-initiated notification (received / accepted / declined / retracted / revoke + supersede on both the accept and revoke cascades). Each opens the affected counterparty's socket, drives the lifecycle RPC, and strict-parses the delivered frame against its canonical params schema. Gated on capabilities.ws.

options

returns

void

describe_role_grant_participation_cross_tests
#

describe_round_trip_validation
#

testing/round_trip.ts view source

(options: RoundTripTestOptions): void import {describe_round_trip_validation} from '@fuzdev/fuz_app/testing/round_trip.js';

Run schema-driven round-trip validation tests.

For each route:

  1. Resolve URL with valid params
  2. Generate a valid request body (or use override)
  3. Pick auth headers matching the route's auth requirement
  4. Fire the request through fixture.transport and validate the response

SSE routes are skipped by Content-Type sniff. Routes returning non-2xx with valid input are still validated against their declared error schemas.

options

returns

void

describe_rpc_attack_surface_tests
#

testing/rpc_attack_surface.ts view source

(options: RpcAttackSurfaceOptions): void import {describe_rpc_attack_surface_tests} from '@fuzdev/fuz_app/testing/rpc_attack_surface.js';

Run the standard RPC attack surface test suite.

Generates 3 test groups:

  1. Auth enforcement — per-method auth checks via JSON-RPC envelopes
  2. Adversarial envelopes — malformed JSON-RPC requests
  3. Adversarial params — schema-invalid params per method

Skips silently when surface.rpc_endpoints is empty.

options

returns

void

describe_rpc_round_trip_tests
#

testing/rpc_round_trip.ts view source

(options: RpcRoundTripTestOptions): void import {describe_rpc_round_trip_tests} from '@fuzdev/fuz_app/testing/rpc_round_trip.js';

Run schema-driven round-trip validation for RPC endpoints.

For each method:

  1. Generate valid params from the action's input schema
  2. Fire a POST request with JSON-RPC envelope
  3. For side_effects: false methods, also fire a GET request
  4. Validate response is well-formed JSON-RPC; successful responses are also validated against the method's declared output schema

Error responses (from missing DB state, etc.) are expected and validated as well-formed JSON-RPC errors. Successful responses are validated against action.spec.output. A method not found (-32601) error is the one exception — it means the backend is missing a method the local surface advertises, so the round-trip fails loud (assert_method_implemented) rather than accepting it as a valid error envelope.

options

returns

void

describe_sse_route_tests
#

testing/sse_round_trip.ts view source

(options: SseRouteTestOptions): void import {describe_sse_route_tests} from '@fuzdev/fuz_app/testing/sse_round_trip.js';

Run SSE route validation tests.

For each route: opens an authenticated SSE connection, asserts the connected comment, fires the trigger, validates the resulting payload, then asserts close-on-revoke (unless opted out).

options

returns

void

throws

  • Error - at setup time when `options.rpc_endpoints` is empty — the

describe_standard_admin_integration_tests
#

testing/admin_integration.ts view source

(options: StandardAdminIntegrationTestOptions): void import {describe_standard_admin_integration_tests} from '@fuzdev/fuz_app/testing/admin_integration.js';

Standard admin integration test suite for fuz_app admin routes.

Exercises account listing, role_grant grant/revoke (via RPC), session management, token management, audit log reads, admin-to-admin isolation, and 401/403 error-coverage on the admin REST surface. Output-schema conformance is not in scope — see the module docstring for the suites that cover it.

options

returns

void

throws

  • Error - at setup time when `options.rpc_endpoints` is empty — admin

describe_standard_adversarial_headers
#

testing/adversarial_headers.ts view source

(suite_name: string, options: TestMiddlewareStackOptions, allowed_origin: string, extra_cases?: AdversarialHeaderCase[] | undefined): void import {describe_standard_adversarial_headers} from '@fuzdev/fuz_app/testing/adversarial_headers.js';

Create a middleware stack app with standard adversarial header tests.

Convenience wrapper combining create_test_middleware_stack_app and create_standard_adversarial_cases. Generates one test() per case inside a describe() block — asserts body content for both error and success cases, and verifies that mock_validate was (or was not) reached per the case's validate_expectation flag, ensuring earlier middleware actually short-circuits before token validation in the rejection cases.

suite_name

the describe block name

type string

options

middleware stack configuration

allowed_origin

an origin that passes the origin check (used for standard cases)

type string

extra_cases?

additional cases appended after the 7 standard ones

type AdversarialHeaderCase[] | undefined
optional

returns

void

describe_standard_attack_surface_tests
#

testing/attack_surface.ts view source

(options: StandardAttackSurfaceOptions): void import {describe_standard_attack_surface_tests} from '@fuzdev/fuz_app/testing/attack_surface.js';

Run the standard attack surface test suite.

Test groups:

  1. Snapshot — live surface matches committed JSON
  2. Determinism — building twice yields identical results
  3. Public routes — bidirectional check (no unexpected, no missing)
  4. Middleware stack — every API route has the full middleware chain
  5. Surface invariants — structural assertions over surface.routes (error schemas, descriptions, duplicates, consistency)
  6. RPC/WS surface invariants — structural assertions over surface.rpc_endpoints + surface.ws_endpoints (descriptions, protocol-action spread, kind ⇔ auth)
  7. Security policy — rate limiting on sensitive routes, no unexpected public mutations, method conventions
  8. Error schema tightness — informational log of generic vs specific error schemas, plus assertion against default_error_schema_tightness by default (opt out with error_schema_tightness: null)
  9. Adversarial auth — unauthenticated/wrong-role/correct-auth enforcement
  10. Adversarial input — input body and params validation
  11. Adversarial 404 — stub 404 handlers, validate response bodies against declared schemas

Consumer test files call this with project-specific options, then add any project-specific assertions in additional describe blocks.

options

returns

void

describe_standard_cross_process_tests
#

testing/cross_backend/standard.ts view source

(options: StandardCrossProcessTestOptions): void import {describe_standard_cross_process_tests} from '@fuzdev/fuz_app/testing/cross_backend/standard.js';

Run the cross-process standard test bundle — integration, admin (when roles provided), round trip, RPC round trip, data exposure. See the module doc for the suites omitted from this bundle and why.

options

returns

void

describe_standard_integration_tests
#

testing/integration.ts view source

(options: StandardIntegrationTestOptions): void import {describe_standard_integration_tests} from '@fuzdev/fuz_app/testing/integration.js';

Standard integration test suite for fuz_app auth routes.

Exercises login/logout, cookie attributes, session security, session revocation, password change (incl. API token revocation), origin verification, bearer auth (incl. browser context discard on mutations), token revocation, cross-account isolation, expired credential rejection, signup invite edge cases, and response body validation.

Each test group asserts that required routes exist, failing with a descriptive message if the consumer's route specs are misconfigured.

The two signup-invite-edge-case tests call invite_create_action_spec (admin-gated) over the fixture's session, so consumers wiring signup + admin actions must thread extra_keeper_roles: [ROLE_ADMIN] through either default_in_process_suite_options or `default_cross_process_setup(handle, {extra_keeper_roles: [ROLE_ADMIN]}). In both modes the fixture's fixture.account` is the fresh keeper, and the extra-keeper-roles list grants the bootstrapped keeper additional roles inline (cross-process via _testing_reset's bootstrap-time seeding; in-process via bootstrap_test_keeper) — no per-role RPC cost, no offer/accept round-trip. The tests run against the production open_signup: false default — the cross-process per-test secondary mint via fixture.create_account() is invite-gated (keeper drives invite_create before signup) so the harness doesn't need to flip the setting. Consumers that don't wire signup or invite_create silently skip these two tests.

options

returns

void

throws

  • Error - at setup time when `options.rpc_endpoints` is empty — the

describe_standard_tests
#

testing/standard.ts view source

(options: StandardTestOptions): void import {describe_standard_tests} from '@fuzdev/fuz_app/testing/standard.js';

Run the full standard test bundle — integration, admin (when roles provided), audit completeness (when roles provided), bootstrap success (when bootstrap.mode === 'live'), round trip, RPC round trip, data exposure, rate limiting.

options

returns

void

describe_testing_backdoor_cross_tests
#

detect_format
#

testing/schema_generators.ts view source

(field_schema: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>): string | null import {detect_format} from '@fuzdev/fuz_app/testing/schema_generators.js';

Detect format constraints on a field by converting to JSON Schema. Returns format string (e.g. 'uuid', 'email') or null.

field_schema

type ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>

returns

string | null

diff_action_manifests
#

testing/cross_backend/action_manifest_parity.ts view source

(a: { methods: { method: string; side_effects: boolean; account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles: string[]; credential_types: string[]; }[]; }, b: { ...; }): ActionManifestDiff[] import {diff_action_manifests} from '@fuzdev/fuz_app/testing/cross_backend/action_manifest_parity.js';

Structural diff between two manifests — empty array means parity holds.

Order is deterministic: methods in sorted order, with each method's side-effect + auth-field sub-diffs grouped together.

a

type { methods: { method: string; side_effects: boolean; account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles: string[]; credential_types: string[]; }[]; }

b

type { methods: { method: string; side_effects: boolean; account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles: string[]; credential_types: string[]; }[]; }

returns

ActionManifestDiff[]

diff_migration_trackers
#

testing/schema_parity.ts view source

(a: { entries: { namespace: string; name: string; sequence: number; }[]; }, b: { entries: { namespace: string; name: string; sequence: number; }[]; }): MigrationTrackerDiff[] import {diff_migration_trackers} from '@fuzdev/fuz_app/testing/schema_parity.js';

Structural diff between two migration trackers — empty array means the two spines recorded byte-identical migration identity. Keyed on (namespace, name); sequence mismatches on a shared key are reported too. Deterministic order: shared/missing rows in sorted (namespace, name) order.

a

type { entries: { namespace: string; name: string; sequence: number; }[]; }

b

type { entries: { namespace: string; name: string; sequence: number; }[]; }

returns

MigrationTrackerDiff[]

diff_schema_snapshots
#

testing/schema_parity.ts view source

(a: { tables: Record<string, { columns: Record<string, { data_type: string; udt_name: string; is_nullable: boolean; column_default: string | null; is_identity: boolean; }>; indexes: { name: string; definition: string; }[]; constraints: { ...; }[]; }>; sequences: Record<...>; enums: Record<...>; }, b: { ...; }): SchemaDiff[] import {diff_schema_snapshots} from '@fuzdev/fuz_app/testing/schema_parity.js';

Structural diff between two snapshots — empty array means parity holds.

Order of diffs is deterministic: tables in sorted order (with column/index/constraint sub-diffs grouped per table), then sequences. Consumers can rely on this for stable diff output.

a

type { tables: Record<string, { columns: Record<string, { data_type: string; udt_name: string; is_nullable: boolean; column_default: string | null; is_identity: boolean; }>; indexes: { name: string; definition: string; }[]; constraints: { ...; }[]; }>; sequences: Record<...>; enums: Record<...>; }

b

type { tables: Record<string, { columns: Record<string, { data_type: string; udt_name: string; is_nullable: boolean; column_default: string | null; is_identity: boolean; }>; indexes: { name: string; definition: string; }[]; constraints: { ...; }[]; }>; sequences: Record<...>; enums: Record<...>; }

returns

SchemaDiff[]

disconnect_event_types
#

realtime/sse_auth_guard.ts view source

ReadonlySet<string> import {disconnect_event_types} from '@fuzdev/fuz_app/realtime/sse_auth_guard.js';

Audit event types that trigger SSE stream disconnection — the union of access-invalidation events. Over-closing a one-way admin feed is cheap (the client reconnects if still authorized), so the SSE set is the full union.

role_grant_revoke requires the revoked role to match the guard's required_role (or is skipped entirely when required_role is null — useful for streams not gated by any specific role_grant). The WS half deliberately omits this event (per-message re-authorization picks role changes up there); a one-way SSE stream has no per-message recheck, so it must close here. session_revoke_all / token_revoke_all / password_change / logout close every stream for the target account. session_revoke closes only the stream tied to the specific revoked session (matched by the blake3 session hash in event.metadata.session_id) — closing all of a user's streams for a single-session revoke would be over-aggressive. The single token_revoke the WS half handles is omitted: an SSE stream is opened under a cookie session, never an API token, so no stream is keyed by a single token id.

dispatch_with_post_commit_rollback
#

http/pending_effects.ts view source

<T>(post_commit_effects: (() => void | Promise<void>)[] | undefined, dispatch: () => T | Promise<T>): Promise<Awaited<T>> import {dispatch_with_post_commit_rollback} from '@fuzdev/fuz_app/http/pending_effects.js';

Run a handler dispatch (the handler plus its wrapping db.transaction), discarding any post_commit_effects it queued via emit_after_commit if it throws. A thrown handler rolls back its transaction, so firing those deferred effects would announce state that never committed. On any throw the queue is truncated back to its pre-dispatch depth — not cleared, so entries a surrounding scope pre-seeded survive — and the error re-thrown unchanged.

The eager pending_effects queue is deliberately never touched here: its pool writes run outside the transaction and intentionally survive rollback (attempt audits). emit_after_commit thus reads as "run iff the wrapping transaction commits."

Both dispatch sites — the REST route wrapper (http/route_spec.ts) and the action dispatcher (actions/perform_action.ts, the RPC + WS path) — wrap their handler call in this so the discard contract lives in one place. The Rust fuz_actions spine pins the same contract.

post_commit_effects is undefined when a handler runs without the app-server pending-effects middleware (bare route / dispatch harnesses): absent ⇒ nothing queued ⇒ nothing to discard.

post_commit_effects

the deferred queue to truncate on throw, or undefined

type (() => void | Promise<void>)[] | undefined

dispatch

invokes the handler (and any wrapping transaction)

type () => T | Promise<T>

returns

Promise<Awaited<T>>

generics

dispatch_with_post_commit_rollback<T>
T

dispatch_ws_message
#

testing/ws_round_trip.ts view source

(on_message: (evt: MessageEvent<WSMessageReceive>, ws: WSContext<unknown>) => void, event: MessageEvent<any>, ws: WSContext<unknown>): Promise<...> import {dispatch_ws_message} from '@fuzdev/fuz_app/testing/ws_round_trip.js';

Hono types WSEvents.onMessage as () => void | Promise<void>. Awaits only the Promise branch so tests observe full dispatch (auth, validation, handler, send).

on_message

type (evt: MessageEvent<WSMessageReceive>, ws: WSContext<unknown>) => void

event

type MessageEvent<any>

ws

type WSContext<unknown>

returns

Promise<void>

Divergence
#

db/status.ts view source

Divergence import type {Divergence} from '@fuzdev/fuz_app/db/status.js';

A divergence between the recorded migration tracker and the code's list.

Either variant is a state the migration runner refuses to boot against — a re-bootstrap (drop + migrate) is needed. Structured (not a pre-formatted string) so programmatic consumers can branch on kind; format_db_status renders the operator-facing line. The discriminated-union twin of the Rust fuz_db Divergence enum.

drop_auth_schema
#

testing/db.ts view source

(db: Db): Promise<void> import {drop_auth_schema} from '@fuzdev/fuz_app/testing/db.js';

Reset the entire public schema for a clean slate before re-migration.

Recommended at the start of init_schema callbacks for create_pg_factory. Persistent test databases accumulate stale DDL across fuz_app versions; DROP SCHEMA public CASCADE; CREATE SCHEMA public wipes every table, type, and sequence regardless of namespace, so migrations always run against a truly empty database. Drift-proof — unlike a hand-maintained drop list it needs no upkeep when the schema gains a table. Despite the historical name, this resets the whole schema, not just auth tables (the only documented use is clean-slate re-migration, which always wanted a full reset).

db

type Db

returns

Promise<void>

mutates

  • db — drops and recreates the `public` schema; all tables gone.

DualSpawnGlobalSetupOptions
#

testing/cross_backend/create_dual_spawn_global_setup.ts view source

DualSpawnGlobalSetupOptions import type {DualSpawnGlobalSetupOptions} from '@fuzdev/fuz_app/testing/cross_backend/create_dual_spawn_global_setup.js';

configs

The two backend config factories to spawn. a is spawned first; on its success b is spawned (with a torn down if b throws). Label them in the consuming test (e.g. via assert_schema_snapshots_equal's labels).

type { readonly a: () => BackendConfig; readonly b: () => BackendConfig }

readonly

provide_keys?

project.provide keys for the two serialized handles (read by inject in the test). Default parity_handle_a / parity_handle_b (the primary parity use); override for other dual-spawn gates. Augment vitest's ProvidedContext for whichever keys you use.

type { readonly a: string; readonly b: string }

readonly

EffectErrorContext
#

server/app_server.ts view source

EffectErrorContext import type {EffectErrorContext} from '@fuzdev/fuz_app/server/app_server.js';

Context passed to on_effect_error when a pending effect rejects.

method

HTTP method of the request that spawned the effect.

type string

path

URL path of the request that spawned the effect.

type string

Email
#

primitive_schemas.ts view source

ZodString import type {Email} from '@fuzdev/fuz_app/primitive_schemas.js';

Email validation. Lives here rather than @fuzdev/fuz_util because every current consumer pairs it with Username (signup, invites, audit log) — keeping the two together avoids a cross-package import for the identity-primitive bundle. Promote to fuz_util if a non-identity consumer surfaces.

Deliberately permissive — a structural shape check (EMAIL_REGEX plus the EMAIL_LENGTH_MAX byte bound), not RFC 5322 conformance or deliverability (real delivery is proven by a confirmation email). Replaces Zod's stricter z.email() so the rule is one explicit regex the Rust spine's is_valid_email mirrors; z.email()'s internal regex (2+ char TLD, no consecutive dots) was brittle to keep in cross-impl parity and rejected addresses like a@b.c. The length bound is the UTF-8 byte count (RFC 5321 measures octets), so a multibyte address is bounded identically to the Rust spine's s.len() rather than diverging on JS's UTF-16 .length. No transform: case is preserved and surrounding whitespace is rejected (not trimmed), so storage is verbatim and the case-insensitive lookup rides the DB-side LOWER(email).

EMAIL_LENGTH_MAX
#

primitive_schemas.ts view source

254 import {EMAIL_LENGTH_MAX} from '@fuzdev/fuz_app/primitive_schemas.js';

Maximum email length in bytes — RFC 5321 §4.5.3.1.3 path-length limit (the limit is octets). Email bounds the UTF-8 byte length, matching the Rust spine's s.len() check; for an all-ASCII address bytes == characters.

emit_after_commit
#

http/pending_effects.ts view source

(ctx: EmitAfterCommitContext, fn: () => void | Promise<void>): void import {emit_after_commit} from '@fuzdev/fuz_app/http/pending_effects.js';

Defer a side effect until after the handler's transaction commits.

Pushes a raw thunk onto ctx.post_commit_effects — the flush middleware (in server/app_server.ts and the per-message WS dispatcher) is the only site that ever invokes fn. This is load-bearing: a previous implementation queued Promise.resolve().then(fn), which JS's microtask scheduler drains before the wrapping await db.query('COMMIT') resumes — fn fired mid-transaction and a rollback would leak a notification for state that never landed.

The thunk shape closes that gap by deferring the work to flush time. The flush owns the per-thunk try/catch + log.error so any directly-pushed thunk (tests included) cannot escape the safety net.

Deferral is only half the contract: a queued thunk is also discarded if the handler's transaction rolls back — via dispatch_with_post_commit_rollback (see its TSDoc). So fn runs iff the wrapping transaction commits, never for state that rolled back. Rollback-resilient writes (attempt audits that must land even when the handler fails) belong on the eager pending_effects queue instead.

ctx

context carrying log and the post_commit_effects queue

fn

side effect to run after commit; may return void or Promise<void>

type () => void | Promise<void>

returns

void

EmitAfterCommitContext
#

EmitDecorator
#

auth/audit_emitter.ts view source

EmitDecorator import type {EmitDecorator} from '@fuzdev/fuz_app/auth/audit_emitter.js';

Wrap the bound emit before it gets captured by emit_role_grant_target's closure and exposed on the returned AuditEmitter. Test instrumentation uses this to record emit invocation ordering against external markers (e.g. eager ConnectionCloser calls in connection_closer.db.test.ts) without paying the freeze-breaking footgun the pre-decorator patch_audit_emit_capture hot-patcher had.

Because the inner closure captures the decorated function (not the outer slot reference), emit_role_grant_target also routes through the wrap — the close-vs-emit ordering helper sees role-grant-shape emissions, not just bare emit calls. Production never sets this.

(call)

type (inner: AuditEmitFn): AuditEmitFn

inner

returns AuditEmitFn

EnumTypeSnapshot
#

testing/schema_introspect.ts view source

ZodObject<{ labels: ZodArray<ZodString>; }, $strip> import type {EnumTypeSnapshot} from '@fuzdev/fuz_app/testing/schema_introspect.js';

Enum-type metadata — the labels of a CREATE TYPE ... AS ENUM, captured in pg_enum.enumsortorder (declaration) order. Order is significant: a Postgres enum's labels are an ordered set, and reordering them is a schema change, so the parity diff compares the arrays positionally.

env_schema_to_surface
#

http/surface.ts view source

(schema: ZodObject<$ZodLooseShape, $strip>): AppSurfaceEnv[] import {env_schema_to_surface} from '@fuzdev/fuz_app/http/surface.js';

Convert env schema to surface entries using .meta() metadata.

schema

Zod object schema with .meta() on fields

type ZodObject<$ZodLooseShape, $strip>

returns

AppSurfaceEnv[]

EnvDeps
#

runtime/deps.ts view source

EnvDeps import type {EnvDeps} from '@fuzdev/fuz_app/runtime/deps.js';

Environment variable access.

env_get

Get an environment variable value.

type (name: string) => string | undefined

env_set

Set an environment variable.

type (name: string, value: string) => void

EnvValidationError
#

env/load.ts view source

import {EnvValidationError} from '@fuzdev/fuz_app/env/load.js';

Error thrown when environment validation fails.

Contains structured information for apps to format their own error messages.

inheritance

extends: Error

raw

The raw env values that were read.

type Record<string, string | undefined>

readonly

zod_error

The Zod validation error.

type z.core.$ZodError

readonly

all_undefined

True if every env var was undefined (nothing loaded).

type boolean

readonly

constructor

type new (raw: Record<string, string | undefined>, zod_error: $ZodError<unknown>): EnvValidationError

raw

type Record<string, string | undefined>

zod_error

type $ZodError<unknown>

format_issues

Format Zod validation issues as human-readable strings.

type (): string[]

returns string[]

array of formatted issue strings like "PORT: Expected number"

EnvValidationResult
#

env/resolve.ts view source

EnvValidationResult import type {EnvValidationResult} from '@fuzdev/fuz_app/env/resolve.js';

Result of env var validation.

Uses discriminated union for better type narrowing:

  • ok: true, missing: null — all vars present
  • ok: false, missing: EnvVarRef[] — some vars missing

EnvVarRef
#

env/resolve.ts view source

EnvVarRef import type {EnvVarRef} from '@fuzdev/fuz_app/env/resolve.js';

An env var reference found in a config.

name

Variable name (without $$ delimiters).

type string

path

Path where the reference was found (e.g., "target.host", "resources[3].path").

type string

optional

Whether the reference is optional ($$?VAR$$). Optional refs resolve to the empty string when unset and are skipped by validate_env_vars — a var that's intentionally blank doesn't count as missing.

type boolean

ERROR_ACCOUNT_NOT_FOUND
#

http/error_schemas.ts view source

"account_not_found" import {ERROR_ACCOUNT_NOT_FOUND} from '@fuzdev/fuz_app/http/error_schemas.js';

Token references a deleted account.

ERROR_ACCOUNT_VANISHED
#

http/error_schemas.ts view source

"account_vanished" import {ERROR_ACCOUNT_VANISHED} from '@fuzdev/fuz_app/http/error_schemas.js';

Authentication validated an account, but a follow-up read in the authorization phase came back null — the account or its named actor row was deleted between the credential check and the dispatcher's build_request_context / build_account_context step. Torn read, not a missing-actor invariant violation. Surfaced as 500 so the operator sees the race signal; clients can retry. Distinct from ERROR_ACCOUNT_NOT_FOUND (stale token referencing a long-deleted account, raised at credential validation) and ERROR_NO_ACTORS_ON_ACCOUNT (the actor list enumerated empty).

ERROR_ACTOR_NOT_ON_ACCOUNT
#

http/error_schemas.ts view source

"actor_not_on_account" import {ERROR_ACTOR_NOT_ON_ACCOUNT} from '@fuzdev/fuz_app/http/error_schemas.js';

Supplied acting field does not name an actor on the authenticated account.

ERROR_ACTOR_REQUIRED
#

http/error_schemas.ts view source

"actor_required" import {ERROR_ACTOR_REQUIRED} from '@fuzdev/fuz_app/http/error_schemas.js';

Multi-actor account requires the request to carry an explicit acting field naming the actor the request is acting as, so the dispatcher's authorization phase doesn't pick a default actor silently. Returned with the available actors so the client can prompt.

ERROR_ACTOR_SEARCH_SCOPE_REQUIRED
#

auth/actor_search_action_specs.ts view source

"actor_search_scope_required" import {ERROR_ACTOR_SEARCH_SCOPE_REQUIRED} from '@fuzdev/fuz_app/auth/actor_search_action_specs.js';

Reason: scope_ids was empty and the caller is not admin. Distinct from standard invalid_params issues so the visiones picker can surface a specific "pick a scope first" message rather than echoing Zod issues.

ERROR_ALREADY_BOOTSTRAPPED
#

http/error_schemas.ts view source

"already_bootstrapped" import {ERROR_ALREADY_BOOTSTRAPPED} from '@fuzdev/fuz_app/http/error_schemas.js';

Bootstrap lock already acquired — system already bootstrapped.

ERROR_AUTHENTICATION_REQUIRED
#

http/error_schemas.ts view source

"authentication_required" import {ERROR_AUTHENTICATION_REQUIRED} from '@fuzdev/fuz_app/http/error_schemas.js';

No valid session or bearer token.

ERROR_CANNOT_DELETE_KEEPER
#

auth/admin_action_specs.ts view source

"cannot_delete_keeper" import {ERROR_CANNOT_DELETE_KEEPER} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

data.reason (403) on account_delete / account_purge when the target account holds an active keeper role_grant. The keeper account is never deletable or purgeable through the API: auth resolution and daemon-token resolution both pivot on the keeper account, so tombstoning or cascading it away would brick keeper/daemon auth with no recovery path (the keeper role is not web-revocable, and account_purge itself requires keeper auth). Keeper-account removal stays out-of-band (bootstrap / DB surgery). Mirrors the Rust ERROR_CANNOT_DELETE_KEEPER.

ERROR_CANNOT_DELETE_LAST_ADMIN
#

auth/admin_action_specs.ts view source

"cannot_delete_last_admin" import {ERROR_CANNOT_DELETE_LAST_ADMIN} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

data.reason (403) on account_delete / account_purge when the target is the sole remaining active admin — removing it would leave the system with no account that can authenticate into the admin surface (and account_undelete is itself admin-gated). Unlike the keeper guard this is keeper-recoverable (a keeper can re-grant admin), but the guard avoids the foot-gun of an admin tombstoning the last admin in one call. Soft-deleted admins don't count toward the tally (they can't log in). Mirrors the Rust ERROR_CANNOT_DELETE_LAST_ADMIN.

ERROR_CELL_CREATE_FORBIDDEN
#

auth/cell_action_specs.ts view source

"cell_create_forbidden" import {ERROR_CELL_CREATE_FORBIDDEN} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Error reason — the parent-aware CellCreateAuthorize denied the create for a viewable parent (or a root creation): "you see it, you can't contribute here." A 403 forbidden, distinct from the create-path 404 mask used when the parent itself isn't viewable (twin of the Rust ERROR_CELL_CREATE_FORBIDDEN).

ERROR_CELL_FIELD_LIST_REQUIRES_SOURCE_OR_TARGET
#

auth/cell_field_action_specs.ts view source

"cell_field_list_requires_source_or_target" import {ERROR_CELL_FIELD_LIST_REQUIRES_SOURCE_OR_TARGET} from '@fuzdev/fuz_app/auth/cell_field_action_specs.js';

Error reason — cell_field_list got neither source_id nor target_id.

ERROR_CELL_GET_REQUIRES_ID_OR_PATH
#

auth/cell_action_specs.ts view source

"cell_get_requires_id_or_path" import {ERROR_CELL_GET_REQUIRES_ID_OR_PATH} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Error reason — input shape for cell_get lacked both id and path.

ERROR_CELL_GRANT_NOT_FOUND
#

auth/cell_grant_action_specs.ts view source

"cell_grant_not_found" import {ERROR_CELL_GRANT_NOT_FOUND} from '@fuzdev/fuz_app/auth/cell_grant_action_specs.js';

Error reason — grant id did not resolve, or caller may not see it.

ERROR_CELL_GRANT_PRINCIPAL_IS_OWNER
#

auth/cell_grant_action_specs.ts view source

"cell_grant_principal_is_owner" import {ERROR_CELL_GRANT_PRINCIPAL_IS_OWNER} from '@fuzdev/fuz_app/auth/cell_grant_action_specs.js';

Error reason — cell_grant_create principal resolves to the cell's owner. Owner access is implicit (cell.created_by); a self-grant row would shadow it without changing access and create a confusing self-leave path.

ERROR_CELL_GRANT_UNKNOWN_ROLE
#

auth/cell_grant_action_specs.ts view source

"cell_grant_unknown_role" import {ERROR_CELL_GRANT_UNKNOWN_ROLE} from '@fuzdev/fuz_app/auth/cell_grant_action_specs.js';

Error reason — role-shaped principal references a role string not registered in the role schema. Would produce a dead grant row that no role_grant could match.

ERROR_CELL_ITEM_LIST_REQUIRES_PARENT_OR_CHILD
#

auth/cell_item_action_specs.ts view source

"cell_item_list_requires_parent_or_child" import {ERROR_CELL_ITEM_LIST_REQUIRES_PARENT_OR_CHILD} from '@fuzdev/fuz_app/auth/cell_item_action_specs.js';

Error reason — cell_item_list got neither parent_id nor child_id.

ERROR_CELL_ITEM_POSITION_TAKEN
#

auth/cell_item_action_specs.ts view source

"cell_item_position_taken" import {ERROR_CELL_ITEM_POSITION_TAKEN} from '@fuzdev/fuz_app/auth/cell_item_action_specs.js';

Error reason — (parent_id, position) collision on cell_item_insert or cell_item_move. Surfaces when two clients computed the same fractional-indexing key (rare given helper-side jitter, which is on by default; the safety net for the residual race). A client that opts out with {jitter: false} gets bare deterministic mids, so two of them racing the same bracket collide every time and this stops being rare — that mode is for reproducible callers (seeders, fixtures), not concurrent ones. Client refreshes its bracket and retries.

ERROR_CELL_KIND_EMPTY
#

auth/cell_action_specs.ts view source

"cell_kind_empty" import {ERROR_CELL_KIND_EMPTY} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Error reason — cell_create was given an empty-string kind. kind is a non-empty capability tag or absent (null = typeless cell); "" (a tag that tags nothing) is rejected fail-loud so kind stays a clean null | non-empty-string (twin of the Rust ERROR_CELL_KIND_EMPTY).

ERROR_CELL_KIND_IN_DATA
#

auth/cell_action_specs.ts view source

"cell_kind_in_data" import {ERROR_CELL_KIND_IN_DATA} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Error reason — a kind key was supplied inside data. kind is a top-level column (cell.kind), not content metadata; accepting it inside data would create two sources of truth. Rejected fail-loud at the create / update / clone-patch boundary so the column stays canonical.

ERROR_CELL_LIST_CREATED_BY_REQUIRES_AUTH
#

auth/cell_action_specs.ts view source

"cell_list_created_by_requires_auth" import {ERROR_CELL_LIST_CREATED_BY_REQUIRES_AUTH} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Error reason — null-auth cell_list caller passed a created_by filter. The filter is a soft account-id enumeration vector ("does account X have any public cells?"), so we require an authenticated caller to use it.

ERROR_CELL_LIST_SHARED_WITH_REQUIRES_AUTH
#

auth/cell_action_specs.ts view source

"cell_list_shared_with_requires_auth" import {ERROR_CELL_LIST_SHARED_WITH_REQUIRES_AUTH} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Error reason — null-auth cell_list caller passed shared_with: 'me'. The filter resolves to the caller's account + role_grants, which only exist for an authenticated session.

ERROR_CELL_MODERATE_FORBIDDEN
#

auth/cell_action_specs.ts view source

"cell_moderate_forbidden" import {ERROR_CELL_MODERATE_FORBIDDEN} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Error reason — cell_moderate caller can view the contribution but lacks moderation authority over its governing root (not admin / not the root's manager). A 403 forbidden. The author can view their own pending contribution, so they reach this gate — and are denied here, which is the anti-self-approval guard (twin of the Rust ERROR_CELL_MODERATE_FORBIDDEN).

ERROR_CELL_NOT_A_CONTRIBUTION
#

auth/cell_action_specs.ts view source

"cell_not_a_contribution" import {ERROR_CELL_NOT_A_CONTRIBUTION} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Error reason — cell_moderate target has no governing root (root_id is null): it's a root or an unparented cell, not a gated contribution, so there is nothing to moderate. A 400 invalid_params (twin of the Rust ERROR_CELL_NOT_A_CONTRIBUTION).

ERROR_CELL_NOT_FOUND
#

auth/cell_action_specs.ts view source

"cell_not_found" import {ERROR_CELL_NOT_FOUND} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Error reason — cell id did not resolve, or caller can't view it.

ERROR_CELL_PATH_ADMIN_ONLY
#

auth/cell_action_specs.ts view source

"cell_path_admin_only" import {ERROR_CELL_PATH_ADMIN_ONLY} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Error reason — caller is not an admin and supplied a path write.

ERROR_CELL_PATH_TAKEN
#

auth/cell_action_specs.ts view source

"cell_path_taken" import {ERROR_CELL_PATH_TAKEN} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Error reason — a path write collided with an existing active cell's path. path is globally unique on active rows (idx_cell_path_unique); the create / update handlers translate the unique-index violation into this conflict (409) reason rather than leaking a raw internal error. Soft-deleted rows free their path (the index is partial on deleted_at IS NULL), so reusing a deleted cell's path does not collide.

ERROR_CELL_VISIBILITY_MANAGE_ONLY
#

auth/cell_action_specs.ts view source

"cell_visibility_manage_only" import {ERROR_CELL_VISIBILITY_MANAGE_ONLY} from '@fuzdev/fuz_app/auth/cell_action_specs.js';

Error reason — caller tried to write cell.visibility without the manage tier (can_manage_cell = admin / owner). Editor-grant holders may edit data but cannot flip a cell's visibility — that is a manage-tier-only operation.

ERROR_CREDENTIAL_TYPE_REQUIRED
#

http/error_schemas.ts view source

"credential_type_required" import {ERROR_CREDENTIAL_TYPE_REQUIRED} from '@fuzdev/fuz_app/http/error_schemas.js';

Route requires a credential type the request didn't arrive on. Symmetric with ERROR_INSUFFICIENT_PERMISSIONS + required_roles: the body carries required_credential_types: ReadonlyArray<string> — what the route demanded, not what arrived. Today the only credential gate is keeper (['daemon_token']); future gates (agent_token, group_actor_token) reuse the same literal and label themselves through the array.

ERROR_DATABASE_CONNECTION_FAILED
#

http/error_schemas.ts view source

"database_connection_failed" import {ERROR_DATABASE_CONNECTION_FAILED} from '@fuzdev/fuz_app/http/error_schemas.js';

Database health-check query failed (connectivity or query error).

ERROR_FORBIDDEN_ORIGIN
#

http/error_schemas.ts view source

"forbidden_origin" import {ERROR_FORBIDDEN_ORIGIN} from '@fuzdev/fuz_app/http/error_schemas.js';

Request origin not in allowlist.

ERROR_FOREIGN_KEY_VIOLATION
#

http/error_schemas.ts view source

"foreign_key_violation" import {ERROR_FOREIGN_KEY_VIOLATION} from '@fuzdev/fuz_app/http/error_schemas.js';

DELETE blocked by a foreign key constraint.

ERROR_INSUFFICIENT_PERMISSIONS
#

http/error_schemas.ts view source

"insufficient_permissions" import {ERROR_INSUFFICIENT_PERMISSIONS} from '@fuzdev/fuz_app/http/error_schemas.js';

Authenticated but missing required role.

ERROR_INVALID_CREDENTIALS
#

http/error_schemas.ts view source

"invalid_credentials" import {ERROR_INVALID_CREDENTIALS} from '@fuzdev/fuz_app/http/error_schemas.js';

Username or password is wrong (intentionally vague for enumeration prevention).

ERROR_INVALID_EVENT_TYPE
#

http/error_schemas.ts view source

"invalid_event_type" import {ERROR_INVALID_EVENT_TYPE} from '@fuzdev/fuz_app/http/error_schemas.js';

Query parameter event_type is not a valid audit event type.

ERROR_INVALID_JSON_BODY
#

http/error_schemas.ts view source

"invalid_json_body" import {ERROR_INVALID_JSON_BODY} from '@fuzdev/fuz_app/http/error_schemas.js';

Request body is not valid JSON or not an object.

ERROR_INVALID_QUERY_PARAMS
#

http/error_schemas.ts view source

"invalid_query_params" import {ERROR_INVALID_QUERY_PARAMS} from '@fuzdev/fuz_app/http/error_schemas.js';

URL query params failed Zod validation.

ERROR_INVALID_REQUEST_BODY
#

http/error_schemas.ts view source

"invalid_request_body" import {ERROR_INVALID_REQUEST_BODY} from '@fuzdev/fuz_app/http/error_schemas.js';

Request body failed Zod validation.

ERROR_INVALID_ROUTE_PARAMS
#

http/error_schemas.ts view source

"invalid_route_params" import {ERROR_INVALID_ROUTE_PARAMS} from '@fuzdev/fuz_app/http/error_schemas.js';

URL path params failed Zod validation.

ERROR_INVALID_TOKEN
#

http/error_schemas.ts view source

"invalid_token" import {ERROR_INVALID_TOKEN} from '@fuzdev/fuz_app/http/error_schemas.js';

Bearer token failed validation (missing, malformed, or revoked).

ERROR_INVITE_ACCOUNT_EXISTS_EMAIL
#

http/error_schemas.ts view source

"invite_account_exists_email" import {ERROR_INVITE_ACCOUNT_EXISTS_EMAIL} from '@fuzdev/fuz_app/http/error_schemas.js';

An account already exists with this invite's email.

ERROR_INVITE_ACCOUNT_EXISTS_USERNAME
#

http/error_schemas.ts view source

"invite_account_exists_username" import {ERROR_INVITE_ACCOUNT_EXISTS_USERNAME} from '@fuzdev/fuz_app/http/error_schemas.js';

An account already exists with this invite's username.

ERROR_INVITE_DUPLICATE
#

http/error_schemas.ts view source

"invite_duplicate" import {ERROR_INVITE_DUPLICATE} from '@fuzdev/fuz_app/http/error_schemas.js';

An unclaimed invite already exists for this email or username.

ERROR_INVITE_NOT_FOUND
#

http/error_schemas.ts view source

"invite_not_found" import {ERROR_INVITE_NOT_FOUND} from '@fuzdev/fuz_app/http/error_schemas.js';

Invite not found (for delete operations).

ERROR_KEEPER_ACCOUNT_NOT_FOUND
#

http/error_schemas.ts view source

"keeper_account_not_found" import {ERROR_KEEPER_ACCOUNT_NOT_FOUND} from '@fuzdev/fuz_app/http/error_schemas.js';

Keeper account ID set but account row not found.

ERROR_NO_ACTORS_ON_ACCOUNT
#

http/error_schemas.ts view source

"no_actors_on_account" import {ERROR_NO_ACTORS_ON_ACCOUNT} from '@fuzdev/fuz_app/http/error_schemas.js';

Authenticated account exists but has no actors. Server invariant violation — signup / bootstrap always create an actor in the same transaction. Surfaced from the dispatcher's authorization phase as a 500 so the operator sees the corruption signal rather than a confusing 4xx. Distinct from ERROR_ACCOUNT_VANISHED: the actor list was enumerated successfully and came back empty.

ERROR_NO_MATCHING_INVITE
#

http/error_schemas.ts view source

"no_matching_invite" import {ERROR_NO_MATCHING_INVITE} from '@fuzdev/fuz_app/http/error_schemas.js';

No unclaimed invite matches the signup credentials.

ERROR_PAYLOAD_TOO_LARGE
#

http/error_schemas.ts view source

"payload_too_large" import {ERROR_PAYLOAD_TOO_LARGE} from '@fuzdev/fuz_app/http/error_schemas.js';

Request body exceeds the maximum allowed size.

ERROR_PURGE_NOT_CONFIRMED
#

auth/admin_action_specs.ts view source

"purge_not_confirmed" import {ERROR_PURGE_NOT_CONFIRMED} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

data.reason on account_purge when confirm: true is absent. Fail-loud: the irreversible purge refuses to run without explicit confirmation. Mirrors the Rust ERROR_PURGE_NOT_CONFIRMED.

ERROR_RATE_LIMIT_EXCEEDED
#

http/error_schemas.ts view source

"rate_limit_exceeded" import {ERROR_RATE_LIMIT_EXCEEDED} from '@fuzdev/fuz_app/http/error_schemas.js';

Rate limiter rejected the request.

error_reason
#

ERROR_ROLE_GRANT_NOT_FOUND
#

http/error_schemas.ts view source

"role_grant_not_found" import {ERROR_ROLE_GRANT_NOT_FOUND} from '@fuzdev/fuz_app/http/error_schemas.js';

Role grant ID not found or not owned by the target actor.

ERROR_ROLE_GRANT_OFFER_ACTOR_ACCOUNT_MISMATCH
#

auth/role_grant_offer_action_specs.ts view source

"role_grant_offer_actor_account_mismatch" import {ERROR_ROLE_GRANT_OFFER_ACTOR_ACCOUNT_MISMATCH} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Error reason — role_grant_offer_create was called with a to_actor_id that does not belong to to_account_id.

ERROR_ROLE_GRANT_OFFER_ACTOR_MISMATCH
#

auth/role_grant_offer_action_specs.ts view source

"role_grant_offer_actor_mismatch" import {ERROR_ROLE_GRANT_OFFER_ACTOR_MISMATCH} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Error reason — actor-targeted offer was accepted by an actor other than to_actor_id.

ERROR_ROLE_GRANT_OFFER_EXPIRED
#

ERROR_ROLE_GRANT_OFFER_NOT_AUTHORIZED
#

auth/role_grant_offer_action_specs.ts view source

"role_grant_offer_not_authorized" import {ERROR_ROLE_GRANT_OFFER_NOT_AUTHORIZED} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Error reason — caller is not authorized to offer this role (default policy: caller lacks the role; consumer authorize callback may add further policy).

ERROR_ROLE_GRANT_OFFER_NOT_FOUND
#

auth/role_grant_offer_action_specs.ts view source

"role_grant_offer_not_found" import {ERROR_ROLE_GRANT_OFFER_NOT_FOUND} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Error reason — offer does not exist or belongs to a different recipient (404-over-403 IDOR mask).

ERROR_ROLE_GRANT_OFFER_ROLE_NOT_GRANTABLE
#

auth/role_grant_offer_action_specs.ts view source

"role_grant_offer_role_not_grantable" import {ERROR_ROLE_GRANT_OFFER_ROLE_NOT_GRANTABLE} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Error reason — the offered role does not include 'admin' in its RoleSpec.grant_paths (nobody may offer it via this surface).

ERROR_ROLE_GRANT_OFFER_SELF_TARGET
#

auth/role_grant_offer_action_specs.ts view source

"role_grant_offer_self_target" import {ERROR_ROLE_GRANT_OFFER_SELF_TARGET} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Error reason — caller tried to offer themselves a role_grant.

ERROR_ROLE_GRANT_OFFER_TERMINAL
#

auth/role_grant_offer_action_specs.ts view source

"role_grant_offer_terminal" import {ERROR_ROLE_GRANT_OFFER_TERMINAL} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Error reason — offer is declined, retracted, or superseded.

ERROR_ROLE_NOT_SELF_SERVICE_ELIGIBLE
#

auth/self_service_role_action_specs.ts view source

"role_not_self_service_eligible" import {ERROR_ROLE_NOT_SELF_SERVICE_ELIGIBLE} from '@fuzdev/fuz_app/auth/self_service_role_action_specs.js';

Error reason — caller asked to self-toggle a role outside the configured allowlist.

ERROR_ROLE_NOT_WEB_GRANTABLE
#

http/error_schemas.ts view source

"role_not_web_grantable" import {ERROR_ROLE_NOT_WEB_GRANTABLE} from '@fuzdev/fuz_app/http/error_schemas.js';

Admin tried to grant a role that is not web-grantable.

ERROR_ROW_NOT_FOUND
#

http/error_schemas.ts view source

"row_not_found" import {ERROR_ROW_NOT_FOUND} from '@fuzdev/fuz_app/http/error_schemas.js';

Row with the given PK value not found.

ERROR_SIGNUP_CONFLICT
#

http/error_schemas.ts view source

"signup_conflict" import {ERROR_SIGNUP_CONFLICT} from '@fuzdev/fuz_app/http/error_schemas.js';

Signup conflict — username or email already taken (intentionally vague for enumeration prevention).

ERROR_TABLE_NO_PRIMARY_KEY
#

http/error_schemas.ts view source

"table_no_primary_key" import {ERROR_TABLE_NO_PRIMARY_KEY} from '@fuzdev/fuz_app/http/error_schemas.js';

Table has no primary key constraint (cannot delete by PK).

ERROR_TABLE_NOT_FOUND
#

http/error_schemas.ts view source

"table_not_found" import {ERROR_TABLE_NOT_FOUND} from '@fuzdev/fuz_app/http/error_schemas.js';

Table name not found in information_schema.

ERROR_TOKEN_FILE_MISSING
#

http/error_schemas.ts view source

"token_file_missing" import {ERROR_TOKEN_FILE_MISSING} from '@fuzdev/fuz_app/http/error_schemas.js';

Bootstrap token file not found on disk.

ErrorCoverageCollector
#

testing/error_coverage.ts view source

import {ErrorCoverageCollector} from '@fuzdev/fuz_app/testing/error_coverage.js';

Tracks which route × status (and route × status × code) combinations have been exercised in tests.

Use record() to log an observed status (optionally with the body's error code), or assert_and_record() to combine response validation with tracking (auto-extracts body.error from the response when present). After all tests, call uncovered() to find declared error paths never exercised.

An observation recorded without a code still satisfies "any-code" coverage requirements for the same status — i.e., if a caller records just the status, all declared codes for that status are considered covered. Per-code tracking is additive: callers who know the body's error value should pass it to get precise per-code gap reporting on routes with literal/enum error schemas.

observed

Observed keys: "METHOD /spec-path:STATUS" or "METHOD /spec-path:STATUS:CODE".

Both shapes coexist — the code-less key marks the status as covered at any code; a code-bearing key adds per-code precision.

type Set<string>

readonly

record

Record an observed error status (optionally with the body's error code) for a route.

Resolves the concrete request path back to the spec template path (e.g., /api/accounts/abc/api/accounts/:id). When code is provided, it is stored alongside the status for per-code coverage tracking.

type (route_specs: RouteSpec[], method: string, path: string, status: number, code?: string | undefined): void

route_specs

type RouteSpec[]

method

type string

path

request path (may be concrete)

type string

status

type number

code?

observed body error code (pass when the route's error schema declares specific codes via z.literal or z.enum)

type string | undefined
optional
returns void

assert_and_record

Validate a response against its route spec and record the status.

Wraps assert_response_matches_spec and records the status code. For error responses, auto-extracts body.error from the JSON body (via a cloned response, so the original stream stays usable) and records it for per-code coverage. Pass an explicit code to override the auto-extracted value or when the body was already consumed.

type (route_specs: RouteSpec[], method: string, path: string, response: Response, code?: string | undefined): Promise<void>

route_specs

type RouteSpec[]

method

type string

path

type string

response

type Response

code?

observed body error code (override; if omitted and the response body is a JSON object with a string error field, that value is auto-extracted)

type string | undefined
optional
returns Promise<void>

throws

  • Error - if the response body fails the route spec's declared

uncovered

Find declared error paths that were never observed.

Computes the declared set from merge_error_schemas for each route spec. For statuses whose error schema names specific codes (via z.literal or z.enum), reports per-code rows; otherwise reports one row per status. A status-only observation (no code) satisfies all declared codes for that status — the "any-code" rule.

type (route_specs: RouteSpec[], options?: CoverageFilterOptions | undefined): UncoveredEntry[]

route_specs

type RouteSpec[]

options?

type CoverageFilterOptions | undefined
optional
returns UncoveredEntry[]

ErrorCoverageOptions
#

ErrorSchemaAuditEntry
#

testing/surface_invariants.ts view source

ErrorSchemaAuditEntry import type {ErrorSchemaAuditEntry} from '@fuzdev/fuz_app/testing/surface_invariants.js';

A single entry in the error schema tightness audit report.

method

type string

route_path

type string

status

type string

specificity

type ErrorSchemaSpecificity

error_codes

The literal value or enum values, if specific.

type Array<string> | null

ErrorSchemaSpecificity
#

testing/surface_invariants.ts view source

ErrorSchemaSpecificity import type {ErrorSchemaSpecificity} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Specificity level of an error schema's error field.

ErrorSchemaTightnessOptions
#

testing/surface_invariants.ts view source

ErrorSchemaTightnessOptions import type {ErrorSchemaTightnessOptions} from '@fuzdev/fuz_app/testing/surface_invariants.js';

min_specificity?

Minimum specificity level. Error schemas below this threshold fail. Default: 'enum'.

type ErrorSchemaSpecificity

ignore_statuses?

HTTP status codes to skip (e.g., middleware-injected codes).

type Array<number>

allowlist?

Routes to skip, in 'METHOD /path' format.

type Array<string>

events_to_surface
#

http/surface.ts view source

(event_specs: EventSpec[]): AppSurfaceEvent[] import {events_to_surface} from '@fuzdev/fuz_app/http/surface.js';

Convert SSE event specs to surface entries.

event_specs

type EventSpec[]

returns

AppSurfaceEvent[]

EventSpec
#

realtime/sse.ts view source

EventSpec import type {EventSpec} from '@fuzdev/fuz_app/realtime/sse.js';

Spec for a push event — declares params schema, description, and channel.

method

Event method name, used as the JSON-RPC notification method.

type string

params

Zod schema for the notification params payload.

type z.ZodType

description

Human-readable description for surface output and docs.

type string

channel?

Channel this event broadcasts on. Omit for cross-channel events.

type string

expect_output
#

testing/cross_backend/cell_cross_helpers.ts view source

<T>(r: RpcResult, schema: ZodType<T, unknown, $ZodTypeInternals<T, unknown>>): T import {expect_output} from '@fuzdev/fuz_app/testing/cross_backend/cell_cross_helpers.js';

Assert the call succeeded and the result matches the verb's declared output schema — the wire-shape parity gate. Returns the parsed output.

r

schema

type ZodType<T, unknown, $ZodTypeInternals<T, unknown>>

returns

T

generics

expect_output<T>
T

ExpectedSchema
#

db/schema_ready.ts view source

ExpectedSchema import type {ExpectedSchema} from '@fuzdev/fuz_app/db/schema_ready.js';

Expected schema: table name → sorted column names, from a fresh bootstrap.

[key: string]

type readonly string[]

EXPIRED_SESSION_OFFSET_SECONDS
#

testing/cross_backend/setup.ts view source

-60 import {EXPIRED_SESSION_OFFSET_SECONDS} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

Backdating offset (seconds) the mint_expired_session seam passes to mint_test_session / _testing_mint_session. A minute in the past is comfortably past NOW() for the DB-row expiry gate without depending on clock precision.

ExtraAccountFixture
#

testing/cross_backend/setup.ts view source

ExtraAccountFixture import type {ExtraAccountFixture} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

Bootstrap-time-seeded secondary account exposed on the fixture.

account

type { readonly id: Uuid; readonly username: string }

readonly

actor

type { readonly id: Uuid }

readonly

api_token

type string

readonly

session_cookie

type string

readonly

create_session_headers

type (extra?: Record<string, string>) => Record<string, string>

readonly

create_bearer_headers

type (extra?: Record<string, string>) => Record<string, string>

readonly

ExtraAccountSpec
#

testing/cross_backend/setup.ts view source

ExtraAccountSpec import type {ExtraAccountSpec} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

Spec for a bootstrap-time secondary account seeded alongside the keeper by _testing_reset (cross-process) or create_test_app (in-process). Used for accounts whose required roles aren't admin-grantable via offer/accept — primarily ROLE_KEEPER (whose RoleSpec.grant_paths is bootstrap-only), where the only way to land the grant is at the bootstrap-equivalent setup step.

For admin-grantable roles, prefer fixture.create_account({roles}) — that goes through the production offer/accept handlers and observes audit + WS fan-out. extra_accounts is the cradle-only bypass; the runtime has no equivalent action.

username

type string

readonly

password_value?

type string

readonly

roles

type ReadonlyArray<string>

readonly

extract_action_result
#

actions/action_event_helpers.ts view source

(event: ActionEvent<string, "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute", "initial" | "parsed" | "handling" | "handled" | "failed">): Result<...> import {extract_action_result} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

Pull the terminal Result from an action event.

data.error populated → error path (covers both explicit failed and the unhandled receive_error / send_error case where no handler was registered for the error phase). step === 'handled' → success path.

event

type ActionEvent<string, "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute", "initial" | "parsed" | "handling" | "handled" | "failed">

returns

Result<{ value: unknown; }, { error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">); message: string; data?: unknown; }; }>

throws

  • Error - if the event is in a non-terminal state (programming error —

extract_declared_error_codes
#

testing/error_coverage.ts view source

(schema: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>): string[] | null import {extract_declared_error_codes} from '@fuzdev/fuz_app/testing/error_coverage.js';

Extract declared error code values from an error response schema.

Recognizes schemas shaped like z.object({error: z.literal(...)}) or z.object({error: z.enum([...])}) (incl. looseObject/strictObject). Returns the set of declared code values, or null if the schema doesn't expose a literal/enum error field (e.g., bare ApiError with z.string()).

Used by coverage reporting to split a single declared status into per-code rows when the route's error schema names specific codes.

schema

type ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>

returns

string[] | null

fact_disk_path
#

db/file_fact_url.ts view source

(hash: string & $brand<"FactHash">): { shard: string; rest: string; } import {fact_disk_path} from '@fuzdev/fuz_app/db/file_fact_url.js';

Split a FactHash into its on-disk <shard>/<rest> parts — the first 2 hex chars of the digest (shard subdir) + the remaining 62. The single source of truth for the disk layout, so the write path (put / put_stream) and the URL minted into the fact row can't disagree. Mirrors the Rust fact_disk_path in fuz_fact.

hash

type string & $brand<"FactHash">

returns

{ shard: string; rest: string; }

FACT_DROP_TABLES
#

FACT_EMBEDDED_THRESHOLD_DEFAULT
#

db/fact_store.ts view source

number import {FACT_EMBEDDED_THRESHOLD_DEFAULT} from '@fuzdev/fuz_app/db/fact_store.js';

Default embedded-vs-referenced cutoff (1 MiB).

FACT_MIGRATION_NAMESPACE
#

db/fact_ddl.ts view source

"fuz_facts" import {FACT_MIGRATION_NAMESPACE} from '@fuzdev/fuz_app/db/fact_ddl.js';

Namespace identifier for fact + memo migrations.

FACT_MIGRATION_NS
#

FACT_MIGRATIONS
#

db/fact_ddl.ts view source

Migration[] import {FACT_MIGRATIONS} from '@fuzdev/fuz_app/db/fact_ddl.js';

Fact + memo migrations.

FACT_REFS_SCHEMA
#

db/fact_ddl.ts view source

"\nCREATE TABLE IF NOT EXISTS fact_ref (\n\tsource_hash TEXT NOT NULL REFERENCES fact(hash) ON DELETE CASCADE,\n\ttarget_hash TEXT NOT NULL,\n\tPRIMARY KEY (source_hash, target_hash)\n)" import {FACT_REFS_SCHEMA} from '@fuzdev/fuz_app/db/fact_ddl.js';

fact_ref table — declared dependency edges between facts.

target_hash is not a foreign key (federation: target may live remotely).

FACT_REFS_TARGET_INDEX
#

db/fact_ddl.ts view source

"\nCREATE INDEX IF NOT EXISTS idx_fact_ref_target ON fact_ref(target_hash)" import {FACT_REFS_TARGET_INDEX} from '@fuzdev/fuz_app/db/fact_ddl.js';

Reverse lookup: which facts reference a given target?

FACT_TMP_DIRNAME
#

db/fact_disk_storage.ts view source

".tmp" import {FACT_TMP_DIRNAME} from '@fuzdev/fuz_app/db/fact_disk_storage.js';

Subdirectory under facts_dir for in-flight atomic temp files.

FACT_TMP_ORPHAN_MAX_AGE_MS
#

db/fact_disk_storage.ts view source

number import {FACT_TMP_ORPHAN_MAX_AGE_MS} from '@fuzdev/fuz_app/db/fact_disk_storage.js';

Default age (1 hour) past which a .tmp/* file is considered orphaned.

FactDiskStorageDeps
#

db/fact_disk_storage.ts view source

FactDiskStorageDeps import type {FactDiskStorageDeps} from '@fuzdev/fuz_app/db/fact_disk_storage.js';

Filesystem capabilities the disk CAS needs, drawn from runtime/deps.ts. A full RuntimeDeps (Node or Deno) satisfies this; each function below picks the narrow subset it actually uses.

stat

Get file/directory stats, or null if path doesn't exist.

type (path: string): Promise<StatResult | null>

path

type string
returns Promise<StatResult | null>

readdir

List directory entries (names, not full paths). Throws if the directory does not exist.

type (path: string): Promise<string[]>

path

type string
returns Promise<string[]>

read_file

Read a file as bytes. Throws if the file does not exist.

type (path: string): Promise<Uint8Array<ArrayBufferLike>>

path

type string
returns Promise<Uint8Array<ArrayBufferLike>>

mkdir

Create a directory.

type (path: string, options?: { recursive?: boolean | undefined; } | undefined): Promise<void>

path

type string

options?

type { recursive?: boolean | undefined; } | undefined
optional
returns Promise<void>

rename

Rename (move) a file.

type (old_path: string, new_path: string): Promise<void>

old_path

type string

new_path

type string
returns Promise<void>

write_file

Write bytes to a file.

type (path: string, data: Uint8Array<ArrayBufferLike>): Promise<void>

path

type string

data

type Uint8Array<ArrayBufferLike>
returns Promise<void>

fsync

Flush a file's data to stable storage (fsync). Call on a temp file after writing it and *before* rename-ing it into place when the renamed path is later served without re-verification — otherwise a host crash after the rename can surface a torn/zero file as authentic content. The fact disk CAS (db/fact_disk_storage.ts) is the one such path; it twins the Rust fuz_fact §fsync posture (data-sync before rename; the parent-dir fsync stays deliberately waived — a lost dirent is regenerable under content addressing). Real runtimes open the path, fsync, and close; create_mock_runtime no-ops (it models no durability).

type (path: string): Promise<void>

path

type string
returns Promise<void>

write_file_stream

Write a ReadableStream of bytes to a file, consuming it with backpressure (peak memory is one chunk). Creates or truncates path. Throws on any I/O error; a partially-written file may remain, so callers needing atomicity write to a temp path then rename.

type (path: string, data: ReadableStream<Uint8Array<ArrayBufferLike>>): Promise<void>

path

type string

data

type ReadableStream<Uint8Array<ArrayBufferLike>>
returns Promise<void>

read_file_stream

Open a file as a ReadableStream of its bytes — read incrementally, so peak memory is one chunk rather than the whole file. Throws if the file does not exist. Use as a streaming upload body or for an incremental hash pass over a large file.

type (path: string): Promise<ReadableStream<Uint8Array<ArrayBufferLike>>>

path

type string
returns Promise<ReadableStream<Uint8Array<ArrayBufferLike>>>

remove

Remove a file or directory.

type (path: string, options?: { recursive?: boolean | undefined; } | undefined): Promise<void>

path

type string

options?

type { recursive?: boolean | undefined; } | undefined
optional
returns Promise<void>

FactExternalFetcher
#

db/fact_store.ts view source

FactExternalFetcher import type {FactExternalFetcher} from '@fuzdev/fuz_app/db/fact_store.js';

Fetcher abstraction so tests can stub external URL retrieval.

fetch_stream

type (url: string) => Promise<ReadableStream<Uint8Array>>

fetch_bytes

type (url: string) => Promise<Uint8Array>

FactMetaRow
#

db/fact_queries.ts view source

FactMetaRow import type {FactMetaRow} from '@fuzdev/fuz_app/db/fact_queries.js';

Subset returned by metadata-only queries (no bytes payload).

hash

type FactHash

external_url

type string | null

content_type

type string | null

size

type number | string

created_at

type Date

FactRow
#

db/fact_queries.ts view source

FactRow import type {FactRow} from '@fuzdev/fuz_app/db/fact_queries.js';

Row shape for SELECT … FROM fact.

hash

type FactHash

bytes

type Uint8Array | null

external_url

type string | null

content_type

type string | null

size

type number | string

created_at

type Date

FACTS_SCHEMA
#

db/fact_ddl.ts view source

"\nCREATE TABLE IF NOT EXISTS fact (\n\thash TEXT PRIMARY KEY,\n\tbytes BYTEA,\n\texternal_url TEXT,\n\tcontent_type TEXT,\n\tsize BIGINT NOT NULL,\n\tcreated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n\tCONSTRAINT fact_storage_present CHECK (bytes IS NOT NULL OR external_url IS NOT NULL)\n)" import {FACTS_SCHEMA} from '@fuzdev/fuz_app/db/fact_ddl.js';

fact table — content-addressed byte store.

FactServingCrossTestOptions
#

testing/cross_backend/fact_serving.ts view source

FactServingCrossTestOptions import type {FactServingCrossTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/fact_serving.js';

The fact suite adds one optional knob to the shared cell options: a setup variant whose keeper carries a second actor. Only the multi-actor case needs it; the rest of the suite runs single-actor, so wiring it is opt-in. Omit it and the multi-actor case silently skips.

inheritance

setup_test_multi_actor?

type SetupTest

readonly

FakeHonoContextOptions
#

testing/ws_round_trip.ts view source

FakeHonoContextOptions import type {FakeHonoContextOptions} from '@fuzdev/fuz_app/testing/ws_round_trip.js';

credential_type

type CredentialType

role?

A single role to grant via create_test_request_context.

type string

auth_session_id?

type string | null

api_token_id?

type string | null

request_context?

Override the RequestContext outright (for multi-role or custom account/actor fixtures). Takes precedence over role.

type RequestContext

FakeWs
#

testing/ws_round_trip.ts view source

FakeWs import type {FakeWs} from '@fuzdev/fuz_app/testing/ws_round_trip.js';

A WSContext paired with capture arrays. Use sends to assert on outgoing frames; use closes to assert on revocation / close.

ws

type WSContext

sends

type Array<string>

closes

type Array<{ code?: number; reason?: string }>

FetchDeps
#

runtime/deps.ts view source

FetchDeps import type {FetchDeps} from '@fuzdev/fuz_app/runtime/deps.js';

HTTP fetch capability.

fetch

Fetch a URL. Same signature as the global fetch.

type typeof globalThis.fetch

FetchTransport
#

testing/transports/fetch_transport.ts view source

FetchTransport import type {FetchTransport} from '@fuzdev/fuz_app/testing/transports/fetch_transport.js';

The transport shape: callable as RpcTestTransport plus a cookies() accessor that returns the current jar state. The accessor exists so ws_transport can thread the session cookie onto the WS upgrade without an HTTP round trip.

inheritance

cookies

Snapshot of every cookie currently in the jar, formatted as full Set-Cookie values (name=value). Used by ws_transport to compose the Cookie header on the upgrade request.

type () => ReadonlyArray<string>

readonly

(call)

type (url: string, init: RequestInit): Promise<Response>

url

type string

init

type RequestInit
returns Promise<Response>

FetchTransportOptions
#

testing/transports/fetch_transport.ts view source

FetchTransportOptions import type {FetchTransportOptions} from '@fuzdev/fuz_app/testing/transports/fetch_transport.js';

Construction options for create_fetch_transport.

base_url

Base URL the binary is reachable at — e.g. http://localhost:8788.

type string

readonly

initial_cookies?

Initial cookie values to seed the jar. Pass the Set-Cookie values captured from a prior bootstrap() call to keep the keeper session across a transport-recreation boundary. Each entry is a full Set-Cookie value (the same string Headers.getSetCookie() returns).

type ReadonlyArray<string>

readonly

origin?

Origin header threaded onto every request when the caller hasn't set one. Defaults to base_url; backends running with ALLOWED_ORIGINS=http://localhost:* accept http://localhost:<port> matching the spawned binary.

Pass null to disable the default — useful for bearer-only probes that must not look like browser-context requests (the auth middleware silently discards bearer credentials when Origin/Referer is present). Callers can still set Origin per-call via init.headers.

type string | null

readonly

FieldJson
#

auth/cell_field_action_specs.ts view source

ZodObject<{ source_id: $ZodBranded<ZodUUID, "Uuid", "out">; name: ZodString; target_id: $ZodBranded<ZodUUID, "Uuid", "out">; created_at: ZodString; }, $strict> import type {FieldJson} from '@fuzdev/fuz_app/auth/cell_field_action_specs.js';

Wire-format for a cell_field row. ISO created_at, branded UUIDs.

FILE_FACT_URL_PATTERN
#

db/file_fact_url.ts view source

RegExp import {FILE_FACT_URL_PATTERN} from '@fuzdev/fuz_app/db/file_fact_url.js';

Anchored, capture-group form: ^file:(<shard>)/(<rest>)$.

FileFactFetcherOptions
#

server/file_fact_fetcher.ts view source

FileFactFetcherOptions import type {FileFactFetcherOptions} from '@fuzdev/fuz_app/server/file_fact_fetcher.js';

Construction options.

facts_dir

Absolute path to the facts directory. Files resolve to <facts_dir>/<shard>/<rest>.

type string

FileFactUrl
#

filter_authenticated_routes
#

http/surface_query.ts view source

(surface: AppSurface): AppSurfaceRoute[] import {filter_authenticated_routes} from '@fuzdev/fuz_app/http/surface_query.js';

Filter routes that require basic authentication only — account === 'required' with no role / credential gate.

surface

returns

AppSurfaceRoute[]

filter_keeper_routes
#

http/surface_query.ts view source

(surface: AppSurface): AppSurfaceRoute[] import {filter_keeper_routes} from '@fuzdev/fuz_app/http/surface_query.js';

Filter routes that require keeper credentials (daemon_token).

surface

returns

AppSurfaceRoute[]

filter_mutation_routes
#

http/surface_query.ts view source

(surface: AppSurface): AppSurfaceRoute[] import {filter_mutation_routes} from '@fuzdev/fuz_app/http/surface_query.js';

Filter routes that are mutations (POST, PUT, DELETE, PATCH).

surface

returns

AppSurfaceRoute[]

filter_protected_routes
#

http/surface_query.ts view source

(surface: AppSurface): AppSurfaceRoute[] import {filter_protected_routes} from '@fuzdev/fuz_app/http/surface_query.js';

Filter routes that require any form of authentication.

surface

returns

AppSurfaceRoute[]

filter_public_routes
#

http/surface_query.ts view source

(surface: AppSurface): AppSurfaceRoute[] import {filter_public_routes} from '@fuzdev/fuz_app/http/surface_query.js';

Filter routes that are publicly accessible (no auth surface at all).

surface

returns

AppSurfaceRoute[]

filter_rate_limited_routes
#

http/surface_query.ts view source

(surface: AppSurface): AppSurfaceRoute[] import {filter_rate_limited_routes} from '@fuzdev/fuz_app/http/surface_query.js';

Filter routes that declare rate limiting.

surface

returns

AppSurfaceRoute[]

filter_role_routes
#

http/surface_query.ts view source

(surface: AppSurface): AppSurfaceRoute[] import {filter_role_routes} from '@fuzdev/fuz_app/http/surface_query.js';

Filter all role-guarded routes (any role declared on auth.roles).

surface

returns

AppSurfaceRoute[]

filter_routes_by_prefix
#

http/surface_query.ts view source

(surface: AppSurface, prefix: string): AppSurfaceRoute[] import {filter_routes_by_prefix} from '@fuzdev/fuz_app/http/surface_query.js';

Filter routes whose path starts with prefix.

surface

prefix

type string

returns

AppSurfaceRoute[]

filter_routes_for_role
#

http/surface_query.ts view source

(surface: AppSurface, role: string): AppSurfaceRoute[] import {filter_routes_for_role} from '@fuzdev/fuz_app/http/surface_query.js';

Filter routes whose auth.roles includes the named role.

surface

role

type string

returns

AppSurfaceRoute[]

filter_routes_with_input
#

http/surface_query.ts view source

(surface: AppSurface): AppSurfaceRoute[] import {filter_routes_with_input} from '@fuzdev/fuz_app/http/surface_query.js';

Filter routes that have a non-null input schema.

surface

returns

AppSurfaceRoute[]

filter_routes_with_params
#

http/surface_query.ts view source

(surface: AppSurface): AppSurfaceRoute[] import {filter_routes_with_params} from '@fuzdev/fuz_app/http/surface_query.js';

Filter routes that have a non-null params schema.

surface

returns

AppSurfaceRoute[]

filter_routes_with_query
#

http/surface_query.ts view source

(surface: AppSurface): AppSurfaceRoute[] import {filter_routes_with_query} from '@fuzdev/fuz_app/http/surface_query.js';

Filter routes that have a non-null query schema.

surface

returns

AppSurfaceRoute[]

filter_visible_target_ids
#

auth/cell_relation_visibility.ts view source

(deps: QueryDeps, auth: RequestContext | null, target_ids: readonly (string & $brand<"Uuid">)[]): Promise<Set<string & $brand<"Uuid">>> import {filter_visible_target_ids} from '@fuzdev/fuz_app/auth/cell_relation_visibility.js';

Return the subset of target_ids the caller may view.

Soft-deleted targets and ids with no matching cell are absent from the result (treated as not-viewable). Grants are loaded only for authenticated callers — null auth admits solely via the public branch of can_view_cell, so the grant load is skipped entirely.

deps

query deps

auth

request context, or null for unauthenticated callers

type RequestContext | null

target_ids

candidate cell ids (duplicates are harmless)

type readonly (string & $brand<"Uuid">)[]

returns

Promise<Set<string & $brand<"Uuid">>>

the set of ids the caller may view

FilterableBroadcastTransport
#

actions/transports_ws_backend.ts view source

FilterableBroadcastTransport import type {FilterableBroadcastTransport} from '@fuzdev/fuz_app/actions/transports_ws_backend.js';

Structural capability for transports that can broadcast with a per-connection ACL predicate. Named separately from Transport so the broadcast API can feature-detect without importing a concrete class.

ConnectionIdentity is the auth-gated identity shape used today. When a second implementation (e.g. SSE backend transport) lands with a different identity, consider parameterizing on TIdentity.

inheritance

extends: Transport

broadcast_filtered

type ( message: JsonrpcMessageFromServerToClient, predicate: (identity: ConnectionIdentity) => boolean ) => number

find_auth_route
#

testing/integration_helpers.ts view source

(specs: RouteSpec[], suffix: "/bootstrap" | "/login" | "/logout" | "/password" | "/verify" | "/signup", method: RouteMethod): RouteSpec | undefined import {find_auth_route} from '@fuzdev/fuz_app/testing/integration_helpers.js';

Find a REST auth route by suffix and method.

Decouples tests from consumer route prefix (/api/account/login, /api/auth/login, etc.). suffix must be one of rest_auth_route_suffixes — throws otherwise so an RPC-only method path (e.g. /sessions/revoke-all) fails loudly at the call site instead of silently returning undefined.

specs

type RouteSpec[]

suffix

type "/bootstrap" | "/login" | "/logout" | "/password" | "/verify" | "/signup"

method

returns

RouteSpec | undefined

throws

  • Error - if `suffix` is not in `rest_auth_route_suffixes`.

find_route_spec
#

testing/integration_helpers.ts view source

(specs: RouteSpec[], method: string, path: string): RouteSpec | undefined import {find_route_spec} from '@fuzdev/fuz_app/testing/integration_helpers.js';

Find a route spec matching the given method and path.

Supports both exact matches and parameterized paths (:param segments).

specs

type RouteSpec[]

method

type string

path

request path (exact or with concrete param values)

type string

returns

RouteSpec | undefined

find_rpc_action
#

testing/rpc_helpers.ts view source

(rpc_endpoints: readonly RpcEndpointSpec[], method: string): { path: string; action: RpcAction; } | undefined import {find_rpc_action} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

Find the RpcAction for a method within a set of RPC endpoint specs. Returns both the endpoint path and the matched action. undefined when the method is not registered.

rpc_endpoints

type readonly RpcEndpointSpec[]

method

type string

returns

{ path: string; action: RpcAction; } | undefined

find_rpc_method
#

testing/rpc_helpers.ts view source

(rpc_endpoints: readonly AppSurfaceRpcEndpoint[], method: string): { path: string; method_spec: AppSurfaceRpcMethod; } | undefined import {find_rpc_method} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

Find the generated surface entry for a method — the shape returned by generate_app_surface (JSON-serializable, useful for schema assertions at the boundary of a consumer test).

rpc_endpoints

type readonly AppSurfaceRpcEndpoint[]

method

type string

returns

{ path: string; method_spec: AppSurfaceRpcMethod; } | undefined

FINGERPRINT_HEADERS
#

testing/cross_backend/conformance_table.ts view source

readonly string[] import {FINGERPRINT_HEADERS} from '@fuzdev/fuz_app/testing/cross_backend/conformance_table.js';

Response headers that fingerprint the backend implementation or framework. Neither spine emits these; the runner asserts they stay absent on EVERY conformance response so a framework upgrade or consumer middleware that adds (say) Server: to one spine can't silently become a backend-identifying oracle. Lowercased — matched against the lowercased-key snapshot headers_to_record produces.

flush_pending_effects
#

http/pending_effects.ts view source

(effects: readonly Promise<void>[], log: Logger, on_rejection?: ((reason: unknown) => void) | undefined): Promise<void> import {flush_pending_effects} from '@fuzdev/fuz_app/http/pending_effects.js';

Drain an eager pending_effects queue: Promise.allSettled the in-flight handles, route every rejection through log.error, and fan out to on_rejection when supplied (production wires this to on_effect_error for monitoring).

Returned promise resolves once every effect has settled. Never rejects. No-op when effects is empty (common on read-only requests).

Symmetric with flush_post_commit_effects for the deferred queue.

effects

type readonly Promise<void>[]

log

type Logger

on_rejection?

type ((reason: unknown) => void) | undefined
optional

returns

Promise<void>

flush_post_commit_effects
#

http/pending_effects.ts view source

(effects: readonly (() => void | Promise<void>)[], log: Logger): Promise<void> import {flush_post_commit_effects} from '@fuzdev/fuz_app/http/pending_effects.js';

Drain a post_commit_effects queue: invoke each thunk under try/catch, collect any returned promises, and Promise.allSettled them. Synchronous throws and async rejections are routed through log.error so one failing effect cannot starve siblings.

Returned promise resolves once every thunk has finished. Never rejects.

effects

type readonly (() => void | Promise<void>)[]

log

type Logger

returns

Promise<void>

ForeignKeyError
#

http/error_schemas.ts view source

ZodObject<{ error: ZodLiteral<"foreign_key_violation">; }, $loose> import type {ForeignKeyError} from '@fuzdev/fuz_app/http/error_schemas.js';

Foreign key violation error — returned when a delete is blocked by references.

format_action_manifest_diffs
#

testing/cross_backend/action_manifest_parity.ts view source

(diffs: readonly ActionManifestDiff[], labels?: ActionManifestDiffLabels): string import {format_action_manifest_diffs} from '@fuzdev/fuz_app/testing/cross_backend/action_manifest_parity.js';

Render a diff list as a human-readable multi-line string. Empty diffs produce an empty string.

diffs

type readonly ActionManifestDiff[]

labels

default {}

returns

string

format_arg_name
#

cli/help.ts view source

(prop: ZodSchemaProperty): string import {format_arg_name} from '@fuzdev/fuz_app/cli/help.js';

Format argument name with short aliases for display.

Only single-char aliases are shown (e.g., -h, --help). Flags use snake_case (e.g., --env_file, --detect_only).

prop

schema property

type ZodSchemaProperty

returns

string

formatted name string

format_audit_metadata
#

ui/ui_format.ts view source

(event_type: string, metadata: Record<string, unknown> | null): string import {format_audit_metadata} from '@fuzdev/fuz_app/ui/ui_format.js';

Format audit event metadata for display based on event type.

event_type

builtin or consumer-registered audit event type

type string

metadata

type Record<string, unknown> | null

returns

string

format_cross_impl_comparison
#

testing/cross_backend/bench/bench_report.ts view source

(entries: readonly CrossImplComparisonEntry[]): string import {format_cross_impl_comparison} from '@fuzdev/fuz_app/testing/cross_backend/bench/bench_report.js';

Render the comparison entries as one line each. The prefix lists the reference first to match benchmark_stats_compare's "First" (= the a arg = reference) / "Second" (= the b arg = backend) in the recommendation text — compare_cross_impl passes (reference, backend).

entries

type readonly CrossImplComparisonEntry[]

returns

string

format_cross_impl_json
#

testing/cross_backend/bench/bench_report.ts view source

(result: CrossImplBenchResult): string import {format_cross_impl_json} from '@fuzdev/fuz_app/testing/cross_backend/bench/bench_report.js';

Self-describing JSON artifact: one entry per backend × scenario with the percentiles (raw-sample tail), the resolved budget, and iteration count. Diffable and reviewable without a TS runtime; the seed for the deferred static-docs comparison surface.

result

returns

string

format_cross_impl_markdown
#

format_datetime_local
#

ui/ui_format.ts view source

(timestamp: string | number | Date): string import {format_datetime_local} from '@fuzdev/fuz_app/ui/ui_format.js';

Format a timestamp as an absolute datetime string for title attributes.

timestamp

type string | number | Date

returns

string

readable absolute datetime like "2026-03-21 14:30:00 UTC"

format_db_status
#

db/status.ts view source

(status: DbStatus): string import {format_db_status} from '@fuzdev/fuz_app/db/status.js';

Format a DbStatus as a human-readable string for CLI output.

status

the status to format

returns

string

multi-line string suitable for console output

format_env_display_value
#

env/mask.ts view source

(value: unknown, secret: boolean): string import {format_env_display_value} from '@fuzdev/fuz_app/env/mask.js';

Format an env value for display, masking secrets.

value

the env value to format

type unknown

secret

whether the value is secret and should be masked

type boolean

returns

string

display string — masked placeholder for secrets, string values as-is, non-strings JSON-stringified

format_migration_tracker_diffs
#

testing/schema_parity.ts view source

(diffs: readonly MigrationTrackerDiff[], labels?: SchemaDiffLabels): string import {format_migration_tracker_diffs} from '@fuzdev/fuz_app/testing/schema_parity.js';

Render migration-tracker diffs as a human-readable multi-line string. Empty diffs produce an empty string.

diffs

type readonly MigrationTrackerDiff[]

labels

default {}

returns

string

format_missing_env_vars
#

env/resolve.ts view source

(missing: EnvVarRef[], options?: FormatMissingEnvVarsOptions | undefined): string import {format_missing_env_vars} from '@fuzdev/fuz_app/env/resolve.js';

Format missing env vars error message.

Groups refs by variable name so each missing var is shown once with all paths where it's referenced.

missing

missing env var references (may contain duplicate names)

type EnvVarRef[]

options?

formatting options

type FormatMissingEnvVarsOptions | undefined
optional

returns

string

formatted error message for display

format_relative_time
#

ui/ui_format.ts view source

(timestamp: string | number | Date, now?: number): string import {format_relative_time} from '@fuzdev/fuz_app/ui/ui_format.js';

Format a timestamp as a relative time string.

timestamp

type string | number | Date

now

type number
default Date.now()

returns

string

human-friendly relative time (e.g. "2m ago", "3h ago", "5d ago", "2mo ago", "1y ago")

format_route_key
#

http/surface_query.ts view source

(route: AppSurfaceRoute): string import {format_route_key} from '@fuzdev/fuz_app/http/surface_query.js';

Format a route as 'METHOD /path' (e.g. 'GET /health').

route

returns

string

format_schema_diffs
#

testing/schema_parity.ts view source

(diffs: readonly SchemaDiff[], labels?: SchemaDiffLabels): string import {format_schema_diffs} from '@fuzdev/fuz_app/testing/schema_parity.js';

Render a diff list as a human-readable multi-line string. Empty diffs produce an empty string.

diffs

type readonly SchemaDiff[]

labels

default {}

returns

string

format_schema_drift
#

db/schema_ready.ts view source

(drift: SchemaDriftResult): string import {format_schema_drift} from '@fuzdev/fuz_app/db/schema_ready.js';

Render a drift result as a one-issue-per-line operator string.

drift

returns

string

format_scope_context
#

ui/format_scope.ts view source

{ get: () => () => FormatScope; set: (value?: (() => FormatScope) | undefined) => () => FormatScope; } import {format_scope_context} from '@fuzdev/fuz_app/ui/format_scope.js';

Svelte context carrying a getter for the consumer's FormatScope. Provisioned by provide_admin_rpc_contexts from its format_scope option. Default getter returns default_format_scope so unprovisioned trees render the raw uuid.

format_uptime
#

ui/ui_format.ts view source

(ms: number): string import {format_uptime} from '@fuzdev/fuz_app/ui/ui_format.js';

Format milliseconds as a human-friendly uptime string.

ms

type number

returns

string

human-friendly duration (e.g. "45s", "12m", "3h 15m", "2d 5h")

format_value
#

ui/ui_format.ts view source

(value: unknown): string import {format_value} from '@fuzdev/fuz_app/ui/ui_format.js';

Format an arbitrary value for table cell display.

value

type unknown

returns

string

FormatMissingEnvVarsOptions
#

env/resolve.ts view source

FormatMissingEnvVarsOptions import type {FormatMissingEnvVarsOptions} from '@fuzdev/fuz_app/env/resolve.js';

env_file?

Path to env file if one was loaded.

type string

setup_hint?

Hint text for how to set up the environment.

type string

FormatScope
#

ui/format_scope.ts view source

FormatScope import type {FormatScope} from '@fuzdev/fuz_app/ui/format_scope.js';

Render a {scope_id, role} pair as a human label. Return null to fall back to the raw scope uuid (or a caller-chosen global_label when scope_id is null).

Returning null for unknown scope ids (stale cache, revoked resource) is the recommended pattern — components show the raw uuid rather than a misleading blank.

(call)

type (args: { scope_id: string | null; role: string; }): string | null

args

type { scope_id: string | null; role: string; }
returns string | null

FormState
#

ui/form_state.svelte.ts view source

import {FormState} from '@fuzdev/fuz_app/ui/form_state.svelte.js';

form

Creates a form attachment that handles Enter key advancing between focusable elements and tracks field touched state via delegated focusout.

Fields are identified by their name attribute.

type (): Attachment<HTMLFormElement>

returns Attachment<HTMLFormElement>

throws

  • Error - in DEV when called while already attached, or when an

is_touched

Whether a field has been blurred at least once.

type (field: string): boolean

field

type string
returns boolean

show

Whether to show validation errors for a field. Returns true if the field has been blurred or a submit attempt was made.

type (field: string): boolean

field

type string
returns boolean

touch

Programmatically marks a field as touched without requiring a blur event.

type (field: string): void

field

type string
returns void

focus

Focuses the named input within the form.

type (field: string): void

field

type string
returns void

attempt

Marks the form as having been submitted, causing all field errors to show.

type (): void

returns void

reset

Resets all touched and attempted state.

type (): void

returns void

attempted

Whether a submit attempt has been made.

type boolean

getter

FrontendHttpTransport
#

actions/transports_http.ts view source

import {FrontendHttpTransport} from '@fuzdev/fuz_app/actions/transports_http.js';

Thin fetch adapter for the JSON-RPC endpoint. POST by default; GET when the optional has_side_effects(method) callback returns false for the method (matches create_rpc_endpoint's GET convention). On non-OK HTTP responses, synthesizes a JSON-RPC error envelope via http_status_to_jsonrpc_error_code. Always reports ready.

inheritance

implements: Transport

transport_name

type "frontend_http_rpc"

readonly

constructor

type new (url: string, headers?: Record<string, string> | undefined, has_side_effects?: ((method: string) => boolean) | undefined): FrontendHttpTransport

url

type string

headers?

type Record<string, string> | undefined
optional

has_side_effects?

type ((method: string) => boolean) | undefined
optional

send

type (message: { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; }, options?: TransportSendOptions | undefined): Promise<...>

message

type { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; }

options?

type TransportSendOptions | undefined
optional
returns Promise<{ [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<...>); message: string; data?: unknown; }; }>

is_ready

type (): boolean

returns boolean

FrontendRpcClient
#

actions/frontend_rpc_client.ts view source

FrontendRpcClient<TApi> import type {FrontendRpcClient} from '@fuzdev/fuz_app/actions/frontend_rpc_client.js';

Bundle returned by create_frontend_rpc_client.

generics

FrontendRpcClient<TApi>
TApi

api

Typed throwing Proxy. await api.method(input) returns the unwrapped value or throws an Error with {code, data} from the JSON-RPC error. Default for call sites that don't inspect errors.

type ThrowingApi<TApi>

api_result

Typed Result-shaped Proxy. await api_result.method(input) returns Result<{value}, {error: JsonrpcErrorObject}>. Use when call sites inspect error.data.reason without try/catch, or when Error allocation per {ok: false} would be wasteful.

type TApi

peer

Underlying peer — exposed for consumers that need to register more transports or send raw messages.

type ActionDispatcher

environment

Action environment — exposed for consumers that need to share it (e.g. attach a notification handler registry).

type ActionEventEnvironment

FrontendWebsocketClient
#

actions/socket.svelte.ts view source

import {FrontendWebsocketClient} from '@fuzdev/fuz_app/actions/socket.svelte.js';

Reactive WebSocket client implementing WebsocketConnection.

Construct with a URL and optional config; call connect() to open the socket and begin auto-reconnect. Register message/error handlers via add_message_handler / add_error_handler — both return unsubscribe functions. FrontendWebsocketTransport consumes this as its connection.

Session-revocation close codes (WS_CLOSE_SESSION_REVOKED) put the client in a permanently-closed state; reconnecting would just loop on 401.

inheritance

implements: WebsocketConnection,Disposable

ws

type WebSocket | null

$state.raw

status

type SocketStatus

$state.raw

reconnect_count

type number

$state.raw

current_reconnect_delay

type number

$state.raw

last_connect_time

Epoch ms of the most recent successful open. Never cleared on close.

type number | null

$state.raw

last_close_time

Epoch ms of the most recent close event or client-initiated close.

type number | null

$state.raw

last_close_code

Close code from the most recent close. Initial null means "never closed."

type number | null

$state.raw

last_close_reason

Reason string from the most recent close event (may be empty).

type string | null

$state.raw

last_send_error

The error thrown by the most recent attempted send(), or null if the most recent attempt succeeded or none has been attempted yet. Populated when the underlying ws.send throws (e.g., buffer full, serialization error); reset to null on the next successful send. Not touched when send() short-circuits because the socket is not connected — consult connected for that case. Wrappers surfacing per-message failure reasons can read this after a false return from send().

type Error | null

$state.raw

connected

type boolean

readonly $derived

constructor

type new (url: string, options?: FrontendWebsocketClientOptions): FrontendWebsocketClient

url

type string

options

default {}

set_reconnect

Swap the auto-reconnect policy in place. Accepts the same shape as the constructor's reconnect option: false disables reconnect, true or null/omitted restores the defaults, or a config object customizes specific fields (missing fields fall back to defaults, not "keep current" — each call defines the whole policy atomically, same as the constructor).

In-flight reconnect schedules are monotonically shortened: the effective total wait from arm-time never exceeds what the new policy prescribes. If the new target is already past the time already elapsed, the reconnect fires immediately (on the next tick). The wait is never extended.

Turning reconnect off while a reconnect timer is pending cancels that timer and transitions status to closed (since the lie of 'reconnecting' would be visible to UI indicators). Turning it back on does not synthesize a reconnect — wait for the next close.

type (reconnect?: boolean | FrontendWebsocketReconnectOptions | null): void

reconnect

type boolean | FrontendWebsocketReconnectOptions | null
default null
returns void

set_heartbeat

Swap the heartbeat policy in place. Accepts the same shape as the constructor's heartbeat option: false disables the timer, true or null/omitted restores the defaults, or a config object customizes specific fields (missing fields fall back to defaults, not "keep current" — each call defines the whole policy atomically, same as the constructor and set_reconnect).

When connected, the live timer is restarted immediately so the new interval / receive_timeout take effect without a reconnect; when disconnected, just stashes the policy for the next open.

type (heartbeat?: boolean | FrontendWebsocketHeartbeatOptions | null): void

heartbeat

type boolean | FrontendWebsocketHeartbeatOptions | null
default null
returns void

cancel_reconnect

Cancel a scheduled reconnect without closing the client or disabling auto-reconnect. Transitions status from reconnectingclosed and resets the backoff counters — the next close still triggers a fresh reconnect cycle under the current policy. No-op when no reconnect is pending.

Use this when UI state asks "stop trying for now" without the finality of disconnect (which also rejects pending/queued requests and clears heartbeat) or the policy change of set_reconnect(false) (which disables future reconnects). The queue stays intact so that calling connect later flushes buffered work.

type (): void

returns void

connect

Open the WebSocket. No-op on SSR, or if the session has been revoked. Cancels any pending reconnect and tears down any existing connection first; an open prior socket is closed with a normal-closure code.

type (): void

returns void

disconnect

Close the WebSocket, cancel any pending reconnect, and reset the reconnect backoff counters. Puts the client in closed status; call connect() to reopen. Safe to call more than once.

type (code?: number): void

code

type number
default DEFAULT_CLOSE_CODE
returns void

[Symbol.dispose]

Explicit-resource-management hook — supports using client = new FrontendWebsocketClient(url).

type (): void

returns void

send

type (data: object): boolean

data

type object
returns boolean

request

Promise-based JSON-RPC over the socket. Auto-assigns a monotonic request id (or uses an explicit one supplied via options.id — used by FrontendWebsocketTransport which delegates to this method and has its own peer-minted UUID), tracks the pending promise, and resolves when the server sends a matching response.

Callers supplying an explicit options.id are responsible for uniqueness — the pending map is keyed by id, and a duplicate silently overwrites the prior entry. Auto-minted ids are monotonic and never collide with themselves or with peer-minted UUIDs (the types differ: integer vs string).

While the socket is disconnected, the request is buffered in a bounded queue (default-on, DEFAULT_QUEUE_MAX_SIZE) and flushed on reopen. Pass {queue: false} to reject immediately when disconnected — used internally by the heartbeat, which must not fight the queue for the disconnect-detection slot.

On AbortSignal fire: rejects the local promise *and* sends the shared cancel notification (cancel_action_spec.method) so the server-side dispatcher can abort the matching handler's ctx.signal. Suppressed for queued-but-never-sent (server doesn't know about it) and response-beat-cancel races.

type <R = unknown>(method: string, params?: unknown, options?: { signal?: AbortSignal | undefined; queue?: boolean | undefined; id?: string | number | undefined; }): Promise<R>

method

type string

params

type unknown
default {}

options

type { signal?: AbortSignal | undefined; queue?: boolean | undefined; id?: string | number | undefined; }
default {}
returns Promise<R>

throws

  • ThrownJsonrpcError - on the returned promise — never thrown

add_message_handler

type (handler: SocketMessageHandler): () => void

handler

returns () => void

add_error_handler

type (handler: SocketErrorHandler): () => void

handler

returns () => void

url

type string

getter

revoked

Whether the server has permanently closed the session. Once true, all connect() calls are no-ops. Distinct from status:'closed', which reflects any closed state (incl. user-initiated disconnect()).

type boolean

getter

FrontendWebsocketClientOptions
#

actions/socket.svelte.ts view source

FrontendWebsocketClientOptions import type {FrontendWebsocketClientOptions} from '@fuzdev/fuz_app/actions/socket.svelte.js';

reconnect?

Auto-reconnect policy. false disables reconnect entirely; true or omit for default timing; pass an object to customize.

type boolean | FrontendWebsocketReconnectOptions | null

heartbeat?

Activity-aware heartbeat. true/null/omit for defaults; false disables the timer entirely (only do this if the server side is also running without heartbeat); pass an object to tune interval / receive_timeout.

type boolean | FrontendWebsocketHeartbeatOptions | null

queue?

Durable queue for FrontendWebsocketClient.request. true or omit for defaults; false disables buffering (requests while disconnected reject immediately). Raw FrontendWebsocketClient.send is never queued — use request() for RPC semantics.

type boolean | FrontendWebsocketQueueOptions

log?

Optional logger for diagnostic messages.

type Logger | null

FrontendWebsocketHeartbeatOptions
#

actions/socket.svelte.ts view source

FrontendWebsocketHeartbeatOptions import type {FrontendWebsocketHeartbeatOptions} from '@fuzdev/fuz_app/actions/socket.svelte.js';

interval?

Idle duration (ms) after which a heartbeat is sent. Reset by any send or receive — chatty clients never emit extras. Defaults to DEFAULT_HEARTBEAT_INTERVAL.

type number

receive_timeout?

Receive-silence (ms) after which the client closes the socket with WS_CLOSE_CLIENT_HEARTBEAT_TIMEOUT, letting auto-reconnect kick in. Should be a comfortable multiple of interval. Defaults to DEFAULT_HEARTBEAT_RECEIVE_TIMEOUT.

type number

FrontendWebsocketQueueOptions
#

actions/socket.svelte.ts view source

FrontendWebsocketQueueOptions import type {FrontendWebsocketQueueOptions} from '@fuzdev/fuz_app/actions/socket.svelte.js';

max_size?

Maximum number of requests held while the socket is disconnected. Enqueue past this rejects the new call with a queue_overflow error. Defaults to DEFAULT_QUEUE_MAX_SIZE.

type number

FrontendWebsocketReconnectOptions
#

actions/socket.svelte.ts view source

FrontendWebsocketReconnectOptions import type {FrontendWebsocketReconnectOptions} from '@fuzdev/fuz_app/actions/socket.svelte.js';

delay?

Base reconnect delay in ms. Defaults to 1000.

type number

delay_max?

Max reconnect delay in ms (cap on exponential backoff). Defaults to 10000.

type number

factor?

Exponential backoff factor. Defaults to 1.5.

type number

FrontendWebsocketTransport
#

actions/transports_ws.ts view source

import {FrontendWebsocketTransport} from '@fuzdev/fuz_app/actions/transports_ws.js';

Thin adapter over WebsocketRpcConnection (canonical implementation: FrontendWebsocketClient). Routes inbound server-pushed requests and notifications into the supplied receive callback and sends the request response back over the socket; an inbound peer/ping is answered by the built-in responder before receive. Responses to requests *we* sent are owned by the connection's own request() pending map and are ignored here.

inheritance

implements: Transport

transport_name

type "frontend_websocket_rpc"

readonly

constructor

type new (connection: WebsocketRpcConnection, receive: (data: unknown) => Promise<unknown>): FrontendWebsocketTransport

connection

receive

type (data: unknown) => Promise<unknown>

send

type (message: { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; }, options?: TransportSendOptions | undefined): Promise<...>

message

type { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; }

options?

type TransportSendOptions | undefined
optional
returns Promise<{ [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<...>); message: string; data?: unknown; }; }>

is_ready

type (): boolean

returns boolean

dispose

Detach the inbound message and error handlers registered on the connection. Idempotent — subsequent calls no-op. Does not close the underlying connection (that lifecycle is owned by the caller).

type (): void

returns void

FsReadDeps
#

runtime/deps.ts view source

FsReadDeps import type {FsReadDeps} from '@fuzdev/fuz_app/runtime/deps.js';

File system read operations.

stat

Get file/directory stats, or null if path doesn't exist.

type (path: string) => Promise<StatResult | null>

read_text_file

Read a file as text. Throws if the file does not exist.

type (path: string) => Promise<string>

read_file

Read a file as bytes. Throws if the file does not exist.

type (path: string) => Promise<Uint8Array>

read_text_from_offset

Read text starting from a byte offset. Throws if the file does not exist.

Returns content, bytes_read, and file_size so callers can detect truncation (when file_size < offset) and tail incrementally without re-reading the whole file.

type (path: string, offset: number) => Promise<ReadTextFromOffsetResult>

readdir

List directory entries (names, not full paths). Throws if the directory does not exist.

type (path: string) => Promise<Array<string>>

FsRemoveDeps
#

runtime/deps.ts view source

FsRemoveDeps import type {FsRemoveDeps} from '@fuzdev/fuz_app/runtime/deps.js';

File system remove operations.

remove

Remove a file or directory.

type (path: string, options?: { recursive?: boolean }) => Promise<void>

FsStreamDeps
#

runtime/deps.ts view source

FsStreamDeps import type {FsStreamDeps} from '@fuzdev/fuz_app/runtime/deps.js';

Streaming file I/O — read a file as a byte stream, or write a byte stream to a file, both bounded in memory (peak ≈ one chunk, not the whole file).

Kept separate from FsReadDeps / FsWriteDeps so the whole-buffer read_file / write_file consumers and their partial test stubs are unaffected; only the full runtime factories implement these. Used for GB-scale artifact transfer (the fuzf file get / put path) where buffering the whole file would OOM the client.

read_file_stream

Open a file as a ReadableStream of its bytes — read incrementally, so peak memory is one chunk rather than the whole file. Throws if the file does not exist. Use as a streaming upload body or for an incremental hash pass over a large file.

type (path: string) => Promise<ReadableStream<Uint8Array>>

write_file_stream

Write a ReadableStream of bytes to a file, consuming it with backpressure (peak memory is one chunk). Creates or truncates path. Throws on any I/O error; a partially-written file may remain, so callers needing atomicity write to a temp path then rename.

type (path: string, data: ReadableStream<Uint8Array>) => Promise<void>

FsWriteDeps
#

runtime/deps.ts view source

FsWriteDeps import type {FsWriteDeps} from '@fuzdev/fuz_app/runtime/deps.js';

File system write operations.

mkdir

Create a directory.

type (path: string, options?: { recursive?: boolean }) => Promise<void>

write_text_file

Write text to a file.

type (path: string, content: string) => Promise<void>

write_file

Write bytes to a file.

type (path: string, data: Uint8Array) => Promise<void>

rename

Rename (move) a file.

type (old_path: string, new_path: string) => Promise<void>

fsync

Flush a file's data to stable storage (fsync). Call on a temp file after writing it and *before* rename-ing it into place when the renamed path is later served without re-verification — otherwise a host crash after the rename can surface a torn/zero file as authentic content. The fact disk CAS (db/fact_disk_storage.ts) is the one such path; it twins the Rust fuz_fact §fsync posture (data-sync before rename; the parent-dir fsync stays deliberately waived — a lost dirent is regenerable under content addressing). Real runtimes open the path, fsync, and close; create_mock_runtime no-ops (it models no durability).

type (path: string) => Promise<void>

full_spine_rpc_endpoints
#

testing/cross_backend/full_spine_mount.ts view source

(ctx: AppServerContext, options: FullSpineMountOptions): RpcEndpointSpec[] import {full_spine_rpc_endpoints} from '@fuzdev/fuz_app/testing/cross_backend/full_spine_mount.js';

Factory-form full mount at , the shape create_app_server's rpc_endpoints slot accepts. The spine binary wires this directly; the surface builder (create_spine_surface_spec) keeps using the narrower spine_rpc_endpoints so the declared surface stays the standard bundle only.

ctx

options

returns

RpcEndpointSpec[]

FullSpineMountOptions
#

testing/cross_backend/full_spine_mount.ts view source

FullSpineMountOptions import type {FullSpineMountOptions} from '@fuzdev/fuz_app/testing/cross_backend/full_spine_mount.js';

Options for / .

daemon_token_state

Daemon-token runtime state threaded into create_testing_actions — the _testing_reset handler mutates keeper_account_id after re-seeding. Pass the same instance the daemon-token middleware reads. For the coverage test (method enumeration only, handlers never run) any stub state satisfies it.

type DaemonTokenState

readonly

notification_sender?

WS notification sender for the role-grant-offer fan-out. Pass the SAME BackendWebsocketTransport the WS endpoint registers connections against (the transport is the connection registry). Omitted for enumeration.

type NotificationSender | null

readonly

fuz_app_stock_route_tightness_allowlist
#

testing/surface_invariants.ts view source

readonly string[] import {fuz_app_stock_route_tightness_allowlist} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Routes shipped by fuz_app whose error schemas require a tightness exemption.

Currently empty — every fuz_app-shipped route (account login/password/ bootstrap/signup, db health/tables/:name/tables/:name/rows/:id) was tightened in place to z.enum([...]) / z.literal(...) against every emit-site error code.

Kept as a forward-compatibility hook: when new stock routes ship with heterogeneous error surfaces that need an interim generic schema, add them here instead of forcing every consumer to hand-maintain the entry.

Paths assume the standard /api/account + /api/db prefixes used by every fuz_app consumer. Merged into default_error_schema_tightness.allowlist so consumers calling assert_error_schema_tightness directly inherit the exemptions; the standard attack-surface suite also prepends these entries underneath any consumer-supplied allowlist so project-specific entries are additive.

fuz_auth_guard_resolver
#

auth/auth_guard_resolver.ts view source

(auth: { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }): AuthGuards import {fuz_auth_guard_resolver} from '@fuzdev/fuz_app/auth/auth_guard_resolver.js';

Standard auth guard resolver for fuz_app.

Reads each axis of the four-axis RouteAuth shape and emits the corresponding middleware:

  • account === 'required' or actor === 'required' → pre-validation require_auth
  • roles?.length → post-authorization require_role(roles) (multi-role any-of)
  • credential_types?.length → post-authorization require_credential_types(types)

Multiple post-authorization guards run in declaration order: credential type check first (since failing it implies the request can never resolve a usable identity), role check second.

auth

type { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }

returns

AuthGuards

fuz_session_config
#

auth/session_cookie.ts view source

SessionOptions<string> import {fuz_session_config} from '@fuzdev/fuz_app/auth/session_cookie.js';

Canonical session config for fuz_app auth.

FuzAuthActionSpecRegistry
#

auth/all_action_spec_registries.ts view source

FuzAuthActionSpecRegistry import type {FuzAuthActionSpecRegistry} from '@fuzdev/fuz_app/auth/all_action_spec_registries.js';

One named entry in the registry-of-registries.

name

Stable identifier matching the source bundle name ('admin', 'role_grant_offer', etc.).

type string

specs

The bundle's spec array — kept readonly here even when the source declares it mutable.

type ReadonlyArray<RequestResponseActionSpec>

generate_action_event_datas
#

actions/action_codegen.ts view source

(specs: readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; })[], imports: ImportBuilder, options?: { ...; } | undefined): string import {generate_action_event_datas} from '@fuzdev/fuz_app/actions/action_codegen.js';

Emit the ActionEventDatas interface — one ActionEvent*Data variant per method, parameterized by the spec's kind:

  • request_responseActionEventRequestResponseData<method, input, output>
  • remote_notificationActionEventRemoteNotificationData<method, input>
  • local_callActionEventLocalCallData<method, input, output>

Adds the per-kind data type imports (only the kinds that appear in specs).

specs

type readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } ...

imports

options?

type { same_file?: boolean | undefined; collections_path?: string | undefined; include_protocol_actions?: boolean | undefined; } | undefined
optional

returns

string

generate_action_inputs_outputs
#

actions/action_codegen.ts view source

(specs: readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; })[], imports: ImportBuilder, options?: { ...; } | undefined): string import {generate_action_inputs_outputs} from '@fuzdev/fuz_app/actions/action_codegen.js';

Emit ActionInputs + ActionOutputs runtime consts and matching interfaces. The runtime consts reference specs.{method}_action_spec.input / .output; the interfaces use z.infer.

Adds import {z} from 'zod'; and the * as specs namespace import.

specs

type readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } ...

imports

options?

type { specs_module?: string | undefined; qualify_spec?: ((spec: { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 7 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }) => string)...
optional

returns

string

generate_action_method_enum_block
#

actions/action_codegen.ts view source

(specs: readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; })[], imports: ImportBuilder, options: { ...; }): string import {generate_action_method_enum_block} from '@fuzdev/fuz_app/actions/action_codegen.js';

Emit a single named z.enum([...]) + z.infer block for an arbitrary spec subset. Lower-level escape hatch from generate_action_method_enums — for cross-product or domain-specific enums the built-in discriminator doesn't cover.

Mirrors the built-in helper's contract: protocol actions filtered by default, empty subsets return '' (skip rather than emit z.enum([])), zod import registered idempotently only when at least one method qualifies.

The cross-product space is open-ended; rather than grow the ActionMethodEnumKind discriminator one cross-product at a time, callers own the subset shape — name, jsdoc, predicate.

specs

type readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } ...

imports

options

type { name: string; jsdoc: string; predicate: (spec: { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 7 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }) => boolean; include_p...

returns

string

generate_action_method_enums
#

actions/action_codegen.ts view source

(specs: readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; })[], imports: ImportBuilder, options?: { ...; } | undefined): string import {generate_action_method_enums} from '@fuzdev/fuz_app/actions/action_codegen.js';

Emit one or more z.enum([...]) declarations for action method names — ActionMethod, RequestResponseActionMethod, RemoteNotificationActionMethod, LocalCallActionMethod, FrontendActionMethod, BackendActionMethod, FrontendRequestResponseMethod, BackendRequestResponseMethod, BroadcastActionMethod. Pairs each runtime const with a z.infer type alias under the same identifier.

Protocol-action methods (heartbeat, cancel) are filtered out by default — pass include_protocol_actions: true if a consumer genuinely wants them on their typed surface. Empty kinds are skipped so the helper never emits z.enum([]) (zod runtime-throws on that).

Adds import {z} from 'zod'; to imports only when at least one block is emitted (idempotent).

For genuinely cross-product enums the discriminator doesn't cover, use generate_action_method_enum_block — caller owns the predicate, name, and jsdoc.

specs

type readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } ...

imports

options?

type { emit?: ReadonlySet<ActionMethodEnumKind> | undefined; include_protocol_actions?: boolean | undefined; } | undefined
optional

returns

string

generate_action_specs_record
#

actions/action_codegen.ts view source

(specs: readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; })[], imports: ImportBuilder, options?: { ...; } | undefined): string import {generate_action_specs_record} from '@fuzdev/fuz_app/actions/action_codegen.js';

Emit the ActionSpecs runtime const + interface + the `action_specs: Array<ActionSpecUnion> value bundling every spec. Adds the * as specs` namespace import + the ActionSpecUnion type import.

specs

type readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } ...

imports

options?

type { specs_module?: string | undefined; qualify_spec?: ((spec: { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 7 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }) => string)...
optional

returns

string

generate_actions_api_method_signature
#

actions/action_codegen.ts view source

(spec: { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }, imports: ImportBuilder, options?: { ...; } | undefined): string import {generate_actions_api_method_signature} from '@fuzdev/fuz_app/actions/action_codegen.js';

Generates one method line of the typed FrontendActionsApi interface for a single spec. Encapsulates the input/options/return-type signature shape so the surface evolves in one place when fields like signal or transport_name are added to per-call options.

Async methods (request_response, remote_notification, async local_call) get an optional second options?: RpcClientCallOptions arg ({signal?, transport_name?, queue?}) and a Promise<Result<...>> return type. Sync local_call methods omit the options arg — signal can't cooperatively interrupt a synchronous handler and there's no transport to select. remote_notification is async because create_remote_notification_method returns a Promise that resolves to a Result<{value: void}> (success) or Result<{error}> (transport send failure). Earlier emit shapes declared notifications as => void — regenerate consumer typed clients to pick up the corrected return.

Registers exactly the imports the emitted line references on imports: ActionInputs (when the spec has input), ActionOutputs (always), RpcClientCallOptions (async only), and Result + JsonrpcErrorObject (any return shape that wraps the value in Result<{value}, {error}> — every async method, plus sync local_call when `sync_returns_value: false). Mirrors the leaf-level pattern get_handler_return_type` already follows so wrappers no longer pre-register imports a per-spec emit might not actually use.

Optional-input detection. The emitted parameter is input?: (caller may omit the argument) when either (a) the schema accepts undefinedz.optional(z.strictObject(...)) and similar wrappers — or (b) the schema accepts the empty object {} — `z.strictObject({acting: ActingActor})` and other all-optional-fields strict objects. The second probe mirrors the dispatcher's HTTP convention (raw_params ?? {} for non-z.void() schemas in actions/action_rpc.ts / http/route_spec.ts): if a request with no params reaches the handler, this is the value the schema is asked to validate. A schema with required fields fails both probes and stays input: (required at the typed surface). Refinements and transforms run as part of safeParse, so their accept/reject decisions feed into the optional/required choice naturally.

spec

type { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }

imports

options?

type { sync_returns_value?: boolean | undefined; collections_path?: string | undefined; } | undefined
optional

returns

string

one line like foo: (input: ActionInputs['foo'], options?: RpcClientCallOptions) => Promise<Result<...>>;

generate_api_token
#

auth/api_token.ts view source

(): { token: string; id: string; token_hash: string; } import {generate_api_token} from '@fuzdev/fuz_app/auth/api_token.js';

Generate a new API token with its hash and public id.

The raw token is returned exactly once — callers must present it to the user immediately.

returns

{ token: string; id: string; token_hash: string; }

the raw token, a public id, and the blake3 hash for storage

generate_app_surface
#

http/surface.ts view source

(options: GenerateAppSurfaceOptions): AppSurface import {generate_app_surface} from '@fuzdev/fuz_app/http/surface.js';

Generate a JSON-serializable attack surface from middleware, route specs, and optional env/event metadata.

options

returns

AppSurface

generate_backend_action_handlers_map
#

actions/action_codegen.ts view source

(imports: ImportBuilder, options?: { type_name?: string | undefined; method_enum_name?: string | undefined; context_type?: string | undefined; collections_path?: string | undefined; metatypes_path?: string | undefined; } | undefined): string import {generate_backend_action_handlers_map} from '@fuzdev/fuz_app/actions/action_codegen.js';

Emit the BackendActionHandlers mapped type — one entry per BackendRequestResponseMethod, each (input, ctx) => output | Promise<output>. Replaces the hand-maintained Exclude<> + parallel mapped-type pattern (zzz had this at zzz/src/lib/server/zzz_action_handlers.ts:42-66).

The context type is consumer-defined (e.g. zzz's ZzzHandlerContext). Pass context_type to name it; the helper assumes it's importable or defined in the emitted module's scope (consumer's responsibility).

Adds ActionInputs / ActionOutputs type imports from collections_path and the BackendRequestResponseMethod import from metatypes_path.

imports

options?

type { type_name?: string | undefined; method_enum_name?: string | undefined; context_type?: string | undefined; collections_path?: string | undefined; metatypes_path?: string | undefined; } | undefined
optional

returns

string

generate_backend_actions_api
#

actions/action_codegen.ts view source

(specs: readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; })[], imports: ImportBuilder, options?: { ...; } | undefined): string import {generate_backend_actions_api} from '@fuzdev/fuz_app/actions/action_codegen.js';

Emit BOTH the typed BackendActionsApi interface AND the broadcast_action_specs runtime array. The interface is shaped for create_broadcast_api: backend-initiated remote_notification methods, each (input) => Promise<void>. The array bundles the matching specs as a ReadonlyArray<ActionSpecUnion>.

Filter: kind === 'remote_notification' && initiator !== 'frontend', additionally excluding methods that are the target of another spec's streams field. Streams targets (e.g. completion_progress, ollama_progress) are request-scoped notifications invoked via ctx.notify inside their parent handler — they're never callable through the broadcast API. The discriminator is ActionSpec.streams, not a manual exclusion list.

Adds the * as specs namespace import (from specs_module), the ActionInputs type import (from collections_path), and the ActionSpecUnion type import.

Method signature shape today is (input) => Promise<void> — matches the fire-and-forget runtime of create_broadcast_api. Generalizing per-kind via generate_actions_api_method_signature is deferred until a second backend runtime constructor lands.

specs

type readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } ...

imports

options?

type { specs_module?: string | undefined; collections_path?: string | undefined; qualify_spec?: ((spec: { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; ... 8 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }) => string) | undefined; include_protocol_a...
optional

returns

string

generate_daemon_token
#

auth/daemon_token.ts view source

(): string import {generate_daemon_token} from '@fuzdev/fuz_app/auth/daemon_token.js';

Generate a new daemon token (256-bit random, base64url).

returns

string

a 43-character base64url string

generate_frontend_action_handlers
#

actions/action_codegen.ts view source

(specs: readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; })[], imports: ImportBuilder, options?: { ...; } | undefined): string import {generate_frontend_action_handlers} from '@fuzdev/fuz_app/actions/action_codegen.js';

Emit the FrontendActionHandlers interface — wraps generate_phase_handlers with the TypedActionEvent action-event type and standard 1-tab per-method indentation. Pairs with generate_typed_action_event_alias (emits the matching TypedActionEvent alias) — call both in the same gen producer.

specs

type readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } ...

imports

options?

type { collections_path?: string | undefined; include_protocol_actions?: boolean | undefined; } | undefined
optional

returns

string

generate_frontend_actions_api
#

actions/action_codegen.ts view source

(specs: readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; })[], imports: ImportBuilder, options?: { ...; } | undefined): string import {generate_frontend_actions_api} from '@fuzdev/fuz_app/actions/action_codegen.js';

Emit the FrontendActionsApi interface — one method signature per spec via generate_actions_api_method_signature. Optionally filter the spec set (e.g. omit additional methods alongside the default protocol-action filter) via method_filter.

Imports are registered by the leaf generate_actions_api_method_signature per emitted line — only what the spec set actually references shows up on imports. A spec set with no async methods skips RpcClientCallOptions; one with no inputs skips ActionInputs; sync local_call methods with sync_returns_value: true (the default) skip Result / JsonrpcErrorObject.

The interface name is fixed at FrontendActionsApi — the symmetric counterpart of BackendActionsApi. Earlier consumer-named variants (MyActionsApi, VisionesActionsApi) were retired in API review III to make the side-of-the-wire intent visible at every call site. If a consumer needs a different name they hand-roll the interface (the helper's job is the standard symmetric shape).

specs

type readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } ...

imports

options?

type { method_filter?: ((spec: { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 7 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }) => boolean) | undefined; collections_path?: ...
optional

returns

string

generate_input_test_cases
#

testing/adversarial_input.ts view source

(input_schema: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>): InputTestCase[] import {generate_input_test_cases} from '@fuzdev/fuz_app/testing/adversarial_input.js';

Generate adversarial test cases for a route's input schema.

Produces focused, non-redundant cases:

  • Whole-body: send array instead of object, extra unknown key
  • Missing required fields (without defaults)
  • One wrong-type value per field
  • Null for required non-nullable fields
  • One format violation per constrained field

input_schema

type ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>

returns

InputTestCase[]

throws

  • Error - if the seed body built by `generate_valid_value` fails

generate_params_test_cases
#

testing/adversarial_input.ts view source

(params_schema: ZodObject<$ZodLooseShape, $strip>): ParamsTestCase[] import {generate_params_test_cases} from '@fuzdev/fuz_app/testing/adversarial_input.js';

Generate adversarial test cases for a route's params schema.

Params are always strings from URL segments. Only generates cases for format-constrained fields (uuid, pattern) since unconstrained string params accept any string value.

params_schema

type ZodObject<$ZodLooseShape, $strip>

returns

ParamsTestCase[]

generate_phase_handlers
#

actions/action_codegen.ts view source

(spec: { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }, executor: "frontend" | "backend", imports: ImportBuilder, options?: { ...; } | undefined): string import {generate_phase_handlers} from '@fuzdev/fuz_app/actions/action_codegen.js';

Generates the phase handlers for an action spec using the unified ActionEvent type with the new phase/step type parameters.

Returns '' when the spec contributes no phases on the given executor side (e.g. a backend-only local_call asked for 'frontend'). Upstream wrappers compose blocks with .filter(Boolean) so empty entries are dropped from the generated handler map. The earlier shape was ${method}?: never, which read as "calling this here is a type error" but in practice produced useless rows on FrontendActionHandlers for methods that don't belong on this side at all — drop the row instead so the typed surface only carries methods the executor actually handles.

spec

type { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }

executor

type "frontend" | "backend"

imports

options?

type { action_event_type?: string | undefined; collections_path?: string | undefined; } | undefined
optional

returns

string

generate_position_styles
#

ui/position_helpers.ts view source

(position?: Position, align?: Alignment, offset?: string): Record<string, string> import {generate_position_styles} from '@fuzdev/fuz_app/ui/position_helpers.js';

Generates CSS positioning styles for UI elements.

position

default 'center'

align

default 'center'

offset

distance from the position (CSS value)

type string
default '0'

returns

Record<string, string>

throws

  • UnreachableError - if `position` is not a known `Position` value

generate_query_test_cases
#

testing/adversarial_input.ts view source

(query_schema: ZodObject<$ZodLooseShape, $strip>): QueryTestCase[] import {generate_query_test_cases} from '@fuzdev/fuz_app/testing/adversarial_input.js';

Generate adversarial test cases for a route's query schema.

Query params are always strings from the URL. Generates cases for:

  • Missing required fields
  • Format violations on constrained fields (uuid, pattern)

query_schema

type ZodObject<$ZodLooseShape, $strip>

returns

QueryTestCase[]

generate_random_base64url
#

crypto.ts view source

(byte_length?: number): string import {generate_random_base64url} from '@fuzdev/fuz_app/crypto.js';

Generate a cryptographically random base64url string.

byte_length

number of random bytes (default 32 = 256 bits)

type number
default 32

returns

string

base64url-encoded string without padding

generate_random_key
#

dev/setup.ts view source

(deps: CommandDeps): Promise<string> import {generate_random_key} from '@fuzdev/fuz_app/dev/setup.js';

Generate a random base64 key using openssl.

deps

command execution capability

returns

Promise<string>

a random 32-byte base64-encoded key

throws

  • Error - if `openssl rand` fails or is unavailable

generate_session_token
#

auth/session_queries.ts view source

(): string import {generate_session_token} from '@fuzdev/fuz_app/auth/session_queries.js';

Generate a cryptographically random session token.

returns

string

a 32-byte base64url-encoded token

generate_typed_action_event_alias
#

actions/action_codegen.ts view source

(imports: ImportBuilder, options?: { collections_path?: string | undefined; metatypes_path?: string | undefined; } | undefined): string import {generate_typed_action_event_alias} from '@fuzdev/fuz_app/actions/action_codegen.js';

Emit the fixed-shape TypedActionEvent alias used by FrontendActionHandlers to narrow ActionEvent.data against the consumer's generated ActionEventDatas map. Registers the four fuz_app type imports it needs (ActionEvent, ActionEventPhase, ActionEventStep, ActionEventDatas) plus the ActionMethod type import — sourced from collections_path and metatypes_path respectively.

Pair with generate_action_method_enums (emits ActionMethod into metatypes_path) and generate_action_event_datas (emits ActionEventDatas into collections_path).

imports

options?

type { collections_path?: string | undefined; metatypes_path?: string | undefined; } | undefined
optional

returns

string

generate_valid_body
#

testing/schema_generators.ts view source

(input_schema: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>): Record<string, unknown> | undefined import {generate_valid_body} from '@fuzdev/fuz_app/testing/schema_generators.js';

Generate a valid request body for a route's input schema.

input_schema

type ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>

returns

Record<string, unknown> | undefined

a generated body that passes safeParse, or undefined for null / non-object schemas

throws

  • Error - if the generated body fails `input_schema.safeParse` — catches

generate_valid_value
#

testing/schema_generators.ts view source

(field: ZodFieldInfo, field_schema: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>): unknown import {generate_valid_value} from '@fuzdev/fuz_app/testing/schema_generators.js';

Generate a valid-ish value for a field based on its base type.

field

type ZodFieldInfo

field_schema

type ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>

returns

unknown

GenerateAppSurfaceOptions
#

http/surface.ts view source

GenerateAppSurfaceOptions import type {GenerateAppSurfaceOptions} from '@fuzdev/fuz_app/http/surface.js';

route_specs

type Array<RouteSpec>

middleware_specs

type Array<MiddlewareSpec>

env_schema?

type z.ZodObject

event_specs?

type Array<EventSpec>

rpc_endpoints?

type Array<RpcEndpointSpec>

ws_endpoints?

Mounted WS endpoints (the same array create_app_server.ws_endpoints auto-mounts). Each entry's actions surface into AppSurface.ws_endpoints[i].methods for attack-surface tests + startup logging.

type ReadonlyArray<WsEndpointSpec>

get_app_dir
#

cli/config.ts view source

(runtime: Pick<EnvDeps, "env_get">, name: string): string | null import {get_app_dir} from '@fuzdev/fuz_app/cli/config.js';

Get the CLI config directory path (~/.{name}).

runtime

runtime with env_get capability

type Pick<EnvDeps, "env_get">

name

application name (e.g., "tx", "zzz")

type string

returns

string | null

path to config directory, or null if $HOME is not set

get_audit_metadata
#

auth/audit_log_schema.ts view source

<T extends AuditEventType>(event: AuditLogEvent & { event_type: T; }): AuditMetadataMap[T] | null import {get_audit_metadata} from '@fuzdev/fuz_app/auth/audit_log_schema.js';

Narrow metadata type for a known event type.

Use after checking event_type to get typed metadata access.

event

type AuditLogEvent & { event_type: T; }

returns

AuditMetadataMap[T] | null

generics

get_audit_metadata<T extends AuditEventType>
T
constraint AuditEventType

get_audit_metadata_validation_failures
#

auth/audit_log_queries.ts view source

(): number import {get_audit_metadata_validation_failures} from '@fuzdev/fuz_app/auth/audit_log_queries.js';

Number of audit metadata validation failures observed since process start.

returns

number

get_audit_unknown_event_type_failures
#

auth/audit_log_queries.ts view source

(): number import {get_audit_unknown_event_type_failures} from '@fuzdev/fuz_app/auth/audit_log_queries.js';

Number of audit unknown-event-type failures observed since process start.

returns

number

get_client_ip
#

http/client_ip.ts view source

(c: Context<any, any, {}>): string import {get_client_ip} from '@fuzdev/fuz_app/http/client_ip.js';

Client IP resolved by the trusted-proxy middleware, or 'unknown' if unset.

c

type Context<any, any, {}>

returns

string

get_config_path
#

cli/config.ts view source

(runtime: Pick<EnvDeps, "env_get">, name: string): string | null import {get_config_path} from '@fuzdev/fuz_app/cli/config.js';

Get the CLI config file path (~/.{name}/config.json).

runtime

runtime with env_get capability

type Pick<EnvDeps, "env_get">

name

application name

type string

returns

string | null

path to config.json, or null if $HOME is not set

get_daemon_info_path
#

cli/daemon.ts view source

(runtime: Pick<EnvDeps, "env_get">, name: string): string | null import {get_daemon_info_path} from '@fuzdev/fuz_app/cli/daemon.js';

Get the daemon info file path (~/.{name}/run/daemon.json).

runtime

runtime with env_get capability

type Pick<EnvDeps, "env_get">

name

application name

type string

returns

string | null

path to daemon.json, or null if $HOME is not set

get_daemon_token_path
#

auth/daemon_token_middleware.ts view source

(runtime: Pick<EnvDeps, "env_get">, name: string): string | null import {get_daemon_token_path} from '@fuzdev/fuz_app/auth/daemon_token_middleware.js';

Get the daemon token file path (~/.{name}/run/daemon_token).

runtime

runtime with env_get capability

type Pick<EnvDeps, "env_get">

name

application name

type string

returns

string | null

path to daemon_token, or null if $HOME is not set

get_env_var_names
#

env/resolve.ts view source

(value: string): string[] import {get_env_var_names} from '@fuzdev/fuz_app/env/resolve.js';

Get list of env var names referenced in a string.

Escaped references are skipped; optional and required references are both included (callers that care about the distinction should use scan_env_vars which preserves the optional flag per ref).

value

string to scan

type string

returns

string[]

array of variable names (without $$ delimiters)

get_executor_phases
#

actions/action_codegen.ts view source

(spec: { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }, executor: "frontend" | "backend"): ("send_request" | ... 7 more ... | "execute")[] import {get_executor_phases} from '@fuzdev/fuz_app/actions/action_codegen.js';

Phases an executor can handle for the given spec — kind + initiator → set of phases.

spec

type { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }

executor

type "frontend" | "backend"

returns

("send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute")[]

get_handler_return_type
#

actions/action_codegen.ts view source

(spec: { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }, phase: "send_request" | ... 7 more ... | "execute", imports: ImportBuilder, collections_path?: string): string import {get_handler_return_type} from '@fuzdev/fuz_app/actions/action_codegen.js';

Gets the handler return type for a specific phase and spec. Adds an ActionOutputs import (from collections_path) when the phase carries an output (request_response receive_request, local_call execute).

spec

type { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }

phase

type "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"

imports

collections_path

type string
default DEFAULT_COLLECTIONS_PATH

returns

string

get_initial_phase
#

actions/action_event_helpers.ts view source

(kind: "request_response" | "remote_notification" | "local_call", initiator: "frontend" | "backend" | "both", executor: "frontend" | "backend"): "send_request" | "receive_request" | ... 7 more ... | null import {get_initial_phase} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

kind

type "request_response" | "remote_notification" | "local_call"

initiator

type "frontend" | "backend" | "both"

executor

type "frontend" | "backend"

returns

"send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute" | null

get_request_context
#

auth/request_context.ts view source

(c: Context<any, any, {}>): RequestContext | null import {get_request_context} from '@fuzdev/fuz_app/auth/request_context.js';

Get the request context from a Hono context, or null if unauthenticated.

c

the Hono context

type Context<any, any, {}>

returns

RequestContext | null

the request context, or null

get_route_error_schema
#

testing/assertions.ts view source

(lookup: Map<string, Partial<Record<number, ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>>>>, route: AppSurfaceRoute, status: number): ZodType<...> | undefined import {get_route_error_schema} from '@fuzdev/fuz_app/testing/assertions.js';

Look up the merged error schema for a route+status from a pre-built schema lookup.

lookup

map from "METHOD /path" to merged error schemas

type Map<string, Partial<Record<number, ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>>>>

route

the surface route to look up

status

HTTP status code

type number

returns

ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>> | undefined

the Zod schema for that route+status, or undefined when no error schema is declared for the status code

get_route_input
#

http/route_spec.ts view source

<S extends z.ZodType>(c: Context<any, any, {}>, schema: S): output<S> import {get_route_input} from '@fuzdev/fuz_app/http/route_spec.js';

Get validated input from the Hono context.

Call after the input validation middleware has run. Pass the route's input Zod schema to infer the typed shape directly:

const input = get_route_input(c, my_route_spec.input);

Or pass an explicit type argument when the schema isn't in scope:

const input = get_route_input<MyInput>(c);

c

type Context<any, any, {}>

schema

type S

overloads

<S extends z.ZodType>(c: Context<any, any, {}>, schema: S): output<S>

Get validated input from the Hono context.

Call after the input validation middleware has run. Pass the route's input Zod schema to infer the typed shape directly:

const input = get_route_input(c, my_route_spec.input);

Or pass an explicit type argument when the schema isn't in scope:

const input = get_route_input<MyInput>(c);
c Context<any, any, {}>
schema S
returns output<S>
<T = unknown>(c: Context<any, any, {}>): T
c Context<any, any, {}>
returns T

returns

output<S>

generics

get_route_input<S extends z.ZodType>
S
constraint z.ZodType

get_route_params
#

http/route_spec.ts view source

<S extends z.ZodType>(c: Context<any, any, {}>, schema: S): output<S> import {get_route_params} from '@fuzdev/fuz_app/http/route_spec.js';

Get validated URL path params from the Hono context.

Call after the params validation middleware has run. Pass the route's params schema to infer the typed shape, or supply an explicit type argument. See get_route_input for the two call shapes.

c

type Context<any, any, {}>

schema

type S

overloads

<S extends z.ZodType>(c: Context<any, any, {}>, schema: S): output<S>

Get validated URL path params from the Hono context.

Call after the params validation middleware has run. Pass the route's params schema to infer the typed shape, or supply an explicit type argument. See get_route_input for the two call shapes.

c Context<any, any, {}>
schema S
returns output<S>
<T = unknown>(c: Context<any, any, {}>): T
c Context<any, any, {}>
returns T

returns

output<S>

generics

get_route_params<S extends z.ZodType>
S
constraint z.ZodType

get_route_query
#

http/route_spec.ts view source

<S extends z.ZodType>(c: Context<any, any, {}>, schema: S): output<S> import {get_route_query} from '@fuzdev/fuz_app/http/route_spec.js';

Get validated URL query params from the Hono context.

Call after the query validation middleware has run. Pass the route's query schema to infer the typed shape, or supply an explicit type argument. See get_route_input for the two call shapes.

c

type Context<any, any, {}>

schema

type S

overloads

<S extends z.ZodType>(c: Context<any, any, {}>, schema: S): output<S>

Get validated URL query params from the Hono context.

Call after the query validation middleware has run. Pass the route's query schema to infer the typed shape, or supply an explicit type argument. See get_route_input for the two call shapes.

c Context<any, any, {}>
schema S
returns output<S>
<T = unknown>(c: Context<any, any, {}>): T
c Context<any, any, {}>
returns T

returns

output<S>

generics

get_route_query<S extends z.ZodType>
S
constraint z.ZodType

get_session_cookie
#

grant_key
#

ui/admin_accounts_state.svelte.ts view source

(account_id: string & $brand<"Uuid">, role: string, to_actor_id?: (string & $brand<"Uuid">) | null | undefined): string import {grant_key} from '@fuzdev/fuz_app/ui/admin_accounts_state.svelte.js';

Compose the grant keyed-slot key for an offer. Account-grain offers key on ${account_id}:${role}; actor-targeted offers add the actor suffix so the two variants can be in flight simultaneously without colliding on per-row spinners.

account_id

type string & $brand<"Uuid">

role

type string

to_actor_id?

type (string & $brand<"Uuid">) | null | undefined
optional

returns

string

GRANT_PATH_ADMIN
#

auth/grant_path_schema.ts view source

"admin" import {GRANT_PATH_ADMIN} from '@fuzdev/fuz_app/auth/grant_path_schema.js';

Admin-mediated grant — role_grant_offer_create plus admin-direct flows.

GRANT_PATH_BOOTSTRAP
#

auth/grant_path_schema.ts view source

"bootstrap" import {GRANT_PATH_BOOTSTRAP} from '@fuzdev/fuz_app/auth/grant_path_schema.js';

Bootstrap grant — one-shot flow during the keep's first-run bootstrap.

GRANT_PATH_NAME_REGEX
#

GRANT_PATH_SELF_SERVICE
#

auth/grant_path_schema.ts view source

"self_service" import {GRANT_PATH_SELF_SERVICE} from '@fuzdev/fuz_app/auth/grant_path_schema.js';

Self-service grant — caller toggles their own role_grant via self_service_role_set.

GRANT_PATH_SYSTEM
#

auth/grant_path_schema.ts view source

"system" import {GRANT_PATH_SYSTEM} from '@fuzdev/fuz_app/auth/grant_path_schema.js';

System-mediated grant — signup hooks, automation, internal service flows.

GrantJson
#

auth/cell_grant_action_specs.ts view source

ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; cell_id: $ZodBranded<ZodUUID, "Uuid", "out">; level: ZodEnum<{ viewer: "viewer"; editor: "editor"; }>; ... 4 more ...; created_at: ZodString; }, $strict> import type {GrantJson} from '@fuzdev/fuz_app/auth/cell_grant_action_specs.js';

Wire-format for a cell_grant row. Mirrors CellJson's shape — ISO-string created_at, branded UUIDs, principal columns surfaced as-is. Caller inspects actor_id xor role to render the right principal label.

GrantPathMeta
#

auth/grant_path_schema.ts view source

GrantPathMeta import type {GrantPathMeta} from '@fuzdev/fuz_app/auth/grant_path_schema.js';

Per-grant-path metadata. description is admin-UI-facing copy (mirrors RoleSpec.description and ScopeKindMeta.description). Open shape so v2 can extend without a breaking change.

description?

type string

GrantPathName
#

GrantPathSchemaResult
#

auth/grant_path_schema.ts view source

GrantPathSchemaResult import type {GrantPathSchemaResult} from '@fuzdev/fuz_app/auth/grant_path_schema.js';

The result of create_grant_path_schema — a Zod schema and metadata map.

GrantPath

Zod schema that validates grant-path name strings against the registered set (builtins + consumer-declared). Use at I/O boundaries (admin UIs, codegen) and as the construction-time check inside create_role_schema for every RoleSpec.grant_paths entry.

type z.ZodType<string>

grant_paths

Map of every registered grant-path to its metadata. Keyed by name. Read at startup by admin / codegen surfaces.

type ReadonlyMap<string, GrantPathMeta>

HandlerForSpec
#

actions/action_rpc.ts view source

HandlerForSpec<TSpec> import type {HandlerForSpec} from '@fuzdev/fuz_app/actions/action_rpc.js';

Conditional handler shape for rpc_action — picks the narrowest ctx.auth type the dispatcher's runtime guarantee allows:

  • auth.actor === 'required'ActorActionHandler (ctx.auth: RequestActorContext).
  • auth.account === 'required' && auth.actor === 'none'AuthActionHandler (ctx.auth: RequestContext).
  • else (public, optional axes) → ActionHandler (ctx.auth: RequestContext | null).

The bracketed form [T] extends ['required'] defeats distributive conditionals so a degraded AuthAxisState union (when the spec was typed without preserving its literal) falls through to the loosest tier instead of collapsing to the narrowest.

generics

HandlerForSpec<TSpec extends RequestResponseActionSpec>
TSpec

has_any_scoped_role
#

auth/request_context.ts view source

(ctx: RequestContext | null, roles: readonly string[], scope_id: string | null, now?: Date): boolean import {has_any_scoped_role} from '@fuzdev/fuz_app/auth/request_context.js';

Whether the request context holds an active role_grant for any role in roles at scope_id. Empty roles short-circuits to false — documents intent at the call site ("zero roles trivially admit no-one"). Same scope and null-tolerance semantics as has_scoped_role.

ctx

the request context, or null for unauthenticated callers

type RequestContext | null

roles

the roles that would admit the caller (any-of)

type readonly string[]

scope_id

the scope to check (null for global)

type string | null

now

current time (defaults to new Date(), pass for testability)

type Date
default new Date()

returns

boolean

true iff the actor holds an active role_grant for any role in roles at the requested scope

has_env_vars
#

env/resolve.ts view source

(value: string): boolean import {has_env_vars} from '@fuzdev/fuz_app/env/resolve.js';

Check if a string contains unresolved env var references.

Escaped references (\$$VAR$$) do not count — they're literal text once resolved.

value

string to check

type string

returns

boolean

true if string contains unescaped $$VAR$$ patterns

has_role
#

auth/request_context.ts view source

(ctx: RequestContext | null, role: string, now?: Date): boolean import {has_role} from '@fuzdev/fuz_app/auth/request_context.js';

Check if a request context has an active role_grant for a given role.

Checks the role_grants already loaded in the context (no DB query). Null-tolerant — null ctx (unauthenticated) returns false. Symmetric with has_scoped_role / has_any_scoped_role so the three helpers compose freely in the same predicate (e.g. has_role(auth, ADMIN) || has_scoped_role(auth, role, scope)).

ctx

the request context, or null for unauthenticated callers

type RequestContext | null

role

the role to check

type string

now

current time (defaults to new Date(), pass for testability and hot-path efficiency)

type Date
default new Date()

returns

boolean

true if the actor has an active role_grant for the role

has_scoped_role
#

auth/request_context.ts view source

(ctx: RequestContext | null, role: string, scope_id: string | null, now?: Date): boolean import {has_scoped_role} from '@fuzdev/fuz_app/auth/request_context.js';

Whether the request context holds an active role_grant for role at scope_id.

Walks the in-memory ctx.role_grants snapshot loaded once per request by the route-spec / RPC dispatcher's authorization phase (when the route declares acting?: ActingActor or has role_grant-requiring auth); zero DB roundtrip per check. The "freshness" framing of a SQL re-query is illusory because the race window is between predicate and the actual mutation, not predicate and authorization load. Closing that race needs a transactional re-check inside the UPDATE/INSERT, which neither style provides.

Null-tolerant — null ctx (unauthenticated) and account-grain contexts (actor: null, empty role_grants) both return false. Same convention as has_role; lets the helper drop into public ({account: 'none', actor: 'none'}) and account-grain ({account: 'required', actor: 'none'}) handlers without a manual narrow. See cell_authorize for the resource-side analog.

scope_id semantics: in-memory role_grant.scope_id is string | null, so JS === matches the SQL IS NOT DISTINCT FROM semantics exactly:

  • scope_id === null matches global role_grants (scope_id IS NULL).
  • scope_id === '<uuid>' matches role_grants bound to that exact scope.

ctx

the request context, or null for unauthenticated callers

type RequestContext | null

role

the role to check

type string

scope_id

the scope to check (null for global)

type string | null

now

current time (defaults to new Date(), pass for testability and hot-path efficiency)

type Date
default new Date()

returns

boolean

true iff the actor holds an active role_grant for the role at the requested scope

hash_api_token
#

auth/api_token.ts view source

(token: string): string import {hash_api_token} from '@fuzdev/fuz_app/auth/api_token.js';

Hash an API token for storage using blake3.

token

the raw API token

type string

returns

string

hex-encoded blake3 hash

hash_password
#

auth/password_argon2.ts view source

(password: string): Promise<string> import {hash_password} from '@fuzdev/fuz_app/auth/password_argon2.js';

Hash a password using Argon2id.

password

the plaintext password to hash

type string

returns

Promise<string>

the Argon2id hash string

hash_session_token
#

auth/session_queries.ts view source

(token: string): string & $brand<"SessionId"> import {hash_session_token} from '@fuzdev/fuz_app/auth/session_queries.js';

Hash a session token to its storage key using blake3.

The sole minting point for SessionIdhash_blake3 returns bare hex, so the brand is applied here, where the value gains its meaning.

token

the raw session token

type string

returns

string & $brand<"SessionId">

hex-encoded blake3 hash

headers_to_record
#

testing/rpc_helpers.ts view source

(headers: Headers): Record<string, string> import {headers_to_record} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

Snapshot a Headers object into a plain Record with lowercased keys (Headers iteration normalizes the casing already). Multiple values for one header collapse to the comma-joined form Headers exposes; Set-Cookie is the lone platform exception these call sites never assert on. Lets a response's headers travel back through RpcCallResult for header-level assertions (no-fingerprint, expect.headers).

headers

type Headers

returns

Record<string, string>

heartbeat_action
#

actions/heartbeat.ts view source

Action<{ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | {... import {heartbeat_action} from '@fuzdev/fuz_app/actions/heartbeat.js';

Protocol-action tuple — spread into the server's actions array for dispatch (or via protocol_actions from actions/protocol.ts) so the dispatcher resolves the heartbeat handler. The frontend-side spread happens via protocol_action_specs — the client doesn't run the echo handler, but the spec must be in ActionRegistry so create_rpc_client types app.api.heartbeat() against the shared spec.

heartbeat_action_spec
#

actions/heartbeat.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "none"; }; side_effects: false; input: ZodDefault<ZodObject<{}, $strict>>; output: ZodObject<...>; async: true; description: string; } import {heartbeat_action_spec} from '@fuzdev/fuz_app/actions/heartbeat.js';

ActionSpec for the shared heartbeat. Account-required, actor-none — upgrade-time auth has already admitted the socket; heartbeats don't need role gating or actor resolution. side_effects: false keeps it orthogonal to state changes.

heartbeat_handler
#

actions/heartbeat.ts view source

(): Record<string, never> import {heartbeat_handler} from '@fuzdev/fuz_app/actions/heartbeat.js';

Handler — nullary echo. Stateless, suitable for high-frequency pings.

returns

Record<string, never>

HelpCategory
#

cli/help.ts view source

HelpCategory<TCategory> import type {HelpCategory} from '@fuzdev/fuz_app/cli/help.js';

Category configuration for help display.

generics

HelpCategory<TCategory extends string = string>
TCategory
constraint string
default string

key

type TCategory

title

type string

HelpGenerator
#

cli/help.ts view source

HelpGenerator import type {HelpGenerator} from '@fuzdev/fuz_app/cli/help.js';

Help generator returned by create_help.

generate_main_help

Generate main help text with all commands grouped by category.

type () => string

generate_command_help

Generate help text for a specific command.

type (command: string, meta: CommandMeta) => string

get_help_text

Get help text for a command or main help.

type (command?: string, subcommand?: string) => string

HelpOptions
#

cli/help.ts view source

HelpOptions<TCategory> import type {HelpOptions} from '@fuzdev/fuz_app/cli/help.js';

Configuration for create_help.

generics

HelpOptions<TCategory extends string = string>
TCategory
constraint string
default string

name

Application name (e.g., "tx", "zzz").

type string

version

Application version string.

type string

description

Short description for the main help header.

type string

commands

Command registry keyed by command path (e.g., "apply", "daemon start").

type Record<string, CommandMeta<TCategory>>

categories

Category display order for main help.

type Array<HelpCategory<TCategory>>

examples

Example commands for main help.

type Array<string>

global_args_schema

Zod schema for global arguments (shown in all help output).

type z.ZodType

use_colors?

Whether to use ANSI colors in output. Defaults to true.

type boolean

http_status_to_jsonrpc_error_code
#

http/jsonrpc_errors.ts view source

(status: number): -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">) import {http_status_to_jsonrpc_error_code} from '@fuzdev/fuz_app/http/jsonrpc_errors.js';

Map an HTTP status code to a JSON-RPC error code.

Returns internal_error (-32603) for unrecognized status codes.

status

type number

returns

-32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">)

HTTP_STATUS_TO_JSONRPC_ERROR_CODE
#

http/jsonrpc_errors.ts view source

Record<number, -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">)> import {HTTP_STATUS_TO_JSONRPC_ERROR_CODE} from '@fuzdev/fuz_app/http/jsonrpc_errors.js';

Maps HTTP status codes to JSON-RPC error codes (reverse mapping).

When multiple error codes map to the same HTTP status (e.g. parse_error and invalid_request both map to 400), the last one wins. Use for best-effort HTTP → JSON-RPC translation.

http_transport
#

testing/rpc_helpers.ts view source

(app: { request: (input: string, init: RequestInit) => Response | Promise<Response>; }): RpcTestTransport import {http_transport} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

Adapt a Hono-style app into an RpcTestTransport.

app

type { request: (input: string, init: RequestInit) => Response | Promise<Response>; }

returns

RpcTestTransport

IdentityParityCrossTestOptions
#

testing/cross_backend/identity_parity.ts view source

IdentityParityCrossTestOptions import type {IdentityParityCrossTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/identity_parity.js';

Options for the identity-primitive parity suite.

setup_test

Per-test fixture producer (in-process or cross-process).

type SetupTest

readonly

login_path?

REST login route path. Default /api/account/login (the spine convention).

type string

readonly

signup_path?

REST signup route path. Default /api/account/signup (the spine convention).

type string

readonly

ImportBuilder
#

actions/action_codegen.ts view source

import {ImportBuilder} from '@fuzdev/fuz_app/actions/action_codegen.js';

Manages imports for generated code, building them on demand. Automatically optimizes type-only imports to use import type syntax.

Why this matters:

  • import type statements are completely removed during compilation
  • Mixed imports like import { type A, B } cannot be safely removed
  • This ensures optimal tree-shaking and smaller bundle sizes

examples

const imports = new ImportBuilder(); imports.add_types('./types.ts', 'Foo', 'Bar'); imports.add('./utils.ts', 'helper'); imports.add_type('./utils.ts', 'HelperOptions'); imports.add('./action_specs.ts', '* as specs'); // Generates: // import type {Foo, Bar} from './types.ts'; // import {helper, type HelperOptions} from './utils.ts'; // import * as specs from './action_specs.ts';

imports

type Map<string, Map<string, ImportItem>>

add

Add a value import. Accepts * as ns strings as namespace imports.

type (from: string, what: string): this

from

type string

what

type string
returns this

this for chaining

add_type

Add a type-only import.

type (from: string, what: string): this

from

type string

what

type string
returns this

this for chaining

add_many

type (from: string, ...items: string[]): this

from

type string

items

type string[]
returns this

add_types

type (from: string, ...items: string[]): this

from

type string

items

type string[]
returns this

build

Generate the import statements. When every import from a module is a type, emits import type {…} so the whole statement disappears at compile time.

type (): string

returns string

has_imports

type (): boolean

returns boolean

preview

Build the same statement list as build without joining — for inspection in tests.

type (): string[]

returns string[]

clear

Clear all imports.

type (): this

returns this

this for chaining

import_count

type number

getter

in_process_capabilities
#

testing/cross_backend/capabilities.ts view source

BackendCapabilities import {in_process_capabilities} from '@fuzdev/fuz_app/testing/cross_backend/capabilities.js';

Capability declarations for the in-process Hono transport. Nearly every flag is true because in-process testing exercises the full backend with no missing optional behaviors. The one exception is peer_request: describe_peer_ping_ws_tests is cross-process-only (it needs a real bound socket with an on_request responder via create_ws_transport), so the in-process driver never runs it — the transport itself supports server-initiated requests. Cross-process consumers declare each flag explicitly per backend.

in_process_shape_notes
#

testing/cross_backend/capabilities.ts view source

BackendShapeNotes import {in_process_shape_notes} from '@fuzdev/fuz_app/testing/cross_backend/capabilities.js';

Shape notes for the in-process Hono transport — every wiring fact present (the in-process app exercises the full middleware stack). Documentation only, like every BackendShapeNotes (nothing reads it).

InProcessSetupOptions
#

testing/cross_backend/in_process_setup.ts view source

InProcessSetupOptions import type {InProcessSetupOptions} from '@fuzdev/fuz_app/testing/cross_backend/in_process_setup.js';

Options for default_in_process_setup. Extends CreateTestAppOptions with the same extra_accounts slot the cross-process variant accepts — both transports observe the same bootstrap-time secondary set so suite bodies can read fixture.extra_accounts[username] uniformly.

inheritance

extra_accounts?

Additional accounts seeded at this transport's bootstrap-equivalent step. See ExtraAccountSpec for the cradle-only-bypass rationale. Most suites pass undefined / []; the ROLE_KEEPER probe (in describe_standard_admin_integration_tests) is the primary user.

type ReadonlyArray<ExtraAccountSpec>

readonly

extra_actors?

Additional actor names to seed on the bootstrapped keeper — exposed on fixture.extra_actors. See CrossProcessSetupOptions.extra_actors / TestFixtureBase.extra_actors. Seeded directly against the live backend DB (in-process has no wire hop).

type ReadonlyArray<string>

readonly

input_schema_declares_acting
#

http/auth_shape.ts view source

(schema: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>): boolean import {input_schema_declares_acting} from '@fuzdev/fuz_app/http/auth_shape.js';

Whether a schema declares the canonical acting?: ActingActor field. Reference-equality on the exported ActingActor schema — consumer schemas with unrelated acting fields don't trip this check.

Peels through Zod wrappers (optional, nullable, default, transform, pipe, prefault) via zod_unwrap_to_object so a spec authored as z.optional(z.strictObject({acting: ActingActor})) or z.strictObject({acting: ActingActor}).default({}) still trips the predicate.

schema

type ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>

returns

boolean

install_audit_drift_guard
#

testing/audit_drift_guard.ts view source

(): void import {install_audit_drift_guard} from '@fuzdev/fuz_app/testing/audit_drift_guard.js';

Register per-test beforeEach + afterEach hooks that catch any audit emission with a metadata shape that fails its audit_metadata_schemas entry, or an event_type not present in the active AuditLogConfig.

The production validation in query_audit_log is fail-open — it bumps process-wide counters and proceeds, so a regression that emits an undeclared metadata field or a typo'd event-type lands a row that passes downstream queries but breaks forensics. Tests that exercise audit emits should fail loudly when this happens.

Call at the top of every describe / describe_db block that fires audit writes through deps.audit.emit. Resets counters before each test and asserts zero on completion.

Pair with await_pending_effects: true (the default for create_test_app) so fire-and-forget audit writes have completed by the time the after-each check observes counter state.

returns

void

Invite
#

auth/invite_schema.ts view source

Invite import type {Invite} from '@fuzdev/fuz_app/auth/invite_schema.js';

Invite row from the database.

id

type Uuid

email

type Email | null

username

type Username | null

claimed_by

type Uuid | null

claimed_at

type string | null

created_at

type string

created_by

type Uuid | null

invite_create_action_spec
#

auth/admin_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; roles: string[]; }; side_effects: true; input: ZodObject<{ email: ZodOptional<ZodNullable<ZodString>>; username: ZodOptional<...>; acting: ZodOptional<...>; }, $strict>; output: ZodObject<...>; async: t... import {invite_create_action_spec} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

invite_delete_action_spec
#

auth/admin_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; roles: string[]; }; side_effects: true; input: ZodObject<{ invite_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<...>; }, $strict>; output: ZodObject<...>; async: true; description: string... import {invite_delete_action_spec} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

INVITE_INDEXES
#

invite_list_action_spec
#

auth/admin_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; roles: string[]; }; side_effects: false; input: ZodDefault<ZodObject<{ acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict>>; output: ZodObject<...>; async: true; description: string; r... import {invite_list_action_spec} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

rate_limit: 'account' bounds admin-side scraping of the invite table — bounded by table size, but every row carries email + username + creator/claimer identifiers worth defense-in-depth against an admin mutation oracle running scripted reads alongside invite_create.

INVITE_SCHEMA
#

auth/auth_ddl.ts view source

"\nCREATE TABLE IF NOT EXISTS invite (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n email TEXT,\n username TEXT,\n claimed_by UUID REFERENCES account(id) ON DELETE SET NULL,\n claimed_at TIMESTAMPTZ,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n created_by UUID REFERENCES actor(id) ON DELETE SET NU... import {INVITE_SCHEMA} from '@fuzdev/fuz_app/auth/auth_ddl.js';

InviteCreateInput
#

auth/admin_action_specs.ts view source

ZodObject<{ email: ZodOptional<ZodNullable<ZodString>>; username: ZodOptional<ZodNullable<ZodPipe<ZodString, ZodTransform<string, string>>>>; acting: ZodOptional<...>; }, $strict> import type {InviteCreateInput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Input for invite_create. At least one of email / username must be provided.

InviteCreateOutput
#

auth/admin_action_specs.ts view source

ZodObject<{ ok: ZodLiteral<true>; invite: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; email: ZodNullable<ZodString>; username: ZodNullable<...>; claimed_by: ZodNullable<...>; claimed_at: ZodNullable<...>; created_at: ZodString; created_by: ZodNullable<...>; }, $strict>; }, $strict> import type {InviteCreateOutput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Output for invite_create.

InviteDeleteInput
#

auth/admin_action_specs.ts view source

ZodObject<{ invite_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict> import type {InviteDeleteInput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Input for invite_delete.

InviteDeleteOutput
#

auth/admin_action_specs.ts view source

ZodObject<{ ok: ZodLiteral<true>; }, $strict> import type {InviteDeleteOutput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Output for invite_delete.

InviteJson
#

auth/invite_schema.ts view source

ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; email: ZodNullable<ZodString>; username: ZodNullable<ZodPipe<ZodString, ZodTransform<string, string>>>; claimed_by: ZodNullable<...>; claimed_at: ZodNullable<...>; created_at: ZodString; created_by: ZodNullable<...>; }, $strict> import type {InviteJson} from '@fuzdev/fuz_app/auth/invite_schema.js';

Zod schema for client-safe invite data.

InviteListInput
#

auth/admin_action_specs.ts view source

ZodDefault<ZodObject<{ acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict>> import type {InviteListInput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Input for invite_list.

InviteListOutput
#

auth/admin_action_specs.ts view source

ZodObject<{ invites: ZodArray<ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; email: ZodNullable<ZodString>; username: ZodNullable<ZodPipe<ZodString, ZodTransform<...>>>; ... 5 more ...; claimed_by_username: ZodNullable<...>; }, $strict>>; }, $strict> import type {InviteListOutput} from '@fuzdev/fuz_app/auth/admin_action_specs.js';

Output for invite_list. Uses the enriched row including creator/claimer usernames.

InviteWithUsernamesJson
#

auth/invite_schema.ts view source

ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; email: ZodNullable<ZodString>; username: ZodNullable<ZodPipe<ZodString, ZodTransform<string, string>>>; ... 5 more ...; claimed_by_username: ZodNullable<...>; }, $strict> import type {InviteWithUsernamesJson} from '@fuzdev/fuz_app/auth/invite_schema.js';

Zod schema for invite data with resolved creator/claimer usernames.

IP_LITERAL_CHARS
#

http/ip_canonical.ts view source

RegExp import {IP_LITERAL_CHARS} from '@fuzdev/fuz_app/http/ip_canonical.js';

Allowed character set for a bare IP literal.

Covers the union of IPv4 (digits + .), IPv6 (hex digits + :), and IPv4-mapped IPv6 forms (::ffff:127.0.0.1). Anything outside this set — brackets, whitespace, control bytes, letters g–z — disqualifies the input from parsing.

Same regex http/proxy.ts's validate_ip_strict uses; exported here so both modules can share one source of truth.

ipv6_bigint_to_canonical
#

http/ip_canonical.ts view source

(bits: bigint): string import {ipv6_bigint_to_canonical} from '@fuzdev/fuz_app/http/ip_canonical.js';

Convert a 128-bit IPv6 binary value into its RFC 5952 canonical string form.

  • IPv4-mapped (groups[0..5] = 0, groups[5] = 0xffff) emits the ::ffff:a.b.c.d dotted form per RFC 5952 §5.
  • Otherwise: lowercase hex with no leading zeros per group (§4.1), the longest run of consecutive zero groups (≥ 2 groups) is replaced with :: (§4.2.1, §4.2.3), and on equal-length runs the first one wins (§4.2.3). Single-zero groups stay as 0 (§4.2.2).

Pure helper exported for the test suite to exercise the canonicalization invariants directly without a full convertIPv6ToBinary round-trip.

bits

the 128-bit IPv6 value as bigint. Must satisfy 0n <= bits < 2n ** 128n; throws RangeError otherwise. Silent truncation would mask caller bugs since the bit-extraction loop only consumes the low 128 bits.

type bigint

returns

string

throws

  • when - `bits` is negative or exceeds 128 bits

is_action_complete
#

actions/action_event_helpers.ts view source

(data: { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }): boolean import {is_action_complete} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

data

type { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }

returns

boolean

is_action_spec
#

actions/action_spec.ts view source

(value: unknown): value is { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<...>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; } import {is_action_spec} from '@fuzdev/fuz_app/actions/action_spec.js';

Structural type guard for any ActionSpecUnion variant — checks kind is one of the three known values.

value

type unknown

returns

boolean

is_browser_context
#

http/origin.ts view source

(c: Context<any, any, {}>): boolean import {is_browser_context} from '@fuzdev/fuz_app/http/origin.js';

True when the request looks like it originated from a browser context — it carries an Origin or Referer header. Browsers attach these automatically on the state-changing surface; loopback / CLI clients don't. Uses !== undefined so an empty-string header still counts as browser context. Checks Referer too (not just Origin) because some browser requests send only Referer. Twin of the Rust spine's bearer_auth::is_browser_context.

The bearer (auth/bearer_auth.ts) and daemon-token (auth/daemon_token_middleware.ts) middleware both silently discard their credential when this is true: a stolen bearer token can't be replayed from a browser, and the loopback daemon token never legitimately carries an Origin. Distinct from verify_request_source — that gates browser requests against an allowlist; this only detects browser context.

c

type Context<any, any, {}>

returns

boolean

IS_CI
#

testing/db.ts view source

boolean import {IS_CI} from '@fuzdev/fuz_app/testing/db.js';

CI detection — CI=true is set automatically by GitHub Actions, GitLab CI, etc.

is_credential_gated_auth
#

http/auth_shape.ts view source

(auth: { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }): boolean import {is_credential_gated_auth} from '@fuzdev/fuz_app/http/auth_shape.js';

True iff the route declares any credential-type gate (auth.credential_types?.length).

auth

type { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }

returns

boolean

is_daemon_running
#

cli/daemon.ts view source

(runtime: CommandDeps, pid: number): Promise<boolean> import {is_daemon_running} from '@fuzdev/fuz_app/cli/daemon.js';

Check if a process is running by PID.

runtime

runtime with command execution capability

pid

process ID to check

type number

returns

Promise<boolean>

true if the process is running

is_enospc_error
#

db/fact_store_errors.ts view source

(err: unknown): boolean import {is_enospc_error} from '@fuzdev/fuz_app/db/fact_store_errors.js';

Whether a thrown value is a Node filesystem ENOSPC (no space left on device). Used by the streaming disk write to translate the raw FS error into a StorageFullError.

err

type unknown

returns

boolean

is_execute
#

actions/action_event_helpers.ts view source

(data: { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }): data is ActionEventLocalCallData & { ...; } import {is_execute} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

data

type { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }

returns

boolean

is_failed
#

actions/action_event_helpers.ts view source

(data: { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }): data is { ...; } & { ...; } import {is_failed} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

data

type { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }

returns

boolean

is_file_fact_url
#

db/file_fact_url.ts view source

(s: string): s is string & $brand<"FileFactUrl"> import {is_file_fact_url} from '@fuzdev/fuz_app/db/file_fact_url.js';

Type guard. Useful when discriminating a string | null column.

s

type string

returns

boolean

is_filterable_broadcast_transport
#

is_handled
#

actions/action_event_helpers.ts view source

(data: { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }): data is { ...; } & { ...; } import {is_handled} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

data

type { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }

returns

boolean

is_handling
#

actions/action_event_helpers.ts view source

(data: { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }): data is { ...; } & { ...; } import {is_handling} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

data

type { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }

returns

boolean

is_initial
#

actions/action_event_helpers.ts view source

(data: { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }): data is { ...; } & { ...; } import {is_initial} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

data

type { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }

returns

boolean

is_jsonrpc_error_response
#

http/jsonrpc_helpers.ts view source

(message: unknown): message is { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">); message: string; data?: unknown; }; } import {is_jsonrpc_error_response} from '@fuzdev/fuz_app/http/jsonrpc_helpers.js';

Checks if a value is a JSON-RPC error response (has error + id).

message

type unknown

returns

boolean

is_jsonrpc_message
#

http/jsonrpc_helpers.ts view source

(message: unknown): message is { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { ...; } | { ...; } | ({ ...; } | ... 2 more ... | { ...; })[] import {is_jsonrpc_message} from '@fuzdev/fuz_app/http/jsonrpc_helpers.js';

Checks if a value is any valid JSON-RPC message or batch array.

message

type unknown

returns

boolean

is_jsonrpc_notification
#

http/jsonrpc_helpers.ts view source

(message: unknown): message is { [x: string]: unknown; jsonrpc: "2.0"; method: string; params?: { [x: string]: unknown; } | undefined; } import {is_jsonrpc_notification} from '@fuzdev/fuz_app/http/jsonrpc_helpers.js';

Checks if a value is a JSON-RPC notification (has method, no id).

message

type unknown

returns

boolean

is_jsonrpc_object
#

http/jsonrpc_helpers.ts view source

(message: unknown): message is { jsonrpc: "2.0"; } import {is_jsonrpc_object} from '@fuzdev/fuz_app/http/jsonrpc_helpers.js';

Checks if a value is a JSON-RPC object (has jsonrpc: '2.0').

message

type unknown

returns

boolean

is_jsonrpc_request
#

http/jsonrpc_helpers.ts view source

(message: unknown): message is { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; } import {is_jsonrpc_request} from '@fuzdev/fuz_app/http/jsonrpc_helpers.js';

Checks if a value is a JSON-RPC request (has method + id).

message

type unknown

returns

boolean

is_jsonrpc_request_id
#

http/jsonrpc_helpers.ts view source

(id: unknown): id is string | number import {is_jsonrpc_request_id} from '@fuzdev/fuz_app/http/jsonrpc_helpers.js';

Checks if a value is a valid JSON-RPC request id (string or finite number).

id

type unknown

returns

boolean

is_jsonrpc_response
#

http/jsonrpc_helpers.ts view source

(message: unknown): message is { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } import {is_jsonrpc_response} from '@fuzdev/fuz_app/http/jsonrpc_helpers.js';

Checks if a value is a JSON-RPC success response (has result + id).

message

type unknown

returns

boolean

is_keeper_auth
#

http/auth_shape.ts view source

(auth: { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }): boolean import {is_keeper_auth} from '@fuzdev/fuz_app/http/auth_shape.js';

True iff the route is the keeper bucket — credential gate restricted to daemon_token. Keeper is the only credential gate today; if more land, this filter widens. Knows the 'daemon_token' literal directly (the keeper composition is fuz_app's only registered credential gate).

auth

type { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }

returns

boolean

is_local_call
#

actions/action_event_helpers.ts view source

(data: { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }): data is ActionEventLocalCallData import {is_local_call} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

data

type { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }

returns

boolean

is_loopback_host
#

testing/cross_backend/testing_server_core.ts view source

(host: string): boolean import {is_loopback_host} from '@fuzdev/fuz_app/testing/cross_backend/testing_server_core.js';

Loopback bind hosts — the only ones the test binary may serve on. It ships deterministic dev secrets (fixed cookie keys + bootstrap token in default_secrets.ts), so binding any network-reachable interface would let anyone who knows those fixed keys forge cookies against it. An allowlist (not an 0.0.0.0/:: blocklist) closes the gap a concrete LAN/public interface IP — e.g. --host 192.168.1.50 — would otherwise slip through. Covers localhost, the IPv4 loopback 127.0.0.0/8, and IPv6 ::1.

host

type string

returns

boolean

is_notification
#

testing/transports/ws_client.ts view source

(method: string): (msg: unknown) => boolean import {is_notification} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

Predicate matching a JSON-RPC notification with the given method name.

method

type string

returns

(msg: unknown) => boolean

is_notification_receive
#

actions/action_event_helpers.ts view source

(data: { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }): data is ({ ...; } & { ...; }) | ... 3 more ... | ({ ...; } & { ...; }) import {is_notification_receive} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

data

type { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }

returns

boolean

is_notification_send
#

actions/action_event_helpers.ts view source

(data: { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }): data is ({ ...; } & { ...; }) | ... 3 more ... | ({ ...; } & { ...; }) import {is_notification_send} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

data

type { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }

returns

boolean

is_notification_send_with_parsed_input
#

actions/action_event_helpers.ts view source

<TMethod extends string = string>(data: { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }): data is ({ ...; } & { ...; }) | ({ ...; } & { ...; }) import {is_notification_send_with_parsed_input} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

data

type { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }

returns

boolean

generics

is_notification_send_with_parsed_input<TMethod extends string = string>
TMethod
constraint string
default string

is_notification_with
#

testing/transports/ws_client.ts view source

<P>(method: string, match: (params: P) => boolean): (msg: unknown) => msg is JsonrpcNotificationFrame<P> import {is_notification_with} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

Type-guard combinator: match a notification whose typed params satisfies match. Collapses the common test pattern of casting msg to JsonrpcNotificationFrame<P> in every predicate body.

const match_roster_for = (id: Uuid) => is_notification_with<RosterChangedParams>( WORLD_METHODS.roster_changed, (params) => params.character_id === id && !params.removed, ); const roster = await client.wait_for(match_roster_for(char_id));

method

type string

match

type (params: P) => boolean

returns

(msg: unknown) => msg is JsonrpcNotificationFrame<P>

generics

is_notification_with<P>
P

is_null_schema
#

http/schema_helpers.ts view source

(schema: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>): boolean import {is_null_schema} from '@fuzdev/fuz_app/http/schema_helpers.js';

Check if a schema is exactly z.null().

Uses instanceof rather than runtime parsing to avoid false positives from z.nullable(z.string()) or similar schemas that accept null but also accept other values.

schema

type ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>

returns

boolean

is_parsed
#

actions/action_event_helpers.ts view source

(data: { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }): data is { ...; } & { ...; } import {is_parsed} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

data

type { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }

returns

boolean

is_pg_unique_violation
#

db/pg_error.ts view source

(error: unknown): boolean import {is_pg_unique_violation} from '@fuzdev/fuz_app/db/pg_error.js';

Check if an error is a PostgreSQL unique constraint violation (error code 23505).

error

the caught error

type unknown

returns

boolean

true if the error is a unique constraint violation

is_plain_authenticated_auth
#

http/auth_shape.ts view source

(auth: { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }): boolean import {is_plain_authenticated_auth} from '@fuzdev/fuz_app/http/auth_shape.js';

True iff the route is plain authenticated — account === 'required' with no role gate and no credential gate. Account-grain authenticated routes (logout, password change, account self-service) fall here.

auth

type { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }

returns

boolean

is_protocol_action_method
#

actions/action_codegen.ts view source

(method: string): method is "heartbeat" | "cancel" | "peer/ping" import {is_protocol_action_method} from '@fuzdev/fuz_app/actions/action_codegen.js';

Type predicate for filtering protocol-action methods out of a typed FrontendActionsApi method_filter. Avoids the (... as never) cast required to call Array.prototype.includes on the readonly tuple at narrow string types.

method

type string

returns

boolean

examples

generate_frontend_actions_api(specs, imports, { method_filter: (s) => !is_protocol_action_method(s.method), });

is_public_auth
#

http/auth_shape.ts view source

(auth: { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }): boolean import {is_public_auth} from '@fuzdev/fuz_app/http/auth_shape.js';

True iff the route is fully public — both account and actor axes are 'none'. Public routes skip the dispatcher's authorization phase entirely (per registry-time invariant 4 they also cannot declare roles or credential gates).

auth

type { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }

returns

boolean

is_receive_request
#

actions/action_event_helpers.ts view source

(data: { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }): data is ({ ...; } & { ...; }) | ... 3 more ... | ({ ...; } & { ...; }) import {is_receive_request} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

data

type { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }

returns

boolean

is_receive_response
#

actions/action_event_helpers.ts view source

(data: { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }): data is ({ ...; } & { ...; }) | ... 3 more ... | ({ ...; } & { ...; }) import {is_receive_response} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

data

type { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }

returns

boolean

is_remote_notification
#

actions/action_event_helpers.ts view source

(data: { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }): data is ActionEventRemoteNotificationData import {is_remote_notification} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

data

type { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }

returns

boolean

is_request_response
#

actions/action_event_helpers.ts view source

(data: { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }): data is ActionEventRequestResponseData import {is_request_response} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

data

type { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }

returns

boolean

is_response_for
#

testing/transports/ws_client.ts view source

(id: string | number): (msg: unknown) => boolean import {is_response_for} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

Predicate matching a JSON-RPC response frame (success or error) for the given request id.

id

type string | number

returns

(msg: unknown) => boolean

is_role_auth
#

http/auth_shape.ts view source

(auth: { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }): boolean import {is_role_auth} from '@fuzdev/fuz_app/http/auth_shape.js';

True iff the route declares any role gate (auth.roles?.length).

auth

type { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }

returns

boolean

is_role_grant_active
#

auth/account_schema.ts view source

(p: { revoked_at?: string | null | undefined; expires_at: string | null; }, now?: Date): boolean import {is_role_grant_active} from '@fuzdev/fuz_app/auth/account_schema.js';

p

type { revoked_at?: string | null | undefined; expires_at: string | null; }

now

type Date
default new Date()

returns

boolean

is_send_request
#

actions/action_event_helpers.ts view source

(data: { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }): data is ({ ...; } & { ...; }) | ... 3 more ... | ({ ...; } & { ...; }) import {is_send_request} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

data

type { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }

returns

boolean

is_send_request_with_parsed_input
#

actions/action_event_helpers.ts view source

<TMethod extends string = string>(data: { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }): data is ({ ...; } & { ...; }) | ({ ...; } & { ...; }) import {is_send_request_with_parsed_input} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

data

type { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }

returns

boolean

generics

is_send_request_with_parsed_input<TMethod extends string = string>
TMethod
constraint string
default string

is_send_response
#

actions/action_event_helpers.ts view source

(data: { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }): data is ({ ...; } & { ...; }) | ... 3 more ... | ({ ...; } & { ...; }) import {is_send_response} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

data

type { kind: "request_response" | "remote_notification" | "local_call"; phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"; ... 9 more ...; notification: { ...; } | null; }

returns

boolean

is_strict_object_schema
#

http/schema_helpers.ts view source

(schema: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>): boolean import {is_strict_object_schema} from '@fuzdev/fuz_app/http/schema_helpers.js';

Check if a schema is a strict object (z.strictObject()).

Strict objects set catchall to ZodNever to reject unknown keys. Regular z.object() has catchall: undefined (strips unknown keys in Zod 4).

schema

type ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>

returns

boolean

is_trusted_ip
#

http/proxy.ts view source

(ip: string, proxies: ParsedProxy[]): boolean import {is_trusted_ip} from '@fuzdev/fuz_app/http/proxy.js';

Check whether ip matches any entry in the trusted proxy list.

Normalizes ip before matching (lowercase, IPv4-mapped IPv6 stripped). Uses validate_ip_strict to reject malformed input — without strict validation, Hono's lax distinctRemoteAddr would let an entry like '203.0.113.1:8080' (false-positive 'IPv6') reach convertIPv6ToBinary in the CIDR-match branch and throw.

ip

type string

proxies

type ParsedProxy[]

returns

boolean

is_void_schema
#

http/schema_helpers.ts view source

(schema: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>): boolean import {is_void_schema} from '@fuzdev/fuz_app/http/schema_helpers.js';

Check if a schema is exactly z.void().

RPC action specs use z.void() to declare a parameterless method — JSON-RPC 2.0 forbids params: null (params must be omitted or be a Structured value), so z.void() is the correct schema for "no params" and the dispatcher maps absent params to undefined for these specs.

schema

type ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>

returns

boolean

ItemJson
#

auth/cell_item_action_specs.ts view source

ZodObject<{ parent_id: $ZodBranded<ZodUUID, "Uuid", "out">; position: $ZodBranded<ZodString, "CellItemPosition", "out">; child_id: $ZodBranded<ZodUUID, "Uuid", "out">; created_at: ZodString; }, $strict> import type {ItemJson} from '@fuzdev/fuz_app/auth/cell_item_action_specs.js';

Wire-format for a cell_item row.

position is branded CellItemPosition so consumers that round-trip the value back into a position_after / position input field don't need a cast at every call site. Wire ingress is validated by the CellItemPosition Zod schema (alphabet + length); wire egress trusts the DB CHECK constraint that backs cell_item.position, so the server-side to_item_json casts a raw string from CellItemRow.

jsonrpc_error_code_to_http_status
#

http/jsonrpc_errors.ts view source

(code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">)): ContentfulStatusCode import {jsonrpc_error_code_to_http_status} from '@fuzdev/fuz_app/http/jsonrpc_errors.js';

Map a JSON-RPC error code to an HTTP status code.

Returns 500 for unrecognized codes (consumer-defined codes without a mapping default to internal server error). The return is narrowed to Hono's ContentfulStatusCode so call sites can pass the result to c.json(body, status) without as any — 499 (nginx "client closed request") is non-standard and gets absorbed by the cast here rather than at every dispatcher branch.

code

type -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">)

returns

ContentfulStatusCode

JSONRPC_ERROR_CODE_TO_HTTP_STATUS
#

http/jsonrpc_errors.ts view source

Record<number, number> import {JSONRPC_ERROR_CODE_TO_HTTP_STATUS} from '@fuzdev/fuz_app/http/jsonrpc_errors.js';

Maps JSON-RPC error codes to HTTP status codes.

Extensible — consumers with domain-specific error codes assign directly (JSONRPC_ERROR_CODE_TO_HTTP_STATUS[-32020] = 502) at module load. The lookup function reads at call time, so mutation is the supported extension mechanism.

jsonrpc_error_code_to_name
#

http/jsonrpc_errors.ts view source

(code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">)): JsonrpcErrorName import {jsonrpc_error_code_to_name} from '@fuzdev/fuz_app/http/jsonrpc_errors.js';

Map a JSON-RPC error code to its canonical name ('not_found', 'forbidden', etc.). Falls back to 'internal_error' for codes outside the standard taxonomy so REST emitters that read this for their error field always have a stable string to emit.

code

type -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">)

returns

JsonrpcErrorName

JSONRPC_ERROR_CODE_TO_NAME
#

http/jsonrpc_errors.ts view source

Readonly<Record<number, JsonrpcErrorName>> import {JSONRPC_ERROR_CODE_TO_NAME} from '@fuzdev/fuz_app/http/jsonrpc_errors.js';

Reverse map of JSONRPC_ERROR_CODES — JSON-RPC error code → name.

Used by REST emitters that need a stable string identifier for the code in their flat-shape error body ({error: '<name>', ...}) without inventing a separate vocabulary. Built once at module load from the canonical JSONRPC_ERROR_CODES map so the two cannot drift.

Consumer-defined codes outside the standard taxonomy are not present; jsonrpc_error_code_to_name falls back to 'internal_error' so the REST shape always carries some reason rather than undefined.

JSONRPC_ERROR_CODES
#

http/jsonrpc_errors.ts view source

Readonly<Record<JsonrpcErrorName, -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">)>> import {JSONRPC_ERROR_CODES} from '@fuzdev/fuz_app/http/jsonrpc_errors.js';

Standard JSON-RPC error codes (5) plus general application codes (10).

Extensible — consumers add domain-specific codes to their own objects by casting as JsonrpcErrorCode. Application codes use the -32000 to -32099 range reserved by the JSON-RPC spec.

Frozen with Object.freeze to convert accidental mutation (test cross-contamination, cast escapes) into loud TypeErrors. Spread into a fresh object to extend.

jsonrpc_error_messages
#

http/jsonrpc_errors.ts view source

Readonly<Record<JsonrpcErrorName, (...args: any[]) => { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">); message: string; data?: unknown; }>> import {jsonrpc_error_messages} from '@fuzdev/fuz_app/http/jsonrpc_errors.js';

Named constructors for JsonrpcErrorObject values.

Each function creates a JSON-RPC error object with the correct code and a sensible default message. Used by the catch layer in apply_route_specs to build response bodies.

Frozen so tests must compose new objects rather than monkey-patch.

jsonrpc_errors
#

http/jsonrpc_errors.ts view source

{ readonly parse_error: (...args: any[]) => ThrownJsonrpcError; readonly invalid_request: (...args: any[]) => ThrownJsonrpcError; readonly method_not_found: (...args: any[]) => ThrownJsonrpcError; ... 11 more ...; readonly request_cancelled: (...args: any[]) => ThrownJsonrpcError; } import {jsonrpc_errors} from '@fuzdev/fuz_app/http/jsonrpc_errors.js';

Named constructors for ThrownJsonrpcError.

Usage: throw jsonrpc_errors.not_found('user') or throw jsonrpc_errors.forbidden().

JSONRPC_INTERNAL_ERROR
#

JSONRPC_INVALID_PARAMS
#

JSONRPC_INVALID_REQUEST
#

JSONRPC_METHOD_NOT_FOUND
#

JSONRPC_PARSE_ERROR
#

JSONRPC_SERVER_ERROR_END
#

http/jsonrpc.ts view source

-32099 import {JSONRPC_SERVER_ERROR_END} from '@fuzdev/fuz_app/http/jsonrpc.js';

End of the server-defined error code range (-32099).

JSONRPC_SERVER_ERROR_START
#

http/jsonrpc.ts view source

-32000 import {JSONRPC_SERVER_ERROR_START} from '@fuzdev/fuz_app/http/jsonrpc.js';

Start of the server-defined error code range (-32000).

JSONRPC_VERSION
#

JsonrpcErrorCode
#

http/jsonrpc.ts view source

ZodUnion<readonly [ZodLiteral<-32700>, ZodLiteral<-32600>, ZodLiteral<-32601>, ZodLiteral<-32602>, ZodLiteral<-32603>, $ZodBranded<...>]> import type {JsonrpcErrorCode} from '@fuzdev/fuz_app/http/jsonrpc.js';

A valid JSON-RPC error code — one of the 5 standard codes or a server-defined code in the -32000 to -32099 range.

JsonrpcErrorName
#

http/jsonrpc_errors.ts view source

JsonrpcErrorName import type {JsonrpcErrorName} from '@fuzdev/fuz_app/http/jsonrpc_errors.js';

Names of standard and general application JSON-RPC error codes.

JsonrpcErrorObject
#

http/jsonrpc.ts view source

ZodObject<{ code: ZodUnion<readonly [ZodLiteral<-32700>, ZodLiteral<-32600>, ZodLiteral<-32601>, ZodLiteral<-32602>, ZodLiteral<-32603>, $ZodBranded<...>]>; message: ZodString; data: ZodOptional<...>; }, $loose> import type {JsonrpcErrorObject} from '@fuzdev/fuz_app/http/jsonrpc.js';

Error object within a JSON-RPC error response.

JsonrpcErrorResponse
#

http/jsonrpc.ts view source

ZodObject<{ jsonrpc: ZodLiteral<"2.0">; id: ZodNullable<ZodUnion<readonly [ZodString, ZodNumber]>>; error: ZodObject<{ code: ZodUnion<readonly [ZodLiteral<-32700>, ... 4 more ..., $ZodBranded<...>]>; message: ZodString; data: ZodOptional<...>; }, $loose>; }, $loose> import type {JsonrpcErrorResponse} from '@fuzdev/fuz_app/http/jsonrpc.js';

A response that indicates an error occurred.

JsonrpcErrorResponseFrame
#

testing/transports/ws_client.ts view source

JsonrpcErrorResponseFrame<D> import type {JsonrpcErrorResponseFrame} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

generics

JsonrpcErrorResponseFrame<D = unknown>
D
default unknown

jsonrpc

type typeof JSONRPC_VERSION

id

type number | string

error

type { code: number; message: string; data?: D }

JsonrpcMcpMeta
#

http/jsonrpc.ts view source

ZodObject<{}, $loose> import type {JsonrpcMcpMeta} from '@fuzdev/fuz_app/http/jsonrpc.js';

MCP metadata object — loose to allow additional properties and .extend.

JsonrpcMessage
#

http/jsonrpc.ts view source

ZodUnion<readonly [ZodObject<{ jsonrpc: ZodLiteral<"2.0">; id: ZodUnion<readonly [ZodString, ZodNumber]>; method: ZodString; params: ZodOptional<ZodObject<{}, $loose>>; }, $loose>, ZodObject<...>, ZodObject<...>, ZodObject<...>]> import type {JsonrpcMessage} from '@fuzdev/fuz_app/http/jsonrpc.js';

Any valid JSON-RPC message (request, notification, response, or error response).

JsonrpcMessageFromClientToServer
#

http/jsonrpc.ts view source

ZodUnion<readonly [ZodObject<{ jsonrpc: ZodLiteral<"2.0">; id: ZodUnion<readonly [ZodString, ZodNumber]>; method: ZodString; params: ZodOptional<ZodObject<{}, $loose>>; }, $loose>, ZodObject<...>]> import type {JsonrpcMessageFromClientToServer} from '@fuzdev/fuz_app/http/jsonrpc.js';

Messages a client can send to a server (request or notification).

JsonrpcMessageFromServerToClient
#

http/jsonrpc.ts view source

ZodUnion<readonly [ZodObject<{ jsonrpc: ZodLiteral<"2.0">; method: ZodString; params: ZodOptional<ZodObject<{}, $loose>>; }, $loose>, ZodObject<...>, ZodObject<...>]> import type {JsonrpcMessageFromServerToClient} from '@fuzdev/fuz_app/http/jsonrpc.js';

Messages a server can send to a client (notification, response, or error response).

JsonrpcMethod
#

http/jsonrpc.ts view source

ZodString import type {JsonrpcMethod} from '@fuzdev/fuz_app/http/jsonrpc.js';

A JSON-RPC method name.

JsonrpcNotification
#

http/jsonrpc.ts view source

ZodObject<{ jsonrpc: ZodLiteral<"2.0">; method: ZodString; params: ZodOptional<ZodObject<{}, $loose>>; }, $loose> import type {JsonrpcNotification} from '@fuzdev/fuz_app/http/jsonrpc.js';

A notification which does not expect a response.

JsonrpcNotificationFrame
#

testing/transports/ws_client.ts view source

JsonrpcNotificationFrame<P> import type {JsonrpcNotificationFrame} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

generics

JsonrpcNotificationFrame<P = unknown>
P
default unknown

jsonrpc

type typeof JSONRPC_VERSION

method

type string

params

type P

JsonrpcNotificationParams
#

http/jsonrpc.ts view source

ZodObject<{}, $loose> import type {JsonrpcNotificationParams} from '@fuzdev/fuz_app/http/jsonrpc.js';

Notification params — loose object. Per-action schemas validate _meta content.

JsonrpcProgressToken
#

http/jsonrpc.ts view source

ZodUnion<readonly [ZodString, ZodNumber]> import type {JsonrpcProgressToken} from '@fuzdev/fuz_app/http/jsonrpc.js';

A progress token, used to associate progress notifications with the original request.

JsonrpcRequest
#

http/jsonrpc.ts view source

ZodObject<{ jsonrpc: ZodLiteral<"2.0">; id: ZodUnion<readonly [ZodString, ZodNumber]>; method: ZodString; params: ZodOptional<ZodObject<{}, $loose>>; }, $loose> import type {JsonrpcRequest} from '@fuzdev/fuz_app/http/jsonrpc.js';

A request that expects a response.

JsonrpcRequestId
#

http/jsonrpc.ts view source

ZodUnion<readonly [ZodString, ZodNumber]> import type {JsonrpcRequestId} from '@fuzdev/fuz_app/http/jsonrpc.js';

A uniquely identifying id for a request in JSON-RPC. Like MCP, excludes null.

JsonrpcRequestParams
#

http/jsonrpc.ts view source

ZodObject<{}, $loose> import type {JsonrpcRequestParams} from '@fuzdev/fuz_app/http/jsonrpc.js';

Request params — loose object. Per-action schemas validate _meta content.

JsonrpcRequestParamsMeta
#

http/jsonrpc.ts view source

ZodObject<{ progressToken: ZodOptional<ZodUnion<readonly [ZodString, ZodNumber]>>; }, $loose> import type {JsonrpcRequestParamsMeta} from '@fuzdev/fuz_app/http/jsonrpc.js';

Request params metadata — extends MCP meta with optional progress token.

JsonrpcResponse
#

http/jsonrpc.ts view source

ZodObject<{ jsonrpc: ZodLiteral<"2.0">; id: ZodUnion<readonly [ZodString, ZodNumber]>; result: ZodJSONSchema; }, $loose> import type {JsonrpcResponse} from '@fuzdev/fuz_app/http/jsonrpc.js';

A successful (non-error) response to a request.

JsonrpcResponseOrError
#

http/jsonrpc.ts view source

ZodUnion<readonly [ZodObject<{ jsonrpc: ZodLiteral<"2.0">; id: ZodUnion<readonly [ZodString, ZodNumber]>; result: ZodJSONSchema; }, $loose>, ZodObject<...>]> import type {JsonrpcResponseOrError} from '@fuzdev/fuz_app/http/jsonrpc.js';

A successful response or an error response.

JsonrpcResult
#

http/jsonrpc.ts view source

ZodJSONSchema import type {JsonrpcResult} from '@fuzdev/fuz_app/http/jsonrpc.js';

Result — any JSON value per JSON-RPC 2.0 §5. Per-action spec.output is the actual contract; the envelope only asserts presence + JSON-ness.

z.json() is required (not implicitly optional like z.unknown() / z.any(), which would let an error envelope {jsonrpc, id, error} parse successfully against the success envelope and break union discrimination).

JsonrpcServerErrorCode
#

http/jsonrpc.ts view source

$ZodBranded<ZodNumber, "JsonrpcServerErrorCode", "out"> import type {JsonrpcServerErrorCode} from '@fuzdev/fuz_app/http/jsonrpc.js';

A server-defined error code in the -32000 to -32099 range.

JsonrpcSuccessResponseFrame
#

testing/transports/ws_client.ts view source

JsonrpcSuccessResponseFrame<R> import type {JsonrpcSuccessResponseFrame} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

generics

JsonrpcSuccessResponseFrame<R = unknown>
R
default unknown

jsonrpc

type typeof JSONRPC_VERSION

id

type number | string

result

type R

keeper_identity
#

testing/ws_round_trip.ts view source

(): WsConnectIdentity import {keeper_identity} from '@fuzdev/fuz_app/testing/ws_round_trip.js';

Convenience: default identity for keeper-authenticated connections.

returns

WsConnectIdentity

KeeperHeaderProvider
#

testing/integration_helpers.ts view source

KeeperHeaderProvider import type {KeeperHeaderProvider} from '@fuzdev/fuz_app/testing/integration_helpers.js';

Header-builder triple shared by TestApp (in-process) and TestFixture (cross-backend fixture protocol). Both satisfy this shape structurally — pick_auth_headers accepts either without a cast.

create_session_headers

type (extra?: Record<string, string>) => Record<string, string>

create_bearer_headers

type (extra?: Record<string, string>) => Record<string, string>

create_daemon_token_headers

type (extra?: Record<string, string>) => Record<string, string>

KeyedAsyncSlot
#

ui/keyed_async_slot.svelte.ts view source

import {KeyedAsyncSlot} from '@fuzdev/fuz_app/ui/keyed_async_slot.svelte.js';

Reactive container for many concurrent async operations keyed by K.

generics

KeyedAsyncSlot<K, T = void, E = string>
K
T
default void
E
default string

constructor

type new <K, T = void, E = string>(options?: KeyedAsyncSlotOptions<T, E>): KeyedAsyncSlot<K, T, E>

options

type KeyedAsyncSlotOptions<T, E>
default {}

has

Reactive — true once run(key, ...) has been called and the entry hasn't been deleted.

type (key: K): boolean

key

type K
returns boolean

get

Direct access to the underlying AsyncSlot for key, or undefined if no run() has been issued for it yet. Reactive on map population and on the slot's $state.raw fields.

Prefer the sugar getters (, ) for templates; reach for get(key) when you need error_data, data, or to call abort() / set() / reset() on the underlying slot.

type (key: K): AsyncSlot<T, E> | undefined

key

type K
returns AsyncSlot<T, E> | undefined

loading

Reactive — false for keys that have never been used.

type (key: K): boolean

key

type K
returns boolean

error

Reactive — null when the key has no entry or hasn't failed.

type (key: K): E | null

key

type K
returns E | null

failed

Reactive — false for keys that have never been used.

type (key: K): boolean

key

type K
returns boolean

succeeded

Reactive — false for keys that have never been used.

type (key: K): boolean

key

type K
returns boolean

keys

Reactive iterator over every key with state.

type (): IterableIterator<K>

returns IterableIterator<K>

values

Reactive iterator over every slot.

type (): IterableIterator<AsyncSlot<T, E>>

returns IterableIterator<AsyncSlot<T, E>>

entries

Reactive iterator over [key, slot] pairs.

type (): IterableIterator<[K, AsyncSlot<T, E>]>

returns IterableIterator<[K, AsyncSlot<T, E>]>

run

Run an async operation for key. Lazily creates an AsyncSlot for the key on first use, inheriting the constructor's map_error / preserve_error_on_retry options.

Supersession is scoped to key: a second run(key, ...) aborts the first's signal AND drops its commit. Calls on different keys are fully independent (each has its own AbortController).

type (key: K, fn: (signal: AbortSignal) => Promise<T>, options?: RunOptions | undefined): Promise<T | undefined>

key

type K

fn

type (signal: AbortSignal) => Promise<T>

options?

type RunOptions | undefined
optional
returns Promise<T | undefined>

the resolved value on success; undefined on failure, abort, or supersession.

abort

Abort the in-flight run for key, if any. No-op when the key has no entry. The slot stays in the map at its prior resolved status — call to remove the entry entirely.

type (key: K, reason?: unknown): void

key

type K

reason?

type unknown
optional
returns void

abort_all

Abort every in-flight run. Resolved entries stay in the map — call to clear them too.

type (reason?: unknown): void

reason?

type unknown
optional
returns void

delete

Abort the in-flight run for key (if any) and remove the entry from the map. After delete(key), has(key) returns false and the sugar getters report the no-entry defaults — typically how a UI dismisses a per-row error indicator.

type (key: K): boolean

key

type K
returns boolean

true if the key had an entry.

reset

Abort every in-flight run and clear the map. The keyed slot looks like a fresh instance afterwards.

type (): void

returns void

size

Total number of keys with state (pending OR resolved). Reactive.

type number

getter

KeyedAsyncSlotOptions
#

ui/keyed_async_slot.svelte.ts view source

KeyedAsyncSlotOptions<T, E> import type {KeyedAsyncSlotOptions} from '@fuzdev/fuz_app/ui/keyed_async_slot.svelte.js';

Constructor options for KeyedAsyncSlot. Propagated to every child AsyncSlot at lazy creation time.

initial from is deliberately omitted — keyed slots have no per-key seed concept (the entries don't exist until run() creates them).

generics

KeyedAsyncSlotOptions<T, E = string>
T
E
default string

map_error?

Convert a caught throw into the error value stored in . Default extracts Error.message (falling back to 'Request failed' for non-Error throws). Pass to_rpc_error_message to unwrap JSON-RPC data.reason codes.

type (e: unknown) => E

preserve_error_on_retry?

When true, the previous error / error_data survive the start of a new run() until the next success (or another failure overwrites them). Useful for retry UX that wants to keep the failure message visible alongside an inline spinner. Default falserun() clears the error at the start so the pending state reads "no current error."

type boolean

Keyring
#

auth/keyring.ts view source

Keyring import type {Keyring} from '@fuzdev/fuz_app/auth/keyring.js';

Opaque keyring that encapsulates secret keys. Only exposes sign/verify operations, never the raw keys.

sign

Sign a value with HMAC SHA-256.

type (value: string) => Promise<string>

verify

Verify a signed value and extract the original. Tries all keys in order to support key rotation.

type (signed_value: string) => Promise<{ value: string; key_index: number } | null>

list_roles_with_grant_path
#

auth/role_schema.ts view source

(role_specs: ReadonlyMap<string, RoleSpec>, grant_path: string): string[] import {list_roles_with_grant_path} from '@fuzdev/fuz_app/auth/role_schema.js';

Filter helper: list every role whose grant_paths includes the given path.

role_specs

type ReadonlyMap<string, RoleSpec>

grant_path

type string

returns

string[]

load_config
#

cli/config.ts view source

<T>(runtime: Pick<FsReadDeps, "stat" | "read_text_file"> & LogDeps, path: string, schema: ZodType<T, unknown, $ZodTypeInternals<T, unknown>>): Promise<...> import {load_config} from '@fuzdev/fuz_app/cli/config.js';

Load CLI configuration from a JSON file with Zod schema validation.

runtime

runtime with file read capability

type Pick<FsReadDeps, "stat" | "read_text_file"> & LogDeps

path

path to the config JSON file

type string

schema

Zod schema to validate against

type ZodType<T, unknown, $ZodTypeInternals<T, unknown>>

returns

Promise<T | null>

parsed config, or null if file doesn't exist or is invalid

generics

load_config<T>
T

load_env
#

env/load.ts view source

<T extends z.ZodObject>(schema: T, get_env: (key: string) => string | undefined): output<T> import {load_env} from '@fuzdev/fuz_app/env/load.js';

Load and validate env vars against a Zod schema.

schema

Zod object schema defining expected env vars

type T

get_env

function to read an env var by key

type (key: string) => string | undefined

returns

output<T>

validated env object

generics

load_env<T extends z.ZodObject>
T
constraint z.ZodObject

throws

  • EnvValidationError - if Zod validation fails

load_env_file
#

env/dotenv.ts view source

(runtime: Pick<FsReadDeps, "read_text_file">, path: string): Promise<Record<string, string> | null> import {load_env_file} from '@fuzdev/fuz_app/env/dotenv.js';

Load and parse an env file.

Returns null only when the file does not exist. Other read errors (permission denied, I/O failure, etc.) are re-thrown so callers can distinguish "no file" from "couldn't read".

runtime

runtime with read_text_file capability

type Pick<FsReadDeps, "read_text_file">

path

path to env file

type string

returns

Promise<Record<string, string> | null>

parsed env record, or null if file doesn't exist

throws

  • Error - if reading fails for any reason other than `ENOENT` / `NotFound`

load_expected_schema
#

http/common_routes.ts view source

(url: string | URL): ExpectedSchema import {load_expected_schema} from '@fuzdev/fuz_app/http/common_routes.js';

Load a consumer's committed expected_schema.json fixture (cached by URL).

The spine ships the readiness *mechanism* but not the *expectation* — the expected column map is per-consumer (each adds its own tables), so the consumer commits the fixture and passes the loaded map to create_ready_route_spec. Call with an import.meta.url-relative URL:

create_ready_route_spec({ expected: load_expected_schema(new URL('./expected_schema.json', import.meta.url)), log: deps.log, });

The fixture is regenerated against a fresh bootstrap by the consumer's gen-time test (see testing/schema_ready_fixture.ts), so it can't silently fall behind the migration chain.

url

the fixture location (a file URL or path)

type string | URL

returns

ExpectedSchema

LocalCallActionSpec
#

actions/action_spec.ts view source

ZodObject<{ method: ZodString; initiator: ZodEnum<{ frontend: "frontend"; backend: "backend"; both: "both"; }>; side_effects: ZodBoolean; input: ZodCustom<ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>, ZodType<...>>; ... 7 more ...; auth: ZodDefault<...>; }, $strict> import type {LocalCallActionSpec} from '@fuzdev/fuz_app/actions/action_spec.js';

Local calls can wrap synchronous or asynchronous actions, and are the escape hatch for remote APIs that do not support SAES.

log_db_factory_status
#

testing/db.ts view source

(factories: DbFactory[]): void import {log_db_factory_status} from '@fuzdev/fuz_app/testing/db.js';

Log factory status to console.

factories

type DbFactory[]

returns

void

log_env_validation_error
#

env/load.ts view source

(error: EnvValidationError, label?: string | undefined): void import {log_env_validation_error} from '@fuzdev/fuz_app/env/load.js';

Log formatted env validation issues to stderr.

Handles the common case: labels each Zod issue with an optional prefix. Callers who want app-specific "getting started" instructions should check error.all_undefined before calling this.

error

the env validation error

label?

optional prefix for log lines (e.g., 'zap daemon', 'env')

type string | undefined
optional

returns

void

log_startup_summary
#

server/startup.ts view source

(surface: AppSurface, log: Logger, env_values?: Record<string, unknown> | undefined): void import {log_startup_summary} from '@fuzdev/fuz_app/server/startup.js';

Log a startup summary from an AppSurface.

Logs route count, middleware count, env breakdown (when non-empty), and event/channel counts (when non-empty). When env_values is provided, non-secret values are logged and secrets are masked with ***.

surface

log

type Logger

env_values?

optional env values to log (secrets are masked)

type Record<string, unknown> | undefined
optional

returns

void

LogDeps
#

runtime/deps.ts view source

LogDeps import type {LogDeps} from '@fuzdev/fuz_app/runtime/deps.js';

Warning/diagnostic output.

warn

Log a warning message.

type (...args: Array<unknown>) => void

LOGIN_RATE_LIMIT_ENABLED_ENV
#

testing/cross_backend/default_backend_configs.ts view source

"FUZ_LOGIN_RATE_LIMIT_ENABLED" import {LOGIN_RATE_LIMIT_ENABLED_ENV} from '@fuzdev/fuz_app/testing/cross_backend/default_backend_configs.js';

Env var both spine binaries read to enable their login rate limiters ('true' on / unset off). The cross-language contract for the login-security cross project: the TS binary reads it via runtime.env_get (a test-only flag, not in BaseServerEnv); the Rust testing_spine_stub reads it via std::env::var — so one backend-config option drives both impls. Shared home here because both ts_spine_backend_config and rust_spine_stub_backend_config already import this module. (The spawned TS binary re-declares the literal locally — it can't import this module, which transitively pulls vitest — mirroring how testing_spine_server_node.ts re-declares TS_SPINE_DIR_ENV.)

LoginForm
#

ui/LoginForm.svelte view source

import LoginForm from '@fuzdev/fuz_app/ui/LoginForm.svelte';

username_label?

Label and placeholder for the username field — set when the surface accepts only one of username/email so the UX matches.

type string
optional default 'username or email'

redirect_on_login?

Path to navigate to on successful login.

type string
optional default resolve('/')

LoginInput
#

auth/account_route_schema.ts view source

ZodObject<{ username: ZodPipe<ZodString, ZodTransform<string, string>>; password: ZodString; }, $strict> import type {LoginInput} from '@fuzdev/fuz_app/auth/account_route_schema.js';

Input for POST /login. Accepts a username or email in the username field.

LoginOutput
#

auth/account_route_schema.ts view source

ZodObject<{ ok: ZodLiteral<true>; }, $strict> import type {LoginOutput} from '@fuzdev/fuz_app/auth/account_route_schema.js';

Output for POST /login. Session cookie is the operative side effect.

LoginSecurityCrossTestOptions
#

testing/cross_backend/login_security.ts view source

LoginSecurityCrossTestOptions import type {LoginSecurityCrossTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/login_security.js';

Options for the login-security parity suite.

setup_test

Per-test fixture producer (cross-process only — see the module doc).

type SetupTest

readonly

login_path?

REST login route path. Default /api/account/login (the spine convention).

type string

readonly

LogoutButton
#

ui/LogoutButton.svelte view source

accepts children

import LogoutButton from '@fuzdev/fuz_app/ui/LogoutButton.svelte';

onclick?

type MouseEventHandler<HTMLButtonElement>
optional

children?

type Snippet<[]>
optional

intersects

Omit<ComponentProps<typeof PendingButton>, 'pending' | 'onclick' | 'children'>

LogoutInput
#

auth/account_route_schema.ts view source

ZodNull import type {LogoutInput} from '@fuzdev/fuz_app/auth/account_route_schema.js';

Input for POST /logout. Session identity flows through the cookie.

LogoutOutput
#

auth/account_route_schema.ts view source

ZodObject<{ ok: ZodLiteral<true>; username: ZodString; }, $strict> import type {LogoutOutput} from '@fuzdev/fuz_app/auth/account_route_schema.js';

Output for POST /logout. Includes the revoked account's username for UI redraw.

make_cross_backend_project
#

testing/cross_backend/make_cross_backend_project.ts view source

({ name, global_setup, include, exclude, group_order }: CrossBackendProjectOptions): { extends: true; test: { name: string; include: string[]; exclude: string[]; globalSetup: string[]; isolate: false; fileParallelism: false; sequence: { ...; }; }; } import {make_cross_backend_project} from '@fuzdev/fuz_app/testing/cross_backend/make_cross_backend_project.js';

Build a single cross-backend vitest project config. Spread the results into test.projects in the consumer's vite.config.ts. isolate: false + fileParallelism: false because a project shares one spawned backend across its files.

__0

returns

{ extends: true; test: { name: string; include: string[]; exclude: string[]; globalSetup: string[]; isolate: false; fileParallelism: false; sequence: { groupOrder: number; }; }; }

make_default_rust_backend_config
#

testing/cross_backend/default_backend_configs.ts view source

(opts: MakeDefaultRustBackendConfigOptions): BackendConfig import {make_default_rust_backend_config} from '@fuzdev/fuz_app/testing/cross_backend/default_backend_configs.js';

Shared builder for Rust-family backends. Owns the common env baseline (RUST_LOG, HOST, port, real Postgres, cookie keys, bootstrap token path, the FUZ_TESTING_RESET_DB_ON_STARTUP=true self-wipe gate) plus the 120s startup window for cargo's first-run build cost.

opts

returns

BackendConfig

make_default_ts_backend_config
#

testing/cross_backend/default_backend_configs.ts view source

(opts: MakeDefaultTsBackendConfigOptions): BackendConfig import {make_default_ts_backend_config} from '@fuzdev/fuz_app/testing/cross_backend/default_backend_configs.js';

Shared builder for TS-family backends (Deno + Node). Owns the common env baseline (NODE_ENV, HOST, PORT, in-memory PGlite, cookie keys, bootstrap token path) so per-backend factories only declare what genuinely differs.

opts

returns

BackendConfig

MakeDefaultRustBackendConfigOptions
#

testing/cross_backend/default_backend_configs.ts view source

MakeDefaultRustBackendConfigOptions import type {MakeDefaultRustBackendConfigOptions} from '@fuzdev/fuz_app/testing/cross_backend/default_backend_configs.js';

name

Diagnostic label; also used as the tmpdir prefix when paths is omitted.

type string

readonly

port

TCP port the binary listens on.

type number

readonly

start_command

argv passed to the spawn (first entry is the binary).

type ReadonlyArray<string>

readonly

database_url

Required — Rust needs real Postgres (PGlite isn't reachable from tokio-postgres). Consumers typically supply 'postgres://localhost/{repo}_test_{name}'.

type string

readonly

extra_env?

Merged on top of the generic env baseline; later keys win.

type Readonly<Record<string, string>>

readonly

capabilities?

type BackendCapabilities

readonly

paths?

Pre-computed paths; defaults to build_test_backend_paths(name).

type TestBackendPaths

readonly

bootstrap_overrides?

Override individual bootstrap fields (username/password/token).

type Partial<BackendBootstrapConfig>

readonly

port_env_var?

Env-var name the binary reads for its port. Defaults to 'PORT'. Consumers whose binary reads a different name (e.g. 'ZZZ_PORT') override.

type string

readonly

rust_log?

Initial value for RUST_LOG. Defaults to 'info'. Consumers pass their binary-specific module filter (e.g. 'info,zzz_server=info,testing_zzz_server=info').

type string

readonly

cookie_name?

Session cookie name the binary uses. Defaults to 'fuz_session'. Must match the consumer's session config (see the TS builder's note).

type string

readonly

MakeDefaultTsBackendConfigOptions
#

testing/cross_backend/default_backend_configs.ts view source

MakeDefaultTsBackendConfigOptions import type {MakeDefaultTsBackendConfigOptions} from '@fuzdev/fuz_app/testing/cross_backend/default_backend_configs.js';

name

Diagnostic label; also used as the tmpdir prefix when paths is omitted.

type string

readonly

port

TCP port the binary listens on.

type number

readonly

start_command

argv passed to the spawn (first entry is the binary).

type ReadonlyArray<string>

readonly

database_url?

Defaults to 'memory://' (in-memory PGlite).

type string

readonly

extra_env?

Merged on top of the generic env baseline; later keys win.

type Readonly<Record<string, string>>

readonly

capabilities?

type BackendCapabilities

readonly

paths?

Pre-computed paths; defaults to build_test_backend_paths(name).

type TestBackendPaths

readonly

bootstrap_overrides?

Override individual bootstrap fields (username/password/token).

type Partial<BackendBootstrapConfig>

readonly

port_env_var?

Env-var name the binary reads for its port. Defaults to 'PORT'. Consumers whose binary reads a different name (e.g. 'ZZZ_PORT') override.

type string

readonly

cookie_name?

Session cookie name the binary's create_session_config uses. Defaults to 'fuz_session'. Must match the consumer's session config — the harness threads the _testing_reset-returned keeper cookie into its jar under this name, so a mismatch surfaces as 401s on the create_account path (e.g. fuz_forge uses 'fuz_forge_session').

type string

readonly

MASKED_VALUE
#

env/mask.ts view source

"***" import {MASKED_VALUE} from '@fuzdev/fuz_app/env/mask.js';

Placeholder displayed in place of secret values.

MAX_IN_FLIGHT_PEER_REQUESTS_PER_CONNECTION
#

actions/peer_request.ts view source

256 import {MAX_IN_FLIGHT_PEER_REQUESTS_PER_CONNECTION} from '@fuzdev/fuz_app/actions/peer_request.js';

Per-connection cap on concurrent in-flight server→client requests. A caller past it gets too_many_in_flight instead of growing the pending map unbounded. Twin of the Rust spine's DEFAULT_MAX_IN_FLIGHT_PER_CONN.

MEMOS_SCHEMA
#

db/fact_ddl.ts view source

"\nCREATE TABLE IF NOT EXISTS memo (\n\tfn_id TEXT NOT NULL,\n\tinput_hash TEXT NOT NULL,\n\toutput_hash TEXT NOT NULL,\n\tcreated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n\tPRIMARY KEY (fn_id, input_hash)\n)" import {MEMOS_SCHEMA} from '@fuzdev/fuz_app/db/fact_ddl.js';

memo table — (fn_id, input_hash) → output_hash for memoized computations.

MenuLink
#

merge_error_schemas
#

http/schema_helpers.ts view source

(spec: { auth: { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }; input: ZodType<unknown, unknown, $ZodTypeInternals<...>>; params?: ZodObject<...> | undefined; query?: ZodObject<...> | undefined; rate_limit?: "both" | ... 2 more ... | undefined; errors?: Partial<...> | undefined; }, middleware_errors?: Partial<...> | ... 1 more ... | undefined): Partial<...> | null import {merge_error_schemas} from '@fuzdev/fuz_app/http/schema_helpers.js';

Merge auto-derived, middleware, and explicit error schemas for a route spec.

Merge order: derived -> middleware -> explicit route errors. Later layers override earlier ones for the same status code.

spec

the route spec (needs auth, input, params, rate_limit, errors)

type { auth: { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; params?: ZodObject<...> | undefined; query?: ZodObject<...> ...

middleware_errors?

errors contributed by middleware whose path matches the route

type Partial<Record<number, ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>>> | null | undefined
optional

returns

Partial<Record<number, ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>>> | null

merged error schemas, or null if empty

MethodCoverageEntry
#

testing/cross_backend/method_coverage.ts view source

MethodCoverageEntry import type {MethodCoverageEntry} from '@fuzdev/fuz_app/testing/cross_backend/method_coverage.js';

One row of the live-RPC-method coverage manifest.

method

The RPC method name (action.spec.method).

type string

readonly

tier

How this method is covered.

type MethodCoverageTier

readonly

capability?

The BackendCapabilities flag gating this method's cross suite, when it is capability-gated. Typed against the real interface so a stale flag is a compile error. Omit for ungated off-surface families (always-mounted, the suite runs unconditionally) and for declared / backdoor tiers.

type keyof BackendCapabilities

readonly

suite?

The cross-backend suite (or test file) that covers this method. Required for off_surface — the manifest's whole point is to name the suite a method without auto-enumeration relies on. Optional for declared (auto-enumerated) / backdoor (infra).

type string

readonly

note?

Optional free-text note printed nowhere — documentation for the reader.

type string

readonly

MethodCoverageTier
#

middleware_applies
#

http/schema_helpers.ts view source

(mw_path: string, route_path: string): boolean import {middleware_applies} from '@fuzdev/fuz_app/http/schema_helpers.js';

Check if a middleware path pattern applies to a route path.

Supports Hono-style patterns:

  • /api/* matches /api/anything
  • /api/zap/* matches /api/zap/runs but not /api/account/login
  • Exact match: /health matches /health

mw_path

type string

route_path

type string

returns

boolean

MiddlewareSpec
#

http/middleware_spec.ts view source

MiddlewareSpec import type {MiddlewareSpec} from '@fuzdev/fuz_app/http/middleware_spec.js';

A named middleware layer.

name

type string

path

type string

handler

type MiddlewareHandler

errors?

Error response schemas this middleware can produce, keyed by HTTP status code.

type RouteErrorSchemas

Migration
#

db/migrate.ts view source

Migration import type {Migration} from '@fuzdev/fuz_app/db/migrate.js';

A single migration: a name + an up function applied inside a transaction.

Throw from up to roll back the entire chain.

name

type string

up

type (db: Db) => Promise<void>

MigrationError
#

db/migrate.ts view source

import {MigrationError} from '@fuzdev/fuz_app/db/migrate.js';

Tagged error thrown by run_migrations and baseline.

Branch on .kind; the message carries an operator-facing remediation hint.

inheritance

extends: Error

kind

type MigrationErrorKind

readonly

namespace?

type string

readonly

at_index?

type number

readonly

unknown_names?

type ReadonlyArray<string>

readonly

constructor

type new (kind: MigrationErrorKind, message: string, context?: MigrationErrorContext | undefined): MigrationError

kind

message

type string

context?

type MigrationErrorContext | undefined
optional

MigrationErrorContext
#

db/migrate.ts view source

MigrationErrorContext import type {MigrationErrorContext} from '@fuzdev/fuz_app/db/migrate.js';

Structured context passed alongside a MigrationError.

namespace?

type string

at_index?

type number

unknown_names?

type ReadonlyArray<string>

cause?

type unknown

MigrationErrorKind
#

db/migrate.ts view source

MigrationErrorKind import type {MigrationErrorKind} from '@fuzdev/fuz_app/db/migrate.js';

Tagged error vocabulary for run_migrations and baseline.

Callers branch on .kind rather than matching error messages — message text is for operators, not control flow.

MigrationNamespace
#

db/migrate.ts view source

MigrationNamespace import type {MigrationNamespace} from '@fuzdev/fuz_app/db/migrate.js';

A named group of ordered migrations.

Array index = position in the chain. Pre-stable: bodies, names, and positions can change between versions (consumers re-bootstrap on upgrade).

namespace

type string

migrations

type Array<Migration>

MigrationResult
#

db/migrate.ts view source

MigrationResult import type {MigrationResult} from '@fuzdev/fuz_app/db/migrate.js';

Result of running migrations for a single namespace.

namespace

type string

applied_names

Migrations applied in this run, in sequence-ascending (execution) order.

type Array<string>

MigrationStatus
#

db/status.ts view source

MigrationStatus import type {MigrationStatus} from '@fuzdev/fuz_app/db/status.js';

Migration status for a single namespace.

namespace

type string

applied_names

Names of migrations recorded in the tracker, sequence-ascending.

type Array<string>

pending_names

Names of code migrations not yet applied (suffix of the code array).

type Array<string>

up_to_date

Whether applied_names is the full code array with no name divergence (no pending work, no diverged history).

type boolean

divergence?

The first applied/code divergence, if any. Absent when the applied names are a clean prefix of the code's list (the only state the runner boots against). Present means a divergent bootstrap history — a re-bootstrap (drop + migrate) is needed.

type Divergence

MigrationTracker
#

testing/schema_introspect.ts view source

ZodObject<{ entries: ZodArray<ZodObject<{ namespace: ZodString; name: ZodString; sequence: ZodNumber; }, $strip>>; }, $strip> import type {MigrationTracker} from '@fuzdev/fuz_app/testing/schema_introspect.js';

The full schema_version tracker as a deterministically-ordered list, wrapped in an object so it round-trips cleanly as a JSON-RPC result. Sorted by (namespace, sequence) on capture.

MigrationTrackerDiff
#

testing/schema_parity.ts view source

MigrationTrackerDiff import type {MigrationTrackerDiff} from '@fuzdev/fuz_app/testing/schema_parity.js';

Structured migration-identity drift entry. Keyed on (namespace, name) — the schema_version PK — so a name rename and a partitioning change both surface as tracker_row_only_in, and a re-order surfaces as tracker_sequence_differs.

MigrationTrackerEntry
#

testing/schema_introspect.ts view source

ZodObject<{ namespace: ZodString; name: ZodString; sequence: ZodNumber; }, $strip> import type {MigrationTrackerEntry} from '@fuzdev/fuz_app/testing/schema_introspect.js';

A single schema_version tracker row — the migration-identity primitive.

Where SchemaSnapshot is provenance-agnostic (it captures the resulting tables and deliberately *excludes* the tracker), this is the tracker: the (namespace, name, sequence) the migration runner records per applied migration. sequence carries order; name carries identity (the PK is (namespace, name)). The cross-impl gate diffs these between the two bootstrapped spines so a migration-name or partitioning drift — invisible to the schema snapshot — is a gated fact, not a latent interop break.

MinimalActionEnvironment
#

testing/ws_round_trip.ts view source

import {MinimalActionEnvironment} from '@fuzdev/fuz_app/testing/ws_round_trip.js';

Minimal ActionEventEnvironment for tests that instantiate an ActionDispatcher without pulling in the full runtime. Pre-loads a spec map from the supplied list.

inheritance

executor

type 'frontend' | 'backend'

constructor

type new (specs: readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; })[]): MinimalActionEnvironment

specs

type readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } ...

lookup_action_handler

type (): undefined

returns undefined

lookup_action_spec

type (method: string): { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 7 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; } | undefined

method

type string
returns { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }...

mint_file_fact_url
#

db/file_fact_url.ts view source

(shard: string, rest: string): string & $brand<"FileFactUrl"> import {mint_file_fact_url} from '@fuzdev/fuz_app/db/file_fact_url.js';

Construct a canonical file:<shard>/<rest> URL. The writer side (db/fact_disk_storage.ts) assembles the shape from a freshly-computed hash via fact_disk_path; this helper centralizes the literal so a future shape change is a single edit.

shard

type string

rest

type string

returns

string & $brand<"FileFactUrl">

mint_test_session
#

testing/app_server.ts view source

(options: MintTestSessionOptions): Promise<{ session_cookie: string; }> import {mint_test_session} from '@fuzdev/fuz_app/testing/app_server.js';

Mint a real auth_session row for an existing account and return a validly-signed session cookie value referencing it. Test-only — the forge behind the cross-backend expiry conformance cases (the expired_session principal): pass a negative expires_in_seconds to produce an *expired server-side session* whose signed cookie envelope is still well-formed. Both the TS _testing_mint_session action and the in-process fixture.mint_expired_session() seam call this so the write semantics match across transports.

options

returns

Promise<{ session_cookie: string; }>

MintTestSessionOptions
#

testing/app_server.ts view source

MintTestSessionOptions import type {MintTestSessionOptions} from '@fuzdev/fuz_app/testing/app_server.js';

Options for mint_test_session.

db

type Db

keyring

type Keyring

session_options

type SessionOptions<string>

account_id

Account the minted session belongs to.

type string

expires_in_seconds

Session lifetime offset in seconds applied to NOW() for the auth_session.expires_at row. A negative value backdates the row so the authoritative DB-row expiry gate (query_session_get_validWHERE expires_at > NOW()) rejects it, while the returned cookie's own signed payload stays valid (future). Resolution therefore passes the cookie-payload check in parse_session and is refused at the DB-row gate — the gate the in-process payload-expiry tests never reach and the one that structurally needs a server-side mint.

type number

MissingColumns
#

db/schema_ready.ts view source

MissingColumns import type {MissingColumns} from '@fuzdev/fuz_app/db/schema_ready.js';

Columns the live DB is missing for a table the expected schema declares.

table

type string

columns

type Array<string>

MockExitError
#

runtime/mock.ts view source

import {MockExitError} from '@fuzdev/fuz_app/runtime/mock.js';

Error thrown when mock runtime.exit() is called.

Tests can catch this to verify exit behavior.

inheritance

extends: Error

code

type number

readonly

constructor

type new (code: number): MockExitError

code

type number

MockFs
#

testing/mock_fs.ts view source

MockFs import type {MockFs} from '@fuzdev/fuz_app/testing/mock_fs.js';

read_file

type (path: string, encoding: string) => Promise<string>

write_file

type (path: string, content: string, encoding: string) => Promise<void>

get_file

type (path: string) => string | undefined

MockRuntime
#

runtime/mock.ts view source

MockRuntime import type {MockRuntime} from '@fuzdev/fuz_app/runtime/mock.js';

Mock RuntimeDeps with observable state for assertions.

inheritance

extends: RuntimeDeps

mock_env

Mock environment variables.

type Map<string, string>

mock_fs

Mock file system (path -> content).

type Map<string, string>

mock_fs_bytes

Mock binary file system (path -> bytes).

type Map<string, Uint8Array>

mock_dirs

Mock directories that exist.

type Set<string>

exit_calls

Exit calls recorded (exit codes).

type Array<number>

command_calls

Commands executed. Captures options when passed so tests can assert cwd/timeout/signal.

type Array<{ cmd: string; args: Array<string>; options?: RunCommandOptions }>

command_inherit_calls

Commands executed with inherit.

type Array<{ cmd: string; args: Array<string> }>

stdout_writes

Stdout writes recorded.

type Array<string>

mock_command_results

Mock command results (cmd -> result).

type Map<string, CommandResult>

stdin_buffer

Stdin buffer for input simulation.

type Uint8Array | null

fetch_calls

Fetch calls recorded.

type Array<{ input: string | URL | Request; init?: RequestInit }>

mock_fetch_responses

Mock fetch responses (URL substring -> Response).

type Map<string, Response>

needs_account
#

http/auth_shape.ts view source

(auth: { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }): boolean import {needs_account} from '@fuzdev/fuz_app/http/auth_shape.js';

True iff the route declares an account axis ('optional' or 'required'). Per registry-time invariant 3 this is implied by needs_actor(auth) in v1 (no accountless actors yet).

auth

type { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }

returns

boolean

needs_actor
#

http/auth_shape.ts view source

(auth: { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }): boolean import {needs_actor} from '@fuzdev/fuz_app/http/auth_shape.js';

True iff the route declares an actor axis ('optional' or 'required'). Equivalent to "the dispatcher's authorization phase may resolve an actor for this request" — which by registry-time invariant 2 also means the input (or query, on REST GETs) declares acting?: ActingActor.

auth

type { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }

returns

boolean

NginxFactsValidation
#

server/x_accel.ts view source

NginxFactsValidation import type {NginxFactsValidation} from '@fuzdev/fuz_app/server/x_accel.js';

Result of the facts-location check.

ok

true when the facts location exists and is internal;.

type boolean

errors

Fatal issues — a missing or non-internal facts location.

type Array<string>

NginxValidationResult
#

server/validate_nginx.ts view source

NginxValidationResult import type {NginxValidationResult} from '@fuzdev/fuz_app/server/validate_nginx.js';

Result of validating an nginx config template string.

ok

True when no errors were detected. Warnings do not affect this flag.

type boolean

warnings

Non-fatal issues — missing optional headers, weakened defaults, etc.

type Array<string>

errors

Fatal issues — missing /api block, missing required security headers, etc.

type Array<string>

no_nested_transaction
#

db/db.ts view source

<T>(fn: (tx_db: Db) => Promise<T>): Promise<T> import {no_nested_transaction} from '@fuzdev/fuz_app/db/db.js';

Sentinel transaction function for transaction-scoped Db instances.

Used by driver adapters when constructing the inner Db passed to transaction callbacks.

fn

type (tx_db: Db) => Promise<T>

returns

Promise<T>

throws

  • Error - always — nested transactions are not supported

NoActorsOnAccountError
#

http/error_schemas.ts view source

ZodObject<{ error: ZodLiteral<"no_actors_on_account">; }, $loose> import type {NoActorsOnAccountError} from '@fuzdev/fuz_app/http/error_schemas.js';

normalize_ip
#

http/proxy.ts view source

(ip: string): string import {normalize_ip} from '@fuzdev/fuz_app/http/proxy.js';

Normalize an IP address for consistent matching and storage.

Delegates to canonicalize_ip from http/ip_canonical.ts — collapses RFC 5952-equivalent IPv6 forms (::1, ::0001, 0:0:0:0:0:0:0:1) into a single key, emits IPv4-mapped IPv6 in dotted form, and strips the ::ffff: prefix from dotted IPv4-mapped values so the bucket collapses to plain IPv4.

  • Lowercases for case-insensitive IPv6 comparison.
  • Idempotent: calling twice produces the same result.
  • Safe on non-IP strings: normalize_ip('unknown') returns 'unknown'. Malformed inputs ('attacker:controlled', '::1\n', '203.0.113.1:8080') pass through unchanged so downstream validate_ip_strict can still reject them — canonicalization never erases the malformed-form signal.

ip

type string

returns

string

NotificationSender
#

auth/role_grant_offer_notifications.ts view source

NotificationSender import type {NotificationSender} from '@fuzdev/fuz_app/auth/role_grant_offer_notifications.js';

Narrow structural capability for sending a JSON-RPC notification to every socket bound to an account.

BackendWebsocketTransport satisfies this interface — its send_to_account(account_id, message) signature accepts the broader JsonrpcMessageFromServerToClient type, which is contravariantly compatible with JsonrpcNotification here. The interface stays local so handlers don't couple to the concrete transport, and tests can inject a capturing stub with no WS machinery.

Returns the number of sockets the notification was sent to — callers typically ignore it (used by telemetry / tests).

send_to_account

type (account_id: Uuid, message: JsonrpcNotification) => number

OFFER_GRANTOR_USERNAME
#

testing/cross_backend/role_grant_offer_enumeration.ts view source

"offer_grantor" import {OFFER_GRANTOR_USERNAME} from '@fuzdev/fuz_app/testing/cross_backend/role_grant_offer_enumeration.js';

Username of the bootstrap-seeded single-actor admin grantor. The entrypoint seeds it via `extra_accounts: [{username: OFFER_GRANTOR_USERNAME, roles: [ROLE_ADMIN]}]`. A *separate* account is required (an account can't offer to itself), and it must be single-actor so its role_grant_offer_create resolves without an acting selector — which rules out the now-multi-actor keeper and fixture.create_account (whose internal invite_create runs as the multi-actor keeper and would hit actor_required).

OpenSignupToggle
#

OriginCrossTestOptions
#

testing/cross_backend/origin.ts view source

RpcPathCrossSuiteOptions import type {OriginCrossTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/origin.js';

Options for the origin parity suite. The standard RPC-dispatched cross-suite shape (setup_test / capabilities / rpc_path); aliases the shared RpcPathCrossSuiteOptions rather than minting a duplicate.

rpc_path?

RPC endpoint path the methods are mounted on. Default /api/rpc.

type string

readonly

setup_test

Per-test fixture-producing function (fresh keeper + db per call).

type (): Promise<TestFixtureBase>

readonly
returns Promise<TestFixtureBase>

capabilities

Backend capability declarations — each suite gates on its own flag.

type BackendCapabilities

readonly

OrphanFactsListResult
#

db/fact_queries.ts view source

OrphanFactsListResult import type {OrphanFactsListResult} from '@fuzdev/fuz_app/db/fact_queries.js';

Summary + sample shape returned by query_orphan_facts_list. The sample is a small page (default 20 rows) shown in the admin panel so the operator has *some* visibility into what they're about to delete. Total count and total_size_bytes are over the full orphan set (matching the same predicate the delete handler will run).

count

type number

total_size_bytes

type number

sample

type Array<{ hash: FactHash; size: number; created_at: string; external_url: string | null; }>

parse_action_event
#

actions/action_event.ts view source

(raw_json: unknown, environment: ActionEventEnvironment): ActionEvent<string, "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute", "initial" | ... 3 more ... | "failed"> import {parse_action_event} from '@fuzdev/fuz_app/actions/action_event.js';

raw_json

type unknown

environment

returns

ActionEvent<string, "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute", "initial" | "parsed" | "handling" | "handled" | "failed">

parse_allowed_origins
#

http/origin.ts view source

(env_value: string | undefined): RegExp[] import {parse_allowed_origins} from '@fuzdev/fuz_app/http/origin.js';

Parses FUZ_ALLOWED_ORIGINS env var into regex matchers for request source verification. Origin allowlisting for locally-running services — not the CSRF layer (that's SameSite: strict on session cookies).

Accepts comma-separated patterns with limited wildcards:

  • Exact origins: https://api.fuz.dev
  • Wildcard subdomains: https://*.fuz.dev (matches exactly one subdomain level)
  • Multiple wildcards: https://*.staging.*.fuz.dev (for deep subdomains)
  • Wildcard ports: http://localhost:* (matches any port or no port)
  • IPv6 addresses: http://[::1]:3000, https://[2001:db8::1]
  • Combined: https://*.fuz.dev:*

Examples:

  • http://localhost:3000,https://prod.fuz.dev
  • https://*.api.fuz.dev,http://127.0.0.1:*
  • http://[::1]:*,https://*.*.corp.fuz.dev:*

env_value

type string | undefined

returns

RegExp[]

throws

  • Error - if any individual pattern is invalid (missing protocol, partial wildcards, etc.)

parse_command_args
#

cli/args.ts view source

<T extends Record<string, unknown>>(remaining: ParsedArgs, schema: ZodType<T, unknown, $ZodTypeInternals<T, unknown>>): ParseResult<T> import {parse_command_args} from '@fuzdev/fuz_app/cli/args.js';

Parse command-specific args against a Zod schema.

Validates remaining args (after global flag extraction) with alias expansion and returns a typed result or a prettified error string.

remaining

remaining args after global flag extraction

type ParsedArgs

schema

Zod schema for the command

type ZodType<T, unknown, $ZodTypeInternals<T, unknown>>

returns

ParseResult<T>

parse result with typed data or error message

generics

parse_command_args<T extends Record<string, unknown>>
T
constraint Record<string, unknown>

parse_db_name
#

dev/setup.ts view source

(url: string): string | null import {parse_db_name} from '@fuzdev/fuz_app/dev/setup.js';

Extract the database name from a PostgreSQL URL.

url

type string

returns

string | null

the database name, or null if the URL is invalid or has no path

parse_dotenv
#

env/dotenv.ts view source

(content: string): Record<string, string> import {parse_dotenv} from '@fuzdev/fuz_app/env/dotenv.js';

Parse a dotenv-format string into a record.

Values wrapped in "..." have \\\, \"", \n → newline, and \r → carriage-return decoded (symmetric with the writer in update_env_variable). Values wrapped in '...' are taken literally — no escape processing. Unquoted values are unchanged.

Inline comments are stripped after a closing quote (e.g. KEY="v" # cv) and after whitespace on unquoted values (e.g. KEY=v # cv). Unquoted values keep # literal when no whitespace precedes it so URL fragments like KEY=https://x.com#frag round-trip unchanged.

A leading export on a line is ignored, so a shell-sourceable .env (export KEY=value) parses identically to a plain KEY=value.

Trailing whitespace on unquoted values is lost (the raw value is trimmed); wrap the value in "..." or '...' to preserve surrounding spacing.

content

dotenv file content

type string

returns

Record<string, string>

parsed key-value pairs

parse_file_fact_url
#

db/file_fact_url.ts view source

(url: string): { url: string & $brand<"FileFactUrl">; shard: string; rest: string; } | null import {parse_file_fact_url} from '@fuzdev/fuz_app/db/file_fact_url.js';

Validate a string against the canonical shape. Returns the branded URL plus its parsed parts, or null on shape mismatch — callers decide whether that's a 404 (read), a skip (GC), or a hard reject (write).

url

type string

returns

{ url: string & $brand<"FileFactUrl">; shard: string; rest: string; } | null

parse_proxy_entry
#

http/proxy.ts view source

(entry: string): ParsedProxy import {parse_proxy_entry} from '@fuzdev/fuz_app/http/proxy.js';

Parse a trusted proxy entry string into a structured form.

Accepts plain IPs ('127.0.0.1', '::1') and CIDR notation ('10.0.0.0/8', 'fe80::/10'). Plain IPs are normalized (lowercase, IPv4-mapped IPv6 stripped) and validated. CIDR prefixes are validated against address family bounds.

entry

IP address or CIDR notation

type string

returns

ParsedProxy

throws

  • Error - on invalid IP, invalid CIDR network, or NaN/negative/over-range prefix

parse_response_error
#

ui/ui_fetch.ts view source

(response: Response, fallback?: string | undefined): Promise<string> import {parse_response_error} from '@fuzdev/fuz_app/ui/ui_fetch.js';

Safely extract an error message from a non-ok response.

Handles responses with non-JSON bodies (e.g. HTML 404 pages) that would throw on response.json().

response

type Response

fallback?

message when no .error field is found

type string | undefined
optional

returns

Promise<string>

parse_session
#

auth/session_cookie.ts view source

<TIdentity>(signed_value: string | undefined, keyring: Keyring, options: SessionOptions<TIdentity>, now_seconds?: number | undefined): Promise<ParsedSession<...> | null | undefined> import {parse_session} from '@fuzdev/fuz_app/auth/session_cookie.js';

Parse a signed session cookie value.

The signed value format is ${encode(identity)}:${expires_at}. Tries all keys in order to support key rotation. The result's should_refresh_expiration flag fires when the cookie is within options.refresh_threshold_seconds of expires_at.

signed_value

the raw cookie value (signed)

type string | undefined

keyring

key ring for verification

type Keyring

options

session configuration with decode logic

type SessionOptions<TIdentity>

now_seconds?

current time in seconds (for testing)

type number | undefined
optional

returns

Promise<ParsedSession<TIdentity> | null | undefined>

ParsedSession if valid, null if invalid/expired, undefined if empty/missing

generics

parse_session<TIdentity>
TIdentity

ParsedProxy
#

http/proxy.ts view source

ParsedProxy import type {ParsedProxy} from '@fuzdev/fuz_app/http/proxy.js';

A parsed proxy entry — either an exact IP or a CIDR range.

ParsedSession
#

auth/session_cookie.ts view source

ParsedSession<TIdentity> import type {ParsedSession} from '@fuzdev/fuz_app/auth/session_cookie.js';

Result of parsing a signed session cookie.

generics

ParsedSession<TIdentity>
TIdentity

identity

The decoded identity.

type TIdentity

should_refresh_signature

True if verified with a non-primary key (needs re-signing).

type boolean

should_refresh_expiration

True if the embedded expires_at is within options.refresh_threshold_seconds of now. Signals that the cookie is valid but should be re-signed to extend its lifetime — mirrors query_session_touch's DB-side extension so the cookie and server session don't drift. Always false when the threshold is 0.

type boolean

key_index

Index of the key that verified the signature.

type number

ParseResult
#

cli/args.ts view source

ParseResult<T> import type {ParseResult} from '@fuzdev/fuz_app/cli/args.js';

Discriminated union result for CLI argument parsing.

generics

ParseResult<T>
T

PARTICIPATION_ROLE
#

testing/cross_backend/test_cell_gated_create_authorize.ts view source

"participant" import {PARTICIPATION_ROLE} from '@fuzdev/fuz_app/testing/cross_backend/test_cell_gated_create_authorize.js';

The app-role a space policy references as min_role (besides admin) — the participant role both reference spines register. Mirrors the Rust policy's "participant" literal.

Password
#

auth/password.ts view source

ZodString import type {Password} from '@fuzdev/fuz_app/auth/password.js';

Password for account creation or password change — enforces current length policy. Also usable for client-side UX validation.

PASSWORD_LENGTH_MAX
#

auth/password.ts view source

300 import {PASSWORD_LENGTH_MAX} from '@fuzdev/fuz_app/auth/password.js';

Maximum password length. Caps hashing cost to prevent DoS via oversized passwords.

PASSWORD_LENGTH_MIN
#

auth/password.ts view source

12 import {PASSWORD_LENGTH_MIN} from '@fuzdev/fuz_app/auth/password.js';

Minimum password length (OWASP recommendation).

PasswordChangeInput
#

auth/account_route_schema.ts view source

ZodObject<{ current_password: ZodString; new_password: ZodString; }, $strict> import type {PasswordChangeInput} from '@fuzdev/fuz_app/auth/account_route_schema.js';

Input for POST /password. current_password is minimally validated; new_password enforces the full policy.

PasswordChangeOutput
#

auth/account_route_schema.ts view source

ZodObject<{ ok: ZodLiteral<true>; sessions_revoked: ZodNumber; tokens_revoked: ZodNumber; }, $strict> import type {PasswordChangeOutput} from '@fuzdev/fuz_app/auth/account_route_schema.js';

Output for POST /password. Counts are returned so the UI can summarize the revoke-all cascade.

PasswordHashDeps
#

auth/password.ts view source

PasswordHashDeps import type {PasswordHashDeps} from '@fuzdev/fuz_app/auth/password.js';

Injectable password hashing dependencies.

Groups all three password operations for injection in route factories and other callers. Use Pick<PasswordHashDeps, ...> when only a subset is needed:

examples

// Login handler only needs verification password: Pick<PasswordHashDeps, 'verify_password' | 'verify_dummy'>; // Bootstrap only needs hashing password: Pick<PasswordHashDeps, 'hash_password'>;

hash_password

type (password: string) => Promise<string>

verify_password

type (password: string, password_hash: string) => Promise<boolean>

verify_dummy

type (password: string) => Promise<boolean>

PasswordProvided
#

auth/password.ts view source

ZodString import type {PasswordProvided} from '@fuzdev/fuz_app/auth/password.js';

Password submitted for login or verification — minimal validation for forward-compatibility if length requirements change.

PayloadTooLargeError
#

db/fact_store_errors.ts view source

import {PayloadTooLargeError} from '@fuzdev/fuz_app/db/fact_store_errors.js';

The streamed upload exceeded the byte cap. Thrown by put_stream when its mid-stream counter passes max_bytes — the backstop for a chunked or mis-declared Content-Length that the cheap header pre-check can't catch. A consumer route maps this to 413.

inheritance

extends: Error

bytes_read

Bytes read before the cap tripped (may exceed max_bytes by one chunk).

type number

readonly

max_bytes

type number

readonly

constructor

type new (bytes_read: number, max_bytes: number): PayloadTooLargeError

bytes_read

type number

max_bytes

type number

PayloadTooLargeError
#

http/error_schemas.ts view source

ZodObject<{ error: ZodLiteral<"payload_too_large">; }, $loose> import type {PayloadTooLargeError} from '@fuzdev/fuz_app/http/error_schemas.js';

Payload too large error — returned when the request body exceeds the size limit.

peer_ping_action
#

actions/peer_ping.ts view source

RpcAction import {peer_ping_action} from '@fuzdev/fuz_app/actions/peer_ping.js';

Protocol-action tuple — spread into the server's actions array (via protocol_actions from actions/protocol.ts) so the dispatcher resolves the peer/ping handler on both the WS endpoint and (for the no-transport refusal) the HTTP RPC endpoint. A plain RpcAction literal (not rpc_action(...)) so this module stays free of the runtime action_rpc.ts import — the same frontend-safety discipline heartbeat_action / cancel_action follow; the handler's input/output are already pinned by peer_ping_handler's signature. Usable directly in the spine's RpcAction[] full mount and the Action[] protocol bundle alike.

peer_ping_action_spec
#

actions/peer_ping.ts view source

{ method: string; kind: "request_response"; initiator: "both"; auth: { account: "none"; actor: "none"; }; side_effects: false; input: ZodDefault<ZodObject<{ nonce: ZodOptional<ZodNumber>; timeout_ms: ZodOptional<...>; }, $strict>>; output: ZodObject<...>; async: true; description: string; } import {peer_ping_action_spec} from '@fuzdev/fuz_app/actions/peer_ping.js';

ActionSpec for the shared peer/ping. initiator: 'both' (the client→server invocation drives a server→client request); auth: public (liveness is non-sensitive; the upgrade authenticated the socket); side_effects: false (no state change — the handler only round-trips a ping).

peer_ping_handler
#

actions/peer_ping.ts view source

(input: { nonce?: number | undefined; timeout_ms?: number | undefined; }, ctx: ActionContext): Promise<{ nonce: number; protocol_version: number; }> import {peer_ping_handler} from '@fuzdev/fuz_app/actions/peer_ping.js';

Handler — initiates a peer/ping request back to the originating client, awaits the reply, validates it against PingResponse, and returns it.

input

type { nonce?: number | undefined; timeout_ms?: number | undefined; }

ctx

returns

Promise<{ nonce: number; protocol_version: number; }>

throws

  • ThrownJsonrpcError - with `data.reason` of `peer_no_transport` (HTTP),

PEER_PING_METHOD
#

peer_ping_responder
#

actions/peer_ping.ts view source

(params: unknown): { nonce: number; protocol_version: number; } import {peer_ping_responder} from '@fuzdev/fuz_app/actions/peer_ping.js';

Reusable responder for an inbound *server→client* peer/ping request — the mirror of peer_ping_handler, run by the client (frontend). Validates the request params and echoes a PingResponse carrying the issued nonce. Pure + transport-agnostic (the production twin of the cross-process test transport's echo_responder); FrontendWebsocketTransport wires it so a real frontend answers the server's liveness probe with zero consumer plumbing. Falls back to nonce: 0 on a malformed request — the server then surfaces a nonce mismatch, the correct outcome for a bad probe.

params

the inbound request's params (PingRequestParams shape)

type unknown

returns

{ nonce: number; protocol_version: number; }

the PingResponse echo to send back as the reply's result

PEER_PROTOCOL_VERSION
#

actions/peer_ping.ts view source

1 import {PEER_PROTOCOL_VERSION} from '@fuzdev/fuz_app/actions/peer_ping.js';

Wire-protocol version reported in peer/ping replies (PingResponse.protocol_version). Twin of the Rust spine's value — bump in lockstep with a breaking peer-protocol change. The far side validates the reply's *shape*, not this value, so it's informational telemetry today, but it's the hook a future protocol negotiation reads.

PeerPingWsTestOptions
#

testing/cross_backend/peer_ping_ws.ts view source

PeerPingWsTestOptions import type {PeerPingWsTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/peer_ping_ws.js';

Configuration for .

setup_test

Per-test fixture producer (default_cross_process_setup(handle, ...)).

type SetupTest

readonly

capabilities

Backend capability flags; every case gates on capabilities.peer_request.

type BackendCapabilities

readonly

base_url

Base URL the backend is reachable at (e.g. http://localhost:1177).

type string

readonly

ws_path

WebSocket endpoint path on the backend (e.g. /api/ws).

type string

readonly

PeerRequestError
#

actions/peer_request.ts view source

PeerRequestError import type {PeerRequestError} from '@fuzdev/fuz_app/actions/peer_request.js';

Why a server→client peer request did not yield a client success reply.

  • timeout — the client did not answer within the deadline.
  • connection_gone — the socket closed (or was never registered) before a reply.
  • too_many_in_flight — the per-connection in-flight cap was hit.
  • client_error — the client answered with a JSON-RPC error; error is forwarded verbatim so the initiating handler can surface the client's own code / message / data.

PeerRequestOptions
#

actions/peer_request.ts view source

PeerRequestOptions import type {PeerRequestOptions} from '@fuzdev/fuz_app/actions/peer_request.js';

Per-call options for a server→client peer request.

timeout_ms?

Deadline (ms) before the request resolves timeout. Defaults to the registry's default_timeout_ms (DEFAULT_PEER_REQUEST_TIMEOUT). Untrusted remote-supplied values should be clamped shorten-only (never lengthen the server's hold on a pooled connection) — see actions/peer_ping.ts.

type number

PeerRequestOutcome
#

PendingOfferSummaryJson
#

auth/account_schema.ts view source

ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; role: ZodString; scope_kind: ZodNullable<ZodString>; scope_id: ZodNullable<$ZodBranded<ZodUUID, "Uuid", "out">>; from_actor_id: $ZodBranded<...>; from_username: ZodString; created_at: ZodString; expires_at: ZodString; }, $strict> import type {PendingOfferSummaryJson} from '@fuzdev/fuz_app/auth/account_schema.js';

Zod schema for a pending role_grant offer surfaced in admin account listings.

Deliberately narrower than RoleGrantOfferJson: omits message and decline_reason so cross-admin visibility of the listing does not expose grantor-authored text that the audit log also withholds. Full offer payloads remain available through the offer-specific RPC surface and the audit log when admins need them.

from_username is resolved server-side so multi-admin deployments can see at a glance whose pending offer is blocking a "+ {role}" button; the resolution runs inside the listing query's parallel batch.

PendingPeerRequests
#

actions/peer_request.ts view source

import {PendingPeerRequests} from '@fuzdev/fuz_app/actions/peer_request.js';

Correlation registry for in-flight server→client requests, nested by connection then by the server-issued request id.

The per-connection nesting makes the in-flight count and the close-time drain O(1) and is the isolation boundary — a reply on connection B can never settle a request issued on connection A (it lands in a different inner map). An inner map is removed as soon as it empties, so an idle connection holds no entry. The registry never sends on the socket; the caller does the send between register and the first await, and routes inbound replies back via resolve.

constructor

type new (options?: PendingPeerRequestsOptions | undefined): PendingPeerRequests

options?

type PendingPeerRequestsOptions | undefined
optional

register

Register a pending request for connection_id with a deadline, returning its server-issued s{n} id and the outcome promise (settled by a reply, the deadline, or drain). Returns null when the connection is at its in-flight cap — the caller maps that to too_many_in_flight. The promise executor runs synchronously, so the entry is registered before this returns.

type (connection_id: string & $brand<"Uuid">, timeout_ms?: number | undefined): { id: string | number; outcome: Promise<PeerRequestOutcome>; } | null

connection_id

type string & $brand<"Uuid">

timeout_ms?

type number | undefined
optional
returns { id: string | number; outcome: Promise<PeerRequestOutcome>; } | null

the allocated id + outcome promise, or null at the cap

resolve

Settle the pending request matching an inbound reply. A success response resolves {ok: true, value: result}; an error response resolves {ok: false, error: {kind: 'client_error', error}} (forwarded verbatim).

Returns false when no entry matches — an unsolicited, cross-connection, or already-settled reply — so the caller drops it.

type (connection_id: string & $brand<"Uuid">, response: { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { ...; }; }): boolean

connection_id

type string & $brand<"Uuid">

response

type { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<...>); message: string; data?: unknown; }; }
returns boolean

whether a pending request was settled

settle

Force-settle one pending request with an explicit outcome (the send-failure path uses this for connection_gone). Clears the timer, drops the entry, resolves the promise. Idempotent — a no-op if the entry is already gone.

type (connection_id: string & $brand<"Uuid">, id: string | number, outcome: PeerRequestOutcome): void

connection_id

type string & $brand<"Uuid">

id

type string | number

outcome

returns void

drain

Settle every pending request on a closing connection as connection_gone. O(1) — drops the connection's inner map in one hop.

type (connection_id: string & $brand<"Uuid">): void

connection_id

type string & $brand<"Uuid">
returns void

size

In-flight request count — for connection_id when given, else across all connections. Telemetry / tests.

type (connection_id?: (string & $brand<"Uuid">) | undefined): number

connection_id?

type (string & $brand<"Uuid">) | undefined
optional
returns number

PendingPeerRequestsOptions
#

perform_action
#

actions/perform_action.ts view source

(input: PerformActionInput, deps: PerformActionDeps): Promise<PerformActionResult> import {perform_action} from '@fuzdev/fuz_app/actions/perform_action.js';

The shared dispatch core. Pure data — no Hono context, no socket. Each transport calls into this with pre-parsed inputs and binds the result to its wire shape.

Phase order: 401 → 400 → 403 → handler. On the test-preset path the dispatcher skips the live authorization phase and uses the supplied pre-baked context for post-authorization checks; pre-validation 401 still fires when the harness omits account_id.

input

deps

returns

Promise<PerformActionResult>

perform_action_result_to_envelope
#

actions/perform_action.ts view source

(id: string | number, result: PerformActionResult): { jsonrpc: string; id: string | number; } & ({ result: unknown; } | { error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<...>); message: string; data?: unknown; }; }) import {perform_action_result_to_envelope} from '@fuzdev/fuz_app/actions/perform_action.js';

Build a JSON-RPC response envelope from a PerformActionResult for transports that wire over the JSON-RPC 2.0 message shape (HTTP RPC + WS).

id

type string | number

result

returns

{ jsonrpc: string; id: string | number; } & ({ result: unknown; } | { error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">); message: string; data?: unknown; }; })

PerformActionDeps
#

actions/perform_action.ts view source

PerformActionDeps import type {PerformActionDeps} from '@fuzdev/fuz_app/actions/perform_action.js';

Per-deps inputs to perform_action. Each transport supplies its own pool-level Db and rate limiters; the dispatcher wraps in a transaction iff spec.side_effects is true.

Pool-resilient fire-and-forget effects (audit writes) run through AppDeps.audit.emit from the action factory's closure — the dispatcher never sees the audit emitter. The bound emitter owns the pool.

db

Pool-level DB. The dispatcher wraps in db.transaction for side_effects: true actions.

type Db

pending_effects

Eager fire-and-forget pool-write queue, flushed by the transport's try/finally via flush_pending_effects.

type Array<Promise<void>>

post_commit_effects

Deferred post-commit thunks pushed via emit_after_commit, flushed by the transport's try/finally after the handler returns.

type Array<() => void | Promise<void>>

log

Logger threaded into ActionContext.log.

type Logger

action_ip_rate_limiter

Per-IP limiter (shared across transports). null disables.

type RateLimiter | null

action_account_rate_limiter

Per-account limiter (shared across transports). null disables.

type RateLimiter | null

PerformActionInput
#

actions/perform_action.ts view source

PerformActionInput import type {PerformActionInput} from '@fuzdev/fuz_app/actions/perform_action.js';

Per-call inputs to perform_action. Each transport assembles this from its wire envelope + connection identity.

action

The resolved spec + handler (transport does method lookup).

type RpcAction

raw_params

Raw params from the wire envelope (post-JsonrpcRequest.parse, pre-spec.input.safeParse).

type unknown

request_id

JSON-RPC request id — echoed onto the response.

type JsonrpcRequestId

account_id

Authenticated account id, or null for anonymous.

type string | null

credential_type

Credential type the request arrived on, or null for anonymous.

type CredentialType | null

client_ip

Resolved client IP ('unknown' if upstream couldn't resolve).

type string

signal

Per-request abort signal. HTTP: c.req.raw.signal. WS: AbortSignal.any([socket, request]).

type AbortSignal

notify

Send a request-scoped notification. HTTP: DEV-warn-and-drop. WS: socket-scoped.

type (method: string, params: unknown) => void

connection_id?

Stable per-socket id on WS; undefined on HTTP.

type Uuid

request_client?

Initiate a server→client request on the originating WS socket and await the typed reply (ActionPeer). Present only on WebSocket dispatch; undefined on HTTP RPC (no return socket). Handlers must handle its absence — e.g. peer/ping surfaces peer_no_transport.

type RequestClient

preset?

Test-harness escape hatch. When set, the live authorization phase is skipped and request_context is used directly for post-authorization checks + handler dispatch. Production callers leave this undefined.

type { request_context: RequestContext | null }

PerformActionResult
#

actions/perform_action.ts view source

PerformActionResult import type {PerformActionResult} from '@fuzdev/fuz_app/actions/perform_action.js';

Discriminated result of perform_action. Each transport binds this to its wire shape: HTTP RPC folds the error into a JSON-RPC envelope and returns via c.json; WS sends the response over the socket.

PermissionError
#

http/error_schemas.ts view source

ZodObject<{ error: ZodLiteral<"insufficient_permissions">; required_roles: ZodReadonly<ZodArray<ZodString>>; }, $loose> import type {PermissionError} from '@fuzdev/fuz_app/http/error_schemas.js';

Permission error — returned by require_role() and the dispatcher's post-authorization role gate when the actor's role_grants don't include any of the route's auth.roles.

required_roles carries the full disjunction the route declared (auth.roles from the new flat-record shape). Single-role specs surface as a one-element array; multi-role disjunctions show every admittable role so clients can render targeted copy ("requires admin or steward").

PgFactStore
#

db/fact_store.ts view source

import {PgFactStore} from '@fuzdev/fuz_app/db/fact_store.js';

PG-backed FactStore. Delegates to db/fact_queries.ts for I/O and adds the lifecycle layer described in the module doc.

inheritance

implements: FactStore

constructor

type new (options: PgFactStoreDeps): PgFactStore

options

put

Store fully-buffered bytes, routing by size: <= embedded_threshold into the PG bytes column; larger into the disk CAS (when disk_root + fs are configured) at <facts_dir>/<shard>/<rest> with a file: URL. Oversize without a disk root throws so the caller routes it through put_ref explicitly. Idempotent — ON CONFLICT DO NOTHING + content-addressed disk filenames make a re-write a no-op.

type (bytes: Uint8Array<ArrayBufferLike>, options?: FactPutOptions | undefined): Promise<string & $brand<"FactHash">>

bytes

type Uint8Array<ArrayBufferLike>

options?

type FactPutOptions | undefined
optional
returns Promise<string & $brand<"FactHash">>

put_stream

Stream bytes into the store with bounded memory, returning the finalized digests + size. Delegates the byte path to stream_fact_to_disk (hash BLAKE3 + SHA-256 in one pass, buffer to the embedded threshold, spill to the disk CAS), then inserts the fact row by placement — embedded bytes go to the PG bytes column, disk-spilled bytes record the file: external_url. The cap is enforced mid-stream (PayloadTooLargeError); a disk-full mid- stream throws StorageFullError.

Refs: explicit options.refs are recorded; JSON auto-extraction is NOT attempted (it would need a buffered re-read, defeating the bounded-memory contract) — streamed uploads are opaque blobs.

Requires fs (and, for the over-threshold spill, disk_root) to be configured. The streaming twin of put; mirrors the Rust FactStore::put_stream.

type (stream: ReadableStream<Uint8Array<ArrayBufferLike>>, max_bytes: number, options?: FactPutOptions | undefined): Promise<...>

stream

type ReadableStream<Uint8Array<ArrayBufferLike>>

max_bytes

type number

options?

type FactPutOptions | undefined
optional
returns Promise<PutStreamOutcome>

put_ref

Stream-hash external content and record (hash, external_url, size). Throws when the streamed byte count disagrees with the caller's declared size — a size mismatch usually means the upload was truncated or the URL points at the wrong content.

type (url: string, size: number, options?: FactPutOptions | undefined): Promise<string & $brand<"FactHash">>

url

type string

size

type number

options?

type FactPutOptions | undefined
optional
returns Promise<string & $brand<"FactHash">>

get

Retrieve bytes. Embedded reads return PG bytes directly; external reads fetch + verify and return null (with a warning log) when the bytes don't match the stored hash.

type (hash: string & $brand<"FactHash">): Promise<Uint8Array<ArrayBufferLike> | null>

hash

type string & $brand<"FactHash">
returns Promise<Uint8Array<ArrayBufferLike> | null>

has

type (hash: string & $brand<"FactHash">): Promise<boolean>

hash

type string & $brand<"FactHash">
returns Promise<boolean>

get_meta

type (hash: string & $brand<"FactHash">): Promise<FactMeta | null>

hash

type string & $brand<"FactHash">
returns Promise<FactMeta | null>

get_refs

type (hash: string & $brand<"FactHash">): Promise<(string & $brand<"FactHash">)[]>

hash

type string & $brand<"FactHash">
returns Promise<(string & $brand<"FactHash">)[]>

delete

Drop a fact row. fact_ref rows referencing this hash as a source cascade via the FK; fact_ref targeting this hash do not — they remain as dangling pointers, consistent with the federation model where target_hash is intentionally not a FK.

Idempotent: deleting an absent fact returns null. The store does NOT verify the fact is unreferenced — that policy lives one layer up (the orphan-fact admin surface in the consumer; a future GC walker).

External-URL unlink is the caller's responsibility — the store doesn't know how to resolve file: / s3: / etc. URLs to a deletable handle. Caller iterates the returned external_url (when non-null) and dispatches to the appropriate cleanup routine. Mirrors the read-side FactExternalFetcher split.

type (hash: string & $brand<"FactHash">): Promise<{ size: number; external_url: string | null; } | null>

hash

type string & $brand<"FactHash">
returns Promise<{ size: number; external_url: string | null; } | null>

{size, external_url} for the deleted row, or null if no row matched the hash.

PgFactStoreDeps
#

db/fact_store.ts view source

PgFactStoreDeps import type {PgFactStoreDeps} from '@fuzdev/fuz_app/db/fact_store.js';

Construction-time deps for PgFactStore.

embedded_threshold (bytes) is the inline-vs-external cutoff: payloads at or under it store embedded in the fact row, larger ones route to the disk CAS. Defaults to FACT_EMBEDDED_THRESHOLD_DEFAULT (1 MiB). Consumers tune it per workload — e.g. a much lower bound (~16 KiB) keeps only small JSON inline and routes image originals + thumbnails to disk.

disk_root is the facts directory backing the <shard>/<rest> disk CAS; fs supplies the filesystem capabilities (a RuntimeDeps satisfies it). When both are set, oversize put + put_stream write to disk and the default fetcher reads from it. When unset, oversize put/put_stream spill throws and reads fall back to the globalThis.fetch-backed default fetcher (or an injected stub). log is optional — the only call site is the verify-mismatch warning path.

deps

type QueryDeps

embedded_threshold?

type number

disk_root?

type string

fs?

type FactDiskStorageDeps

fetcher?

type FactExternalFetcher

log?

type Logger

pick_auth_headers
#

testing/integration_helpers.ts view source

(spec: RouteSpec, keeper: KeeperHeaderProvider, authed_account: TestAccount, admin_account: TestAccount): Record<...> import {pick_auth_headers} from '@fuzdev/fuz_app/testing/integration_helpers.js';

Pick request headers matching a route spec's auth requirement.

Maps RouteAuth onto a test account's credentials:

  • none — origin headers only
  • authenticated — the authed account's session cookie
  • role: admin — the admin account's session cookie
  • role: <other> — the keeper provider's session
  • keeper — the keeper provider's daemon token

spec

keeper

authed_account

admin_account

returns

Record<string, string>

PingActionInput
#

actions/peer_ping.ts view source

ZodDefault<ZodObject<{ nonce: ZodOptional<ZodNumber>; timeout_ms: ZodOptional<ZodNumber>; }, $strict>> import type {PingActionInput} from '@fuzdev/fuz_app/actions/peer_ping.js';

Input to the client→server peer/ping invocation. Both fields optional (mirrors the Rust PingActionInput): nonce defaults to a server-issued value, timeout_ms to DEFAULT_PEER_REQUEST_TIMEOUT (clamped shorten-only).

PingRequestParams
#

actions/peer_ping.ts view source

ZodObject<{ nonce: ZodNumber; }, $strict> import type {PingRequestParams} from '@fuzdev/fuz_app/actions/peer_ping.js';

Params of the server→client peer/ping request frame. Twin of the Rust PingRequest.

PingResponse
#

actions/peer_ping.ts view source

ZodObject<{ nonce: ZodNumber; protocol_version: ZodNumber; }, $strict> import type {PingResponse} from '@fuzdev/fuz_app/actions/peer_ping.js';

The client's reply shape — also the action's output (the handler returns the validated echo). Twin of the Rust PingResponse.

Popover
#

ui/popover.svelte.ts view source

import {Popover} from '@fuzdev/fuz_app/ui/popover.svelte.js';

Class that manages state and provides actions for popovers.

visible

Whether the popover is currently visible.

type boolean

$state.raw

position

Position of the popover relative to its trigger.

type Position

$state.raw

align

Alignment along the position edge.

type Alignment

$state.raw

offset

Distance from the position.

type string

$state.raw

disable_outside_click

Whether to disable closing when clicking outside.

type boolean

$state.raw

popover_class

Custom class for the popover.

type string

$state.raw

constructor

type new (params?: PopoverParameters | undefined): Popover

params?

type PopoverParameters | undefined
optional

update

Updates the popover configuration. Swaps popover_class on the live content element when supplied and re-syncs the outside-click handler if disable_outside_click changed.

type (params: PopoverParameters): void

params

returns void

show

Shows the popover, registering the outside-click handler and updating aria-expanded on the trigger. No-ops when already visible.

type (): void

returns void

hide

Hides the popover, removing the outside-click handler. No-ops when already hidden.

type (): void

returns void

toggle

Toggles the popover visibility, or sets it to the given value.

type (visible?: boolean): void

visible

desired visibility (defaults to the inverse of current)

type boolean
default !this.visible
returns void

container

Attachment for the container element.

type Attachment<HTMLElement>

trigger

Attachment factory for the trigger element that shows/hides the popover.

type (params?: PopoverTriggerParameters | undefined) => Attachment<HTMLElement>

content

Attachment factory for the popover content element.

type (params?: PopoverContentParameters | undefined) => Attachment<HTMLElement>

PopoverButton
#

ui/PopoverButton.svelte view source

accepts children

import PopoverButton from '@fuzdev/fuz_app/ui/PopoverButton.svelte';

position?

optional default 'bottom'

align?

optional default 'center'

disable_outside_click?

type boolean
optional default false

popover_class?

type string
optional

popover_attrs?

type HTMLAttributes<HTMLDivElement>
optional

popover_content

type Snippet<[popover: Popover]>
snippet parameters
popover Popover

popover_container_attrs?

type HTMLAttributes<HTMLDivElement>
optional

button?

type Snippet<[popover: Popover]>
optional
snippet parameters
popover Popover

children?

type Snippet<[popover: Popover]>
optional
snippet parameters
popover Popover

intersects

OmitStrict<SvelteHTMLElements['button'], 'children'>

PopoverContentParameters
#

PopoverParameters
#

ui/popover.svelte.ts view source

PopoverParameters import type {PopoverParameters} from '@fuzdev/fuz_app/ui/popover.svelte.js';

Parameters for configuring the popover.

position?

Position of the popover relative to its trigger.

type Position

align?

Alignment along the position edge.

type Alignment

offset?

Distance from the position.

type string

disable_outside_click?

Whether to disable closing when clicking outside.

type boolean

popover_class?

Custom class for the popover content.

type string

onshow?

Optional callback when popover is shown.

type () => void

onhide?

Optional callback when popover is hidden.

type () => void

PopoverTriggerParameters
#

ui/popover.svelte.ts view source

PopoverTriggerParameters import type {PopoverTriggerParameters} from '@fuzdev/fuz_app/ui/popover.svelte.js';

Parameters for the popover trigger action.

inheritance

content?

Content to render in the popover (as a snippet).

type Snippet

Position
#

ui/position_helpers.ts view source

Position import type {Position} from '@fuzdev/fuz_app/ui/position_helpers.js';

Extended position options including overlay and center.

prefix_route_specs
#

http/route_spec.ts view source

(prefix: string, specs: RouteSpec[]): RouteSpec[] import {prefix_route_specs} from '@fuzdev/fuz_app/http/route_spec.js';

Prepend a prefix to all route spec paths.

prefix

the path prefix (e.g. /api/account)

type string

specs

type RouteSpec[]

returns

RouteSpec[]

a new array — the input specs are not mutated

PreparedWebsocket
#

testing/cross_backend/testing_server_core.ts view source

PreparedWebsocket import type {PreparedWebsocket} from '@fuzdev/fuz_app/testing/cross_backend/testing_server_core.js';

Result of an adapter's WS preparation step.

upgrade_websocket is the Hono UpgradeWebSocket closure the caller's WS mount uses to register the endpoint. attach_to_server runs after serve() returns a — Node uses it for injectWebSocket(server); Deno leaves it undefined.

upgrade_websocket

type UpgradeWebSocket

attach_to_server?

type (handle: ServeHandle) => void

PrimaryKeyInfo
#

http/db_routes.ts view source

PrimaryKeyInfo import type {PrimaryKeyInfo} from '@fuzdev/fuz_app/http/db_routes.js';

Primary key constraint info.

column_name

type string

process_session_cookie
#

ProcessDeps
#

runtime/deps.ts view source

ProcessDeps import type {ProcessDeps} from '@fuzdev/fuz_app/runtime/deps.js';

Process lifecycle.

exit

Exit the process with a code.

type (code: number) => never

ProcessSessionResult
#

auth/session_cookie.ts view source

ProcessSessionResult<TIdentity> import type {ProcessSessionResult} from '@fuzdev/fuz_app/auth/session_cookie.js';

Result of processing a session cookie.

generics

ProcessSessionResult<TIdentity>
TIdentity

valid

Whether the session is valid.

type boolean

action

Action the adapter should take.

type 'none' | 'clear' | 'refresh'

new_signed_value?

New signed value when action is 'refresh'.

type string

identity?

The decoded identity if the cookie was valid.

type TIdentity

PROTOCOL_ACTION_METHODS
#

actions/action_codegen.ts view source

readonly ["heartbeat", "cancel", "peer/ping"] import {PROTOCOL_ACTION_METHODS} from '@fuzdev/fuz_app/actions/action_codegen.js';

Method names of fuz_app's protocol actions — heartbeat (auth-aware client liveness probe) and cancel (request-scoped abort signal). Consumers spread this list when filtering backend request_response methods so the dispatcher-owned protocol actions don't show up in BackendRequestResponseMethod / handler maps. Pairs with protocol_actions / protocol_action_specs from actions/protocol.ts (the runtime bundles).

protocol_action_specs
#

actions/protocol.ts view source

readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } ... import {protocol_action_specs} from '@fuzdev/fuz_app/actions/protocol.js';

Canonical protocol specs for ActionRegistry construction on the frontend. Spread before consumer-owned specs so dispatcher-owned methods are present in the lookup map even though codegen excludes them from the generated action_specs array:

new ActionRegistry([...protocol_action_specs, ...action_specs])

Derived from protocol_actions so a future protocol action lands in one place — the two arrays cannot drift.

protocol_actions
#

actions/protocol.ts view source

readonly Action<{ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { .... import {protocol_actions} from '@fuzdev/fuz_app/actions/protocol.js';

Canonical protocol {spec, handler} tuples for the server's register_action_ws actions array. Spread before consumer-owned actions so disconnect detection and per-request cancel work uniformly:

register_action_ws({actions: [...protocol_actions, ...consumer_actions], ...})

ProtocolActionMethod
#

actions/action_codegen.ts view source

"heartbeat" | "cancel" | "peer/ping" import type {ProtocolActionMethod} from '@fuzdev/fuz_app/actions/action_codegen.js';

Methods that ship from fuz_app, kept out of consumer-owned method enums + handler maps.

provide_admin_rpc_contexts
#

ui/admin_rpc_adapters.ts view source

(adapters: AdminRpcAdapters, options?: ProvideAdminRpcContextsOptions | undefined): void import {provide_admin_rpc_contexts} from '@fuzdev/fuz_app/ui/admin_rpc_adapters.js';

Wire all four admin RPC contexts in a single call.

Call once at the admin shell layout (e.g. src/routes/admin/+layout.svelte) with adapters built from create_admin_rpc_adapters. Every Admin*.svelte component that reads a context below this point sees the adapters.

Each context accessor reads adapters.{domain} on every invocation, so mutating an adapter field on the same object propagates. Replacing the whole adapter set requires calling provide_admin_rpc_contexts again during init — in practice this is one-shot at layout mount.

Pass options.format_scope to render role_grant/offer scope_id values as human labels across AdminAccounts, AdminRoleGrantHistory, RoleGrantOfferInbox, RoleGrantOfferForm, and RoleGrantOfferHistory. Components that accept a format_scope prop honor the prop first; the context is the fallback.

adapters

options?

type ProvideAdminRpcContextsOptions | undefined
optional

returns

void

ProvideAdminRpcContextsOptions
#

ui/admin_rpc_adapters.ts view source

ProvideAdminRpcContextsOptions import type {ProvideAdminRpcContextsOptions} from '@fuzdev/fuz_app/ui/admin_rpc_adapters.js';

Optional knobs alongside the adapters when wiring admin contexts.

format_scope?

Render {scope_id, role} as a human label across role-grant-display components. Omit (or return null) to fall back to the raw uuid.

type FormatScope

ProxyOptions
#

http/proxy.ts view source

ProxyOptions import type {ProxyOptions} from '@fuzdev/fuz_app/http/proxy.js';

Configuration for trusted proxy resolution.

trusted_proxies

Trusted proxy IPs or CIDR ranges (e.g. '127.0.0.1', '10.0.0.0/8', '::1').

type Array<string>

get_connection_ip

Extract the raw TCP connection IP from the Hono context.

type (c: Context) => string | undefined

log?

Optional logger for proxy resolution diagnostics.

type Logger

query_accept_offer
#

auth/role_grant_offer_queries.ts view source

(deps: QueryDeps, input: AcceptOfferInput): Promise<AcceptOfferResult> import {query_accept_offer} from '@fuzdev/fuz_app/auth/role_grant_offer_queries.js';

Accept an offer atomically: mark accepted, insert the role_grant, stamp resulting_role_grant_id, supersede sibling pending offers for the same (to_account, role, scope), and emit role_grant_offer_accept + role_grant_create + one role_grant_offer_supersede per sibling. Must run inside a transaction — the caller's route spec should declare transaction: true (or wrap explicitly).

Idempotent on race: if a second concurrent call observes the offer already accepted, returns the existing role_grant rather than creating a duplicate or throwing.

Error map:

Sibling supersede is what closes the "accept a pre-revoke sibling offer to bypass a revoke" path: once A is accepted, B/C/... can no longer be accepted even if the resulting role_grant is later revoked.

deps

input

returns

Promise<AcceptOfferResult>

throws

  • RoleGrantOfferNotFoundError - if the offer is missing or belongs to another recipient
  • RoleGrantOfferAlreadyTerminalError - if the offer is declined, retracted, or superseded
  • RoleGrantOfferExpiredError - if the offer is pending but past `expires_at`
  • Error - if the accepting `actor_id` does not belong to `to_account_id`, or invariant assertions fail

query_account_by_email
#

auth/account_queries.ts view source

(deps: QueryDeps, email: string): Promise<Account | undefined> import {query_account_by_email} from '@fuzdev/fuz_app/auth/account_queries.js';

Find an account by email (case-insensitive).

deps

email

type string

returns

Promise<Account | undefined>

query_account_by_id
#

auth/account_queries.ts view source

(deps: QueryDeps, id: string): Promise<Account | undefined> import {query_account_by_id} from '@fuzdev/fuz_app/auth/account_queries.js';

Find an active account by id (deleted_at IS NULL).

This is the auth-resolution workhorse (build_request_context / build_account_context) and the admin-target lookup, so it deliberately excludes soft-deleted accounts: a tombstoned account must not authenticate (delete = soft, purge = hard). Purge, which must operate on soft-deleted rows too, uses query_purge_account directly.

deps

id

type string

returns

Promise<Account | undefined>

query_account_by_username
#

auth/account_queries.ts view source

(deps: QueryDeps, username: string): Promise<Account | undefined> import {query_account_by_username} from '@fuzdev/fuz_app/auth/account_queries.js';

Find an account by username (case-insensitive).

deps

username

type string

returns

Promise<Account | undefined>

query_account_by_username_or_email
#

auth/account_queries.ts view source

(deps: QueryDeps, input: string): Promise<Account | undefined> import {query_account_by_username_or_email} from '@fuzdev/fuz_app/auth/account_queries.js';

Find an account by username or email.

If the input contains @, tries email lookup first then username. Otherwise tries username first then email. This supports a single login field that accepts either format.

Excludes soft-deleted accounts (deleted_at IS NULL) — this is the login lookup, and a tombstoned account must not authenticate. The underlying query_account_by_username / query_account_by_email stay unfiltered because the invite-collision checks need to see soft-deleted accounts (usernames/emails stay reserved after a soft-delete).

deps

query dependencies

input

username or email address

type string

returns

Promise<Account | undefined>

the matching active account, or undefined

query_account_has_active_global_role
#

auth/role_grant_queries.ts view source

(deps: QueryDeps, account_id: string, role: string): Promise<boolean> import {query_account_has_active_global_role} from '@fuzdev/fuz_app/auth/role_grant_queries.js';

Like query_account_has_global_role, but only counts the grant when the account and the granting actor are both active (deleted_at IS NULL). Used by the last-admin branch of the removability guard: a soft-deleted admin can't log in and is excluded from query_count_active_accounts_with_global_role, so the guard must use the same active predicate when testing whether the *target* is an admin — otherwise removing an already-tombstoned admin is falsely blocked as cannot_delete_last_admin. Soft-delete does not revoke role_grant rows (it's reversible), so the actor join filters `deleted_at IS NULL` too: a grant held only by a tombstoned actor must not keep the account reading as an admin (the same tombstone-exclusion the acting-actor resolution applies). The keeper branch deliberately uses the unconditional query_account_has_global_role (a keeper is never removable regardless of tombstone state).

deps

query dependencies

account_id

the account to check

type string

role

the role to check for (e.g. ROLE_ADMIN)

type string

returns

Promise<boolean>

true if the account is active and any of its actors holds an active global role grant

query_account_has_any
#

auth/account_queries.ts view source

(deps: QueryDeps): Promise<boolean> import {query_account_has_any} from '@fuzdev/fuz_app/auth/account_queries.js';

Check if any account exists.

deps

returns

Promise<boolean>

query_account_has_global_role
#

auth/role_grant_queries.ts view source

(deps: QueryDeps, account_id: string, role: string): Promise<boolean> import {query_account_has_global_role} from '@fuzdev/fuz_app/auth/role_grant_queries.js';

Account-grain check: does any actor on account_id hold an active global role_grant for role?

Symmetric with query_role_grant_has_role but keyed on the account instead of a single actor — for surfaces with auth: actor: 'none' that don't load auth.role_grants and can't use the in-memory has_scoped_role predicate. Joins role_grantactor; matches only global role_grants (scope_id IS NULL) since the use case is "is the caller's account broadly admin", not scope-aware.

Deliberately unconditional — no deleted_at filter on the account or the granting actor. This backs the keeper removability guard, where the keeper must read as present *even if* its account or keeper-actor is already tombstoned (deleting it would brick keeper/daemon auth with no in-band recovery). That is the opposite posture from resolve_keeper_account_id, which *resolves* the daemon identity and so excludes a tombstoned keeper actor.

Fast under the existing idx_role_grant_actor index — the inner actor_id IN (...) subquery is index-scan, and the outer EXISTS stops at the first match.

deps

query dependencies

account_id

the account to check

type string

role

the role to check for (e.g. ROLE_ADMIN)

type string

returns

Promise<boolean>

true if any actor on the account has an active global role_grant for role

query_account_soft_delete
#

auth/account_queries.ts view source

(deps: QueryDeps, id: string, deleted_by: string | null): Promise<AccountIdentitySnapshot | undefined> import {query_account_soft_delete} from '@fuzdev/fuz_app/auth/account_queries.js';

Soft-delete an account — the reversible tombstone (delete = soft).

Stamps deleted_at + deleted_by (the initiator's actor) on the active row — paired like role_grant's revoked_at / revoked_by, and deliberately leaving updated_at / updated_by untouched (deletion isn't a content edit). Returns the identity snapshot for the account_delete audit event, or undefined when no active row matched (missing or already soft-deleted). Auth resolution (query_account_by_id) already excludes soft-deleted accounts; the caller revokes sessions/tokens.

deps

id

type string

deleted_by

type string | null

returns

Promise<AccountIdentitySnapshot | undefined>

query_account_undelete
#

auth/account_queries.ts view source

(deps: QueryDeps, id: string): Promise<AccountIdentitySnapshot | undefined> import {query_account_undelete} from '@fuzdev/fuz_app/auth/account_queries.js';

Reactivate a soft-deleted account — clears the deleted_at / deleted_by tombstone (the inverse of query_account_soft_delete).

Operates only on a currently soft-deleted row (deleted_at IS NOT NULL) and returns the identity snapshot for the account_undelete audit event, or undefined when no soft-deleted row matched (missing or already active). Reactivation does not restore the revoked sessions/tokens — the account is live again but its principals re-auth fresh. updated_at / updated_by stay untouched.

deps

id

type string

returns

Promise<AccountIdentitySnapshot | undefined>

query_active_actors_by_account
#

auth/account_queries.ts view source

(deps: QueryDeps, account_id: string): Promise<Actor[]> import {query_active_actors_by_account} from '@fuzdev/fuz_app/auth/account_queries.js';

List active (non-tombstoned) actors on an account, ordered by created_at.

Filters deleted_at IS NULL so a soft-deleted actor can never be resolved as the acting actor (and carry its role_grants into the role gate). Used by resolve_acting_actor to resolve the acting actor for a request: 1 actor picks transparently, multiple require an explicit acting field on the request payload. The admin/snapshot handlers that legitimately need tombstoned rows stay on the unfiltered query_actors_by_account.

deps

account_id

type string

returns

Promise<Actor[]>

query_actor_by_id
#

auth/account_queries.ts view source

(deps: QueryDeps, id: string): Promise<Actor | undefined> import {query_actor_by_id} from '@fuzdev/fuz_app/auth/account_queries.js';

Find an actor by id.

deps

id

type string

returns

Promise<Actor | undefined>

query_actor_search
#

query_actor_soft_delete
#

auth/account_queries.ts view source

(deps: QueryDeps, id: string, deleted_by: string | null): Promise<boolean> import {query_actor_soft_delete} from '@fuzdev/fuz_app/auth/account_queries.js';

Soft-delete one actor (sets deleted_at + deleted_by). Returns true when an active row flipped, false when none matched. Emitted per actor alongside the account-level soft-delete.

deps

id

type string

deleted_by

type string | null

returns

Promise<boolean>

query_actor_undelete
#

auth/account_queries.ts view source

(deps: QueryDeps, id: string): Promise<boolean> import {query_actor_undelete} from '@fuzdev/fuz_app/auth/account_queries.js';

Reactivate one soft-deleted actor (clears deleted_at / deleted_by). Returns true when a soft-deleted row flipped back, false when none matched. Emitted per actor alongside the account-level reactivation.

deps

id

type string

returns

Promise<boolean>

query_actors_by_account
#

auth/account_queries.ts view source

(deps: QueryDeps, account_id: string): Promise<Actor[]> import {query_actors_by_account} from '@fuzdev/fuz_app/auth/account_queries.js';

List every actor on an account, ordered by created_at, including soft-deleted (tombstoned) rows.

Used by the admin/snapshot handlers (account_delete / account_purge / account_undelete) that must enumerate every actor that ever existed on the account to snapshot names into per-actor audit events. For the acting-actor resolution path — which must never resolve a tombstoned actor — use query_active_actors_by_account. For lookups by id, use query_actor_by_id instead.

deps

account_id

type string

returns

Promise<Actor[]>

query_actors_by_ids
#

auth/actor_lookup_queries.ts view source

(deps: QueryDeps, ids: readonly (string & $brand<"Uuid">)[]): Promise<ActorLookupRow[]> import {query_actors_by_ids} from '@fuzdev/fuz_app/auth/actor_lookup_queries.js';

Resolve a batch of actor ids to (id, username, display_name). Empty input fast-paths to []. Hard-deleted actors (or account-cascade- orphaned rows) drop out of the result silently.

deps

ids

type readonly (string & $brand<"Uuid">)[]

returns

Promise<ActorLookupRow[]>

query_admin_account_list
#

auth/account_queries.ts view source

(deps: QueryDeps, options?: AdminAccountListOptions | undefined): Promise<{ account: { id: string & $brand<"Uuid">; username: string; ... 5 more ...; deleted_at: string | null; }; actor: { ...; } | null; role_grants: { ...; }[]; pending_offers: { ...; }[]; }[]> import {query_admin_account_list} from '@fuzdev/fuz_app/auth/account_queries.js';

List accounts with their actors, active role_grants, and pending inbound role_grant offers for admin display.

Pages the accounts query (one round-trip), then fans out three parallel lookups scoped to the page's account_ids (one round-trip). The role_grants and offers queries use a subquery on actor.account_id so the page bound pushes through to the DB without round-tripping actor.ids back to the application. Pending offers surface the "offer pending — awaiting acceptance" UX; message is intentionally excluded (cross-admin visibility of grantor notes would expand beyond what the audit log discloses).

deps

query dependencies

options?

optional {limit, offset}. Default limit is ADMIN_ACCOUNT_LIST_DEFAULT_LIMIT; pass limit: null to disable.

type AdminAccountListOptions | undefined
optional

returns

Promise<{ account: { id: string & $brand<"Uuid">; username: string; email: string | null; email_verified: boolean; created_at: string; updated_at: string; updated_by: (string & $brand<"Uuid">) | null; deleted_at: string | null; }; actor: { ...; } | null; role_grants: { ...; }[]; pending_offers: { ...; }[]; }[]>

admin account entries sorted by creation date (oldest first)

query_api_token_enforce_limit
#

auth/api_token_queries.ts view source

(deps: QueryDeps, account_id: string, max_tokens: number): Promise<number> import {query_api_token_enforce_limit} from '@fuzdev/fuz_app/auth/api_token_queries.js';

Enforce a per-account token limit by evicting the oldest tokens.

Race safety: this function must run inside a transaction alongside the INSERT that created the new token. The caller (the account_token_create RPC handler) runs under the dispatcher's transaction path because the spec declares side_effects: true, making one creator's INSERT + enforce_limit pair atomic. That does not serialize concurrent creators: under Read Committed, two transactions can't see each other's uncommitted token row, so each evicts against a stale count and both can commit above max_tokens. Closing this needs one serialization point per (account_id, credential_kind) before count/evict/insert — see the matching note on query_session_enforce_limit.

deps

query dependencies (must be transaction-scoped)

account_id

the account to enforce the limit for

type string

max_tokens

maximum number of tokens to keep

type number

returns

Promise<number>

the number of tokens evicted

query_api_token_list_for_account
#

auth/api_token_queries.ts view source

(deps: QueryDeps, account_id: string): Promise<Omit<ApiToken, "token_hash">[]> import {query_api_token_list_for_account} from '@fuzdev/fuz_app/auth/api_token_queries.js';

List all tokens for an account (does not include hashes).

Columns are enumerated explicitly to exclude token_hash. Must be updated if the api_token table gains new columns.

deps

account_id

type string

returns

Promise<Omit<ApiToken, "token_hash">[]>

query_app_settings_load
#

auth/app_settings_queries.ts view source

(deps: QueryDeps): Promise<AppSettings> import {query_app_settings_load} from '@fuzdev/fuz_app/auth/app_settings_queries.js';

Load the current app settings.

deps

query dependencies

returns

Promise<AppSettings>

the app settings row

throws

  • Error - if the singleton `app_settings` row is missing (migration drift — should not occur in practice)

query_app_settings_load_with_username
#

auth/app_settings_queries.ts view source

(deps: QueryDeps): Promise<{ open_signup: boolean; updated_at: string | null; updated_by: (string & $brand<"Uuid">) | null; updated_by_username: string | null; }> import {query_app_settings_load_with_username} from '@fuzdev/fuz_app/auth/app_settings_queries.js';

Load the current app settings with resolved updater username.

deps

query dependencies

returns

Promise<{ open_signup: boolean; updated_at: string | null; updated_by: (string & $brand<"Uuid">) | null; updated_by_username: string | null; }>

the app settings with updated_by_username

throws

  • Error - if the singleton `app_settings` row is missing

query_app_settings_update
#

auth/app_settings_queries.ts view source

(deps: QueryDeps, open_signup: boolean, actor_id: string): Promise<AppSettings> import {query_app_settings_update} from '@fuzdev/fuz_app/auth/app_settings_queries.js';

Update app settings and return the updated row.

deps

query dependencies

open_signup

new value for the open_signup toggle

type boolean

actor_id

the actor making the change

type string

returns

Promise<AppSettings>

the updated app settings row

throws

  • Error - if the singleton `app_settings` row is missing

query_audit_log
#

auth/audit_log_queries.ts view source

<T extends string>(deps: QueryDeps, input: AuditLogInput<T>, config?: AuditLogConfig): Promise<AuditLogEvent> import {query_audit_log} from '@fuzdev/fuz_app/auth/audit_log_queries.js';

Insert an audit log entry.

RETURNING * so callers receive DB-assigned fields (id, seq, created_at). Validates metadata against config.metadata_schemas; unknown event_type and metadata mismatches log + bump their counters but write the row anyway. Consumers extend the recognized set via create_audit_log_config({extra_events}).

In-transaction call site for query helpers that must atomically write the row alongside other mutations (e.g. query_accept_offer). Fire-and-forget call sites should reach for AppDeps.audit.emit instead — that wrapper closes over the pool so audit rows persist when the parent transaction rolls back.

deps

query dependencies

input

the audit event to record

type AuditLogInput<T>

config

audit-log config. Defaults to builtin_audit_log_config.

default builtin_audit_log_config

returns

Promise<AuditLogEvent>

the inserted audit log row

generics

query_audit_log<T extends string>
T
constraint string

mutates

  • drift — counters - bumps `audit_unknown_event_type_failures` and/or `audit_metadata_validation_failures` on mismatch

query_audit_log_cleanup_before
#

auth/audit_log_queries.ts view source

(deps: QueryDeps, before: Date): Promise<number> import {query_audit_log_cleanup_before} from '@fuzdev/fuz_app/auth/audit_log_queries.js';

Delete audit log entries older than the given date.

deps

query dependencies

before

delete entries created before this date

type Date

returns

Promise<number>

the number of entries deleted

query_audit_log_list
#

auth/audit_log_queries.ts view source

(deps: QueryDeps, options?: AuditLogListOptions | undefined): Promise<AuditLogEvent[]> import {query_audit_log_list} from '@fuzdev/fuz_app/auth/audit_log_queries.js';

List audit log entries, newest first.

deps

query dependencies

options?

filters and pagination

type AuditLogListOptions | undefined
optional

returns

Promise<AuditLogEvent[]>

matching audit log entries

query_audit_log_list_by_cell
#

db/cell_audit_queries.ts view source

(deps: QueryDeps, cell_id: string & $brand<"Uuid">, options: CellAuditListOptions): Promise<AuditLogEvent[]> import {query_audit_log_list_by_cell} from '@fuzdev/fuz_app/db/cell_audit_queries.js';

Fetch audit rows mentioning cell_id on any cell-domain metadata key. Ordered newest-first by seq for cursor pagination through before.

deps

cell_id

type string & $brand<"Uuid">

options

returns

Promise<AuditLogEvent[]>

query_audit_log_list_role_grant_history
#

auth/audit_log_queries.ts view source

(deps: QueryDeps, limit?: number, offset?: number): Promise<{ id: string & $brand<"Uuid">; seq: number; event_type: string; outcome: "success" | "failure"; actor_id: (string & $brand<...>) | null; ... 7 more ...; target_username: string | null; }[]> import {query_audit_log_list_role_grant_history} from '@fuzdev/fuz_app/auth/audit_log_queries.js';

List role_grant grant/revoke events with resolved usernames.

deps

query dependencies

limit

maximum entries to return

type number
default AUDIT_LOG_DEFAULT_LIMIT

offset

number of entries to skip

type number
default 0

returns

Promise<{ id: string & $brand<"Uuid">; seq: number; event_type: string; outcome: "success" | "failure"; actor_id: (string & $brand<"Uuid">) | null; account_id: (string & $brand<...>) | null; ... 6 more ...; target_username: string | null; }[]>

role_grant history events with username and target_username

query_audit_log_list_with_usernames
#

auth/audit_log_queries.ts view source

(deps: QueryDeps, options?: AuditLogListOptions | undefined): Promise<{ id: string & $brand<"Uuid">; seq: number; event_type: string; outcome: "success" | "failure"; ... 8 more ...; target_username: string | null; }[]> import {query_audit_log_list_with_usernames} from '@fuzdev/fuz_app/auth/audit_log_queries.js';

List audit log entries with resolved usernames, newest first.

deps

query dependencies

options?

filters and pagination

type AuditLogListOptions | undefined
optional

returns

Promise<{ id: string & $brand<"Uuid">; seq: number; event_type: string; outcome: "success" | "failure"; actor_id: (string & $brand<"Uuid">) | null; account_id: (string & $brand<...>) | null; ... 6 more ...; target_username: string | null; }[]>

matching audit log entries with username and target_username

query_cell_create
#

db/cell_queries.ts view source

(deps: QueryDeps, input: CellCreateQueryInput): Promise<CellRow> import {query_cell_create} from '@fuzdev/fuz_app/db/cell_queries.js';

Insert a cell row, deriving refs from data.

updated_by is left NULL on insert — same convention as updated_at (NULL until first update). The "last modifier" stamp is meaningful only after a real edit; copying the creator's id into updated_by at create time would make a no-op update by a different actor look authored by the creator.

deps

query deps

input

data, optional visibility, path, and ownership

returns

Promise<CellRow>

the inserted row

query_cell_delete
#

db/cell_queries.ts view source

(deps: QueryDeps, id: string & $brand<"Uuid">, options?: { deleted_by?: (string & $brand<"Uuid">) | null | undefined; } | undefined): Promise<boolean> import {query_cell_delete} from '@fuzdev/fuz_app/db/cell_queries.js';

Soft-delete a cell. Sets deleted_at = NOW(), updated_at = NOW(), and updated_by = options.deleted_by (or NULL). No-op when the row is already deleted.

deps

query deps

id

cell id

type string & $brand<"Uuid">

options?

deleted_by records who triggered the delete

type { deleted_by?: (string & $brand<"Uuid">) | null | undefined; } | undefined
optional

returns

Promise<boolean>

true when a row was soft-deleted, false when no active row matched

query_cell_field_delete
#

db/cell_field_queries.ts view source

(deps: QueryDeps, source_id: string & $brand<"Uuid">, name: string): Promise<CellFieldRow | null> import {query_cell_field_delete} from '@fuzdev/fuz_app/db/cell_field_queries.js';

Delete a field row by primary key. Returns the deleted row so callers can audit the prior target_id without a pre-fetch.

deps

source_id

type string & $brand<"Uuid">

name

type string

returns

Promise<CellFieldRow | null>

the deleted row, or null when no row matched (idempotent delete: a 200 response is correct even when nothing was deleted)

query_cell_field_get
#

db/cell_field_queries.ts view source

(deps: QueryDeps, source_id: string & $brand<"Uuid">, name: string): Promise<CellFieldRow | null> import {query_cell_field_get} from '@fuzdev/fuz_app/db/cell_field_queries.js';

Fetch one field row by primary key.

Does NOT JOIN cell — the caller decides whether to filter by deleted_at. Used by handlers that need the row's current target_id for audit envelopes before issuing the delete.

deps

query deps

source_id

source cell id

type string & $brand<"Uuid">

name

field name

type string

returns

Promise<CellFieldRow | null>

the row or null when not found

query_cell_field_list_for_source
#

db/cell_field_queries.ts view source

(deps: QueryDeps, source_id: string & $brand<"Uuid">, options?: { limit?: number | undefined; name_after?: string | undefined; } | undefined): Promise<CellFieldRow[]> import {query_cell_field_list_for_source} from '@fuzdev/fuz_app/db/cell_field_queries.js';

Forward fields list (source.fields[]).

Filters target by deleted_at IS NULL so relations to tombstoned cells don't surface; the source filter is the caller's responsibility (gated upstream by can_view_cell(source)).

deps

query deps

source_id

source cell id

type string & $brand<"Uuid">

options?

type { limit?: number | undefined; name_after?: string | undefined; } | undefined
optional

returns

Promise<CellFieldRow[]>

matching rows, oldest first by name (lex order)

query_cell_field_list_for_target
#

db/cell_field_queries.ts view source

(deps: QueryDeps, target_id: string & $brand<"Uuid">, options?: { limit?: number | undefined; } | undefined): Promise<CellFieldRow[]> import {query_cell_field_list_for_target} from '@fuzdev/fuz_app/db/cell_field_queries.js';

Reverse fields list (target.upfields[]).

Returns rows whose target_id = $1, joined to cell on source_id so relations from tombstoned sources don't surface. The caller-side authz filter (per-source can_view_cell) runs after the SQL fetch — see the 2-layer authz contract on cell_field_list({target_id}).

Bounded by limit (the wire cell_field_list cap) so a heavily inbound-linked target can't force an unbounded fetch + per-source authz pass on the public, IP-rate-limited reverse endpoint.

deps

query deps

target_id

target cell id

type string & $brand<"Uuid">

options?

limit caps the row count

type { limit?: number | undefined; } | undefined
optional

returns

Promise<CellFieldRow[]>

matching rows, oldest first by source created_at

query_cell_field_set
#

db/cell_field_queries.ts view source

(deps: QueryDeps, input: CellFieldSetQueryInput): Promise<CellFieldRow> import {query_cell_field_set} from '@fuzdev/fuz_app/db/cell_field_queries.js';

Insert or update a field row.

UPSERT on (source_id, name) — re-setting the same name updates target_id and bumps created_at (timestamp reflects last write). Idempotent at the row level: caller can re-issue with the same input without checking existence first.

deps

query deps

input

source, name, target

returns

Promise<CellFieldRow>

the inserted-or-updated row

query_cell_get
#

db/cell_queries.ts view source

(deps: QueryDeps, id: string & $brand<"Uuid">, options?: { include_deleted?: boolean | undefined; } | undefined): Promise<CellRow | null> import {query_cell_get} from '@fuzdev/fuz_app/db/cell_queries.js';

Fetch a cell by id. Excludes soft-deleted rows by default.

deps

query deps

id

cell id

type string & $brand<"Uuid">

options?

include_deleted: true returns tombstones

type { include_deleted?: boolean | undefined; } | undefined
optional

returns

Promise<CellRow | null>

the row or null when not found (or soft-deleted and not requested)

query_cell_get_by_path
#

db/cell_queries.ts view source

(deps: QueryDeps, path: string): Promise<CellRow | null> import {query_cell_get_by_path} from '@fuzdev/fuz_app/db/cell_queries.js';

Fetch a cell by path. Excludes soft-deleted rows; the global partial unique index on path WHERE deleted_at IS NULL guarantees at most one result.

deps

query deps

path

the named lookup alias (e.g. /map/main)

type string

returns

Promise<CellRow | null>

the row or null when not found

query_cell_grant_create
#

db/cell_grant_queries.ts view source

(deps: QueryDeps, input: CellGrantCreateQueryInput): Promise<CellGrantRow> import {query_cell_grant_create} from '@fuzdev/fuz_app/db/cell_grant_queries.js';

Insert a grant, or update the existing row's level + granted_by when one already exists for the same (cell_id, principal) pair.

Idempotent re-share: caller doesn't need to check existence first. The UPSERT path runs even when the existing row's level matches — handlers reading the row's prior state for audit ("create vs. update") must do so before this call.

deps

query deps

input

cell, level, principal, grantor

returns

Promise<CellGrantRow>

the inserted-or-updated row

query_cell_grant_delete
#

db/cell_grant_queries.ts view source

(deps: QueryDeps, grant_id: string & $brand<"Uuid">): Promise<CellGrantRow | null> import {query_cell_grant_delete} from '@fuzdev/fuz_app/db/cell_grant_queries.js';

Delete a grant by id, returning the deleted row.

Returning the row lets the caller audit the principal + level after the delete and (for self-revoke) recompute still_admitted against the remaining grants on the cell without a second fetch.

deps

query deps

grant_id

grant id

type string & $brand<"Uuid">

returns

Promise<CellGrantRow | null>

the deleted row or null when no row matched

query_cell_grant_get
#

db/cell_grant_queries.ts view source

(deps: QueryDeps, grant_id: string & $brand<"Uuid">): Promise<CellGrantRow | null> import {query_cell_grant_get} from '@fuzdev/fuz_app/db/cell_grant_queries.js';

Fetch a grant by id.

deps

query deps

grant_id

grant id

type string & $brand<"Uuid">

returns

Promise<CellGrantRow | null>

the row or null when not found

query_cell_grant_list_for_cell
#

db/cell_grant_queries.ts view source

(deps: QueryDeps, cell_id: string & $brand<"Uuid">): Promise<CellGrantRow[]> import {query_cell_grant_list_for_cell} from '@fuzdev/fuz_app/db/cell_grant_queries.js';

List all grants on a cell, oldest first.

Used by cell_grant_list (RPC) and by handlers that need grants alongside the cell row for the authorize predicate.

deps

query deps

cell_id

cell id

type string & $brand<"Uuid">

returns

Promise<CellGrantRow[]>

matching rows

query_cell_grant_list_for_cells
#

db/cell_grant_queries.ts view source

(deps: QueryDeps, cell_ids: readonly (string & $brand<"Uuid">)[]): Promise<CellGrantRow[]> import {query_cell_grant_list_for_cells} from '@fuzdev/fuz_app/db/cell_grant_queries.js';

List all grants across a set of cells, ordered by cell then creation. Used by the strict relation-read filter to test can_view_cell per target in memory — the caller groups the flat result by cell_id. Returns every grant on each cell (not caller-filtered), because can_view_cell needs the full grant list to decide admission.

deps

query deps

cell_ids

cells to fetch grants for (duplicates are harmless)

type readonly (string & $brand<"Uuid">)[]

returns

Promise<CellGrantRow[]>

matching grant rows (group by cell_id caller-side)

query_cell_grants_for_caller_in_cells
#

db/cell_grant_queries.ts view source

(deps: QueryDeps, cell_ids: (string & $brand<"Uuid">)[], caller_actor_id: (string & $brand<"Uuid">) | null, role_grant_roles: string[], role_grant_scope_ids: ((string & $brand<...>) | null)[]): Promise<...> import {query_cell_grants_for_caller_in_cells} from '@fuzdev/fuz_app/db/cell_grant_queries.js';

Load grants that admit the caller (by actor or role-scoped role_grants) across multiple cells. Used to enrich cell_list responses with context about what granted access. Returns grants for the given cells that match the caller's identity or role_grant set.

deps

cell_ids

cells to fetch grants for

type (string & $brand<"Uuid">)[]

caller_actor_id

actor id of the caller (null for unauth)

type (string & $brand<"Uuid">) | null

role_grant_roles

active role_grant roles (parallel array)

type string[]

role_grant_scope_ids

active role_grant scope ids (parallel array, parallel to roles)

type ((string & $brand<"Uuid">) | null)[]

returns

Promise<CellGrantRow[]>

matching grants (may include grants the caller doesn't match; caller's list handler must filter when returning to the API)

query_cell_item_delete
#

db/cell_item_queries.ts view source

(deps: QueryDeps, parent_id: string & $brand<"Uuid">, position: string): Promise<CellItemRow | null> import {query_cell_item_delete} from '@fuzdev/fuz_app/db/cell_item_queries.js';

Delete one item row by (parent_id, position). Returns the deleted row so callers can audit child_id after the delete without a pre-fetch.

deps

parent_id

type string & $brand<"Uuid">

position

type string

returns

Promise<CellItemRow | null>

the deleted row, or null when nothing matched

query_cell_item_get
#

db/cell_item_queries.ts view source

(deps: QueryDeps, parent_id: string & $brand<"Uuid">, position: string): Promise<CellItemRow | null> import {query_cell_item_get} from '@fuzdev/fuz_app/db/cell_item_queries.js';

Fetch one item row by (parent_id, position). Used by move + delete handlers to confirm the row exists before issuing the mutation.

deps

parent_id

type string & $brand<"Uuid">

position

type string

returns

Promise<CellItemRow | null>

the row or null when not found

query_cell_item_insert
#

db/cell_item_queries.ts view source

(deps: QueryDeps, input: CellItemInsertQueryInput): Promise<CellItemRow> import {query_cell_item_insert} from '@fuzdev/fuz_app/db/cell_item_queries.js';

Insert one item row at the caller-supplied position.

Throws on (parent_id, position) collision (Postgres 23505); handler callers detect via is_pg_unique_violation and surface as cell_item_position_taken. Helper-side jitter (fractional_index) makes the collision rate negligible at realistic UX concurrency, so the throw is the cold-path safety net, not the hot path.

deps

input

returns

Promise<CellItemRow>

query_cell_item_list_for_child
#

db/cell_item_queries.ts view source

(deps: QueryDeps, child_id: string & $brand<"Uuid">, options?: { limit?: number | undefined; } | undefined): Promise<CellItemRow[]> import {query_cell_item_list_for_child} from '@fuzdev/fuz_app/db/cell_item_queries.js';

Reverse items list (child.lists[]).

Returns rows whose child_id = $1, joined to cell on parent_id so items from tombstoned parents don't surface. The caller-side authz filter (per-parent can_view_cell) runs after the SQL fetch — see the 2-layer authz contract on cell_item_list({child_id}).

Bounded by limit (the wire cell_item_list cap) so a heavily inbound-linked child can't force an unbounded fetch + per-parent authz pass on the public, IP-rate-limited reverse endpoint.

deps

child_id

type string & $brand<"Uuid">

options?

type { limit?: number | undefined; } | undefined
optional

returns

Promise<CellItemRow[]>

query_cell_item_list_for_parent
#

db/cell_item_queries.ts view source

(deps: QueryDeps, parent_id: string & $brand<"Uuid">, options?: { limit?: number | undefined; position_after?: string | undefined; } | undefined): Promise<CellItemRow[]> import {query_cell_item_list_for_parent} from '@fuzdev/fuz_app/db/cell_item_queries.js';

Forward items list (parent.items[]), ordered by lex position.

Filters child by deleted_at IS NULL so items pointing at tombstoned cells don't surface; the parent filter is the caller's responsibility (gated upstream by can_view_cell(parent)).

deps

parent_id

type string & $brand<"Uuid">

options?

type { limit?: number | undefined; position_after?: string | undefined; } | undefined
optional

returns

Promise<CellItemRow[]>

query_cell_item_move
#

db/cell_item_queries.ts view source

(deps: QueryDeps, parent_id: string & $brand<"Uuid">, position_old: string, position_new: string): Promise<CellItemRow | null> import {query_cell_item_move} from '@fuzdev/fuz_app/db/cell_item_queries.js';

Move an item row from position_old to position_new (same parent).

Implemented as an UPDATE on the PK; throws 23505 on collision with an existing row at position_new so handlers can surface cell_item_position_taken. The caller-supplied position_new is what fractional-indexing produced for the new slot — collisions are rare but the error path keeps the client truthful.

deps

parent_id

type string & $brand<"Uuid">

position_old

type string

position_new

type string

returns

Promise<CellItemRow | null>

the updated row, or null when the source row was missing (raced with a deleter)

query_cell_list
#

db/cell_queries.ts view source

(deps: QueryDeps, params: CellListParams): Promise<CellRow[]> import {query_cell_list} from '@fuzdev/fuz_app/db/cell_queries.js';

Filterable list query for the generic cell_list RPC.

Takes a flat filter shape (single optional clause per dimension; the cell_list API explicitly does NOT support OR'd alternatives within a dimension — keep it simple) plus an optional viewer-aware visibility predicate.

The visibility predicate mirrors can_view_cell in SQL form:

(viewer_is_admin OR cell.visibility = 'public' OR (viewer_actor_id IS NOT NULL AND created_by = viewer_actor_id) OR (viewer_actor_id IS NOT NULL AND <grant admits caller>))

The grants branch closes parity with can_view_cell: a SQL EXISTS over cell_grant, parameterized by the caller's actor_id and the parallel (role[], scope_id[]) projection of auth.role_grants. The caller's role_grants are materialized once via a caller_role_grants CTE so the role-grant unnest isn't re-scanned per outer row. Empty role_grant arrays are fine: the CTE yields zero rows, the inner EXISTS returns false, and the actor-grant branch still fires for actor-shaped grants.

shared_with_caller_only: true (shared_with: 'me' at the wire layer) takes a different SQL shape: instead of layering an extra conjunction on the cell-driven scan, it semi-joins through cell_grant, letting the planner drive from the (typically tiny) admitted-grant set via idx_cell_grant_actor / idx_cell_grant_role_scope rather than scanning every cell row. For a sharee with N grants over a table of M cells, the cost drops from O(M) to O(N + matched-cells). Owner-is-implicit (a cell's owner never appears as a grant principal) means the grants branch is itself owner-excluding, but the explicit created_by IS DISTINCT FROM caller guards against any future deviation. The shared_with branch does NOT bypass for admin: an admin asking "what's shared with me" wants their own grant footprint, not every cell.

Soft-deleted rows are excluded by default; opt-in via include_deleted.

deps

query deps

params

filter + visibility + ordering + pagination

returns

Promise<CellRow[]>

matching rows, ordered per order_by / order_direction

query_cell_list_by_creator
#

db/cell_queries.ts view source

(deps: QueryDeps, actor_id: string & $brand<"Uuid">, options?: Pick<CellListOptions, "limit" | "offset"> | undefined): Promise<...> import {query_cell_list_by_creator} from '@fuzdev/fuz_app/db/cell_queries.js';

List active cells created by an actor, newest first. Backed by the idx_cell_created_by partial index.

deps

query deps

actor_id

the creator's actor id

type string & $brand<"Uuid">

options?

pagination

type Pick<CellListOptions, "limit" | "offset"> | undefined
optional

returns

Promise<CellRow[]>

matching active rows

query_cell_list_by_kind
#

db/cell_queries.ts view source

(deps: QueryDeps, kind: string, options?: Pick<CellListOptions, "limit" | "offset"> | undefined): Promise<CellRow[]> import {query_cell_list_by_kind} from '@fuzdev/fuz_app/db/cell_queries.js';

List active cells with the given kind, newest first. Uses the idx_cell_kind index (cell.kind = ?).

deps

query deps

kind

cell.kind value to match (e.g. 'collection', 'entry')

type string

options?

pagination

type Pick<CellListOptions, "limit" | "offset"> | undefined
optional

returns

Promise<CellRow[]>

matching active rows

query_cell_load_many
#

db/cell_queries.ts view source

(deps: QueryDeps, ids: readonly (string & $brand<"Uuid">)[]): Promise<CellRow[]> import {query_cell_load_many} from '@fuzdev/fuz_app/db/cell_queries.js';

Bulk-load active cell rows by id, no visibility filter applied. Used by the strict relation-read filter (auth/cell_relation_visibility.ts's filter_visible_target_ids), which runs can_view_cell per row in memory rather than in SQL. Soft-deleted rows are excluded so relations to tombstones never surface.

deps

query deps

ids

cell ids to load (duplicates are harmless)

type readonly (string & $brand<"Uuid">)[]

returns

Promise<CellRow[]>

active rows in arbitrary order (caller indexes by id)

query_cell_set_moderation
#

db/cell_queries.ts view source

(deps: QueryDeps, id: string & $brand<"Uuid">, moderation: string, options: { set_visibility_public: boolean; updated_by?: (string & $brand<"Uuid">) | null | undefined; }): Promise<...> import {query_cell_set_moderation} from '@fuzdev/fuz_app/db/cell_queries.js';

Transition a contribution's moderation marker (the cell_moderate verb's write). On set_visibility_public (an approval) it also flips visibility to 'public' so the approved contribution goes live; a rejection leaves visibility untouched (stays private). updated_at / updated_by are stamped.

moderation is deliberately not writable through query_cell_update (it's absent from CellUpdatePatch) — gating the transition on a dedicated query (with the manage-tier authority check in the handler) is what stops an author self-approving their own pending cell.

deps

query deps

id

cell id

type string & $brand<"Uuid">

moderation

the terminal marker to write ('approved' | 'rejected')

type string

options

set_visibility_public flips visibility on approval; updated_by records the moderator

type { set_visibility_public: boolean; updated_by?: (string & $brand<"Uuid">) | null | undefined; }

returns

Promise<CellRow | null>

the updated row, or null when no active row matched (raced delete)

query_cell_update
#

db/cell_queries.ts view source

(deps: QueryDeps, id: string & $brand<"Uuid">, patch: CellUpdatePatch): Promise<CellRow | null> import {query_cell_update} from '@fuzdev/fuz_app/db/cell_queries.js';

Update a cell. Fields left undefined in the patch keep their existing value; explicit null writes NULL. refs is re-derived from data whenever the patch updates data. updated_at is bumped to NOW() on every successful update.

deps

query deps

id

cell id

type string & $brand<"Uuid">

patch

subset of mutable fields

returns

Promise<CellRow | null>

the updated row, or null when no row matched (already deleted or never existed)

query_count_active_accounts_with_global_role
#

auth/role_grant_queries.ts view source

(deps: QueryDeps, role: string): Promise<number> import {query_count_active_accounts_with_global_role} from '@fuzdev/fuz_app/auth/role_grant_queries.js';

Count active accounts (deleted_at IS NULL) holding an active global role_grant for role, counting only grants on active actors (act.deleted_at IS NULL). Used by the last-admin guard on account_delete / account_purge: soft-delete revokes neither a tombstoned account's nor a tombstoned actor's admin grant, so an unfiltered count would include unusable admins — this joins account *and* filters the actor, so an account whose only admin grant sits on a tombstoned actor no longer counts (otherwise the guard could fail-open and let the last usable admin be deleted).

deps

query dependencies

role

the role to count (e.g. ROLE_ADMIN)

type string

returns

Promise<number>

the number of distinct active accounts with an active global role grant

query_create_account
#

auth/account_queries.ts view source

(deps: QueryDeps, input: CreateAccountInput): Promise<Account> import {query_create_account} from '@fuzdev/fuz_app/auth/account_queries.js';

Create a new account.

deps

query dependencies

input

the account fields

returns

Promise<Account>

the created account

query_create_account_with_actor
#

auth/account_queries.ts view source

(deps: QueryDeps, input: CreateAccountInput): Promise<{ account: Account; actor: Actor; }> import {query_create_account_with_actor} from '@fuzdev/fuz_app/auth/account_queries.js';

Create an account and its actor in a single operation.

For v1, every account gets exactly one actor with the same name as the username.

deps

query dependencies

input

the account fields

returns

Promise<{ account: Account; actor: Actor; }>

the created account and actor

query_create_actor
#

auth/account_queries.ts view source

(deps: QueryDeps, account_id: string, name: string): Promise<Actor> import {query_create_actor} from '@fuzdev/fuz_app/auth/account_queries.js';

Create a new actor for an account.

deps

query dependencies

account_id

the owning account

type string

name

display name (defaults to account username)

type string

returns

Promise<Actor>

the created actor

query_create_api_token
#

auth/api_token_queries.ts view source

(deps: QueryDeps, id: string, account_id: string, name: string, token_hash: string, expires_at?: Date | null | undefined): Promise<ApiToken> import {query_create_api_token} from '@fuzdev/fuz_app/auth/api_token_queries.js';

Store a new API token (the hash, not the raw token).

deps

query dependencies

id

the public token id (e.g. tok_abc123)

type string

account_id

the owning account

type string

name

human-readable name

type string

token_hash

blake3 hash of the raw token

type string

expires_at?

optional expiration

type Date | null | undefined
optional

returns

Promise<ApiToken>

the stored token record

query_create_invite
#

auth/invite_queries.ts view source

(deps: QueryDeps, input: CreateInviteInput): Promise<Invite> import {query_create_invite} from '@fuzdev/fuz_app/auth/invite_queries.js';

Create a new invite.

deps

query dependencies

input

the invite fields

returns

Promise<Invite>

the created invite

query_create_role_grant
#

auth/role_grant_queries.ts view source

(deps: QueryDeps, input: CreateRoleGrantInput): Promise<RoleGrant> import {query_create_role_grant} from '@fuzdev/fuz_app/auth/role_grant_queries.js';

Grant a role_grant to an actor. Idempotent — if an active role_grant already exists for this actor, role, and scope, returns the existing role_grant instead of creating a duplicate.

The ON CONFLICT target and the fallback SELECT both collapse NULL scopes via the same sentinel + index-side 'GLOBAL' token used by the partial unique index (role_grant_actor_role_scope_active_unique). The IS NOT DISTINCT FROM form on the fallback is deliberate — plain = would miss the NULL-scope case where the conflict fired.

scope_kind is paired-null with scope_id per the role_grant_scope_kind_paired CHECK; mismatched pairs raise at the DB layer rather than producing silent rows.

deps

query dependencies

input

the role_grant fields

returns

Promise<RoleGrant>

the created or existing active role_grant

query_create_session
#

auth/session_queries.ts view source

(deps: QueryDeps, token_hash: string, account_id: string, expires_at: Date): Promise<void> import {query_create_session} from '@fuzdev/fuz_app/auth/session_queries.js';

Create a new auth session.

deps

query dependencies

token_hash

blake3 hash of the session token (use hash_session_token)

type string

account_id

the account this session belongs to

type string

expires_at

when the session expires

type Date

returns

Promise<void>

query_db_status
#

db/status.ts view source

(db: Db, namespaces?: MigrationNamespace[] | undefined): Promise<DbStatus> import {query_db_status} from '@fuzdev/fuz_app/db/status.js';

Query database status including connectivity, tables, and migration state.

Designed for CLI db:status commands. Does not modify the database.

db

the database instance

type Db

namespaces?

migration namespaces to check status for

type MigrationNamespace[] | undefined
optional

returns

Promise<DbStatus>

a snapshot of database status; connected: false with error set when the initial connectivity probe fails

throws

  • Error - propagated from the driver if a query fails after the

query_delete_fact
#

db/fact_queries.ts view source

(deps: QueryDeps, hash: string & $brand<"FactHash">): Promise<{ size: number; external_url: string | null; } | null> import {query_delete_fact} from '@fuzdev/fuz_app/db/fact_queries.js';

Drop a fact row. Cascades fact_ref rows via the ON DELETE CASCADE FK on source_hash. Returns the deleted row's (size, external_url) so the caller can unlink the disk file (if any) and tally freed bytes, or null when no row matched (idempotent: deleting an absent fact is not an error).

NOTE: this is a low-level primitive — callers MUST verify the fact is truly orphan (no referencing cell) before calling. The orphan check lives in query_orphan_facts_* below; the lifecycle wrapper in PgFactStore.delete handles the disk-file unlink.

deps

hash

type string & $brand<"FactHash">

returns

Promise<{ size: number; external_url: string | null; } | null>

query_get_fact
#

db/fact_queries.ts view source

(deps: QueryDeps, hash: string & $brand<"FactHash">): Promise<FactRow | null> import {query_get_fact} from '@fuzdev/fuz_app/db/fact_queries.js';

Fetch a fact's full row (including embedded bytes). Use this from FactStore.get; cheaper accessors live below.

deps

hash

type string & $brand<"FactHash">

returns

Promise<FactRow | null>

query_get_fact_meta
#

db/fact_queries.ts view source

(deps: QueryDeps, hash: string & $brand<"FactHash">): Promise<FactMetaRow | null> import {query_get_fact_meta} from '@fuzdev/fuz_app/db/fact_queries.js';

Fetch metadata only — skips the (potentially large) bytes column.

deps

hash

type string & $brand<"FactHash">

returns

Promise<FactMetaRow | null>

query_get_fact_refs
#

db/fact_queries.ts view source

(deps: QueryDeps, source_hash: string & $brand<"FactHash">): Promise<(string & $brand<"FactHash">)[]> import {query_get_fact_refs} from '@fuzdev/fuz_app/db/fact_queries.js';

List declared targets for a source fact. Order is unspecified; callers that need stable ordering should sort.

deps

source_hash

type string & $brand<"FactHash">

returns

Promise<(string & $brand<"FactHash">)[]>

query_has_fact
#

db/fact_queries.ts view source

(deps: QueryDeps, hash: string & $brand<"FactHash">): Promise<boolean> import {query_has_fact} from '@fuzdev/fuz_app/db/fact_queries.js';

Cheap existence check. Backed by the fact PK index.

deps

hash

type string & $brand<"FactHash">

returns

Promise<boolean>

query_invite_claim_unscoped
#

auth/invite_queries.ts view source

(deps: QueryDeps, invite_id: string, account_id: string): Promise<boolean> import {query_invite_claim_unscoped} from '@fuzdev/fuz_app/auth/invite_queries.js';

Claim an invite by setting the claimed_by and claimed_at fields.

The _unscoped suffix is the safety signal — the SQL only checks the row state (claimed_at IS NULL), not whether the claiming account's email or username matches the invite. Callers must scope the lookup upstream via one of the _find_unclaimed_match* siblings (production uses _for_update to make find + claim atomic). Skipping the find step lets a caller claim any unclaimed invite by id.

Mirrors the query_session_revoke_by_hash_unscoped precedent — there is no scoped sibling because the scoping is provided by a separate find query, not by an alternate variant of this query.

deps

query dependencies

invite_id

the invite to claim

type string

account_id

the account claiming the invite

type string

returns

Promise<boolean>

true if the invite was claimed, false if already claimed or not found

query_invite_delete_unclaimed
#

auth/invite_queries.ts view source

(deps: QueryDeps, id: string): Promise<boolean> import {query_invite_delete_unclaimed} from '@fuzdev/fuz_app/auth/invite_queries.js';

Delete an unclaimed invite.

deps

query dependencies

id

the invite id

type string

returns

Promise<boolean>

true if deleted, false if not found or already claimed

query_invite_find_unclaimed_by_email
#

auth/invite_queries.ts view source

(deps: QueryDeps, email: string): Promise<Invite | undefined> import {query_invite_find_unclaimed_by_email} from '@fuzdev/fuz_app/auth/invite_queries.js';

Find an unclaimed invite by email (case-insensitive).

deps

email

type string

returns

Promise<Invite | undefined>

query_invite_find_unclaimed_by_username
#

auth/invite_queries.ts view source

(deps: QueryDeps, username: string): Promise<Invite | undefined> import {query_invite_find_unclaimed_by_username} from '@fuzdev/fuz_app/auth/invite_queries.js';

Find an unclaimed invite by username (case-insensitive).

deps

username

type string

returns

Promise<Invite | undefined>

query_invite_find_unclaimed_match_for_update
#

auth/invite_queries.ts view source

(deps: QueryDeps, email: string | null, username: string): Promise<Invite | undefined> import {query_invite_find_unclaimed_match_for_update} from '@fuzdev/fuz_app/auth/invite_queries.js';

Find an unclaimed invite matching email and/or username, taking a row-level write lock on the matched row.

Three scoping modes:

  • Email-only invite (email set, username NULL) → matches only if signup provides matching email.
  • Username-only invite (username set, email NULL) → matches only if signup provides matching username.
  • Both-field invite (both set) → requires BOTH email and username to match.

Must run inside the same transaction as query_invite_claim_unscoped: FOR UPDATE makes find + claim atomic, so a concurrent signup that matched the same invite blocks on the lock until this transaction commits/rolls back. After commit, the loser's find_for_update returns no row (the winner flipped claimed_at) and falls through to ERROR_NO_MATCHING_INVITE — no race window between find and claim.

deps

query dependencies — deps.db MUST be a transaction

email

email to match (or null if signup provides none)

type string | null

username

username to match

type string

returns

Promise<Invite | undefined>

the matching invite (locked), or undefined

query_invite_list_all
#

auth/invite_queries.ts view source

(deps: QueryDeps): Promise<Invite[]> import {query_invite_list_all} from '@fuzdev/fuz_app/auth/invite_queries.js';

List all invites, newest first.

deps

returns

Promise<Invite[]>

query_invite_list_all_with_usernames
#

auth/invite_queries.ts view source

(deps: QueryDeps): Promise<{ id: string & $brand<"Uuid">; email: string | null; username: string | null; claimed_by: (string & $brand<"Uuid">) | null; ... 4 more ...; claimed_by_username: string | null; }[]> import {query_invite_list_all_with_usernames} from '@fuzdev/fuz_app/auth/invite_queries.js';

List all invites with resolved creator/claimer usernames, newest first.

deps

query dependencies

returns

Promise<{ id: string & $brand<"Uuid">; email: string | null; username: string | null; claimed_by: (string & $brand<"Uuid">) | null; claimed_at: string | null; created_at: string; created_by: (string & $brand<...>) | null; created_by_username: string | null; claimed_by_username: string | null; }[]>

invites with created_by_username and claimed_by_username

query_migration_tracker
#

testing/schema_introspect.ts view source

(db: Db): Promise<{ entries: { namespace: string; name: string; sequence: number; }[]; }> import {query_migration_tracker} from '@fuzdev/fuz_app/testing/schema_introspect.js';

Read every schema_version row into a deterministic MigrationTracker.

The migration-identity twin of query_schema_snapshot: that captures the resulting schema (and excludes this tracker); this captures the tracker rows themselves, so the cross-impl gate can assert the two spines record byte-identical migration identity.

db

type Db

returns

Promise<{ entries: { namespace: string; name: string; sequence: number; }[]; }>

query_orphan_facts_list
#

db/fact_queries.ts view source

(deps: QueryDeps, older_than: Date | null, sample_limit: number): Promise<OrphanFactsListResult> import {query_orphan_facts_list} from '@fuzdev/fuz_app/db/fact_queries.js';

Compute the "orphan facts" set: rows in fact where no active (non-tombstone) cell.refs array contains the hash.

The cell join is deliberately app-coupled — fact lives in the fuz_facts namespace and cell.refs lives in fuz_cell, but the orphan predicate only makes sense in apps that route content through cells. When a non-cell fact consumer ever appears (signed memo outputs? external fact mirrors?) the predicate moves to a generic fact_consumers registry; today the cell layer is the only consumer.

The older_than filter applies to fact.created_at. Pass null to skip the filter (used by the list-summary preview); the delete handler always passes a non-null cutoff (default 0, meaning "any orphan").

deps

query deps

older_than

filter to facts created before this Date (or null to skip)

type Date | null

sample_limit

row cap for the returned sample

type number

returns

Promise<OrphanFactsListResult>

query_orphan_facts_select_for_delete
#

db/fact_queries.ts view source

(deps: QueryDeps, older_than: Date): Promise<{ hash: string & $brand<"FactHash">; size: number; external_url: string | null; }[]> import {query_orphan_facts_select_for_delete} from '@fuzdev/fuz_app/db/fact_queries.js';

Select the orphan-fact hashes for deletion. Returns the rows directly (no row-count limit) — callers iterate to unlink disk files. The older_than cutoff is required (non-null) here: bulk delete should always be operator-scoped to a time window. A "delete all" sweep passes a far-future cutoff, not null.

deps

older_than

type Date

returns

Promise<{ hash: string & $brand<"FactHash">; size: number; external_url: string | null; }[]>

query_public_columns
#

db/schema_ready.ts view source

(db: Db): Promise<Record<string, string[]>> import {query_public_columns} from '@fuzdev/fuz_app/db/schema_ready.js';

Introspect every column in the public schema, grouped by relation. Shared by the runtime /ready check and the fixture-generating helper so both observe the exact same shape. information_schema.columns spans tables and views; for the drift check that's harmless (a never-bootstrapped schema has neither, and extra relations are ignored — see check_schema_drift).

Unlike query_schema_snapshot (which excludes the schema_version migration tracker as framework bookkeeping), this keeps schema_version — a never-migrated DB then correctly fails readiness instead of passing on an empty expectation.

db

type Db

returns

Promise<Record<string, string[]>>

relation name → sorted column names

query_purge_account
#

auth/account_queries.ts view source

(deps: QueryDeps, id: string): Promise<AccountIdentitySnapshot | undefined> import {query_purge_account} from '@fuzdev/fuz_app/auth/account_queries.js';

Hard-purge an account — irreversible cascading removal (purge = hard).

Physically deletes the row, cascading to actors, role_grants, sessions, and tokens. Operates on active OR already-soft-deleted rows. Returns the identity snapshot for the account_purge audit event, or undefined when no row matched. The audit_log identity columns carry no FK, so the purged id survives on historical rows for forensic correlation back to the purge event.

Keeper-gated, loud, irreversible — restrict to the keeper credential and confirm explicitly at the call site. The purge name flags the danger.

deps

id

type string

returns

Promise<AccountIdentitySnapshot | undefined>

query_put_fact
#

db/fact_queries.ts view source

(deps: QueryDeps, input: { hash: string & $brand<"FactHash">; bytes: Uint8Array<ArrayBufferLike> | null; external_url: string | null; content_type: string | null; size: number; }): Promise<...> import {query_put_fact} from '@fuzdev/fuz_app/db/fact_queries.js';

Idempotently insert a fact row.

bytes xor external_url per the fact_storage_present CHECK constraint; the caller is responsible for satisfying it (the queries layer does not second-guess). Returns true when a new row was inserted, false when a row already existed (caller can use this to decide whether to also write fact_ref).

deps

input

type { hash: string & $brand<"FactHash">; bytes: Uint8Array<ArrayBufferLike> | null; external_url: string | null; content_type: string | null; size: number; }

returns

Promise<boolean>

query_put_fact_refs
#

db/fact_queries.ts view source

(deps: QueryDeps, source_hash: string & $brand<"FactHash">, target_hashes: (string & $brand<"FactHash">)[]): Promise<void> import {query_put_fact_refs} from '@fuzdev/fuz_app/db/fact_queries.js';

Idempotently insert declared refs for a fact. No-ops on `(source_hash, target_hash)` collisions and skips the round trip entirely when target_hashes is empty.

deps

source_hash

type string & $brand<"FactHash">

target_hashes

type (string & $brand<"FactHash">)[]

returns

Promise<void>

query_revoke_all_api_tokens_for_account
#

auth/api_token_queries.ts view source

(deps: QueryDeps, account_id: string): Promise<number> import {query_revoke_all_api_tokens_for_account} from '@fuzdev/fuz_app/auth/api_token_queries.js';

Revoke all tokens for an account.

deps

query dependencies

account_id

the account whose tokens to revoke

type string

returns

Promise<number>

the number of tokens revoked

query_revoke_api_token_for_account
#

auth/api_token_queries.ts view source

(deps: QueryDeps, id: string, account_id: string): Promise<boolean> import {query_revoke_api_token_for_account} from '@fuzdev/fuz_app/auth/api_token_queries.js';

Revoke a token only if it belongs to the specified account.

Prevents cross-account token revocation.

deps

query dependencies

id

the public token id

type string

account_id

the account that must own the token

type string

returns

Promise<boolean>

true if a token was revoked, false if not found or wrong account

query_revoke_role_grant
#

auth/role_grant_queries.ts view source

(deps: QueryDeps, role_grant_id: string & $brand<"Uuid">, actor_id: string & $brand<"Uuid">, revoked_by: (string & $brand<"Uuid">) | null, reason?: string | ... 1 more ... | undefined): Promise<...> import {query_revoke_role_grant} from '@fuzdev/fuz_app/auth/role_grant_queries.js';

Revoke a role_grant by id, constrained to a specific actor.

Requires actor_id to prevent cross-account revocation (IDOR guard). Returns null if the role_grant is not found, already revoked, or belongs to a different actor.

Supersedes any pending offers for the revoked role_grant's (to_account, role, scope) in the same transaction. Prevents the "accept a pre-revoke offer to bypass the revoke" path — any stale offer becomes terminal at revoke time. A fresh post-revoke grant requires the grantor to call query_role_grant_offer_create again.

deps

query dependencies

role_grant_id

the role_grant to revoke

type string & $brand<"Uuid">

actor_id

the actor that must own the role_grant

type string & $brand<"Uuid">

revoked_by

the actor who revoked it (for audit trail)

type (string & $brand<"Uuid">) | null

reason?

optional free-form reason, stamped on role_grant.revoked_reason and surfaced to the revokee notification.

type string | null | undefined
optional

returns

Promise<RevokeRoleGrantResult | null>

query_role_grant_find_account_id_for_role
#

auth/role_grant_queries.ts view source

(deps: QueryDeps, role: string): Promise<string | null> import {query_role_grant_find_account_id_for_role} from '@fuzdev/fuz_app/auth/role_grant_queries.js';

Find the account ID of an account that holds an active role_grant for a given role.

Joins role_grant → actor → account, counting only an active account + active actor (deleted_at IS NULL on both). This *resolves* an identity (the daemon-token keeper via resolve_keeper_account_id), so a tombstoned actor must not grant the role — the opposite posture from the removability guard query_account_has_global_role, which stays unconditional. Returns the first match by ascending account.id (deterministic under multiple holders), or null if none.

deps

query dependencies

role

the role to search for

type string

returns

Promise<string | null>

the account ID, or null

query_role_grant_find_active_for_actor
#

auth/role_grant_queries.ts view source

(deps: QueryDeps, actor_id: string): Promise<RoleGrant[]> import {query_role_grant_find_active_for_actor} from '@fuzdev/fuz_app/auth/role_grant_queries.js';

Find all active (non-revoked, non-expired) role_grants for an actor.

deps

actor_id

type string

returns

Promise<RoleGrant[]>

query_role_grant_find_active_role_for_actor
#

auth/role_grant_queries.ts view source

(deps: QueryDeps, role_grant_id: string, actor_id: string): Promise<{ role: string; account_id: string & $brand<"Uuid">; } | null> import {query_role_grant_find_active_role_for_actor} from '@fuzdev/fuz_app/auth/role_grant_queries.js';

Look up the role of an active role_grant (constrained to a specific actor) plus the actor's account_id.

Used by admin routes to inspect the role_grant's role before acting (e.g., enforcing the admin-grant-path gate on revoke). The actor constraint mirrors query_revoke_role_grant so IDOR protection is consistent: a caller can only see role_grants belonging to the target actor.

The JOIN to actor collapses what used to be a second query_actor_by_id round-trip in the revoke handler into one read, which closes the small TOCTOU window where the actor row could be deleted between the IDOR check and the actor lookup. The account_id is needed by the audit envelope's target_account_id field and the SSE/WS socket-close fan-out targeting.

Returns null if the role_grant is not found, already revoked, or belongs to a different actor.

deps

query dependencies

role_grant_id

the role_grant id to look up

type string

actor_id

the actor that must own the role_grant

type string

returns

Promise<{ role: string; account_id: string & $brand<"Uuid">; } | null>

{role, account_id} on a match, or null

query_role_grant_has_role
#

auth/role_grant_queries.ts view source

(deps: QueryDeps, actor_id: string, role: string, scope_id?: string | null | undefined): Promise<boolean> import {query_role_grant_has_role} from '@fuzdev/fuz_app/auth/role_grant_queries.js';

Check if an actor has an active role_grant for a given role.

The scope_id parameter selects between global and scoped checks:

  • Omitted or null — matches a global role_grant (scope_id IS NULL). Pre-scope callers keep their existing semantics.
  • A scope uuid — matches a role_grant bound to that exact scope.

The IS NOT DISTINCT FROM comparison handles the NULL case uniformly.

deps

actor_id

type string

role

type string

scope_id?

type string | null | undefined
optional

returns

Promise<boolean>

query_role_grant_list_for_actor
#

auth/role_grant_queries.ts view source

(deps: QueryDeps, actor_id: string): Promise<RoleGrant[]> import {query_role_grant_list_for_actor} from '@fuzdev/fuz_app/auth/role_grant_queries.js';

List all role_grants for an actor (including revoked/expired).

deps

actor_id

type string

returns

Promise<RoleGrant[]>

query_role_grant_offer_create
#

auth/role_grant_offer_queries.ts view source

(deps: QueryDeps, input: CreateRoleGrantOfferInput): Promise<RoleGrantOffer> import {query_role_grant_offer_create} from '@fuzdev/fuz_app/auth/role_grant_offer_queries.js';

Create a new role_grant offer, or refresh an existing pending offer for the same (to_account_id, role, scope_id, from_actor_id) tuple.

Re-offer semantics: a second call by the same grantor with the same (to_account, role, scope) while pending upserts the existing row, refreshing message and expires_at (and to_actor_id — supplying a different to_actor_id on re-offer narrows the existing row to the named actor; supplying null widens it back to account-grain). A different grantor offering the same (to_account, role, scope) creates a distinct row — multiple pending grantors coexist. After a terminal state, a re-offer is a fresh INSERT.

Self-offer rejection: throws RoleGrantOfferSelfTargetError if the offering actor belongs to the recipient account.

Actor-targeted offers: when to_actor_id is supplied, query_accept_offer rejects any actor other than the named one. Closes the audit hole where offer-shape events would otherwise leave target_actor_id null even when the recipient binding is known at offer time. The actor↔account binding is verified here in one SELECT.

deps

input

returns

Promise<RoleGrantOffer>

throws

  • RoleGrantOfferSelfTargetError - if the offering actor belongs to `to_account_id`
  • RoleGrantOfferActorAccountMismatchError - if `to_actor_id` is set but does not belong to `to_account_id`

query_role_grant_offer_decline
#

auth/role_grant_offer_queries.ts view source

(deps: QueryDeps, offer_id: string, to_account_id: string, reason: string | null): Promise<DeclinedOffer | null> import {query_role_grant_offer_decline} from '@fuzdev/fuz_app/auth/role_grant_offer_queries.js';

Mark an offer declined.

Guarded by to_account_id (IDOR). Returns null if the offer does not exist or belongs to a different account. Throws RoleGrantOfferAlreadyTerminalError if the offer exists for the caller but is already in a terminal state.

Returns the declined offer with the grantor's from_account_id joined in via CTE — the decline audit envelope populates both target_actor_id (the grantor actor) and target_account_id (the grantor account), satisfying the "both populated → same account" invariant the audit-log column comments describe.

deps

offer_id

type string

to_account_id

type string

reason

type string | null

returns

Promise<DeclinedOffer | null>

throws

  • RoleGrantOfferAlreadyTerminalError - if the offer is already accepted, declined, retracted, or superseded

query_role_grant_offer_find_pending
#

auth/role_grant_offer_queries.ts view source

(deps: QueryDeps, offer_id: string): Promise<RoleGrantOffer | null> import {query_role_grant_offer_find_pending} from '@fuzdev/fuz_app/auth/role_grant_offer_queries.js';

Look up a pending offer by id. Returns null if the offer is terminal, expired (server-side filter), or missing.

deps

offer_id

type string

returns

Promise<RoleGrantOffer | null>

query_role_grant_offer_history_for_account
#

auth/role_grant_offer_queries.ts view source

(deps: QueryDeps, account_id: string, limit?: number, offset?: number): Promise<RoleGrantOffer[]> import {query_role_grant_offer_history_for_account} from '@fuzdev/fuz_app/auth/role_grant_offer_queries.js';

List every offer involving an account (either direction), newest first.

Includes terminal offers — used by the grantor-side admin / history view.

deps

account_id

type string

limit

type number
default 100

offset

type number
default 0

returns

Promise<RoleGrantOffer[]>

query_role_grant_offer_list
#

auth/role_grant_offer_queries.ts view source

(deps: QueryDeps, to_account_id: string): Promise<RoleGrantOffer[]> import {query_role_grant_offer_list} from '@fuzdev/fuz_app/auth/role_grant_offer_queries.js';

List pending, non-expired offers for an account, soonest expiry first.

Expired offers are filtered server-side (expires_at > NOW()) so the inbox never surfaces a row that can no longer be accepted. The periodic sweep (query_role_grant_offer_sweep_expired) handles audit tombstoning.

deps

to_account_id

type string

returns

Promise<RoleGrantOffer[]>

query_role_grant_offer_retract
#

auth/role_grant_offer_queries.ts view source

(deps: QueryDeps, offer_id: string, from_actor_id: string): Promise<RoleGrantOffer | null> import {query_role_grant_offer_retract} from '@fuzdev/fuz_app/auth/role_grant_offer_queries.js';

Mark an offer retracted by the grantor.

Guarded by from_actor_id (IDOR). Returns null if the offer does not exist or was issued by a different actor. Throws RoleGrantOfferAlreadyTerminalError if the offer exists for this grantor but is already in a terminal state.

deps

offer_id

type string

from_actor_id

type string

returns

Promise<RoleGrantOffer | null>

throws

  • RoleGrantOfferAlreadyTerminalError - if the offer is already accepted, declined, retracted, or superseded

query_role_grant_offer_sweep_expired
#

auth/role_grant_offer_queries.ts view source

(deps: QueryDeps): Promise<RoleGrantOffer[]> import {query_role_grant_offer_sweep_expired} from '@fuzdev/fuz_app/auth/role_grant_offer_queries.js';

Return pending offers whose expires_at has passed.

Callers fire role_grant_offer_expire audit events for each row. The schema does not tombstone the row, so callers are responsible for their own idempotency (e.g. check whether a role_grant_offer_expire audit event already exists for the offer id).

deps

returns

Promise<RoleGrantOffer[]>

query_role_grant_revoke_for_scope
#

auth/role_grant_queries.ts view source

(deps: QueryDeps, scope_id: string & $brand<"Uuid">, revoked_by: (string & $brand<"Uuid">) | null, reason?: string | null | undefined): Promise<RevokeForScopeResult> import {query_role_grant_revoke_for_scope} from '@fuzdev/fuz_app/auth/role_grant_queries.js';

Revoke every active role_grant bound to a scope and supersede every pending offer at the scope, in one cascade.

Use this from a consumer's parent-scope delete handler (e.g., classroom deletion) — role_grant.scope_id and role_grant_offer.scope_id are polymorphic with no FK constraint by design, so a parent row deletion would otherwise orphan role_grants and offers. The cascade is role-agnostic: anything attached to the destroyed scope is cleaned up.

Both updates run as separate statements inside the caller's transaction (mirrors query_role_grant_revoke_role's shape). The two halves are independent — orphan pending offers can exist at a scope with no active role_grants, so the supersede half always runs even when no role_grant was revoked.

deps

query dependencies

scope_id

the scope whose role_grants and offers to terminate

type string & $brand<"Uuid">

revoked_by

the actor performing the cascade (audit trail)

type (string & $brand<"Uuid">) | null

reason?

optional free-form reason, stamped on role_grant.revoked_reason.

type string | null | undefined
optional

returns

Promise<RevokeForScopeResult>

the revoked role_grants (with account_id for fan-out) and superseded offers (with from_account_id for fan-out)

query_role_grant_revoke_role
#

auth/role_grant_queries.ts view source

(deps: QueryDeps, actor_id: string, role: string, revoked_by: string | null, reason?: string | null | undefined): Promise<RevokeRoleResult> import {query_role_grant_revoke_role} from '@fuzdev/fuz_app/auth/role_grant_queries.js';

Revoke every active role_grant an actor holds for a given role.

With scoped role_grants a single actor+role tuple can hold several active role_grants (one per scope), so this revokes all of them. Pass query_revoke_role_grant(role_grant_id, ...) when a single scoped role_grant is the target.

Also supersedes pending offers for the actor's account across every scope of this role (the actor can no longer hold the role, so any pending offer of the same role is a bypass vector).

deps

query dependencies

actor_id

the actor whose role_grants to revoke

type string

role

the role to revoke

type string

revoked_by

the actor who revoked it (for audit trail)

type string | null

reason?

optional free-form reason, stamped on role_grant.revoked_reason.

type string | null | undefined
optional

returns

Promise<RevokeRoleResult>

the list of revoked role_grants (empty if none were active) and superseded pending offers

query_schema_snapshot
#

testing/schema_introspect.ts view source

(db: Db, options?: QuerySchemaSnapshotOptions): Promise<{ tables: Record<string, { columns: Record<string, { data_type: string; udt_name: string; is_nullable: boolean; column_default: string | null; is_identity: boolean; }>; indexes: { ...; }[]; constraints: { ...; }[]; }>; sequences: Record<...>; enums: Record<...>; }> import {query_schema_snapshot} from '@fuzdev/fuz_app/testing/schema_introspect.js';

Introspect a live database into a deterministic SchemaSnapshot.

Reads information_schema and pg_catalog to capture tables, columns, indexes, constraints, and sequences.

The schema_version migration tracker never appears in the tables field — it's framework bookkeeping created by the migration runner, identical across consumers, and would only add noise.

db

type Db

options

default {}

returns

Promise<{ tables: Record<string, { columns: Record<string, { data_type: string; udt_name: string; is_nullable: boolean; column_default: string | null; is_identity: boolean; }>; indexes: { name: string; definition: string; }[]; constraints: { ...; }[]; }>; sequences: Record<...>; enums: Record<...>; }>

query_session_cleanup_expired
#

auth/session_queries.ts view source

(deps: QueryDeps): Promise<number> import {query_session_cleanup_expired} from '@fuzdev/fuz_app/auth/session_queries.js';

Delete expired sessions.

deps

returns

Promise<number>

the number of sessions cleaned up

query_session_enforce_limit
#

auth/session_queries.ts view source

(deps: QueryDeps, account_id: string, max_sessions: number): Promise<number> import {query_session_enforce_limit} from '@fuzdev/fuz_app/auth/session_queries.js';

Enforce a per-account session limit by evicting the oldest sessions.

Keeps the newest max_sessions sessions and deletes the rest.

Race safety: this function must run inside a transaction alongside the INSERT that created the new session. All callers satisfy this requirement:

  • POST /login uses the default transaction: true (framework-managed transaction wrapping in apply_route_specs)
  • The account_token_create RPC handler runs under the dispatcher's transaction path because its spec declares side_effects: true
  • POST /bootstrap and POST /signup manage their own transactions and pass the transaction-scoped deps to create_session_and_set_cookie

The transaction makes one creator's INSERT + enforce_limit pair atomic, but it does not serialize concurrent creators. Under Read Committed, two transactions can't see each other's uncommitted session row, so each computes its OFFSET eviction against a stale count, each preserves its own row, and both commit above max_sessions. A transaction is necessary here but not sufficient. Closing this needs one serialization point per (account_id, credential_kind) — a locked parent row or a transaction-scoped advisory lock — taken before count/evict/insert, plus a stable tie-breaker alongside created_at so the survivors are deterministic.

deps

query dependencies (must be transaction-scoped)

account_id

the account to enforce the limit for

type string

max_sessions

maximum number of sessions to keep

type number

returns

Promise<number>

the number of sessions evicted

query_session_get_valid
#

auth/session_queries.ts view source

(deps: QueryDeps, token_hash: string): Promise<AuthSession | undefined> import {query_session_get_valid} from '@fuzdev/fuz_app/auth/session_queries.js';

Get a session if it exists, is not expired, and has not been revoked.

deps

query dependencies

token_hash

blake3 hash of the session token

type string

returns

Promise<AuthSession | undefined>

query_session_list_all_active
#

auth/session_queries.ts view source

(deps: QueryDeps, limit?: number): Promise<(AuthSession & { username: string; })[]> import {query_session_list_all_active} from '@fuzdev/fuz_app/auth/session_queries.js';

List all active sessions across all accounts with usernames.

deps

query dependencies

limit

maximum entries to return

type number
default 200

returns

Promise<(AuthSession & { username: string; })[]>

active sessions joined with account usernames, newest activity first

query_session_list_for_account
#

auth/session_queries.ts view source

(deps: QueryDeps, account_id: string, limit?: number): Promise<AuthSession[]> import {query_session_list_for_account} from '@fuzdev/fuz_app/auth/session_queries.js';

List sessions for an account, newest first.

deps

account_id

type string

limit

type number
default 50

returns

Promise<AuthSession[]>

query_session_revoke_all_for_account
#

auth/session_queries.ts view source

(deps: QueryDeps, account_id: string): Promise<number> import {query_session_revoke_all_for_account} from '@fuzdev/fuz_app/auth/session_queries.js';

Revoke all sessions for an account.

deps

account_id

type string

returns

Promise<number>

the number of sessions revoked

query_session_revoke_by_hash_unscoped
#

auth/session_queries.ts view source

(deps: QueryDeps, token_hash: string): Promise<void> import {query_session_revoke_by_hash_unscoped} from '@fuzdev/fuz_app/auth/session_queries.js';

Revoke (delete) a session by its token hash, with no account scoping.

The _unscoped suffix is the safety signal — there is no account_id constraint, so callers must guarantee the hash came from a trusted source (the authenticated session cookie path is the only safe production caller — see auth/account_routes.ts /logout). For user-facing revocation of a specific session by ID, use query_session_revoke_for_account (IDOR-guarded).

deps

token_hash

type string

returns

Promise<void>

query_session_revoke_for_account
#

auth/session_queries.ts view source

(deps: QueryDeps, token_hash: string, account_id: string): Promise<boolean> import {query_session_revoke_for_account} from '@fuzdev/fuz_app/auth/session_queries.js';

Revoke a session only if it belongs to the specified account.

Prevents cross-account session revocation.

deps

query dependencies

token_hash

blake3 hash of the session token

type string

account_id

the account that must own the session

type string

returns

Promise<boolean>

true if a session was revoked, false if not found or wrong account

query_session_touch
#

auth/session_queries.ts view source

(deps: QueryDeps, token_hash: string): Promise<void> import {query_session_touch} from '@fuzdev/fuz_app/auth/session_queries.js';

Update last_seen_at and optionally extend expiry for a session.

Extends if less than AUTH_SESSION_EXTEND_THRESHOLD_MS remaining.

deps

query dependencies

token_hash

blake3 hash of the session token

type string

returns

Promise<void>

query_update_account_password
#

auth/account_queries.ts view source

(deps: QueryDeps, id: string, password_hash: string, updated_by: string | null, expected_hash: string): Promise<boolean> import {query_update_account_password} from '@fuzdev/fuz_app/auth/account_queries.js';

Update the password hash for an account, conditional on the current stored hash matching expected_hash — the verify-write atomic guard.

The condition closes the race where two concurrent password changes both verify against the pre-update hash (loaded by the authorization phase outside the route's transaction) and would otherwise both UPDATE, silently clobbering whichever lands first. With the conditional WHERE, the second UPDATE matches zero rows; the route reads the boolean return and surfaces 401 instead of pretending success.

Pass the same hash the verify ran against — typically ctx.account.password_hash from the request context.

deps

id

type string

password_hash

type string

updated_by

type string | null

expected_hash

type string

returns

Promise<boolean>

true if the row was updated, false if expected_hash no longer matched (concurrent change won — caller should treat as a stale-credential failure).

query_validate_api_token
#

auth/api_token_queries.ts view source

(deps: ApiTokenQueryDeps, raw_token: string, ip: string | undefined, pending_effects: Promise<void>[] | undefined): Promise<ApiToken | undefined> import {query_validate_api_token} from '@fuzdev/fuz_app/auth/api_token_queries.js';

Validate a raw API token and return the token record.

Hashes the token with blake3, looks up the hash, and checks expiration. Updates last_used_at and last_used_ip on success (fire-and-forget — errors logged, never thrown).

deps

query dependencies with logger

raw_token

the raw API token from the Authorization header

type string

ip

the client IP address (for audit)

type string | undefined

pending_effects

optional array to register the usage-tracking effect for later awaiting

type Promise<void>[] | undefined

returns

Promise<ApiToken | undefined>

the token record if valid, or undefined

QueryDeps
#

db/query_deps.ts view source

QueryDeps import type {QueryDeps} from '@fuzdev/fuz_app/db/query_deps.js';

Base dependency for all query functions.

db

type Db

QuerySchemaSnapshotOptions
#

testing/schema_introspect.ts view source

QuerySchemaSnapshotOptions import type {QuerySchemaSnapshotOptions} from '@fuzdev/fuz_app/testing/schema_introspect.js';

Filter options for query_schema_snapshot.

schema?

Schema name to introspect — defaults to 'public'. Single-schema only; cross-schema introspection isn't a current need.

type string

readonly

exclude_tables?

Tables to exclude from the snapshot. The schema_version migration tracker is always excluded — it's framework bookkeeping created by the migration runner, identical across impls, and not part of any consumer's domain schema.

type ReadonlyArray<string>

readonly

rate_limit_exceeded_response
#

rate_limiter.ts view source

(c: Context<any, any, {}>, retry_after: number): Response import {rate_limit_exceeded_response} from '@fuzdev/fuz_app/rate_limiter.js';

Build a 429 rate-limit-exceeded JSON response with Retry-After header.

c

Hono context

type Context<any, any, {}>

retry_after

seconds until the client should retry

type number

returns

Response

a 429 Response

RateLimiter
#

rate_limiter.ts view source

import {RateLimiter} from '@fuzdev/fuz_app/rate_limiter.js';

In-memory sliding window rate limiter.

Stores an array of timestamps per key. On check/record, timestamps outside the window are pruned. retry_after reports seconds until the oldest active timestamp expires.

The backing store is an LruMap when options.max_keys is a positive number (default DEFAULT_RATE_LIMITER_MAX_KEYS) and a plain Map when max_keys is null. The LruMap path bounds memory under key-enumeration attack at the cost of a slight per-op overhead and the LRU trade-off described on RateLimiterOptions.max_keys.

Parameters that accept RateLimiter | null (e.g. ip_rate_limiter, login_account_rate_limiter) silently disable rate limiting when null is passed — no checks are performed and all requests are allowed through.

options

type RateLimiterOptions

readonly

constructor

type new (options: RateLimiterOptions): RateLimiter

options

[Symbol.for('nodejs.util.inspect.custom')]

Custom inspect output that exposes only options and size, never the tracked keys. The key set is sensitive (usernames / IP addresses) and unbounded, so it must not leak into logs via util.inspect / console.log. The #attempts field is already #private, but this keeps the boundary explicit against inspect(…, {showHidden: true}) and future field-visibility changes.

type (): { options: RateLimiterOptions; size: number; }

returns { options: RateLimiterOptions; size: number; }

check

Check whether key is allowed without recording an attempt.

Prunes timestamps that fell outside the window as a side effect (and removes the key entirely when none remain), so the backing map stays bounded even under read-only traffic.

type (key: string, now?: number): RateLimitResult

key

rate limit key (e.g. IP address)

type string

now

current timestamp in ms (defaults to Date.now())

type number
default Date.now()

record

Record a failed attempt for key and return the updated result.

type (key: string, now?: number): RateLimitResult

key

rate limit key (e.g. IP address)

type string

now

current timestamp in ms (defaults to Date.now())

type number
default Date.now()

reset

Clear all attempts for key (e.g. after successful login).

type (key: string): void

key

type string
returns void

cleanup

Remove entries whose timestamps are all outside the window.

type (now?: number): void

now

current timestamp in ms (defaults to Date.now())

type number
default Date.now()
returns void

dispose

Stop the cleanup timer. Safe to call multiple times.

type (): void

returns void

size

Number of tracked keys.

type number

getter

RateLimiterOptions
#

rate_limiter.ts view source

RateLimiterOptions import type {RateLimiterOptions} from '@fuzdev/fuz_app/rate_limiter.js';

Configuration for a rate limiter instance.

max_attempts

Maximum allowed attempts within the window.

type number

window_ms

Sliding window duration in milliseconds.

type number

cleanup_interval_ms

Interval for pruning stale entries (0 disables the timer).

type number

max_keys?

Maximum tracked keys. When exceeded, the least-recently-used key is evicted — bounds memory under key-enumeration attacks. Default: DEFAULT_RATE_LIMITER_MAX_KEYS (100_000). Pass null to disable the cap (falls back to an unbounded Map — only recommended when the key set is known to be closed, e.g. a per-account limiter keyed to a bounded-size account table).

LRU trade-off: every check / record call marks the key as most-recently-used, so keys under active attack stay fresh and won't be evicted. A slow-burn attacker spread across many low-volume keys can, however, drop out of the table and reset their budget — set max_keys high enough to fit the expected legitimate key set and this stays theoretical.

type number | null

RateLimitError
#

http/error_schemas.ts view source

ZodObject<{ error: ZodLiteral<"rate_limit_exceeded">; retry_after: ZodNumber; }, $loose> import type {RateLimitError} from '@fuzdev/fuz_app/http/error_schemas.js';

Rate limit error — returned when a rate limiter rejects the request.

RateLimitingTestOptions
#

testing/rate_limiting.ts view source

RateLimitingTestOptions import type {RateLimitingTestOptions} from '@fuzdev/fuz_app/testing/rate_limiting.js';

Configuration for describe_rate_limiting_tests.

session_options

Session config for cookie-based auth.

type SessionOptions<string>

create_route_specs

Route spec factory — same one used in production.

type (ctx: AppServerContext) => Array<RouteSpec>

app_options?

Optional overrides for AppServerOptions.

type SuiteAppOptions

db_factories?

Database factories to run tests against. Default: pglite only.

type Array<DbFactory>

max_attempts?

Maximum attempts before rate limiting kicks in. Default: 2 (tight limit for fast tests).

type number

rpc_endpoints

RPC endpoint specs — required so the bearer-auth rate limiting test can probe an authenticated method via the account_verify RPC action. Hard-fails via require_rpc_endpoint_path on setup.

Accepts either an array (eager) or a factory (ctx: AppServerContext) => Array<RpcEndpointSpec> — the factory form is required when action handlers must close over the per-test ctx.deps. The factory must return the same endpoint path regardless of ctx — it is invoked once at setup with a stub ctx for path lookup and again per-test by create_app_server for live dispatch.

type RpcEndpointsSuiteOption

RateLimitKey
#

http/error_schemas.ts view source

ZodEnum<{ both: "both"; account: "account"; ip: "ip"; }> import type {RateLimitKey} from '@fuzdev/fuz_app/http/error_schemas.js';

Rate limit key type — declares what a route or RPC action's rate limiter is keyed on.

  • 'ip' — per-IP rate limiting (bootstrap, password change, bearer auth)
  • 'account' — per-account rate limiting. On REST auth routes the key is the submitted identifier (login). On RPC actions (post-auth) the key is the resolved actor id (request_context.actor.id) — separate namespace.
  • 'both' — both keys.

RateLimitResult
#

rate_limiter.ts view source

RateLimitResult import type {RateLimitResult} from '@fuzdev/fuz_app/rate_limiter.js';

Result of a rate limit check or record operation.

allowed

Whether the request is allowed.

type boolean

remaining

Remaining attempts before blocking.

type number

retry_after

Seconds until the oldest active attempt expires (0 if allowed).

type number

read_daemon_info
#

cli/daemon.ts view source

(runtime: Pick<EnvDeps, "env_get"> & Pick<FsReadDeps, "stat" | "read_text_file"> & LogDeps, name: string): Promise<{ version: number; pid: number; port: number; started: string; app_version: string; } | null> import {read_daemon_info} from '@fuzdev/fuz_app/cli/daemon.js';

Read and validate daemon info from the PID file.

runtime

runtime with file read and env capabilities

type Pick<EnvDeps, "env_get"> & Pick<FsReadDeps, "stat" | "read_text_file"> & LogDeps

name

application name

type string

returns

Promise<{ version: number; pid: number; port: number; started: string; app_version: string; } | null>

parsed daemon info, or null if missing or invalid

read_env_var
#

dev/setup.ts view source

(deps: Pick<FsReadDeps, "read_text_file">, env_path: string, name: string): Promise<string | undefined> import {read_env_var} from '@fuzdev/fuz_app/dev/setup.js';

Read a single env var from a dotenv-style file.

Delegates to load_env_file so the value is tokenized exactly like every other dotenv read — surrounding quotes, inline comments, and escapes are handled, and a leading export is tolerated. Returns undefined when the file is absent or the variable is unset; a read error other than the file not existing (permission denied, I/O failure) propagates rather than being masked as "not found".

deps

file read capability

type Pick<FsReadDeps, "read_text_file">

env_path

path to the .env file

type string

name

the variable name to read

type string

returns

Promise<string | undefined>

the value, or undefined if the file or variable doesn't exist

throws

  • Error - if reading fails for any reason other than `ENOENT` / `NotFound`

ReadTextFromOffsetResult
#

runtime/deps.ts view source

ReadTextFromOffsetResult import type {ReadTextFromOffsetResult} from '@fuzdev/fuz_app/runtime/deps.js';

Result of reading text from a byte offset.

content

Decoded text content read from the offset.

type string

bytes_read

Number of bytes actually read.

type number

file_size

Total file size at the time of the read (for truncation detection).

type number

READY_ERROR
#

db/schema_ready.ts view source

{ readonly schema_drift: "schema_drift"; readonly db_unreachable: "db_unreachable"; } import {READY_ERROR} from '@fuzdev/fuz_app/db/schema_ready.js';

Error codes a readiness check returns at 503 (conforms to {error: string}).

ReadyCrossTestOptions
#

testing/cross_backend/ready.ts view source

ReadyCrossTestOptions import type {ReadyCrossTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/ready.js';

Options for the readiness-probe parity suite.

inheritance

ready_path?

Readiness probe path. Default /ready.

type string

readonly

ReadyRouteOptions
#

http/common_routes.ts view source

ReadyRouteOptions import type {ReadyRouteOptions} from '@fuzdev/fuz_app/http/common_routes.js';

Options for the readiness probe route.

expected

The committed expected column map — typically load_expected_schema(url). DI'd because the spine can't resolve a path relative to the consumer's fixture.

type ExpectedSchema

log?

Logger for server-side drift diagnostics (the public body stays minimal).

type Logger

REASON_PEER_CONNECTION_GONE
#

actions/peer_ping.ts view source

"peer_connection_gone" import {REASON_PEER_CONNECTION_GONE} from '@fuzdev/fuz_app/actions/peer_ping.js';

The socket closed before the peer replied.

REASON_PEER_NO_TRANSPORT
#

actions/peer_ping.ts view source

"peer_no_transport" import {REASON_PEER_NO_TRANSPORT} from '@fuzdev/fuz_app/actions/peer_ping.js';

No return socket — the action ran over a transport that can't initiate a server→client request (HTTP RPC).

REASON_PEER_PING_INVALID_REPLY
#

REASON_PEER_PING_NONCE_MISMATCH
#

actions/peer_ping.ts view source

"peer_ping_nonce_mismatch" import {REASON_PEER_PING_NONCE_MISMATCH} from '@fuzdev/fuz_app/actions/peer_ping.js';

The peer replied with a PingResponse whose nonce didn't echo the issued one.

REASON_PEER_TIMEOUT
#

actions/peer_ping.ts view source

"peer_timeout" import {REASON_PEER_TIMEOUT} from '@fuzdev/fuz_app/actions/peer_ping.js';

The peer did not reply within the deadline.

REASON_PEER_TOO_MANY_IN_FLIGHT
#

actions/peer_ping.ts view source

"peer_too_many_in_flight" import {REASON_PEER_TOO_MANY_IN_FLIGHT} from '@fuzdev/fuz_app/actions/peer_ping.js';

The per-connection in-flight server→client request cap was hit.

reconstruct_bootstrapped_handle
#

testing/cross_backend/setup.ts view source

(serialized: SerializableBootstrappedBackendHandle): ReconstructedBootstrappedBackendHandle import {reconstruct_bootstrapped_handle} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

Rebuild a usable handle from the serialized subset. Synthesizes a fresh primed with the keeper's Set-Cookie values so _testing_reset and other keeper-authenticated calls work. The returned shape omits child and teardown — lifecycle stays with globalSetup; tests that try to teardown themselves wouldn't have a serializable reference anyway.

serialized

returns

ReconstructedBootstrappedBackendHandle

ReconstructedBootstrappedBackendHandle
#

testing/cross_backend/setup.ts view source

ReconstructedBootstrappedBackendHandle import type {ReconstructedBootstrappedBackendHandle} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

BootstrappedBackendHandle minus the live child / teardown references that only make sense in the globalSetup process. The cross-process provide/inject path strips them on serialization, and the per-test helpers (mint_account, fire_testing_reset, default_cross_process_setup) never read either field — so the test-worker view of the handle has this shape. Also the return type of reconstruct_bootstrapped_handle.

daemon_token

Deterministic daemon token captured from config.bootstrap.daemon_token_path after the binary booted. default_cross_process_setup builds keeper-daemon-token headers from this for _testing_reset calls.

type string

readonly

keeper_transport

Transport carrying the keeper session cookie + cookie jar.

type (url: string, init: RequestInit): Promise<Response>

readonly

url

type string

init

type RequestInit
returns Promise<Response>

keeper_daemon_transport

Daemon-token transport for the keeper — same cookie jar as keeper_transport but with origin: null so it sends no Origin header. The daemon-token middleware discards the credential in a browser context (any Origin / Referer), so the _testing_* daemon-token calls (_testing_reset / _testing_schema_snapshot / _testing_action_manifest / _testing_mint_session) must route through this Origin-free transport. CSRF/cookie-session calls (invite_create, role_grant_offer_create) stay on keeper_transport, which keeps its default Origin.

type (url: string, init: RequestInit): Promise<Response>

readonly

url

type string

init

type RequestInit
returns Promise<Response>

keeper_account

Keeper account JSON captured from POST /bootstrap.

type { readonly id: string & $brand<"Uuid">; readonly username: string; }

readonly

keeper_actor

Keeper actor JSON captured from POST /bootstrap.

type { readonly id: string & $brand<"Uuid">; }

readonly

keeper_cookies

Raw keeper Set-Cookie values — thread into ws_transport for keeper-authenticated WS upgrades.

type readonly string[]

readonly

config

The config used to spawn this backend. Carried for diagnostic + downstream access.

type BackendConfig

readonly

RecordedClose
#

testing/connection_closer_helpers.ts view source

RecordedClose import type {RecordedClose} from '@fuzdev/fuz_app/testing/connection_closer_helpers.js';

Record of a single ConnectionCloser method invocation. at is the value of a monotonically-increasing sequence counter at the time of the call — pair with create_emit_ordering_audit_factory to record both close + audit emit calls into the same sequence for ordering tests.

method

type 'session' | 'token' | 'account'

id

type string

at

type number

RecordingAuditEmitter
#

testing/audit_drift_guard.ts view source

RecordingAuditEmitter import type {RecordingAuditEmitter} from '@fuzdev/fuz_app/testing/audit_drift_guard.js';

Pair returned by — the AuditEmitter to inject as deps.audit, plus the shared calls array that records every captured emission. Both fields are live — callers read calls after exercising the handler to assert on the audit metadata shape.

emitter

type AuditEmitter

calls

type Array<AuditLogInput>

RecordingCloser
#

refresh_role_grants
#

auth/request_context.ts view source

(ctx: RequestContext, deps: QueryDeps): Promise<RequestContext> import {refresh_role_grants} from '@fuzdev/fuz_app/auth/request_context.js';

Reload active role_grants from the database, returning a new request context.

Useful for long-lived WebSocket connections where role_grants may change (grant or revoke) during the connection lifetime. Call periodically or after receiving a revocation signal.

Returns a new RequestContext with updated role_grants — the original context is not mutated, making concurrent calls safe. Throws when ctx.actor is null; account-grain contexts have no role_grants to refresh.

ctx

the request context to refresh

deps

query dependencies

returns

Promise<RequestContext>

a new RequestContext with fresh role_grants

throws

  • Error - when called on an account-grain context (`actor: null`)

register_action_ws
#

actions/register_action_ws.ts view source

(options: RegisterActionWsOptions): RegisterActionWsResult import {register_action_ws} from '@fuzdev/fuz_app/actions/register_action_ws.js';

Mount a JSON-RPC WebSocket endpoint that dispatches via the shared perform_action core.

Wire behavior:

  • Batch JSON-RPC is rejected (single-message only).
  • Notifications (method + no id) are silently dropped per JSON-RPC spec. Exception: cancel notifications abort the matching pending request's ctx.signal before bubbling out.
  • Per-message dispatch goes through perform_action: pre-validation auth (401) → input validation (400) → authorization phase → post-authorization auth (403) → rate limit (429) → handler (with transaction wrap iff spec.side_effects: true) → DEV output validation.
  • Authorization phase runs per message — role_grant changes during a connection lifetime are picked up on the next message without any in-place refresh. Authentication invalidation closes the socket via create_ws_auth_guard.

options

returns

RegisterActionWsResult

the transport (supplied or freshly created) — retain it to wire create_ws_auth_guard or broadcast on audit events.

register_pg_type_parsers
#

db/db_pg.ts view source

(): Promise<void> import {register_pg_type_parsers} from '@fuzdev/fuz_app/db/db_pg.js';

Register the shared pg type-parser overrides on the module-global pg.types.

Dynamically imports the pg runtime (so pglite-only consumers, who never call this, don't need pg installed) and coerces int8 (BIGINT, OID 20) to a JS number. pg defaults to returning int8 as a string to avoid 2^53 precision loss; our int8 columns today (audit_log.seq, cell_history.id, fact.size) stay well under that bound, and reading as a number keeps the wire shape uniform with PGlite — which returns int8 as a number — so AuditLogEvent.seq and friends validate identically across both drivers.

Both create_db (production) and the test pg factory (testing/db.ts) register through this single site so test and prod read the same shape; a divergence here is exactly the test/prod write-semantics gap the parser exists to close.

CAVEAT: setTypeParser mutates pg.types globally — every pg.Pool in the process inherits the coercion, including pools the consumer constructs against unrelated databases. Any future int8 column that could legitimately exceed 2^53 (byte offsets, counters) will silently round; if one lands, localize via a per-pool types override instead of widening this global.

returns

Promise<void>

register_ws_endpoint
#

actions/register_ws_endpoint.ts view source

(options: RegisterWsEndpointOptions): RegisterActionWsResult import {register_ws_endpoint} from '@fuzdev/fuz_app/actions/register_ws_endpoint.js';

Mount a WebSocket endpoint with the standard upgrade stack (origin check + auth + actor resolution + optional role) and JSON-RPC dispatch.

Returns the BackendWebsocketTransport (supplied or freshly created), same as register_action_ws — retain it to wire create_ws_auth_guard on on_audit_event or to broadcast.

options

returns

RegisterActionWsResult

RegisterActionWsOptions
#

actions/register_action_ws.ts view source

RegisterActionWsOptions import type {RegisterActionWsOptions} from '@fuzdev/fuz_app/actions/register_action_ws.js';

Options for register_action_ws.

path

Mount path (e.g., /api/ws).

type string

app

The Hono app to mount on.

type Hono

upgradeWebSocket

Hono's upgradeWebSocket helper from the runtime adapter.

type UpgradeWebSocket

actions

The actions registered on this endpoint — each carries a spec (drives method lookup, per-action auth, input/output validation) and an optional handler (omit for client-only specs like inbound notifications). Spread protocol_actions from actions/protocol.ts here to complete the disconnect-detection + per-request cancel pairing with the frontend client.

type ReadonlyArray<Action>

db

Pool-level DB. The dispatcher wraps in db.transaction for side_effects: true actions, the same way HTTP RPC does. Per-message authorization phase reads through this pool.

Audit writes and other rollback-resilient fire-and-forget calls run through AppDeps.audit.emit from the action factory's closure — the dispatcher never holds an audit-side pool reference; the bound emitter owns the pool.

type Db

transport?

Existing transport to register connections with. When omitted, a fresh one is created and returned in the result. Pass your own to keep a handle for create_ws_auth_guard and send_to/broadcast.

type BackendWebsocketTransport

heartbeat?

Server-side heartbeat policy. Default-on (receive-silence detection, 60s timeout). false disables the timer entirely — only do this if the upstream stack (TCP keepalive, Cloudflare idle timeout, etc.) already owns disconnect detection. Pass an object to tune the timeout.

type boolean | ServerHeartbeatOptions

artificial_delay?

Optional per-message delay for testing loading states. Ignored when 0.

type number

log?

Optional logger; defaults to [ws] namespace.

type LoggerType

on_socket_open?

Called once per socket, after the transport registers the connection. Awaited before any message is dispatched. Throwing logs an error and closes the socket with an internal_error frame — a failing bootstrap should not leave a partially-initialized socket alive.

type (ctx: SocketOpenContext) => void | Promise<void>

on_socket_close?

Called once per socket on close, *before* the transport removes the connection. Receives connection_id and identity captured at open time, so it is safe to read even when the audit guard has already torn down the transport's internal state. Errors are logged and swallowed.

type (ctx: SocketCloseContext) => void | Promise<void>

action_ip_rate_limiter?

Per-IP rate limiter consulted for actions whose spec declares rate_limit: 'ip' or 'both'. null (or omitted) disables the IP check. Same limiter is shared with the HTTP RPC dispatcher so one budget covers both transports per action. Resolved at upgrade time and reused for every message on the socket.

type RateLimiter | null

action_account_rate_limiter?

Per-account rate limiter consulted for actions whose spec declares rate_limit: 'account' or 'both'. Keyed on request_context.account.id. null (or omitted) disables the account check. Same limiter is shared with the HTTP RPC dispatcher.

type RateLimiter | null

RegisterActionWsResult
#

RegisterWsEndpointOptions
#

actions/register_ws_endpoint.ts view source

RegisterWsEndpointOptions import type {RegisterWsEndpointOptions} from '@fuzdev/fuz_app/actions/register_ws_endpoint.js';

inheritance

allowed_origins

Origin allowlist regexes — typically parsed from the FUZ_ALLOWED_ORIGINS env var via parse_allowed_origins. Passed straight to verify_request_source.

type ReadonlyArray<RegExp>

required_roles?

Roles permitted to upgrade — any-of disjunction (matches the underlying require_role semantics). Omit (or pass []) for any authenticated account (require_auth + actor resolution alone); set to e.g. [ROLE_ADMIN] to gate the endpoint behind a single role or [ROLE_ADMIN, ROLE_KEEPER] to permit either. The per-action auth in each spec still applies at dispatch time — this is a coarse upgrade-time gate.

type ReadonlyArray<RoleName>

RemoteNotificationActionSpec
#

actions/action_spec.ts view source

ZodObject<{ method: ZodString; initiator: ZodEnum<{ frontend: "frontend"; backend: "backend"; both: "both"; }>; input: ZodCustom<ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>, ZodType<...>>; ... 8 more ...; async: ZodDefault<...>; }, $strict> import type {RemoteNotificationActionSpec} from '@fuzdev/fuz_app/actions/action_spec.js';

REQUEST_CONTEXT_KEY
#

auth/request_context.ts view source

"request_context" import {REQUEST_CONTEXT_KEY} from '@fuzdev/fuz_app/auth/request_context.js';

Hono context variable name for the request context.

RequestActorContext
#

auth/request_context.ts view source

RequestActorContext import type {RequestActorContext} from '@fuzdev/fuz_app/auth/request_context.js';

Request context narrowed to a resolved acting actor.

Used by handlers bound through rpc_action against an actor-implying spec (auth.actor === 'required') — the binder's conditional return type tightens ctx.auth to this shape because the dispatcher's authorization phase always resolves an actor before the handler runs. The biconditional actor !== 'none' ⟺ input declares acting?: ActingActor is enforced at registry time.

inheritance

actor

type Actor

RequestClient
#

actions/peer_request.ts view source

RequestClient import type {RequestClient} from '@fuzdev/fuz_app/actions/peer_request.js';

Initiate a JSON-RPC request to the connected client and await its reply — the server→client direction of ActionPeer. Threaded onto ActionContext.request_client for WebSocket handlers (absent on HTTP RPC, where there is no return socket). Returns a PeerRequestOutcome; never throws.

(call)

type (method: string, params: { [x: string]: unknown; } | undefined, options?: PeerRequestOptions | undefined): Promise<PeerRequestOutcome>

method

type string

params

type { [x: string]: unknown; } | undefined

options?

type PeerRequestOptions | undefined
optional
returns Promise<PeerRequestOutcome>

RequestContext
#

auth/request_context.ts view source

RequestContext import type {RequestContext} from '@fuzdev/fuz_app/auth/request_context.js';

The resolved identity context for an authenticated request.

actor is null on account-grain routes (no acting field on input, no role / keeper auth) — those handlers don't trigger actor resolution. role_grants is empty in that case. Role grant checks (has_role, has_scoped_role, has_any_scoped_role) are null-tolerant on RequestContext | null; they additionally treat actor: null as "no role_grants" so callers don't have to narrow.

Multi-actor invariant: when populated, actor.account_id === account.id. build_request_context enforces this; the dispatcher's authorization phase rejects with actor_not_on_account before reaching the handler.

account

type Account

actor

type Actor | null

role_grants

type Array<RoleGrant>

RequestResponseActionSpec
#

actions/action_spec.ts view source

ZodObject<{ method: ZodString; initiator: ZodEnum<{ frontend: "frontend"; backend: "backend"; both: "both"; }>; side_effects: ZodBoolean; input: ZodCustom<ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>, ZodType<...>>; ... 7 more ...; async: ZodDefault<...>; }, $strict> import type {RequestResponseActionSpec} from '@fuzdev/fuz_app/actions/action_spec.js';

RequestTracker
#

actions/request_tracker.svelte.ts view source

import {RequestTracker} from '@fuzdev/fuz_app/actions/request_tracker.svelte.js';

Reactive pending-request store with per-request timeouts. Used by transports that don't delegate request/response correlation to a WebsocketRpcConnection.

pending_requests

type SvelteMap<JsonrpcRequestId, RequestTrackerItem>

readonly

request_timeout_ms

type number

readonly

constructor

type new (request_timeout_ms?: number): RequestTracker

request_timeout_ms

type number
default 120_000

track_request

Track a new request keyed by id.

type (id: string | number): Deferred<{ [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | ... 4 more ... | (number & $brand<...>); message: string; data?: unknown; }; }>

id

type string | number
returns Deferred<{ [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<...>); message: string; data?: unknown; }; }>

deferred resolved on response, or rejected via the timeout

resolve_request

Resolve a pending request with its response.

type (id: string | number, response: { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | ... 4 more ... | (number & $brand<...>); message: string; data?: unknown; }; }): void

id

type string | number

response

type { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<...>); message: string; data?: unknown; }; }
returns void

reject_request

Reject a pending request with error_message.

type (id: string | number, error_message: { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">); message: string; data?: unknown; }; }): void

id

type string | number

error_message

type { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">); message: string; data?: unknown; }; }
returns void

handle_message

Handles an incoming JSON-RPC message. Resolves or rejects the associated request. Ignores notifications and unknown/invalid messages.

type (message: any): void

message

type any
returns void

cancel_request

Cancel a pending request without rejecting its deferred — just cleanup. The caller's promise stays unsettled; pair with an external resolution if needed.

type (id: string | number): void

id

type string | number
returns void

cancel_all_requests

Cancel all pending requests.

type (reason?: string | undefined): void

reason?

optional reason to include in rejection

type string | undefined
optional
returns void

RequestTrackerItem
#

actions/request_tracker.svelte.ts view source

import {RequestTrackerItem} from '@fuzdev/fuz_app/actions/request_tracker.svelte.js';

id

type JsonrpcRequestId

readonly

deferred

type Deferred<JsonrpcResponseOrError>

readonly

created

type Datetime

readonly

status

type AsyncStatus

$state.raw

timeout

type NodeJS.Timeout | undefined

$state.raw

constructor

type new (id: string | number, deferred: Deferred<{ [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | ... 4 more ... | (number & $brand<...>); message: string; data?: unknown; }; }>, created: Datetime, status: AsyncStatus, timeout: Timeout | undefined): RequestTrackerItem

id

type string | number

deferred

type Deferred<{ [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<...>); message: string; data?: unknown; }; }>

created

type Datetime

status

type AsyncStatus

timeout

type Timeout | undefined

require_audit_sse
#

server/app_server.ts view source

(source: { audit_sse: AuditLogSse | null; }): AuditLogSse import {require_audit_sse} from '@fuzdev/fuz_app/server/app_server.js';

Assert that audit_sse was wired by create_app_server and return it as a non-null AuditLogSse. Throws a labelled error when the audit_log_sse option was not passed to create_app_server.

Use in route factories that depend on factory-managed audit SSE:

create_route_specs: (ctx) => create_audit_log_route_specs({ stream: require_audit_sse(ctx), }),

Preferred over ctx.audit_sse!! lies to the type system and produces a downstream cannot-read-property crash if a consumer wires the route without enabling the option.

source

type { audit_sse: AuditLogSse | null; }

returns

AuditLogSse

require_auth
#

auth/request_context.ts view source

(c: Context<any, string, {}>, next: Next): Promise<void | Response> import {require_auth} from '@fuzdev/fuz_app/auth/request_context.js';

Middleware that requires authentication.

Returns 401 if the auth middleware did not set c.var.auth_account_id.

c

type Context<any, string, {}>

next

type Next

returns

Promise<void | Response>

require_credential_types
#

auth/request_context.ts view source

(credential_types: readonly string[]): MiddlewareHandler import {require_credential_types} from '@fuzdev/fuz_app/auth/request_context.js';

Create middleware that requires the request's credential_type to be one of the given values.

Returns 401 if unauthenticated, 403 with ERROR_CREDENTIAL_TYPE_REQUIRED + required_credential_types echoing the spec's allowlist when the wire-side credential isn't in it. Body shape is symmetric with the role gate (ERROR_INSUFFICIENT_PERMISSIONS + required_roles) and matches what the RPC dispatcher's post-auth gate emits for the same condition. Today's only credential gate is keeper (['daemon_token']); future gates (agent_token, group_actor_token) reuse this literal and label themselves through the array.

credential_types

allowed credential types (any-of)

type readonly string[]

returns

MiddlewareHandler

require_request_context
#

auth/request_context.ts view source

(c: Context<any, any, {}>): RequestContext import {require_request_context} from '@fuzdev/fuz_app/auth/request_context.js';

Get the request context, throwing if unauthenticated.

Use in route handlers where the dispatcher's authorization phase guarantees a context exists (i.e., routes with auth: {type: 'authenticated'} or stricter). Prefer this over get_request_context(c)! for explicit error handling.

c

the Hono context

type Context<any, any, {}>

returns

RequestContext

the request context (never null)

throws

  • Error - if no request context is set (dispatcher misconfiguration)

require_role
#

auth/request_context.ts view source

(roles: readonly string[]): MiddlewareHandler import {require_role} from '@fuzdev/fuz_app/auth/request_context.js';

Create middleware that requires the actor to hold any of the given roles globally (scope_id IS NULL).

Returns 401 if unauthenticated, 403 if none of the roles are present. Reads REQUEST_CONTEXT_KEY because role-gated routes always run the dispatcher's authorization phase before this guard (the phase sets the actor-bound RequestContext).

Uses has_any_scoped_role(ctx, roles, null) so the gate matches global / unscoped role_grants only. A scoped role_grant ({role: 'admin', scope_id: <some uuid>}) does not unlock route-spec gates that are inherently global. The same scope-aware check is mirrored in actions/action_rpc.ts (HTTP RPC dispatcher) and actions/register_action_ws.ts (WS dispatcher) so all three transports agree.

Multi-role disjunction (any-of) lets auth.roles: ['admin', 'steward'] specs translate to one middleware that admits either role. Single-role routes pass [role_name]; the array shape is uniform.

roles

the roles to admit (any-of)

type readonly string[]

returns

MiddlewareHandler

require_rpc_endpoint_path
#

testing/rpc_helpers.ts view source

(rpc_endpoints: readonly RpcEndpointSpec[]): string import {require_rpc_endpoint_path} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

Resolve a single RPC endpoint path — the common case where a consumer mounts exactly one create_rpc_endpoint.

Used at suite setup time to hard-fail integration suites (admin / audit / SSE / rate-limiting) when the consumer omitted rpc_endpoints rather than letting tests fail mid-run with confusing errors.

Callers that need multi-endpoint support should iterate rpc_endpoints directly.

rpc_endpoints

type readonly RpcEndpointSpec[]

returns

string

throws

  • Error - if `rpc_endpoints` is empty (hard-fail; see the suite options

reserved_migration_namespaces
#

auth/migrations.ts view source

readonly string[] import {reserved_migration_namespaces} from '@fuzdev/fuz_app/auth/migrations.js';

Migration namespaces reserved by fuz_app. Consumers passing migration_namespaces to create_app_backend must choose a name not in this list — the runtime check rejects matches with a thrown error. Typed as ReadonlyArray<string> (not a literal tuple) so .includes() accepts any consumer-supplied namespace string without a cast.

reset_audit_metadata_validation_failures
#

auth/audit_log_queries.ts view source

(): void import {reset_audit_metadata_validation_failures} from '@fuzdev/fuz_app/auth/audit_log_queries.js';

Reset the counter — for tests only.

returns

void

reset_audit_unknown_event_type_failures
#

auth/audit_log_queries.ts view source

(): void import {reset_audit_unknown_event_type_failures} from '@fuzdev/fuz_app/auth/audit_log_queries.js';

Reset the counter — for tests only.

returns

void

reset_bootstrap_token
#

dev/setup.ts view source

(deps: FsReadDeps & FsWriteDeps & FsRemoveDeps & CommandDeps & EnvDeps, app_name: string, options?: SetupBootstrapTokenOptions | undefined): Promise<...> import {reset_bootstrap_token} from '@fuzdev/fuz_app/dev/setup.js';

Remove an existing bootstrap token and create a new one.

deps

file, command, env, and remove capabilities

type FsReadDeps & FsWriteDeps & FsRemoveDeps & CommandDeps & EnvDeps

app_name

application name

type string

options?

state_dir override, permissions, logger

type SetupBootstrapTokenOptions | undefined
optional

returns

Promise<SetupTokenResult>

result from creating the new token

mutates

  • filesystem — removes the existing token file (if any) then writes a fresh one

reset_database
#

dev/setup.ts view source

(deps: CommandDeps & FsReadDeps & FsRemoveDeps, database_url: string, options?: ResetDatabaseOptions | undefined): Promise<...> import {reset_database} from '@fuzdev/fuz_app/dev/setup.js';

Reset a database to a clean slate.

For PostgreSQL: drops and recreates the database. For pglite: removes the data directory if pglite_data_dir is provided. For empty/missing URLs: skips.

deps

command and file capabilities

type CommandDeps & FsReadDeps & FsRemoveDeps

database_url

the DATABASE_URL value

type string

options?

pglite_data_dir, logger

type ResetDatabaseOptions | undefined
optional

returns

Promise<ResetDbResult>

result describing what happened

mutates

  • external — database - drops and recreates the PostgreSQL database, or removes the PGlite data directory
  • filesystem — removes `options.pglite_data_dir` recursively for PGlite URLs

reset_mock_runtime
#

runtime/mock.ts view source

(runtime: MockRuntime): void import {reset_mock_runtime} from '@fuzdev/fuz_app/runtime/mock.js';

Reset a mock runtime to initial state.

runtime

returns

void

mutates

  • runtime — clears all mock state (env, fs, dirs, exit/command/stdout/fetch call records, mock results, stdin buffer)

reset_pglite
#

testing/db.ts view source

(db: Db): Promise<void> import {reset_pglite} from '@fuzdev/fuz_app/testing/db.js';

Reset a PGlite database to a clean state by dropping and recreating the public schema.

Removes all tables, sequences, indexes, types, and functions. The database instance remains usable after reset.

db

type Db

returns

Promise<void>

mutates

  • db — drops the `public` schema and recreates it; all rows in all

ResetDatabaseOptions
#

dev/setup.ts view source

ResetDatabaseOptions import type {ResetDatabaseOptions} from '@fuzdev/fuz_app/dev/setup.js';

Options for reset_database.

pglite_data_dir?

Directory to remove for file-based pglite.

type string

log?

type SetupLogger

ResetDbResult
#

dev/setup.ts view source

ResetDbResult import type {ResetDbResult} from '@fuzdev/fuz_app/dev/setup.js';

Result of reset_database.

reset

Whether the database was actually reset.

type boolean

skipped

Whether the operation was skipped (e.g. pglite with no data dir).

type boolean

db_type

What type of database was detected.

type 'postgres' | 'pglite' | 'none'

resolve_acting_actor
#

auth/request_context.ts view source

(deps: QueryDeps, account_id: string, acting_actor_id: string | undefined): Promise<ResolveActingActorResult> import {resolve_acting_actor} from '@fuzdev/fuz_app/auth/request_context.js';

Resolve the acting actor for an authenticated request.

Called from the route-spec / RPC dispatcher's authorization phase with the authenticated account id and the validated acting value (from the request payload). Applies the uniform resolution rules:

  • acting_actor_id omitted + 1 actor → use it.
  • acting_actor_id omitted + 0 actors → no_actors (defensive — signup / bootstrap always create an actor in the same tx, so this is a server error).
  • acting_actor_id omitted + multiple actors → actor_required with the available list so the client can prompt; never pick silently.
  • acting_actor_id present + matches an actor on the account → use it.
  • acting_actor_id present + does not match → actor_not_on_account. The available list is intentionally not echoed in this branch (treat as opaque rejection).

deps

query dependencies

account_id

the authenticated account

type string

acting_actor_id

the requested acting actor id, or undefined

type string | undefined

returns

Promise<ResolveActingActorResult>

resolve_client_ip
#

http/proxy.ts view source

(forwarded_for: string, proxies: ParsedProxy[]): string | undefined import {resolve_client_ip} from '@fuzdev/fuz_app/http/proxy.js';

Resolve the real client IP from an X-Forwarded-For header value.

Walks right-to-left, skipping trusted proxy entries AND any entry that fails strict IP validation (validate_ip_strict). The first untrusted, strictly-valid entry is the client IP. If every walked entry is trusted or malformed, returns the leftmost strictly-valid (trusted) entry (likely-misconfigured all-trusted case) or undefined (everything was malformed — middleware falls back to the connection IP). All entries are normalized before matching and in the returned value.

Skipping malformed entries is the rate-limit-key fix for the "attacker controls XFF and the proxy passes it through" surface — without the skip, an attacker could rotate arbitrary strings (incl. 'attacker:controlled', which Hono's lax distinctRemoteAddr misclassifies as IPv6) as XFF values to get fresh per-IP rate-limit buckets. Tradeoff: legitimate non-standard proxies that include ports in XFF entries (e.g. 203.0.113.1:8080) also fail strict validation, so those entries get skipped and the rate-limit bucket collapses to the proxy's connection IP (one bucket for everyone behind that proxy). Standard proxies (nginx, cloud LBs) don't include ports.

forwarded_for

the X-Forwarded-For header value

type string

proxies

parsed trusted proxy entries

type ParsedProxy[]

returns

string | undefined

the normalized client IP, or undefined if the header is empty / all entries malformed

resolve_env_vars
#

env/resolve.ts view source

(runtime: Pick<EnvDeps, "env_get">, value: string): string import {resolve_env_vars} from '@fuzdev/fuz_app/env/resolve.js';

Resolve environment variable references in a string.

  • $$VAR$$ resolves from the runtime env; missing values are left as-is for the validation phase to report.
  • $$?VAR$$ is the optional form — missing or empty resolves to the empty string. Required validation skips refs marked optional.
  • \$$VAR$$ / \$$?VAR$$ are escapes — the leading backslash is dropped and the body is emitted literally (no resolution attempted).

runtime

runtime with env_get capability

type Pick<EnvDeps, "env_get">

value

string that may contain $$VAR$$ references

type string

returns

string

string with env vars resolved

resolve_env_vars_in_object
#

env/resolve.ts view source

<T extends Record<string, unknown>>(runtime: Pick<EnvDeps, "env_get">, obj: T): T import {resolve_env_vars_in_object} from '@fuzdev/fuz_app/env/resolve.js';

Resolve env vars in an object's string values (shallow).

runtime

runtime with env_get capability

type Pick<EnvDeps, "env_get">

obj

object with string values

type T

returns

T

new object with env vars resolved

generics

resolve_env_vars_in_object<T extends Record<string, unknown>>
T
constraint Record<string, unknown>

resolve_env_vars_required
#

env/resolve.ts view source

(runtime: Pick<EnvDeps, "env_get">, value: string, context: string): string import {resolve_env_vars_required} from '@fuzdev/fuz_app/env/resolve.js';

Resolve env vars and throw if any are missing/empty.

Use this for values that must be present. $$?VAR$$ (optional) refs resolve to the empty string on miss without contributing to the error. Escaped references (\$$VAR$$) emit literally and never check the env.

runtime

runtime with env_get capability

type Pick<EnvDeps, "env_get">

value

string with $$VAR$$ references

type string

context

description for error message (e.g., "target.host")

type string

returns

string

resolved string

throws

  • Error - if any referenced env var is missing or empty

resolve_fixture_path
#

testing/assertions.ts view source

(filename: string, import_meta_url: string): string import {resolve_fixture_path} from '@fuzdev/fuz_app/testing/assertions.js';

Resolve an absolute path relative to the caller's module.

filename

type string

import_meta_url

the caller's import.meta.url

type string

returns

string

resolve_keeper_account_id
#

auth/daemon_token_middleware.ts view source

(deps: QueryDeps): Promise<string | null> import {resolve_keeper_account_id} from '@fuzdev/fuz_app/auth/daemon_token_middleware.js';

Resolve the keeper account ID by querying for the account with an active keeper role_grant.

There is exactly one keeper account (the bootstrap account). Runs once at server startup — the result is cached in DaemonTokenState.keeper_account_id. The acting actor is resolved per-request by the dispatcher's authorization phase (which runs resolve_acting_actor against this account id), so multi-actor keeper accounts surface actor_required if a daemon caller doesn't pass an explicit acting.

deps

query dependencies

returns

Promise<string | null>

the keeper account ID, or null if no keeper exists yet (pre-bootstrap)

resolve_rpc_endpoints_for_setup
#

testing/rpc_helpers.ts view source

(rpc_endpoints: RpcEndpointsSuiteOption, session_options: SessionOptions<string>): RpcEndpointSpec[] import {resolve_rpc_endpoints_for_setup} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

Resolve a suite's rpc_endpoints option to an array for setup-time inspection (path lookup, action presence checks).

For the factory form this invokes the factory twice with stub AppServerContexts and asserts that both invocations produce the same (path, method-list) shape — catching factories that close over mutable state or otherwise diverge across calls. The first array is returned; the second is discarded after the comparison. create_app_server invokes the factory again per-test with its real ctx, and those are the handlers that actually serve requests.

Safe as long as the factory is pure with respect to the endpoint path and the action spec.method list — the canonical helpers (create_standard_rpc_actions, create_admin_actions, create_account_actions, etc.) are. Factories that return a different path based on ctx will produce a setup/runtime mismatch; the path-purity assert below surfaces that as a clear gro check error rather than a silent test/runtime drift.

rpc_endpoints

session_options

type SessionOptions<string>

returns

RpcEndpointSpec[]

throws

  • Error - if the factory's two stub-ctx invocations produce different

resolve_scope_label
#

ui/format_scope.ts view source

<G extends string | null>(scope_id: string | null, role: string, format_scope: FormatScope, global_label: G): string | G import {resolve_scope_label} from '@fuzdev/fuz_app/ui/format_scope.js';

Resolve a scope label across the context → raw-uuid fallback chain.

global_label is returned for scope_id === null. Callers pass null to render no chip (admin tables — global is the implicit default) or 'global' for explicit labels (offer surfaces). The return type propagates null only when global_label is null.

scope_id

type string | null

role

type string

format_scope

global_label

type G

returns

string | G

generics

resolve_scope_label<G extends string | null>
G
constraint string | null

resolve_spec_qualifier
#

actions/action_codegen.ts view source

(imports: ImportBuilder, options?: { specs_module?: string | undefined; qualify_spec?: ((spec: { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; ... 8 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }) => string) | undefined; } | undefined): (spec: { ...; } | ... 1 more ... | { ...; }) => string import {resolve_spec_qualifier} from '@fuzdev/fuz_app/actions/action_codegen.js';

Resolve a per-spec identifier qualifier with the standard default-vs-callback dance. When qualify_spec is set, returns the caller's callback verbatim and registers no imports — the caller owns its namespace setup (the multi-source case where specs come from several modules). Otherwise, registers * as specs from specs_module (defaulting to './action_specs.ts') on imports and returns (spec) => 'specs.' + to_action_spec_identifier(spec.method).

Used internally by every multi-source-aware helper in this module (generate_action_specs_record, generate_action_inputs_outputs, generate_backend_actions_api); exported so consumers writing their own codegen helpers can reuse the same defaulting + import-registration behavior instead of reimplementing it.

imports

options?

type { specs_module?: string | undefined; qualify_spec?: ((spec: { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 7 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }) => string)...
optional

returns

(spec: { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | {...

resolve_standard_error_schema_tightness
#

testing/attack_surface.ts view source

(consumer: ErrorSchemaTightnessOptions | null | undefined): ErrorSchemaTightnessOptions | null import {resolve_standard_error_schema_tightness} from '@fuzdev/fuz_app/testing/attack_surface.js';

Merge a consumer's error_schema_tightness option with default_error_schema_tightness so allowlist and ignore_statuses are additive rather than replacing.

  • undefined → return the default as-is.
  • null → return null (opt out of the assertion).
  • object → spread the default, then consumer overrides for scalar fields (min_specificity), then concat stock-then-consumer for the list fields (allowlist, ignore_statuses) so consumer entries extend rather than replace.

Exported for direct use when a consumer calls assert_error_schema_tightness outside the standard suite but still wants the additive merge.

consumer

type ErrorSchemaTightnessOptions | null | undefined

returns

ErrorSchemaTightnessOptions | null

resolve_test_path
#

testing/auth_apps.ts view source

(path: string): string import {resolve_test_path} from '@fuzdev/fuz_app/testing/auth_apps.js';

Replace Hono route params (:foo) with dummy values for HTTP testing.

path

type string

returns

string

resolve_valid_path
#

testing/schema_generators.ts view source

(path: string, params_schema?: ZodObject<$ZodLooseShape, $strip> | undefined): string import {resolve_valid_path} from '@fuzdev/fuz_app/testing/schema_generators.js';

Resolve a route path with valid-ish param values so params validation passes. Used when testing input on routes that also have params.

path

type string

params_schema?

type ZodObject<$ZodLooseShape, $strip> | undefined
optional

returns

string

ResolveActingActorResult
#

rest_auth_route_suffixes
#

testing/integration_helpers.ts view source

readonly ["/login", "/logout", "/password", "/verify", "/signup", "/bootstrap"] import {rest_auth_route_suffixes} from '@fuzdev/fuz_app/testing/integration_helpers.js';

REST auth route suffixes on the account/bootstrap surface — the only routes still REST. find_auth_route rejects any other suffix at runtime; session/token CRUD, admin operations, and role_grant flows live on the RPC surface and should be reached via rpc_call.

RestAuthRouteSuffix
#

testing/integration_helpers.ts view source

"/bootstrap" | "/login" | "/logout" | "/password" | "/verify" | "/signup" import type {RestAuthRouteSuffix} from '@fuzdev/fuz_app/testing/integration_helpers.js';

RevokeForScopeResult
#

auth/role_grant_queries.ts view source

RevokeForScopeResult import type {RevokeForScopeResult} from '@fuzdev/fuz_app/auth/role_grant_queries.js';

Result of query_role_grant_revoke_for_scope — every role_grant revoked plus every pending offer superseded by the scope-wide cascade.

revoked

One entry per role_grant revoked by this call. Carries both the revokee's actor_id (the role_grant's grantee — drives target_actor_id audit envelopes) and account_id (the actor's account — drives target_account_id for SSE/WS socket-close fan-out). Empty array means no active role_grant was bound to the scope. scope_kind is surfaced for forensic completeness; the cascade itself keys on scope_id regardless of kind.

type Array<{ role_grant_id: Uuid; role: string; scope_kind: string | null; scope_id: Uuid; actor_id: Uuid; account_id: Uuid; }>

superseded_offers

Every pending offer at the scope — tuple-matched and orphan, undifferentiated — superseded in the same cascade. Each entry carries its grantor's from_account_id for role_grant_offer_supersede notification fan-out.

The caller is responsible for emitting role_grant_offer_supersede audit events with reason: 'scope_destroyed' and cause_id: <destroyed scope row id> per entry — the cause of every supersede here is the scope deletion, not any individual role_grant revoke (the revokes are themselves consequences of the scope going away).

type Array<SupersededOffer>

RevokeRoleGrantResult
#

auth/role_grant_queries.ts view source

RevokeRoleGrantResult import type {RevokeRoleGrantResult} from '@fuzdev/fuz_app/auth/role_grant_queries.js';

Result of query_revoke_role_grant — the revoked role_grant plus any pending offers superseded by the revoke.

id

type Uuid

role

type string

scope_kind

type string | null

scope_id

type Uuid | null

superseded_offers

Pending offers for the revoked role_grant's (account, role, scope) that were marked superseded as a side effect. Each entry carries its grantor's from_account_id so callers can fan out role_grant_offer_supersede notifications without a second round-trip. The caller is responsible for emitting a role_grant_offer_supersede audit event per entry (with reason: 'role_grant_revoked' and cause_id: <revoked role_grant id>).

type Array<SupersededOffer>

RevokeRoleResult
#

auth/role_grant_queries.ts view source

RevokeRoleResult import type {RevokeRoleResult} from '@fuzdev/fuz_app/auth/role_grant_queries.js';

Result of query_role_grant_revoke_role — every role_grant revoked plus the pending offers superseded by the bulk revoke.

revoked

One entry per role_grant revoked by this call. Carries the revokee's account_id so callers can fan out a role_grant_revoke notification per scope-instance. Empty array means nothing was active for (actor, role).

type Array<{ role_grant_id: string; role: string; scope_kind: string | null; scope_id: string | null; account_id: string; }>

superseded_offers

Pending offers for the actor's account+role (all scopes) superseded by the bulk revoke. Each entry carries its grantor's from_account_id so callers can fan out role_grant_offer_supersede notifications without a second round-trip.

type Array<SupersededOffer>

ROLE_ADMIN
#

auth/role_schema.ts view source

"admin" import {ROLE_ADMIN} from '@fuzdev/fuz_app/auth/role_schema.js';

App-level administrative role. Granted via the admin path.

role_grant_assign_action_spec
#

auth/role_grant_offer_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; roles: string[]; }; side_effects: true; input: ZodObject<{ to_account_id: $ZodBranded<ZodUUID, "Uuid", "out">; to_actor_id: ZodOptional<...>; role: ZodString; scope_id: ZodOptional<...>; acting: ZodOpti... import {role_grant_assign_action_spec} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

rate_limit: 'account' bounds admin-side burn of role_grant_assign — the action is admin-gated and audit-trailed, but the per-account cap matches role_grant_revoke so a single admin script can't churn grants in a loop.

ROLE_GRANT_INDEXES
#

role_grant_offer_accept_action_spec
#

auth/role_grant_offer_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ offer_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<...>; }, $strict>; output: ZodObject<...>; async: true; description: string; error_reasons: (... import {role_grant_offer_accept_action_spec} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

ROLE_GRANT_OFFER_ACCEPTED_NOTIFICATION_METHOD
#

role_grant_offer_accepted_notification_spec
#

auth/role_grant_offer_notifications.ts view source

{ method: string; kind: "remote_notification"; initiator: "backend"; auth: null; side_effects: true; input: ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<...>; ... 13 more ...; resulting_role_grant_id: ZodNullable<...>; }, $strict>; }, $strict>; output: ZodVoid; a... import {role_grant_offer_accepted_notification_spec} from '@fuzdev/fuz_app/auth/role_grant_offer_notifications.js';

role_grant_offer_and_accept
#

testing/role_grant_helpers.ts view source

(args: RoleGrantOfferAndAcceptArgs): Promise<{ offer_id: string & $brand<"Uuid">; role_grant_id: string & $brand<"Uuid">; }> import {role_grant_offer_and_accept} from '@fuzdev/fuz_app/testing/role_grant_helpers.js';

Drive the full consent flow (grantor offer → recipient accept) over the production RPC surface and return the materialized role_grant id.

grantor and recipient carry both the account id (for to_account_id derivation) and the create_session_headers factory (for cookie-threaded auth) — closing that loop on a single object per party rules out caller-side header/account mismatch.

args

returns

Promise<{ offer_id: string & $brand<"Uuid">; role_grant_id: string & $brand<"Uuid">; }>

role_grant_offer_create_action_spec
#

auth/role_grant_offer_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ to_account_id: $ZodBranded<ZodUUID, "Uuid", "out">; ... 5 more ...; acting: ZodOptional<...>; }, $strict>; ... 4 more ...; rate_limit: "account"; } import {role_grant_offer_create_action_spec} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

rate_limit: 'account' throttles offer-spam at the authenticated grantor and bounds the account-existence oracle on to_account_id — the same shape as invite_create_action_spec upstream addresses, where a hostile authed caller iterates recipients to probe ERROR_ACCOUNT_NOT_FOUND (and the actor-binding via ERROR_ROLE_GRANT_OFFER_ACTOR_ACCOUNT_MISMATCH) as an enumeration vector. Failure-outcome audit rows preserve the forensic trail; the rate cap closes the budget.

role_grant_offer_decline_action_spec
#

auth/role_grant_offer_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ offer_id: $ZodBranded<ZodUUID, "Uuid", "out">; reason: ZodOptional<...>; acting: ZodOptional<...>; }, $strict>; output: ZodObject<...>; async: true; description... import {role_grant_offer_decline_action_spec} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

ROLE_GRANT_OFFER_DECLINED_NOTIFICATION_METHOD
#

role_grant_offer_declined_notification_spec
#

auth/role_grant_offer_notifications.ts view source

{ method: string; kind: "remote_notification"; initiator: "backend"; auth: null; side_effects: true; input: ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<...>; ... 13 more ...; resulting_role_grant_id: ZodNullable<...>; }, $strict>; }, $strict>; output: ZodVoid; a... import {role_grant_offer_declined_notification_spec} from '@fuzdev/fuz_app/auth/role_grant_offer_notifications.js';

ROLE_GRANT_OFFER_DEFAULT_TTL_MS
#

auth/role_grant_offer_schema.ts view source

number import {ROLE_GRANT_OFFER_DEFAULT_TTL_MS} from '@fuzdev/fuz_app/auth/role_grant_offer_schema.js';

Default TTL for a newly created offer — 30 days. Matches GitHub org-invite expiry.

role_grant_offer_history_action_spec
#

auth/role_grant_offer_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: false; input: ZodDefault<ZodObject<{ account_id: ZodOptional<ZodNullable<$ZodBranded<ZodUUID, "Uuid", "out">>>; limit: ZodOptional<...>; offset: ZodOptional<...>; acting: ZodOptional<..... import {role_grant_offer_history_action_spec} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

ROLE_GRANT_OFFER_INBOX_INDEX
#

auth/role_grant_offer_ddl.ts view source

"\nCREATE INDEX IF NOT EXISTS role_grant_offer_inbox\n ON role_grant_offer (to_account_id, expires_at)\n WHERE accepted_at IS NULL\n AND declined_at IS NULL\n AND retracted_at IS NULL\n AND superseded_at IS NULL" import {ROLE_GRANT_OFFER_INBOX_INDEX} from '@fuzdev/fuz_app/auth/role_grant_offer_ddl.js';

Inbox lookup — pending offers for an account, ordered by soonest expiry.

role_grant_offer_list_action_spec
#

auth/role_grant_offer_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: false; input: ZodDefault<ZodObject<{ account_id: ZodOptional<ZodNullable<$ZodBranded<ZodUUID, "Uuid", "out">>>; acting: ZodOptional<...>; }, $strict>>; output: ZodObject<...>; async: tr... import {role_grant_offer_list_action_spec} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

ROLE_GRANT_OFFER_MESSAGE_LENGTH_MAX
#

auth/role_grant_offer_schema.ts view source

500 import {ROLE_GRANT_OFFER_MESSAGE_LENGTH_MAX} from '@fuzdev/fuz_app/auth/role_grant_offer_schema.js';

Maximum length of the optional message attached to an offer.

role_grant_offer_notification_specs
#

ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX
#

auth/role_grant_offer_ddl.ts view source

"\nCREATE UNIQUE INDEX IF NOT EXISTS role_grant_offer_pending_unique\n ON role_grant_offer (\n to_account_id,\n role,\n COALESCE(scope_kind, 'GLOBAL'),\n COALESCE(scope_id, '00000000-0000-0000-0000-000000000000'::uuid),\n from_actor_id\n )\n WHERE accepted_at IS NULL\n AND declined_at IS NULL\n ... import {ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX} from '@fuzdev/fuz_app/auth/role_grant_offer_ddl.js';

At most one pending offer per (to_account, role, scope_kind, scope, from_actor).

Including from_actor_id in the tuple lets multiple grantors coexist — teacher A and teacher B can each have a pending classroom_student offer for the same student and scope. A same-grantor re-offer upserts the existing pending row. COALESCE collapses NULL scopes into the sentinel values so Postgres's NULL-in-unique-index quirk does not allow duplicate global pending offers; the scope_kind / scope_id pair is always either both null (global) or both non-null (scoped) per the role_grant_offer_scope_kind_paired CHECK, so the two COALESCE expressions always agree. The ON CONFLICT target in query_role_grant_offer_create must match this expression literally.

ROLE_GRANT_OFFER_RECEIVED_NOTIFICATION_METHOD
#

role_grant_offer_received_notification_spec
#

auth/role_grant_offer_notifications.ts view source

{ method: string; kind: "remote_notification"; initiator: "backend"; auth: null; side_effects: true; input: ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<...>; ... 13 more ...; resulting_role_grant_id: ZodNullable<...>; }, $strict>; }, $strict>; output: ZodVoid; a... import {role_grant_offer_received_notification_spec} from '@fuzdev/fuz_app/auth/role_grant_offer_notifications.js';

role_grant_offer_retract_action_spec
#

auth/role_grant_offer_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ offer_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<...>; }, $strict>; output: ZodObject<...>; async: true; description: string; error_reasons: (... import {role_grant_offer_retract_action_spec} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

ROLE_GRANT_OFFER_RETRACTED_NOTIFICATION_METHOD
#

role_grant_offer_retracted_notification_spec
#

auth/role_grant_offer_notifications.ts view source

{ method: string; kind: "remote_notification"; initiator: "backend"; auth: null; side_effects: true; input: ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<...>; ... 13 more ...; resulting_role_grant_id: ZodNullable<...>; }, $strict>; }, $strict>; output: ZodVoid; a... import {role_grant_offer_retracted_notification_spec} from '@fuzdev/fuz_app/auth/role_grant_offer_notifications.js';

ROLE_GRANT_OFFER_SCHEMA
#

auth/role_grant_offer_ddl.ts view source

"\nCREATE TABLE IF NOT EXISTS role_grant_offer (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n from_actor_id UUID NOT NULL REFERENCES actor(id) ON DELETE CASCADE,\n to_account_id UUID NOT NULL REFERENCES account(id) ON DELETE CASCADE,\n to_actor_id UUID NULL REFERENCES actor(id) ON DELETE CASCADE,\n role T... import {ROLE_GRANT_OFFER_SCHEMA} from '@fuzdev/fuz_app/auth/role_grant_offer_ddl.js';

ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN
#

auth/role_grant_offer_ddl.ts view source

"GLOBAL" import {ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN} from '@fuzdev/fuz_app/auth/role_grant_offer_ddl.js';

Index-side token for the global case in the partial unique index. Uppercase so it cannot collide with consumer-declared ScopeKindName values (which are lowercase by regex). Never appears as a column value — column-level scope_kind = NULL and scope_id = NULL together encode the global case.

ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID
#

auth/role_grant_offer_ddl.ts view source

"00000000-0000-0000-0000-000000000000" import {ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID} from '@fuzdev/fuz_app/auth/role_grant_offer_ddl.js';

Sentinel UUID used inside the partial unique indexes to collapse scope_id IS NULL into a comparable value.

ROLE_GRANT_OFFER_SUPERSEDE_NOTIFICATION_METHOD
#

role_grant_offer_supersede_notification_spec
#

auth/role_grant_offer_notifications.ts view source

{ method: string; kind: "remote_notification"; initiator: "backend"; auth: null; side_effects: true; input: ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<...>; ... 13 more ...; resulting_role_grant_id: ZodNullable<...>; }, $strict>; reason: ZodEnum<...>; cause_id:... import {role_grant_offer_supersede_notification_spec} from '@fuzdev/fuz_app/auth/role_grant_offer_notifications.js';

role_grant_offers_state_context
#

ui/role_grant_offers_state.svelte.ts view source

{ get: (error_message?: string | undefined) => RoleGrantOffersState; get_maybe: () => RoleGrantOffersState | undefined; set: (value: RoleGrantOffersState) => RoleGrantOffersState; } import {role_grant_offers_state_context} from '@fuzdev/fuz_app/ui/role_grant_offers_state.svelte.js';

Svelte context for RoleGrantOffersState. Use role_grant_offers_state_context.set(state) in the provider and role_grant_offers_state_context.get() to access.

role_grant_revoke_action_spec
#

auth/role_grant_offer_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; roles: string[]; }; side_effects: true; input: ZodObject<{ actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; role_grant_id: $ZodBranded<...>; reason: ZodOptional<...>; acting: ZodOptional<...>; }, $strict>... import {role_grant_revoke_action_spec} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

rate_limit: 'account' bounds admin-side burn of role_grant_revoke — the action is admin-gated and audit-trailed, but the per-account cap keeps a single admin script from churning role_grants in a loop and obscuring audit context for unrelated activity.

ROLE_GRANT_REVOKE_NOTIFICATION_METHOD
#

role_grant_revoke_notification_spec
#

auth/role_grant_offer_notifications.ts view source

{ method: string; kind: "remote_notification"; initiator: "backend"; auth: null; side_effects: true; input: ZodObject<{ role_grant_id: $ZodBranded<ZodUUID, "Uuid", "out">; role: ZodString; scope_id: ZodNullable<...>; reason: ZodNullable<...>; }, $strict>; output: ZodVoid; async: true; description: string; } import {role_grant_revoke_notification_spec} from '@fuzdev/fuz_app/auth/role_grant_offer_notifications.js';

ROLE_GRANT_REVOKED_REASON_LENGTH_MAX
#

auth/account_schema.ts view source

500 import {ROLE_GRANT_REVOKED_REASON_LENGTH_MAX} from '@fuzdev/fuz_app/auth/account_schema.js';

Maximum length of the optional free-form revoked_reason attached to a revoked role_grant. Bounds the value at the schema layer so both the admin input (when the route surfaces a reason field) and the revokee-facing role_grant_revoke WS notification validate against the same ceiling.

ROLE_GRANT_SCHEMA
#

auth/auth_ddl.ts view source

"\nCREATE TABLE IF NOT EXISTS role_grant (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n actor_id UUID NOT NULL REFERENCES actor(id) ON DELETE CASCADE,\n role TEXT NOT NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n expires_at TIMESTAMPTZ,\n revoked_at TIMESTAMPTZ,\n revoked_by UUID REFERENCES a... import {ROLE_GRANT_SCHEMA} from '@fuzdev/fuz_app/auth/auth_ddl.js';

role_has_grant_path
#

auth/role_schema.ts view source

(role_specs: ReadonlyMap<string, RoleSpec>, role: string, grant_path: string): boolean import {role_has_grant_path} from '@fuzdev/fuz_app/auth/role_schema.js';

Predicate over a RoleSpec map: does the named role include the given grant path? Returns false for unknown roles. Used by admin_actions.create_admin_actions (path = 'admin') and self_service_role_actions.create_self_service_role_actions (path = 'self_service') to derive their default eligibility filters.

role_specs

type ReadonlyMap<string, RoleSpec>

role

type string

grant_path

type string

returns

boolean

ROLE_KEEPER
#

auth/role_schema.ts view source

"keeper" import {ROLE_KEEPER} from '@fuzdev/fuz_app/auth/role_schema.js';

System-level role. Requires daemon token (filesystem proof). Controls the keep.

RoleGrant
#

auth/account_schema.ts view source

RoleGrant import type {RoleGrant} from '@fuzdev/fuz_app/auth/account_schema.js';

Role grant — time-bounded, revocable grant of a role to an actor.

id

type Uuid

actor_id

type Uuid

role

type string

scope_kind

Machine-readable kind tag for the polymorphic scope_id. Paired-null with scope_id per the role_grant_scope_kind_paired CHECK: both null (global) or both non-null (scoped). Consumer-declared via create_scope_kind_schema(...); v1 keeps validation registry-membership only, with no INSERT-time (role, scope_kind) enforcement.

type string | null

scope_id

Resource scope this grant applies to (e.g. a classroom id). null for global role_grants.

type Uuid | null

created_at

type string

expires_at

type string | null

revoked_at

type string | null

revoked_by

type Uuid | null

revoked_reason

Optional free-form reason attached on revoke (rides on the role_grant_revoke WS notification to the revokee).

type string | null

granted_by

type Uuid | null

source_offer_id

Offer that produced this role_grant (set by query_accept_offer). null for direct grants.

type Uuid | null

RoleGrantAssignInput
#

auth/role_grant_offer_action_specs.ts view source

ZodObject<{ to_account_id: $ZodBranded<ZodUUID, "Uuid", "out">; to_actor_id: ZodOptional<ZodNullable<$ZodBranded<ZodUUID, "Uuid", "out">>>; role: ZodString; scope_id: ZodOptional<...>; acting: ZodOptional<...>; }, $strict> import type {RoleGrantAssignInput} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Input for role_grant_assign — the immediate admin-only conferral path, the consent-free sibling of role_grant_offer_create. An admin assigns a role_grant straight onto the target actor; there is no offer for a grantee to accept. No message (no offer to carry it on) and no scope_kind (scope_id alone is the v1 scope discriminator — the grant row's scope_kind stays null).

RoleGrantAssignOutput
#

auth/role_grant_offer_action_specs.ts view source

ZodObject<{ ok: ZodLiteral<true>; role_grant_id: $ZodBranded<ZodUUID, "Uuid", "out">; }, $strict> import type {RoleGrantAssignOutput} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Output for role_grant_assign.

RoleGrantHistoryEventJson
#

auth/audit_log_schema.ts view source

ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; seq: ZodNumber; event_type: ZodString; outcome: ZodEnum<{ success: "success"; failure: "failure"; }>; ... 8 more ...; target_username: ZodNullable<...>; }, $strict> import type {RoleGrantHistoryEventJson} from '@fuzdev/fuz_app/auth/audit_log_schema.js';

Zod schema for role_grant history events with resolved usernames.

RoleGrantOffer
#

auth/role_grant_offer_schema.ts view source

RoleGrantOffer import type {RoleGrantOffer} from '@fuzdev/fuz_app/auth/role_grant_offer_schema.js';

Role grant offer row as returned by the database.

id

type Uuid

from_actor_id

type Uuid

to_account_id

type Uuid

to_actor_id

Optional actor-grain target on the recipient account. When set, accept is gated to this specific actor — query_accept_offer rejects any other actor with role_grant_offer_actor_mismatch even when they belong to to_account_id. When null the offer is account-grain and any actor on to_account_id may accept (the v1 default).

Drives the audit envelope's target_actor_id on offer-shape events (role_grant_offer_create / _expire / _retract / _supersede) — when set, the actor-grain forensic field carries the named actor; when null the offer-shape events leave it null by design.

type Uuid | null

role

type string

scope_kind

Machine-readable kind tag for the polymorphic scope_id. Paired-null with scope_id per the role_grant_offer_scope_kind_paired CHECK: both null (global) or both non-null (scoped). Consumer-declared via create_scope_kind_schema(...); v1 keeps validation registry-membership only, with no INSERT-time (role, scope_kind) enforcement.

type string | null

scope_id

type Uuid | null

message

type string | null

created_at

type string

expires_at

type string

accepted_at

type string | null

declined_at

type string | null

decline_reason

type string | null

retracted_at

type string | null

superseded_at

Set when the offer was obsoleted by an external event — a sibling offer was accepted (yielding the role_grant this offer's role+scope maps to) or the resulting role_grant for this (to_account, role, scope) was revoked. Closes the "accept a pre-revoke offer to bypass the revoke" path.

type string | null

resulting_role_grant_id

type Uuid | null

RoleGrantOfferAcceptedParams
#

auth/role_grant_offer_notifications.ts view source

ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; to_account_id: $ZodBranded<ZodUUID, "Uuid", "out">; ... 12 more ...; resulting_role_grant_id: ZodNullable<...>; }, $strict>; }, $strict> import type {RoleGrantOfferAcceptedParams} from '@fuzdev/fuz_app/auth/role_grant_offer_notifications.js';

Params for role_grant_offer_accepted — recipient accepted the offer.

RoleGrantOfferAcceptInput
#

auth/role_grant_offer_action_specs.ts view source

ZodObject<{ offer_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict> import type {RoleGrantOfferAcceptInput} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Input for role_grant_offer_accept.

RoleGrantOfferAcceptOutput
#

auth/role_grant_offer_action_specs.ts view source

ZodObject<{ role_grant_id: $ZodBranded<ZodUUID, "Uuid", "out">; offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; ... 13 more ...; resulting_role_grant_id: ZodNullable<...>; }, $strict>; superseded_offer_ids: ZodArray<...>; }, $strict> import type {RoleGrantOfferAcceptOutput} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Output for role_grant_offer_accept.

RoleGrantOfferActionOptions
#

auth/role_grant_offer_actions.ts view source

RoleGrantOfferActionOptions import type {RoleGrantOfferActionOptions} from '@fuzdev/fuz_app/auth/role_grant_offer_actions.js';

roles?

Role schema result from create_role_schema(). Defaults to builtin roles only. Drives the grantability gate: a role is offerable / revocable through this surface only when its RoleSpec.grant_paths includes 'admin' (the GRANT_PATH_ADMIN constant).

type RoleSchemaResult

default_ttl_ms?

TTL applied to newly-created offers. Defaults to ROLE_GRANT_OFFER_DEFAULT_TTL_MS.

type number

authorize?

Custom authorization for role_grant_offer_create. The default requires the caller to hold an active role_grant for the offered role *and* the role's RoleSpec.grant_paths to include 'admin'. Consumers with richer policies (scope-aware, chained roles) override this.

type RoleGrantOfferCreateAuthorize

RoleGrantOfferActorAccountMismatchError
#

auth/role_grant_offer_queries.ts view source

import {RoleGrantOfferActorAccountMismatchError} from '@fuzdev/fuz_app/auth/role_grant_offer_queries.js';

Error thrown when query_role_grant_offer_create is called with a to_actor_id that does not exist or does not belong to to_account_id. Surfaces the actor↔account binding mismatch at the boundary instead of letting the FK silently disagree with the recipient field.

inheritance

extends: Error

constructor

type new (): RoleGrantOfferActorAccountMismatchError

RoleGrantOfferActorMismatchError
#

auth/role_grant_offer_queries.ts view source

import {RoleGrantOfferActorMismatchError} from '@fuzdev/fuz_app/auth/role_grant_offer_queries.js';

Error thrown when an actor-targeted offer is being accepted by an actor other than offer.to_actor_id. Distinct from RoleGrantOfferNotFoundError (the IDOR mask): once an offer has been resolved to the recipient account, a wrong-actor accept on a same-account actor is a contract violation, not a privacy boundary — surface a specific error so the client UI can distinguish "this offer isn't for you" from "no such offer".

inheritance

extends: Error

constructor

type new (offer_id: string): RoleGrantOfferActorMismatchError

offer_id

type string

RoleGrantOfferAlreadyTerminalError
#

auth/role_grant_offer_queries.ts view source

import {RoleGrantOfferAlreadyTerminalError} from '@fuzdev/fuz_app/auth/role_grant_offer_queries.js';

Error thrown by offer-lifecycle queries when the offer is in a non-pending state (accepted / declined / retracted / superseded) and therefore not actionable. Distinct from RoleGrantOfferExpiredError — expiry has its own user-facing story ("ask the grantor to re-send") so it travels separately.

inheritance

extends: Error

constructor

type new (offer_id: string): RoleGrantOfferAlreadyTerminalError

offer_id

type string

RoleGrantOfferAndAcceptArgs
#

testing/role_grant_helpers.ts view source

RoleGrantOfferAndAcceptArgs import type {RoleGrantOfferAndAcceptArgs} from '@fuzdev/fuz_app/testing/role_grant_helpers.js';

app

type RpcCallArgs['app']

rpc_path

type string

grantor

Account doing the granting. TestApp / TestAccount cover the in-process shape; TestFixture covers the cross-backend fixture protocol. All three carry account.id + create_session_headers.

type TestApp | TestAccount | TestFixture

recipient

type TestAccount

role

type string

RoleGrantOfferCreateAuthorize
#

auth/role_grant_offer_actions.ts view source

RoleGrantOfferCreateAuthorize import type {RoleGrantOfferCreateAuthorize} from '@fuzdev/fuz_app/auth/role_grant_offer_actions.js';

Authorization callback for role_grant_offer_create. Returns true to allow, false to reject (handler converts to forbidden).

Provided with the fully-resolved request context and the parsed input (pre-TTL, pre-normalization). Consumers override the default to implement policies like "teacher may offer classroom_student only in classrooms they teach".

(call)

type (auth: RequestContext, input: { to_account_id: string; role: string; scope_id: string | null; }, deps: { log: Logger; }, ctx: ActionContext): boolean | Promise<...>

auth

input

type { to_account_id: string; role: string; scope_id: string | null; }

deps

type { log: Logger; }

ctx

returns boolean | Promise<boolean>

RoleGrantOfferCreateInput
#

auth/role_grant_offer_action_specs.ts view source

ZodObject<{ to_account_id: $ZodBranded<ZodUUID, "Uuid", "out">; to_actor_id: ZodOptional<ZodNullable<$ZodBranded<ZodUUID, "Uuid", "out">>>; ... 4 more ...; acting: ZodOptional<...>; }, $strict> import type {RoleGrantOfferCreateInput} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Input for role_grant_offer_create.

to_actor_id (optional) narrows the offer to a specific actor on the recipient account. When supplied, role_grant_offer_accept will only admit the named actor — wrong-actor accepts reject with role_grant_offer_actor_mismatch. The audit envelope's target_actor_id is stamped from this column on the create / supersede / expire / retract events. Omit (or pass null) for the account-grain default — any actor on to_account_id may accept.

RoleGrantOfferCreateOutput
#

auth/role_grant_offer_action_specs.ts view source

ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; to_account_id: $ZodBranded<ZodUUID, "Uuid", "out">; ... 12 more ...; resulting_role_grant_id: ZodNullable<...>; }, $strict>; }, $strict> import type {RoleGrantOfferCreateOutput} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Output for role_grant_offer_create.

RoleGrantOfferDeclinedParams
#

auth/role_grant_offer_notifications.ts view source

ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; to_account_id: $ZodBranded<ZodUUID, "Uuid", "out">; ... 12 more ...; resulting_role_grant_id: ZodNullable<...>; }, $strict>; }, $strict> import type {RoleGrantOfferDeclinedParams} from '@fuzdev/fuz_app/auth/role_grant_offer_notifications.js';

Params for role_grant_offer_declined. The decline reason (if any) rides along inside offer.decline_reason — the DB stamps it on the offer row during decline, so a sibling reason field would just duplicate it.

RoleGrantOfferDeclineInput
#

auth/role_grant_offer_action_specs.ts view source

ZodObject<{ offer_id: $ZodBranded<ZodUUID, "Uuid", "out">; reason: ZodOptional<ZodNullable<ZodString>>; acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict> import type {RoleGrantOfferDeclineInput} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Input for role_grant_offer_decline.

RoleGrantOfferEnumerationCrossTestOptions
#

testing/cross_backend/role_grant_offer_enumeration.ts view source

RoleGrantOfferEnumerationCrossTestOptions import type {RoleGrantOfferEnumerationCrossTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/role_grant_offer_enumeration.js';

Options for the offer-enumeration parity suite.

setup_test

Per-test fixture producer. Must be configured with extra_actors (≥1) so the keeper is multi-actor and the sibling-actor mismatch arm is reachable — the entrypoint passes default_cross_process_setup(handle, {extra_actors: [...]}).

type SetupTest

readonly

rpc_path?

RPC endpoint path. Default /api/rpc.

type string

readonly

RoleGrantOfferExpiredError
#

auth/role_grant_offer_queries.ts view source

import {RoleGrantOfferExpiredError} from '@fuzdev/fuz_app/auth/role_grant_offer_queries.js';

Error thrown when an offer's expires_at has passed. The accept path enforces this independently of the sweep — a stale offer past its expiry must not be accepted, even in the race window between expiry and the sweep stamping the audit event.

inheritance

extends: Error

constructor

type new (offer_id: string): RoleGrantOfferExpiredError

offer_id

type string

RoleGrantOfferForm
#

ui/RoleGrantOfferForm.svelte view source

import RoleGrantOfferForm from '@fuzdev/fuz_app/ui/RoleGrantOfferForm.svelte';

to_account_id

type string

to_actor_id?

Narrow the offer to a specific actor on to_account_id. Omit (or null, the default) for the account-grain default — any actor on the recipient account may accept.

type string
optional default null

roles

Roles the caller may offer — caller filters upstream (default: admin-grant-path).

type string[]

scope_id?

Resource scope for the offer; null (default) yields a global offer.

type string
optional default null

on_created?

type (offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; to_actor_id: (string & $brand<"Uuid">) | null; ... 11 more ...; resulting_role_grant_id: (string & $brand<...>) | null; }) => void
optional

format_role?

type (role: string) => string
optional default (role: string) => role

RoleGrantOfferHistory
#

ui/RoleGrantOfferHistory.svelte view source

import RoleGrantOfferHistory from '@fuzdev/fuz_app/ui/RoleGrantOfferHistory.svelte';

current_actor_id

Used to label a row as sent vs received. When null, direction shows as -.

type string | null

format_actor?

type (from_actor_id: string) => string
optional default truncate_uuid

format_scope?

Display label for an offer's scope. Bypasses format_scope_context when supplied — return null to fall back to a truncated uuid (or 'global' for null scope_id). Omit to use the context value directly.

optional

format_role?

type (role: string) => string
optional default (role: string) => role

RoleGrantOfferHistoryInput
#

auth/role_grant_offer_action_specs.ts view source

ZodDefault<ZodObject<{ account_id: ZodOptional<ZodNullable<$ZodBranded<ZodUUID, "Uuid", "out">>>; limit: ZodOptional<ZodNullable<ZodNumber>>; offset: ZodOptional<...>; acting: ZodOptional<...>; }, $strict>> import type {RoleGrantOfferHistoryInput} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Input for role_grant_offer_history. Returns every offer involving the account in either direction (recipient or grantor), including terminal rows, newest first. account_id is admin-only.

RoleGrantOfferHistoryOutput
#

auth/role_grant_offer_action_specs.ts view source

ZodObject<{ offers: ZodArray<ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; to_account_id: $ZodBranded<ZodUUID, "Uuid", "out">; ... 12 more ...; resulting_role_grant_id: ZodNullable<...>; }, $strict>>; }, $strict> import type {RoleGrantOfferHistoryOutput} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Output for role_grant_offer_history.

RoleGrantOfferInbox
#

ui/RoleGrantOfferInbox.svelte view source

import RoleGrantOfferInbox from '@fuzdev/fuz_app/ui/RoleGrantOfferInbox.svelte';

format_actor?

Display label for from_actor_id. Defaults to a truncated uuid.

type (from_actor_id: string) => string
optional default truncate_uuid

format_scope?

Display label for an offer's scope. Bypasses format_scope_context when supplied — return null to fall back to a truncated uuid (or 'global' for null scope_id). Omit to use the context value directly.

optional

format_role?

Display label for a role constant. Defaults to identity.

type (role: string) => string
optional default (role: string) => role

RoleGrantOfferJson
#

auth/role_grant_offer_schema.ts view source

ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; to_account_id: $ZodBranded<ZodUUID, "Uuid", "out">; ... 12 more ...; resulting_role_grant_id: ZodNullable<...>; }, $strict> import type {RoleGrantOfferJson} from '@fuzdev/fuz_app/auth/role_grant_offer_schema.js';

Zod schema for client-safe role_grant offer data.

RoleGrantOfferListInput
#

auth/role_grant_offer_action_specs.ts view source

ZodDefault<ZodObject<{ account_id: ZodOptional<ZodNullable<$ZodBranded<ZodUUID, "Uuid", "out">>>; acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict>> import type {RoleGrantOfferListInput} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Input for role_grant_offer_list. account_id is admin-only (inspect another account's inbox).

RoleGrantOfferListOutput
#

auth/role_grant_offer_action_specs.ts view source

ZodObject<{ offers: ZodArray<ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; to_account_id: $ZodBranded<ZodUUID, "Uuid", "out">; ... 12 more ...; resulting_role_grant_id: ZodNullable<...>; }, $strict>>; }, $strict> import type {RoleGrantOfferListOutput} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Output for role_grant_offer_list.

RoleGrantOfferNotFoundError
#

auth/role_grant_offer_queries.ts view source

import {RoleGrantOfferNotFoundError} from '@fuzdev/fuz_app/auth/role_grant_offer_queries.js';

Error thrown when an offer cannot be located for the caller. Covers both "offer does not exist" and "offer belongs to a different recipient" (IDOR guard) — the standard 404-over-403 pattern that avoids disclosing whether an offer id exists.

inheritance

extends: Error

constructor

type new (offer_id: string): RoleGrantOfferNotFoundError

offer_id

type string

RoleGrantOfferNotification
#

RoleGrantOfferNotificationWsTestOptions
#

testing/cross_backend/role_grant_offer_notification_ws.ts view source

RoleGrantOfferNotificationWsTestOptions import type {RoleGrantOfferNotificationWsTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/role_grant_offer_notification_ws.js';

Configuration for .

setup_test

Per-test fixture producer (default_cross_process_setup(handle, ...)).

type SetupTest

readonly

capabilities

Backend capability flags; every case gates on capabilities.ws.

type BackendCapabilities

readonly

base_url

Base URL the backend is reachable at (e.g. http://localhost:1178).

type string

readonly

ws_path

WebSocket endpoint path on the backend (e.g. /api/ws).

type string

readonly

RoleGrantOfferOkOutput
#

auth/role_grant_offer_action_specs.ts view source

ZodObject<{ ok: ZodLiteral<true>; }, $strict> import type {RoleGrantOfferOkOutput} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Output for role_grant_offer_decline / role_grant_offer_retract.

RoleGrantOfferReceivedParams
#

auth/role_grant_offer_notifications.ts view source

ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; to_account_id: $ZodBranded<ZodUUID, "Uuid", "out">; ... 12 more ...; resulting_role_grant_id: ZodNullable<...>; }, $strict>; }, $strict> import type {RoleGrantOfferReceivedParams} from '@fuzdev/fuz_app/auth/role_grant_offer_notifications.js';

Params for role_grant_offer_received — offer delivered to its recipient.

RoleGrantOfferRetractedParams
#

auth/role_grant_offer_notifications.ts view source

ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; to_account_id: $ZodBranded<ZodUUID, "Uuid", "out">; ... 12 more ...; resulting_role_grant_id: ZodNullable<...>; }, $strict>; }, $strict> import type {RoleGrantOfferRetractedParams} from '@fuzdev/fuz_app/auth/role_grant_offer_notifications.js';

Params for role_grant_offer_retracted — grantor-side retraction.

RoleGrantOfferRetractInput
#

auth/role_grant_offer_action_specs.ts view source

ZodObject<{ offer_id: $ZodBranded<ZodUUID, "Uuid", "out">; acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict> import type {RoleGrantOfferRetractInput} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Input for role_grant_offer_retract.

RoleGrantOfferSelfTargetError
#

auth/role_grant_offer_queries.ts view source

import {RoleGrantOfferSelfTargetError} from '@fuzdev/fuz_app/auth/role_grant_offer_queries.js';

Error thrown when a grantor attempts to offer a role_grant to their own account.

Enforced via a single SELECT on the grantor's actor.account_id (rather than via a CHECK constraint or a denormalized column). Resolving from the grantor side keeps the check multi-actor-correct: under multi-actor the recipient account may host many actors, but the grantor → account binding remains 1:1 by definition of actor.

inheritance

extends: Error

constructor

type new (): RoleGrantOfferSelfTargetError

RoleGrantOffersRpc
#

ui/role_grant_offers_state.svelte.ts view source

RoleGrantOffersRpc import type {RoleGrantOffersRpc} from '@fuzdev/fuz_app/ui/role_grant_offers_state.svelte.js';

Narrow RPC surface consumed by RoleGrantOffersState. Consumers adapt their typed client (e.g. a create_rpc_client Proxy) to this shape — the state class stays decoupled from the client's Result return type so tests can inject plain-function stubs.

list

type () => Promise<{ offers: Array<RoleGrantOfferJson> }>

history

type (options?: { limit?: number; offset?: number; }) => Promise<{ offers: Array<RoleGrantOfferJson> }>

create

type (params: { to_account_id: string; to_actor_id?: string | null; role: string; scope_id?: string | null; message?: string | null; }) => Promise<{ offer: RoleGrantOfferJson }>

accept

type (offer_id: string) => Promise<{ role_grant_id: string; offer: RoleGrantOfferJson; superseded_offer_ids: Array<string>; }>

decline

type (offer_id: string, reason?: string | null) => Promise<{ ok: true }>

retract

type (offer_id: string) => Promise<{ ok: true }>

RoleGrantOffersState
#

ui/role_grant_offers_state.svelte.ts view source

import {RoleGrantOffersState} from '@fuzdev/fuz_app/ui/role_grant_offers_state.svelte.js';

list

type AsyncSlot<void, string>

readonly

list_history

type AsyncSlot<void, string>

readonly

create

type AsyncSlot<{ id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; to_actor_id: (string & $brand<"Uuid">) | null; ... 11 more ...; resulting_role_grant_id: (string & $brand<...>) | null; }, string>

readonly

accept

type AsyncSlot<void, string>

readonly

decline

type AsyncSlot<void, string>

readonly

retract

type AsyncSlot<void, string>

readonly

incoming

Pending offers for the current account, soonest-expiring first.

type Array<RoleGrantOfferJson>

readonly $derived.by

outgoing

Pending offers from the current actor, newest-created first.

type Array<RoleGrantOfferJson>

readonly $derived.by

history

Every offer known to this state, newest-created first. Feeds the history view.

type Array<RoleGrantOfferJson>

readonly $derived.by

incoming_count

type number

readonly $derived

constructor

type new (options: RoleGrantOffersStateOptions): RoleGrantOffersState

options

fetch

Seed the cache with the recipient-side pending inbox.

type (): Promise<void>

returns Promise<void>

fetch_history

Seed both-directions history (includes terminal rows).

type (options?: { limit?: number | undefined; offset?: number | undefined; } | undefined): Promise<void>

options?

type { limit?: number | undefined; offset?: number | undefined; } | undefined
optional
returns Promise<void>

submit_create

Issue a new offer; merges the returned offer into the cache on success.

to_actor_id (optional) narrows the offer to a specific actor on to_account_id; omit / null for the account-grain default (any actor on the recipient account may accept).

type (params: { to_account_id: string; to_actor_id?: string | null | undefined; role: string; scope_id?: string | null | undefined; message?: string | null | undefined; }): Promise<{ id: string & $brand<...>; ... 14 more ...; resulting_role_grant_id: (string & $brand<...>) | null; } | undefined>

params

type { to_account_id: string; to_actor_id?: string | null | undefined; role: string; scope_id?: string | null | undefined; message?: string | null | undefined; }
returns Promise<{ id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; to_actor_id: (string & $brand<"Uuid">) | null; ... 11 more ...; resulting_role_grant_id: (string & $brand<...>) | null; } | undefined>

submit_accept

Accept an offer; stamps it terminal in the cache and drops any siblings the server superseded.

type (offer_id: string): Promise<void>

offer_id

type string
returns Promise<void>

submit_decline

type (offer_id: string, reason?: string | null | undefined): Promise<void>

offer_id

type string

reason?

type string | null | undefined
optional
returns Promise<void>

submit_retract

type (offer_id: string): Promise<void>

offer_id

type string
returns Promise<void>

subscribe

Wire a notification subscription. The handler dispatches each matching notification into apply_notification; the returned disposer unwires.

type (subscribe_fn: RoleGrantOfferSubscribe): () => void

subscribe_fn

returns () => void

apply_notification

Reduce a single WS notification into the cache. Exposed so consumers wiring their WS receiver directly (without subscribe) and tests can drive the reducer without allocating a subscription.

type (notification: RoleGrantOfferNotification): void

notification

returns void

reset

Clear the cache and reset every slot.

type (): void

returns void

RoleGrantOffersStateOptions
#

ui/role_grant_offers_state.svelte.ts view source

RoleGrantOffersStateOptions import type {RoleGrantOffersStateOptions} from '@fuzdev/fuz_app/ui/role_grant_offers_state.svelte.js';

rpc

type RoleGrantOffersRpc

account_id

Reactive accessor for the current account id; returns null when logged out.

type () => string | null

actor_id

Reactive accessor for the current actor id — required to classify offers as outgoing. Returns null when unknown.

type () => string | null

RoleGrantOfferSubscribe
#

ui/role_grant_offers_state.svelte.ts view source

RoleGrantOfferSubscribe import type {RoleGrantOfferSubscribe} from '@fuzdev/fuz_app/ui/role_grant_offers_state.svelte.js';

Subscription primitive — consumer wires their WS receiver; returns a disposer.

(call)

type (handler: (notification: RoleGrantOfferNotification) => void): () => void

handler

type (notification: RoleGrantOfferNotification) => void
returns () => void

RoleGrantOfferSupersedeParams
#

auth/role_grant_offer_notifications.ts view source

ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; to_account_id: $ZodBranded<ZodUUID, "Uuid", "out">; ... 12 more ...; resulting_role_grant_id: ZodNullable<...>; }, $strict>; reason: ZodEnum<...>; cause_id: $ZodBranded<...>; }, $strict> import type {RoleGrantOfferSupersedeParams} from '@fuzdev/fuz_app/auth/role_grant_offer_notifications.js';

Params for role_grant_offer_supersede. Fires to the grantor's sockets when their pending offer is obsoleted — either by a sibling accept (reason: 'sibling_accepted'), by revoke of the resulting role_grant (reason: 'role_grant_revoked'), or by deletion of the parent scope row the offer was bound to (reason: 'scope_destroyed'). cause_id points at the accepted offer id, the revoked role_grant id, or the destroyed scope row id respectively.

RoleGrantParticipationCrossTestOptions
#

testing/cross_backend/role_grant_participation.ts view source

RoleGrantParticipationCrossTestOptions import type {RoleGrantParticipationCrossTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/role_grant_participation.js';

Options for the role-gated-participation success-path parity suite.

setup_test

Per-test fixture producer (in-process or cross-process).

type SetupTest

readonly

rpc_path?

RPC endpoint path. Default /api/rpc.

type string

readonly

RoleGrantRevokeInput
#

auth/role_grant_offer_action_specs.ts view source

ZodObject<{ actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; role_grant_id: $ZodBranded<ZodUUID, "Uuid", "out">; reason: ZodOptional<ZodNullable<ZodString>>; acting: ZodOptional<...>; }, $strict> import type {RoleGrantRevokeInput} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Input for role_grant_revoke. Admin-only mutation that revokes an active role_grant on a target actor. actor_id is the natural key — role_grants are actor-scoped, and the admin UI reads row.actor.id straight from the listing. Deriving actor_id from account_id would collapse under multi-actor accounts.

RoleGrantRevokeOutput
#

auth/role_grant_offer_action_specs.ts view source

ZodObject<{ ok: ZodLiteral<true>; revoked: ZodLiteral<true>; }, $strict> import type {RoleGrantRevokeOutput} from '@fuzdev/fuz_app/auth/role_grant_offer_action_specs.js';

Output for role_grant_revoke.

RoleGrantRevokeParams
#

auth/role_grant_offer_notifications.ts view source

ZodObject<{ role_grant_id: $ZodBranded<ZodUUID, "Uuid", "out">; role: ZodString; scope_id: ZodNullable<$ZodBranded<ZodUUID, "Uuid", "out">>; reason: ZodNullable<...>; }, $strict> import type {RoleGrantRevokeParams} from '@fuzdev/fuz_app/auth/role_grant_offer_notifications.js';

Params for role_grant_revoke. Delivered to the revokee's sockets when one of their active role_grants is revoked. Flat wire shape — revoked_by is admin-UI-visible but deliberately omitted here (the revokee doesn't need to learn the admin's identity). Target account is implicit in the send target.

RoleGrantSummaryJson
#

auth/account_schema.ts view source

ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; role: ZodString; scope_kind: ZodNullable<ZodString>; scope_id: ZodNullable<$ZodBranded<ZodUUID, "Uuid", "out">>; created_at: ZodString; expires_at: ZodNullable<...>; granted_by: ZodNullable<...>; }, $strict> import type {RoleGrantSummaryJson} from '@fuzdev/fuz_app/auth/account_schema.js';

Zod schema for the role_grant summary returned in admin account listings.

RoleName
#

auth/role_schema.ts view source

ZodString import type {RoleName} from '@fuzdev/fuz_app/auth/role_schema.js';

Valid role name: lowercase letters and underscores, no leading/trailing underscore.

RoleSchemaResult
#

auth/role_schema.ts view source

RoleSchemaResult import type {RoleSchemaResult} from '@fuzdev/fuz_app/auth/role_schema.js';

The result of create_role_schema — a Zod schema and spec map for all roles.

Role

Zod schema that validates role strings. Use at I/O boundaries (grant endpoint, role_grant queries).

type z.ZodType<string>

role_specs

Specs for every role (builtins + app-defined). Keyed by role name.

type ReadonlyMap<string, RoleSpec>

RoleSpec
#

auth/role_schema.ts view source

RoleSpec import type {RoleSpec} from '@fuzdev/fuz_app/auth/role_schema.js';

Configuration for a role.

Each role declares the credential types its holders must use, the scope kinds it applies to, and the grant paths through which it can be granted. Every cross-axis field is an open-registry string array — required_credential_types against create_credential_type_schema, applicable_scope_kinds against create_scope_kind_schema, grant_paths against create_grant_path_schema. Pass the registry results to create_role_schema and every entry is checked at construction time.

Empty arrays carry meaning:

  • required_credential_types: [] — any authenticated credential type may exercise the role (the default for app-defined roles).
  • applicable_scope_kinds: [] — the role applies at the global scope only (no scope_kind / scope_id set on its role_grants). This is the default for app-defined roles; consumers add scope kinds explicitly.
  • grant_paths: [] — the role has no grant path declared in this registry; it is unreachable through admin / self-service / system flows. Only useful for diagnostic snapshotting.

Builtins (keeper, admin) ship preconfigured in builtin_role_specs_by_name.

name

Unique role name. Must match RoleName regex; collisions with builtins throw.

type string

description?

Admin-UI-facing copy describing the role's intent.

type string

required_credential_types?

Credential types whose holders are permitted to exercise this role. Each entry is checked at construction time against the credential_types registry passed to create_role_schema. Empty array = any authenticated credential type.

type ReadonlyArray<string>

applicable_scope_kinds?

Scope kinds at which this role's role_grants may be granted. Each entry is checked at construction time against the scope_kinds registry passed to create_role_schema. Empty array = global only. v1 keeps this informative-only (no INSERT-time enforcement).

type ReadonlyArray<string>

grant_paths?

Grant paths through which this role can be granted. Each entry is checked at construction time against the grant_paths registry passed to create_role_schema. Drives downstream defaults:

  • admin_actions.grantable_roles ⊇ {role : 'admin' ∈ grant_paths}
  • self_service_role_actions default eligibility ⊇ {role : 'self_service' ∈ grant_paths}

Empty array = role is not granted via any registered path (only exists for diagnostic / future use).

type ReadonlyArray<string>

RoundTripTestOptions
#

testing/round_trip.ts view source

RoundTripTestOptions import type {RoundTripTestOptions} from '@fuzdev/fuz_app/testing/round_trip.js';

setup_test

Per-test fixture-producing function. describe_round_trip_validation invokes this once in beforeAll (per-describe cadence — see module docstring) to share a single bootstrapped keeper + accounts across every route case.

type SetupTest

surface_source

App surface (with route specs) for route iteration. Constructed in TS by the consumer; same shape for in-process and cross-process tests.

type AppSurfaceSpec

capabilities

Backend capability declarations — see cross_backend/capabilities.ts.

type BackendCapabilities

skip_routes?

Routes to skip, in 'METHOD /path' format.

type Array<string>

input_overrides?

Override generated bodies for specific routes ('METHOD /path' → body).

type Map<string, Record<string, unknown>>

success_fixtures?

Success-case fixtures for routes whose populated success body the generic nil-id input can't reach — referential REST routes whose path params / body must point at existing rows. Maps 'METHOD /path' to an async factory that receives the per-test fixture (so it can seed the referenced state) and returns {url?, body?}: an explicit resolved url (when the factory built it from the ids it just seeded) and/or a request body. Omit url to fall back to the generated valid path.

Distinct from input_overrides (body-only, accepts a valid error envelope): a success_fixtures entry asserts a 2xx response and validates it against the route's output schema — the success-shape parity check the nil-id round-trip can't perform.

type Map< string, (fixture: TestFixture) => Promise<{ url?: string; body?: Record<string, unknown> }> >

RouteAuth
#

http/auth_shape.ts view source

ZodObject<{ account: ZodEnum<{ none: "none"; optional: "optional"; required: "required"; }>; actor: ZodEnum<{ none: "none"; optional: "optional"; required: "required"; }>; roles: ZodOptional<ZodReadonly<...>>; credential_types: ZodOptional<...>; }, $strict> import type {RouteAuth} from '@fuzdev/fuz_app/http/auth_shape.js';

The canonical four-axis auth shape used by both ActionSpec.auth and RouteSpec.auth.

Cross-axis registry invariants enforced via .superRefine:

  1. Roles imply actor. roles?.lengthactor === 'required'. Role checks read the actor's role_grants, so a role-gated spec without a resolved actor would have nothing to check.
  2. No accountless actors yet. account === 'none' && actor !== 'none' is invalid in v1. The credential resolver always binds account before actor today; agent-token / group-actor credentials will lift this.
  3. Unrestricted is leaf. account === 'none' && actor === 'none' ⟹ no roles, no credential_types (nothing left to gate).

Invariant 2 — the `actor !== 'none' ⟺ input or query declares acting?: ActingActor` biconditional — needs introspection of the spec's input/query schemas, so it is checked at registration time, not on this schema. See assert_route_auth_acting_biconditional below.

RouteAuthCategory
#

http/surface_query.ts view source

RouteAuthCategory import type {RouteAuthCategory} from '@fuzdev/fuz_app/http/surface_query.js';

Categorize a RouteAuth into one of the legacy auth buckets.

Returns:

  • 'none' for fully public routes (account === 'none' && actor === 'none')
  • 'keeper' when credential_types includes 'daemon_token'
  • 'role:<name>' for each role declared on auth.roles (multi-role specs are emitted multiple times; callers that need single-bucket grouping should pre-collapse)
  • 'authenticated' for account === 'required' without role / credential gate
  • 'optional' when either axis is 'optional' and no other bucket fits
  • 'other' as a last-resort bucket for shapes that don't match above

RouteContext
#

http/route_spec.ts view source

RouteContext import type {RouteContext} from '@fuzdev/fuz_app/http/route_spec.js';

Per-request deps provided by the framework to route handlers.

Audit writes and other rollback-resilient fire-and-forget calls run through AppDeps.audit.emit(ctx, input) (see auth/audit_emitter.ts), which captures the pool inside its closure — handlers can never accidentally write audits against the request transaction.

Routes whose body manages its own transaction (signup, bootstrap) declare transaction: false on the spec, which makes route.db the pool — they reach for it directly.

db

Transaction-scoped when RouteSpec.transaction is true (the default for non-GET); pool-level otherwise.

type Db

pending_effects

Eager fire-and-forget queue — push the in-flight Promise<void> for pool writes already running (audit emits, session touch, api-token usage tracking). The flush middleware drains via flush_pending_effects after the handler returns.

type Array<Promise<void>>

post_commit_effects

Deferred post-commit thunks — do not push directly; reach for emit_after_commit(ctx, fn) from http/pending_effects.ts. The flush middleware invokes each thunk after the handler (and any wrapping db.transaction) returns, closing the microtask-ordering window that an eager Promise.resolve().then(fn) leaves open inside the transaction.

type Array<() => void | Promise<void>>

RouteErrorSchemas
#

http/error_schemas.ts view source

Partial<Record<number, ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>>> import type {RouteErrorSchemas} from '@fuzdev/fuz_app/http/error_schemas.js';

Error schema map — maps HTTP status codes to Zod schemas.

Used on RouteSpec.errors and internally by derive_error_schemas.

[key: number]

type ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>> | undefined

RouteFactoryDeps
#

auth/deps.ts view source

RouteFactoryDeps import type {RouteFactoryDeps} from '@fuzdev/fuz_app/auth/deps.js';

Capabilities for route spec factories.

AppDeps without db — route handlers receive database connections via RouteContext, so factories don't capture a pool-level Db.

log

Structured logger instance.

type Logger

stat

Get file/directory stats, or null if path doesn't exist.

type (path: string): Promise<StatResult | null>

path

type string
returns Promise<StatResult | null>

read_text_file

Read a file as text.

type (path: string): Promise<string>

path

type string
returns Promise<string>

password

Password hashing operations. Use argon2_password_deps in production.

type PasswordHashDeps

delete_file

Delete a file.

type (path: string): Promise<void>

path

type string
returns Promise<void>

keyring

HMAC-SHA256 cookie signing keyring.

type Keyring

audit

Bound audit emitter. Closes over the pool, its registered listeners, and the optional AuditLogConfig. Built once at backend assembly via create_audit_emitter so handlers can never accidentally write audits against the request transaction — there is no pool slot on the handler context.

type AuditEmitter

fact_store?

Optional content-addressed byte store. Present only on backends that serve binary content (facts) — minimal consumers leave it unset. The consumer constructs a PgFactStore (db/fact_store.ts) wired to a file_fact_fetcher (server/file_fact_fetcher.ts) at its own backend assembly and assigns it here; create_app_backend stays facts-agnostic.

type FactStore

RouteHandler
#

http/route_spec.ts view source

RouteHandler import type {RouteHandler} from '@fuzdev/fuz_app/http/route_spec.js';

Route handler function — receives the Hono context and a RouteContext with per-request deps (db, pending_effects).

TypeScript allows fewer params, so handlers that don't need route can use (c) => ... without changes.

(call)

type (c: Context<any, any, {}>, route: RouteContext): Response | Promise<Response>

c

type Context<any, any, {}>

route

returns Response | Promise<Response>

RouteMethod
#

http/route_spec.ts view source

RouteMethod import type {RouteMethod} from '@fuzdev/fuz_app/http/route_spec.js';

HTTP methods supported by route specs.

routes_by_auth_type
#

http/surface_query.ts view source

(surface: AppSurface): Map<string, AppSurfaceRoute[]> import {routes_by_auth_type} from '@fuzdev/fuz_app/http/surface_query.js';

Group routes by auth category (see RouteAuthCategory). Multi-role specs appear under each of their role buckets.

surface

returns

Map<string, AppSurfaceRoute[]>

RouteSpec
#

http/route_spec.ts view source

RouteSpec import type {RouteSpec} from '@fuzdev/fuz_app/http/route_spec.js';

A single route definition — the unit of the surface map.

input and output schemas align with SAES ActionSpec naming. Use z.null() for routes with no request body (GET, DELETE without body).

method

type RouteMethod

path

type string

auth

type RouteAuth

handler

type RouteHandler

description

type string

params?

URL path parameter schema. Use z.strictObject() with string fields matching :param segments.

REST-only — actions dispatch through a single JSON-RPC endpoint and encode everything in input, so params doesn't appear on ActionSpec.

type z.ZodObject

query?

URL query parameter schema. Use z.strictObject() with string fields.

type z.ZodObject

input

Request body schema. Use z.null() for routes with no body.

type z.ZodType

output

Success response body schema.

type z.ZodType

raw_body?

Marks a route whose request and/or response carries raw bytes or a streaming protocol rather than JSON — git smart-HTTP, file-store binary uploads/downloads, raw internal callbacks. Disambiguates the overloaded input: z.null(), which otherwise can't distinguish "no body" (GET /health) from "raw bytes" (a binary upload).

Purely descriptive metadata — the dispatcher doesn't read it. Its one consumer is the schema-driven round-trip test suite, which auto-skips raw_body routes (it can neither synthesize a meaningful body nor assert a JSON output shape), so consumers no longer hand-maintain a skip_routes entry per binary route. Also surfaces in AppSurfaceRoute so generated docs render "raw" instead of a misleading null body.

type boolean

rate_limit?

Rate limit key type — declares what this route's rate limiter is keyed on.

When set, 429 (RateLimitError) is auto-derived in derive_error_schemas. The actual RateLimiter instance is still wired imperatively in the handler — this field is metadata for surface introspection and policy invariants.

type RateLimitKey

errors?

Handler-specific error response schemas keyed by HTTP status code.

Middleware errors (auth 401/403, validation 400, rate limit 429) are auto-derived from auth, input, and rate_limit. Declare handler-specific errors here (e.g., 404 for not-found, 409 for conflicts).

Explicit entries override auto-derived ones for the same status code.

type RouteErrorSchemas

transaction?

Whether to wrap the handler in a database transaction.

When omitted, defaults are derived from the HTTP method:

  • GETfalse (read-only, no transaction)
  • All others (POST, PUT, DELETE, PATCH) → true

Set explicitly to override the default (e.g., false for a POST that manages its own transaction like signup).

type boolean

rpc_action
#

actions/action_rpc.ts view source

<TSpec extends RequestResponseActionSpec>(spec: TSpec, handler: HandlerForSpec<TSpec>): RpcAction import {rpc_action} from '@fuzdev/fuz_app/actions/action_rpc.js';

Pair a spec with a handler while preserving per-method input/output types and selecting the narrowest ctx.auth shape the spec literal admits.

Constructing {spec, handler} literals widens handler to ActionHandler<any, any>, so spec/handler drift (renamed Zod schema, output field removal, input shape change) slips past the typechecker. rpc_action(spec, handler) binds the handler signature to (input: z.infer<spec.input>, ctx) => z.infer<spec.output> via the generic spec parameter — drift surfaces at the call site.

The ctx.auth narrowing follows the spec's auth.account / auth.actor literals (see HandlerForSpec): an actor-implying spec gets ctx.auth: RequestActorContext; an account-grain spec gets ctx.auth: RequestContext; everything else stays `ctx.auth: RequestContext | null`. Handlers can rely on the dispatcher's runtime guarantee without a manual narrowing call.

Fits fuz_app's factory-closure pattern (handlers close over grantable_roles, app_settings ref, notification_sender, etc.). zzz uses a different shape — a codegen-keyed Record<Method, Handler> map typed via generated ActionInputs/ActionOutputs — which works when handlers are pure (no closure state) and specs are codegen-enumerated. fuz_app's admin + role-grant-offer actions have neither, so per-pair typing at the registration site is the right fit.

Spec-literal preservation is load-bearing: declare specs with satisfies RequestResponseActionSpec (canonical) so auth.actor keeps its 'required' / 'none' literal type. A spec typed directly as RequestResponseActionSpec widens the axes to AuthAxisState and the handler defaults to the loosest tier — sound, but loses the ergonomic narrowing.

spec

type TSpec

handler

type HandlerForSpec<TSpec>

returns

RpcAction

generics

rpc_action<TSpec extends RequestResponseActionSpec>
TSpec

examples

// actor-implying spec → ctx.auth: RequestActorContext rpc_action(role_grant_revoke_action_spec, async (input, ctx) => { const revoker_id = ctx.auth.actor.id; // no narrowing needed }); // account-grain spec → ctx.auth: RequestContext (actor: null) rpc_action(account_verify_action_spec, (_input, ctx) => { return to_session_account(ctx.auth.account); // no narrowing needed });

rpc_call
#

testing/rpc_helpers.ts view source

(args: RpcCallArgs): Promise<RpcCallResult> import {rpc_call} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

One-shot JSON-RPC call over a Hono app.

Merges sensible defaults (host, origin, Content-Type) under caller-provided headers, fires POST (default) or GET, parses the envelope, and returns a discriminated result.

args

returns

Promise<RpcCallResult>

throws

  • Error - if the response body is neither a valid `JsonrpcResponse`

rpc_call_for_spec
#

testing/rpc_helpers.ts view source

<TSpec extends RequestResponseActionSpec>(args: RpcCallForSpecArgs<TSpec>): Promise<RpcCallResultForSpec<TSpec>> import {rpc_call_for_spec} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

Typed wrapper over rpc_call — binds params to z.infer<spec.input> and the success result to z.infer<spec.output> via the generic.

Success results are validated at runtime against spec.output (same contract as rpc_call_typed); a mismatch throws. Error responses come back on the discriminated {ok: false, error} branch — use this for happy-path + denial-path assertions where the error data.reason shape is still asserted manually. For adversarial input tests that send malformed params, use the untyped rpc_call.

args

type RpcCallForSpecArgs<TSpec>

returns

Promise<RpcCallResultForSpec<TSpec>>

generics

rpc_call_for_spec<TSpec extends RequestResponseActionSpec>
TSpec

throws

  • Error - if the success `result` does not parse against `spec.output`,

rpc_call_non_browser
#

testing/rpc_helpers.ts view source

(args: Omit<RpcCallArgs, "suppress_default_origin">): Promise<RpcCallResult> import {rpc_call_non_browser} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

Same as rpc_call but without the default origin header. Use for bearer-auth probes: bearer_auth discards the token when Origin or Referer is present (browser context), so a bearer probe via rpc_call would short-circuit to 401 before the token is ever validated.

Equivalent to rpc_call({...args, suppress_default_origin: true}).

args

type Omit<RpcCallArgs, "suppress_default_origin">

returns

Promise<RpcCallResult>

rpc_call_typed
#

testing/rpc_helpers.ts view source

<T>(args: RpcCallArgs, output_schema: ZodType<T, unknown, $ZodTypeInternals<T, unknown>>): Promise<T> import {rpc_call_typed} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

Same as rpc_call but parses the success result through the given output schema and returns typed data. Envelope-level failures or error responses throw — use the untyped rpc_call for tests that need to assert on specific error shapes.

args

output_schema

type ZodType<T, unknown, $ZodTypeInternals<T, unknown>>

returns

Promise<T>

generics

rpc_call_typed<T>
T

throws

  • Error - if the response is a JSON-RPC error, if `rpc_call` throws

RpcAction
#

RpcAttackSurfaceOptions
#

testing/rpc_attack_surface.ts view source

RpcAttackSurfaceOptions import type {RpcAttackSurfaceOptions} from '@fuzdev/fuz_app/testing/rpc_attack_surface.js';

build

Build the app surface bundle (surface + route specs + middleware specs + rpc_endpoints).

type () => AppSurfaceSpec

roles

All roles in the app (e.g. ['admin', 'keeper']).

type Array<string>

RpcCallApp
#

testing/rpc_helpers.ts view source

RpcCallApp import type {RpcCallApp} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

App shape accepted by rpc_call. Either a Hono-like object with a .request(input, init) method (in-process TestApp.app directly) or a bare RpcTestTransport callable (cross-process fixture.transport).

RpcCallArgs
#

testing/rpc_helpers.ts view source

RpcCallArgs import type {RpcCallArgs} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

Arguments for rpc_call.

app

Hono-like app or bare RpcTestTransport callable.

type RpcCallApp

path

RPC endpoint path, e.g. '/api/rpc'.

type string

method

JSON-RPC method name.

type string

params?

Params for the call. Omit (or pass undefined) for parameterless (z.void()) methods — the helper drops params from the envelope either way. See create_rpc_post_init for the null-stripping affordance and JSON-RPC 2.0 §4.2's prohibition on params: null.

type unknown

headers?

Extra request headers (session cookie, bearer, etc.). Overrides defaults.

type Record<string, string>

id?

Request id. Defaults to 'test'.

type string | number

verb?

HTTP verb — 'POST' (default) or 'GET' for side_effects: false methods.

type 'POST' | 'GET'

suppress_default_origin?

Suppress the default origin header. Required for bearer-auth paths: bearer_auth discards the token when Origin or Referer is present (browser context), so probing it via rpc_call needs this flag — or use rpc_call_non_browser, which sets it for you.

type boolean

RpcCallForSpecArgs
#

testing/rpc_helpers.ts view source

RpcCallForSpecArgs<TSpec> import type {RpcCallForSpecArgs} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

Arguments for rpc_call_for_spec. spec replaces the loose method field.

generics

RpcCallForSpecArgs<TSpec extends RequestResponseActionSpec>
TSpec

path

RPC endpoint path, e.g. '/api/rpc'.

type string

id?

Request id. Defaults to 'test'.

type string | number

suppress_default_origin?

Suppress the default origin header. Required for bearer-auth paths: bearer_auth discards the token when Origin or Referer is present (browser context), so probing it via rpc_call needs this flag — or use rpc_call_non_browser, which sets it for you.

type boolean

app

Hono-like app or bare RpcTestTransport callable.

type RpcCallApp

headers?

Extra request headers (session cookie, bearer, etc.). Overrides defaults.

type Record<string, string>

verb?

HTTP verb — 'POST' (default) or 'GET' for side_effects: false methods.

type "GET" | "POST"

spec

Action spec whose method drives the envelope and whose input/output types pin params + result.

type TSpec

params

Params, typed against spec.input.

type output<TSpec["input"]>

RpcCallResult
#

testing/rpc_helpers.ts view source

RpcCallResult import type {RpcCallResult} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

Discriminated return from rpc_call. status is the HTTP status; headers is the response-headers snapshot (lowercased keys) so callers can assert header-level properties (e.g. no backend-fingerprinting headers).

RpcCallResultForSpec
#

testing/rpc_helpers.ts view source

RpcCallResultForSpec<TSpec> import type {RpcCallResultForSpec} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

Typed discriminated result returned by rpc_call_for_spec. The success branch's result is inferred from TSpec['output']. The error branch stays untyped because JSON-RPC error.data shapes vary per error and are asserted per call site.

generics

RpcCallResultForSpec<TSpec extends RequestResponseActionSpec>
TSpec

RpcClientCallOptions
#

actions/rpc_client.ts view source

RpcClientCallOptions import type {RpcClientCallOptions} from '@fuzdev/fuz_app/actions/rpc_client.js';

Per-call options accepted by every typed Proxy method. Same shape as ActionDispatcherSendOptions — the client threads these through unchanged to the underlying peer. transport_name overrides the per-method transport_for_method selector for this call.

inheritance

RpcEndpointSpec
#

http/surface.ts view source

RpcEndpointSpec import type {RpcEndpointSpec} from '@fuzdev/fuz_app/http/surface.js';

An RPC endpoint definition for surface generation.

path

type string

actions

type Array<RpcAction>

RpcEndpointsSuiteOption
#

testing/rpc_helpers.ts view source

RpcEndpointsSuiteOption import type {RpcEndpointsSuiteOption} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

Union accepted by the suite-level rpc_endpoints option — eager array or a factory that takes an AppServerContext and returns endpoint specs. The factory form is required when action handlers must close over the per-test ctx.deps (e.g. the canonical create_standard_rpc_actions(ctx.deps) pattern). create_app_server resolves either shape natively; test helpers forward the raw value to the top-level rpc_endpoints slot on CreateTestAppOptions for live dispatch.

RpcMethodCoverageInput
#

testing/cross_backend/method_coverage.ts view source

RpcMethodCoverageInput import type {RpcMethodCoverageInput} from '@fuzdev/fuz_app/testing/cross_backend/method_coverage.js';

Inputs for .

live_methods

Every method the live RPC endpoint mounts (action.spec.method).

type ReadonlyArray<string>

readonly

declared_methods

The declared-surface method names (from create_*_surface_spec).

type ReadonlyArray<string>

readonly

manifest

The tagged manifest the live set must reconcile against.

type ReadonlyArray<MethodCoverageEntry>

readonly

testing_method_prefix?

Backdoor method prefix. Defaults to '_testing_'.

type string

readonly

RpcPathCrossSuiteOptions
#

testing/cross_backend/setup.ts view source

RpcPathCrossSuiteOptions import type {RpcPathCrossSuiteOptions} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

CrossSuiteOptions plus the RPC endpoint path the suite dispatches against — the shape every JSON-RPC-driven imperative suite takes (cell verbs, origin, body-size, actor lookup/search, account lifecycle, app settings, testing backdoor). Suites alias this under a self-documenting per-suite name (e.g. OriginCrossTestOptions = RpcPathCrossSuiteOptions).

inheritance

rpc_path?

RPC endpoint path the methods are mounted on. Default /api/rpc.

type string

readonly

RpcResult
#

testing/cross_backend/cell_cross_helpers.ts view source

RpcResult import type {RpcResult} from '@fuzdev/fuz_app/testing/cross_backend/cell_cross_helpers.js';

Minimal JSON-RPC envelope shape the suites read off responses.

ok

type boolean

readonly

result?

type unknown

readonly

error?

type { readonly code: number; readonly message: string; readonly data?: unknown }

readonly

RpcRoundTripTestOptions
#

testing/rpc_round_trip.ts view source

RpcRoundTripTestOptions import type {RpcRoundTripTestOptions} from '@fuzdev/fuz_app/testing/rpc_round_trip.js';

setup_test

Per-test fixture-producing function (per-describe cadence).

type SetupTest

surface_source

App surface (with route + RPC endpoint specs) for RPC endpoint enumeration. Constructed in TS by the consumer; same shape for in-process and cross-process tests.

type AppSurfaceSpec

capabilities

Backend capability declarations.

type BackendCapabilities

session_options

Session config — only needed to resolve factory-form rpc_endpoints against a stub AppServerContext at setup time (the actions' input schemas drive params generation; auth/dispatch run against the real backend through fixture.transport).

type SessionOptions<string>

rpc_endpoints

RPC endpoint specs — eager array or factory. The factory must return the same endpoint path + spec.method list regardless of ctx (invoked once at setup with a stub ctx; the real per-test live dispatch goes through whatever the backend was started with).

type RpcEndpointsSuiteOption

skip_methods?

Methods to skip, by name (e.g., 'zap_plan').

type Array<string>

input_overrides?

Override generated params for specific methods (method name → params).

type Map<string, Record<string, unknown>>

success_fixtures?

Success-case fixtures for methods whose populated success body the generic nil-id input can't reach — referential reads (*_get, *_log) whose required ids must point at existing rows. Maps method name to an async factory that receives the per-test fixture (so it can seed the referenced state — e.g. create a repo via fixture.transport + fixture.create_session_headers()) and returns the params that drive a success response.

Distinct from input_overrides, which only swaps the request params; the response may still be a valid *error* envelope (missing-row not_found), which the generic loop accepts. A success_fixtures entry asserts the response is ok and validates result against the method's output schema — so a backend that drops a field, or errors where the other backend succeeds, fails loud. This is the success-shape parity check the nil-id round-trip structurally cannot perform (it only ever sees error envelopes for referential methods).

Fired as POST. The factory runs against the shared per-describe fixture, so it must not assume a clean slate between entries (seed unique state).

type Map<string, (fixture: TestFixture) => Promise<Record<string, unknown>>>

RpcTestTransport
#

testing/rpc_helpers.ts view source

RpcTestTransport import type {RpcTestTransport} from '@fuzdev/fuz_app/testing/rpc_helpers.js';

Minimal transport surface — the duck type Hono.request already satisfies. Extracted so test setups that want an in-process / WS / mock path can plug a different dispatcher without changing call sites.

(call)

type (url: string, init: RequestInit): Promise<Response>

url

type string

init

type RequestInit
returns Promise<Response>

run_auth_cleanup
#

auth/cleanup.ts view source

(deps: AuthCleanupDeps): Promise<AuthCleanupResult> import {run_auth_cleanup} from '@fuzdev/fuz_app/auth/cleanup.js';

Run every auth cleanup sweep — expired sessions and expired role_grant offers — and return the counts.

Consumers call this from a scheduled task (setInterval, cron, etc.) alongside their own domain cleanup. Errors from individual sweeps are re-thrown so the caller's scheduler can log/alert; use the per-task helpers (query_session_cleanup_expired, cleanup_expired_role_grant_offers) directly if you need finer error isolation.

deps

returns

Promise<AuthCleanupResult>

throws

  • Error - re-thrown from any sweep that fails (no per-sweep isolation here)

run_cross_impl_bench
#

run_local
#

cli/util.ts view source

(runtime: CommandDeps, command: string, args: string[]): Promise<CommandResult> import {run_local} from '@fuzdev/fuz_app/cli/util.js';

Run a local command and return the result.

runtime

runtime with run_command capability

command

command to run

type string

args

command arguments

type string[]

returns

Promise<CommandResult>

command result

run_migrations
#

db/migrate.ts view source

(db: Db, namespaces: MigrationNamespace[]): Promise<MigrationResult[]> import {run_migrations} from '@fuzdev/fuz_app/db/migrate.js';

Run pending migrations for each namespace.

For each namespace: acquires an advisory lock, reads applied rows ordered by sequence, length-checks (binary-older-than-db short-circuits), name- prefix-verifies, then runs the pending tail in a single chain transaction. Each migration's row is INSERTed with sequence = max(sequence) + 1 for the namespace.

Length check before name verify is load-bearing: a binary-older case with a rename in the overlap would otherwise fire name-divergence-at-N first and the operator would chase a phantom source-revert before discovering the binary is the real problem.

Atomicity: any failure rolls back every migration that ran in that invocation. Namespaces are independent: a later namespace's failure does not roll back an earlier namespace that already committed.

Concurrency: per-namespace advisory locks reduce contention in multi-instance deployments but are best-effort on pool drivers (see the module docstring's "Advisory locking" notes). Correctness on concurrent boots falls out of chain-tx atomicity + the (namespace, name) PK — the loser's INSERT triggers PK violation and rollback; subsequent boots see the committed state.

db

the database instance

type Db

namespaces

migration namespaces, processed in the order passed

type MigrationNamespace[]

returns

Promise<MigrationResult[]>

one result per namespace where work happened (already-up-to-date namespaces are omitted)

throws

  • MigrationError - with `kind` of `binary-older-than-db`,

mutates

  • schema_version — inserts one row per applied migration

RunCommandOptions
#

runtime/deps.ts view source

RunCommandOptions import type {RunCommandOptions} from '@fuzdev/fuz_app/runtime/deps.js';

Options for run_command.

cwd?

Working directory for the child process.

type string

signal?

AbortSignal to terminate the child process.

type AbortSignal

timeout_ms?

Kill the process and return timed_out: true after this many milliseconds.

type number

RunCrossImplBenchOptions
#

testing/cross_backend/bench/run_cross_impl_bench.ts view source

RunCrossImplBenchOptions import type {RunCrossImplBenchOptions} from '@fuzdev/fuz_app/testing/cross_backend/bench/run_cross_impl_bench.js';

handles

Already-bootstrapped backends to benchmark. Each one's keeper_transport is the pre-authed transport scenarios fire against — bootstrap once, then hammer; no per-iteration reset.

type ReadonlyArray<BootstrappedBackendHandle>

readonly

scenarios

type ReadonlyArray<BenchScenario>

readonly

config?

Overrides merged over the network-tuned defaults below.

type Partial<BenchmarkConfig>

readonly

RunOptions
#

ui/async_slot.svelte.ts view source

RunOptions import type {RunOptions} from '@fuzdev/fuz_app/ui/async_slot.svelte.js';

signal?

External signal chained into the slot's internal controller. Aborts the in-flight run when fired (alongside automatic supersession by the next run() and manual calls).

type AbortSignal

RuntimeDeps
#

runtime/deps.ts view source

RuntimeDeps import type {RuntimeDeps} from '@fuzdev/fuz_app/runtime/deps.js';

Full runtime capabilities returned by create_deno_runtime or create_node_runtime.

Extends all *Deps interfaces with additional app-level capabilities. Functions should accept narrow *Deps interfaces, not this full type — this type is for the wiring layer that creates and passes the runtime.

inheritance

env_all

Get all environment variables.

type () => Record<string, string>

args

CLI arguments passed to the program.

type ReadonlyArray<string>

readonly

cwd

Get current working directory.

type () => string

run_command_inherit

Run a command with inherited stdout/stderr (output goes directly to terminal).

type (cmd: string, args: Array<string>) => Promise<number>

rust_default_capabilities
#

rust_default_shape_notes
#

testing/cross_backend/default_backend_configs.ts view source

BackendShapeNotes import {rust_default_shape_notes} from '@fuzdev/fuz_app/testing/cross_backend/default_backend_configs.js';

Shape notes for Rust-family backends — wiring facts, not gating flags. Adds trusted_proxy: true (the Rust spine's client-IP middleware is always wired; the env-gate only chooses XFF vs the TCP peer IP) and login_rate_limit: true (env-gated bucket on /login + /password). Documentation only — see BackendShapeNotes.

rust_spine_stub_backend_config
#

testing/cross_backend/rust_spine_stub_backend_config.ts view source

(options?: SpineStubBackendConfigOptions): BackendConfig import {rust_spine_stub_backend_config} from '@fuzdev/fuz_app/testing/cross_backend/rust_spine_stub_backend_config.js';

Build the BackendConfig for testing_spine_stub. Resolves the binary from options.binary_path or FUZ_TESTING_RUST_SPINE_STUB_BIN; throws when neither is set so a missing build surfaces as a clear error rather than a confusing spawn failure. Reconciles the binary's env contract: port via --port (and FUZ_RUST_SPINE_STUB_PORT), daemon-token dir via FUZ_RUST_SPINE_STUB_DIR (anchored to paths.root so the written {dir}/run/daemon_token matches the path spawn_backend reads).

options

default {}

returns

BackendConfig

throws

  • Error - when no binary path is available.

RUST_SPINE_STUB_BIN_ENV
#

RUST_SPINE_STUB_DEFAULT_DATABASE_URL
#

testing/cross_backend/rust_spine_stub_backend_config.ts view source

"postgres://localhost/fuz_app_test_rust_spine_stub" import {RUST_SPINE_STUB_DEFAULT_DATABASE_URL} from '@fuzdev/fuz_app/testing/cross_backend/rust_spine_stub_backend_config.js';

Default Postgres database — real PG (PGlite isn't reachable from tokio-postgres).

RUST_SPINE_STUB_DEFAULT_PORT
#

RUST_SPINE_STUB_EXPECTED_SCHEMA_PATH_ENV
#

testing/cross_backend/rust_spine_stub_backend_config.ts view source

"FUZ_RUST_SPINE_STUB_EXPECTED_SCHEMA_PATH" import {RUST_SPINE_STUB_EXPECTED_SCHEMA_PATH_ENV} from '@fuzdev/fuz_app/testing/cross_backend/rust_spine_stub_backend_config.js';

Env var the stub reads for the absolute path of the committed expected_schema.json its /ready gate introspects against. Pointed at the same fixture the TS spine reads () — column-presence is engine-portable, so one file is the cross-impl contract.

save_config
#

cli/config.ts view source

<T>(runtime: Pick<FsWriteDeps, "mkdir" | "write_text_file">, path: string, dir: string, config: T): Promise<void> import {save_config} from '@fuzdev/fuz_app/cli/config.js';

Save CLI configuration to a JSON file.

runtime

runtime with file write capability

type Pick<FsWriteDeps, "mkdir" | "write_text_file">

path

path to the config JSON file

type string

dir

directory containing the config file (created if missing)

type string

config

configuration to save

type T

returns

Promise<void>

generics

save_config<T>
T

mutates

  • filesystem — creates `dir` (recursive) and writes JSON to `path`

scan_env_vars
#

env/resolve.ts view source

(obj: unknown): EnvVarRef[] import {scan_env_vars} from '@fuzdev/fuz_app/env/resolve.js';

Recursively scan an object for $$VAR$$ env var references.

Walks all string values in the object tree and extracts env var names with their path context for error reporting. Escaped references (\$$VAR$$) are skipped — they're literal text, not references. The optional flag on each ref distinguishes $$VAR$$ (required) from $$?VAR$$ (optional) for downstream validation.

obj

object to scan (typically a config)

type unknown

returns

EnvVarRef[]

array of env var references with paths and optional flags

schema_to_surface
#

http/schema_helpers.ts view source

(schema: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>): unknown import {schema_to_surface} from '@fuzdev/fuz_app/http/schema_helpers.js';

Convert a Zod schema to a JSON-serializable representation for the surface.

Returns null for null schemas, JSON Schema for object schemas.

schema

type ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>

returns

unknown

SchemaDiff
#

testing/schema_parity.ts view source

SchemaDiff import type {SchemaDiff} from '@fuzdev/fuz_app/testing/schema_parity.js';

Structured drift entry. where is the named source impl ('a' or 'b').

SchemaDiffLabels
#

testing/schema_parity.ts view source

SchemaDiffLabels import type {SchemaDiffLabels} from '@fuzdev/fuz_app/testing/schema_parity.js';

Labels used in formatted output — defaults to 'a' and 'b'.

a?

type string

readonly

b?

type string

readonly

SchemaDriftResult
#

db/schema_ready.ts view source

SchemaDriftResult import type {SchemaDriftResult} from '@fuzdev/fuz_app/db/schema_ready.js';

Outcome of a schema-drift check.

ok

type boolean

missing_tables

Expected tables absent from the live DB.

type Array<string>

missing_columns

Per-table columns the expected schema declares that the live DB lacks.

type Array<MissingColumns>

SchemaFieldMeta
#

schema_meta.ts view source

SchemaFieldMeta import type {SchemaFieldMeta} from '@fuzdev/fuz_app/schema_meta.js';

Zod .meta() shape for fuz_app schema metadata conventions.

description?

type string

sensitivity?

Sensitivity level for masking/redaction. 'secret' masks the value.

type Sensitivity

SchemaSnapshot
#

testing/schema_introspect.ts view source

ZodObject<{ tables: ZodRecord<ZodString, ZodObject<{ columns: ZodRecord<ZodString, ZodObject<{ data_type: ZodString; udt_name: ZodString; is_nullable: ZodBoolean; column_default: ZodNullable<...>; is_identity: ZodBoolean; }, $strip>>; indexes: ZodArray<...>; constraints: ZodArray<...>; }, $strip>>; sequences: ZodRec... import type {SchemaSnapshot} from '@fuzdev/fuz_app/testing/schema_introspect.js';

Normalized database schema snapshot for parity comparison — the single source of truth for the snapshot shape across the introspection query (query_schema_snapshot), the diff comparator (testing/schema_parity.ts), and the cross-impl RPC action's wire validator (testing/cross_backend/testing_reset_actions.ts).

All fields are deterministically ordered on capture so structural equality via JSON.stringify or per-key comparison yields stable results.

SCOPE_KIND_NAME_REGEX
#

auth/scope_kind_schema.ts view source

RegExp import {SCOPE_KIND_NAME_REGEX} from '@fuzdev/fuz_app/auth/scope_kind_schema.js';

Letter (lowercase a-z) start and end (or single letter), with letters and underscores in between. Mirrors RoleName. Rejects empty strings, leading or trailing underscores, uppercase, digits, and the index-side 'GLOBAL' token.

ScopeKindMeta
#

auth/scope_kind_schema.ts view source

ScopeKindMeta import type {ScopeKindMeta} from '@fuzdev/fuz_app/auth/scope_kind_schema.js';

Per-scope-kind metadata. description is admin-UI-facing copy (mirrors RoleSpec.description). Open shape so v2 can extend without a breaking change.

description?

type string

ScopeKindName
#

ScopeKindSchemaResult
#

auth/scope_kind_schema.ts view source

ScopeKindSchemaResult import type {ScopeKindSchemaResult} from '@fuzdev/fuz_app/auth/scope_kind_schema.js';

The result of create_scope_kind_schema — a Zod schema and metadata map.

ScopeKind

Zod schema that validates scope-kind name strings against the registered set. Use at I/O boundaries (admin UIs, codegen) and as the construction-time check inside create_role_schema for every RoleSpec.applicable_scope_kinds entry.

type z.ZodType<string>

scope_kinds

Map of every registered scope-kind to its metadata. Keyed by name. Read at startup by admin / codegen surfaces.

type ReadonlyMap<string, ScopeKindMeta>

seed_dev_account
#

dev/setup.ts view source

(deps: SeedDevAccountDeps, input: SeedDevAccountInput, options?: { log?: SetupLogger | undefined; } | undefined): Promise<...> import {seed_dev_account} from '@fuzdev/fuz_app/dev/setup.js';

Seed a development test account, bypassing username/password policy.

Idempotent by username — if an account with the given username already exists, reuses it and only reconciles the requested role grants. Never updates an existing password (rerun would silently rotate it).

Intended for scripts/dev_setup.ts — do not call in production.

deps

input

options?

type { log?: SetupLogger | undefined; } | undefined
optional

returns

Promise<SeedDevAccountResult>

throws

  • Error - if an existing account is found without an associated actor row

mutates

  • database — inserts an account/actor pair when missing and grants any requested role role_grants

SeedDevAccountDeps
#

dev/setup.ts view source

SeedDevAccountDeps import type {SeedDevAccountDeps} from '@fuzdev/fuz_app/dev/setup.js';

Dependencies for seed_dev_account.

inheritance

extends: QueryDeps

hash_password

Password hasher (e.g., argon2_password_deps.hash_password).

type (password: string) => Promise<string>

SeedDevAccountInput
#

dev/setup.ts view source

SeedDevAccountInput import type {SeedDevAccountInput} from '@fuzdev/fuz_app/dev/setup.js';

username

Account username. Policy is bypassed — any non-empty string is accepted.

type string

password

Account password. Policy is bypassed — any non-empty string is accepted.

type string

roles?

Roles to grant via role_grant (idempotent).

type ReadonlyArray<string>

SeedDevAccountResult
#

dev/setup.ts view source

SeedDevAccountResult import type {SeedDevAccountResult} from '@fuzdev/fuz_app/dev/setup.js';

Result of seed_dev_account.

account_id

type string

actor_id

type string

created

True if a new account was created; false if one already existed.

type boolean

select_auth_app
#

testing/auth_apps.ts view source

(apps: AuthTestApps, auth: { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }): Hono<...> import {select_auth_app} from '@fuzdev/fuz_app/testing/auth_apps.js';

Select the Hono test app with correct auth for a route.

apps

auth

type { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }

returns

Hono<BlankEnv, BlankSchema, "/">

throws

  • Error - if `auth.roles` names a role not present in `apps.by_role` —

self_service_role_set_action_spec
#

auth/self_service_role_action_specs.ts view source

{ method: string; kind: "request_response"; initiator: "frontend"; auth: { account: "required"; actor: "required"; }; side_effects: true; input: ZodObject<{ role: ZodString; enabled: ZodBoolean; acting: ZodOptional<...>; }, $strict>; output: ZodObject<...>; async: true; description: string; rate_limit: "account"; } import {self_service_role_set_action_spec} from '@fuzdev/fuz_app/auth/self_service_role_action_specs.js';

rate_limit: 'account' bounds audit-row churn. The toggle is idempotent (changed: false re-grants/re-revokes), but every call still writes a role_grant_create or role_grant_revoke audit row with self_service: true. Without the cap, a caller could flap the role in a loop to inflate the audit log and obscure other activity.

SelfServiceRoleActionsOptions
#

auth/self_service_role_actions.ts view source

SelfServiceRoleActionsOptions import type {SelfServiceRoleActionsOptions} from '@fuzdev/fuz_app/auth/self_service_role_actions.js';

eligible_roles?

Optional override allowlist of role strings eligible for self-service. When omitted, eligibility is derived from roles.role_specs (or builtin_role_specs_by_name when roles is also omitted) by selecting every role whose RoleSpec.grant_paths includes 'self_service'. Pass an empty array to lock the surface down (every call comes back as forbidden with reason role_not_self_service_eligible).

When supplied alongside roles, every entry is checked against roles.role_specs at factory time so typos throw at startup.

type ReadonlyArray<string>

roles?

Optional role schema. Drives default eligibility derivation from RoleSpec.grant_paths and validates the eligible_roles override (when supplied) against the registered role set.

type RoleSchemaResult

SelfServiceRoleSetInput
#

auth/self_service_role_action_specs.ts view source

ZodObject<{ role: ZodString; enabled: ZodBoolean; acting: ZodOptional<$ZodBranded<ZodUUID, "Uuid", "out">>; }, $strict> import type {SelfServiceRoleSetInput} from '@fuzdev/fuz_app/auth/self_service_role_action_specs.js';

Input for self_service_role_set.

SelfServiceRoleSetOutput
#

auth/self_service_role_action_specs.ts view source

ZodObject<{ ok: ZodLiteral<true>; enabled: ZodBoolean; changed: ZodBoolean; }, $strict> import type {SelfServiceRoleSetOutput} from '@fuzdev/fuz_app/auth/self_service_role_action_specs.js';

Output for self_service_role_set. enabled echoes the post-call state (always equals the input enabled on success). changed is true only when the call mutated — re-grants / re-revokes return false.

sensitive_field_blocklist
#

testing/integration_helpers.ts view source

readonly string[] import {sensitive_field_blocklist} from '@fuzdev/fuz_app/testing/integration_helpers.js';

Field names that must never appear in any HTTP response body.

Sensitivity
#

sensitivity.ts view source

"secret" import type {Sensitivity} from '@fuzdev/fuz_app/sensitivity.js';

Sensitivity level for a schema field.

  • 'secret' — value is masked in logs and UI (e.g. passwords, API keys, signing keys)

SequenceSnapshot
#

testing/schema_introspect.ts view source

ZodObject<{ data_type: ZodString; }, $strip> import type {SequenceSnapshot} from '@fuzdev/fuz_app/testing/schema_introspect.js';

Sequence metadata — data_type is bigint (BIGSERIAL) or integer (SERIAL).

SerializableBootstrappedBackendHandle
#

testing/cross_backend/setup.ts view source

SerializableBootstrappedBackendHandle import type {SerializableBootstrappedBackendHandle} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

Serializable subset of suitable for vitest's project.provide() — vitest 4 hard-rejects non-serializable values, so the live child: ChildProcess + teardown: () => Promise<void> + keeper_transport: FetchTransport (closure) must stay in the globalSetup process. The handful of fields tests actually read (config, daemon_token, keeper_account, keeper_actor, keeper_cookies) round-trip through structured clone fine.

globalSetup calls before project.provide; test files call on the injected value to rebuild a usable handle (without child / teardown — lifecycle stays with globalSetup).

config

type BackendHandle['config']

readonly

daemon_token

type BackendHandle['daemon_token']

readonly

keeper_account

type BootstrappedBackendHandle['keeper_account']

readonly

keeper_actor

type BootstrappedBackendHandle['keeper_actor']

readonly

keeper_cookies

type ReadonlyArray<string>

readonly

serialize_bootstrapped_handle
#

testing/cross_backend/setup.ts view source

(handle: BootstrappedBackendHandle): SerializableBootstrappedBackendHandle import {serialize_bootstrapped_handle} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

Strip the non-serializable members so the result can be passed to vitest's project.provide. Call in globalSetup before provide.

handle

returns

SerializableBootstrappedBackendHandle

ServeHandle
#

testing/cross_backend/testing_server_core.ts view source

ServeHandle import type {ServeHandle} from '@fuzdev/fuz_app/testing/cross_backend/testing_server_core.js';

Adapter-built handle to a bound HTTP server.

shutdown stops accepting new connections and drains in-flight ones. native is an adapter-specific server reference — used by Node's @hono/node-ws injectWebSocket(server) post-serve hook; Deno leaves it unset.

shutdown

type () => Promise<void>

native?

Adapter-specific server ref for post-serve hooks. Type-erased at the seam.

type unknown

ServerEnvOptions
#

server/env.ts view source

ServerEnvOptions import type {ServerEnvOptions} from '@fuzdev/fuz_app/server/env.js';

Validated server env config — the artifacts create_app_server() needs.

ok

type true

keyring

type Keyring

allowed_origins

type Array<RegExp>

bootstrap_token_path

type string | null

ServerEnvOptionsError
#

server/env.ts view source

ServerEnvOptionsError import type {ServerEnvOptionsError} from '@fuzdev/fuz_app/server/env.js';

Error from validate_server_env — keyring or origin validation failed.

ok

type false

field

type 'SECRET_FUZ_COOKIE_KEYS' | 'FUZ_ALLOWED_ORIGINS'

errors

type Array<string>

ServerEnvOptionsResult
#

server/env.ts view source

ServerEnvOptionsResult import type {ServerEnvOptionsResult} from '@fuzdev/fuz_app/server/env.js';

ServerHeartbeatOptions
#

actions/register_action_ws.ts view source

ServerHeartbeatOptions import type {ServerHeartbeatOptions} from '@fuzdev/fuz_app/actions/register_action_ws.js';

timeout?

Receive-silence (ms) past which the server closes the socket with WS_CLOSE_SERVER_HEARTBEAT_TIMEOUT. Any incoming message resets the counter — chatty clients never trip it. First timeout window after socket open is exempt (cold-start grace).

type number

ServerStatusOptions
#

http/common_routes.ts view source

ServerStatusOptions import type {ServerStatusOptions} from '@fuzdev/fuz_app/http/common_routes.js';

Options for the authenticated server status route.

version

Application version string.

type string

get_uptime_ms

Returns milliseconds since server start.

type () => number

ServeStaticFactory
#

server/static.ts view source

ServeStaticFactory import type {ServeStaticFactory} from '@fuzdev/fuz_app/server/static.js';

Factory function that creates a static file serving middleware.

Matches the signature of serveStatic from hono/deno and @hono/node-server/serve-static.

(call)

type (options: ServeStaticOptions): MiddlewareHandler

options

returns MiddlewareHandler

ServeStaticOptions
#

server/static.ts view source

ServeStaticOptions import type {ServeStaticOptions} from '@fuzdev/fuz_app/server/static.js';

Options for serve_static factory functions (matches Hono's serveStatic signature).

root

type string

rewriteRequestPath?

type (path: string) => string

mimes?

type Record<string, string>

SESSION_AGE_MAX
#

auth/session_cookie.ts view source

number import {SESSION_AGE_MAX} from '@fuzdev/fuz_app/auth/session_cookie.js';

Cookie max age in seconds (30 days — aligned with AUTH_SESSION_LIFETIME_MS).

session_cookie_options
#

SESSION_REFRESH_THRESHOLD_S
#

auth/session_cookie.ts view source

number import {SESSION_REFRESH_THRESHOLD_S} from '@fuzdev/fuz_app/auth/session_cookie.js';

Threshold (seconds) at which process_session_cookie re-signs a still-valid cookie to extend its embedded expiration. Mirrors the DB-side AUTH_SESSION_EXTEND_THRESHOLD_MS so a continuously-active user's cookie stays in sync with their server-side session lifetime. Set SessionOptions.refresh_threshold_seconds = 0 to disable.

session_touch_fire_and_forget
#

auth/session_queries.ts view source

(deps: QueryDeps, token_hash: string, pending_effects: Promise<void>[] | undefined, log: Logger): Promise<void> import {session_touch_fire_and_forget} from '@fuzdev/fuz_app/auth/session_queries.js';

Touch a session without blocking the caller.

Errors are logged to console — session touching never breaks request flows. Pass pending_effects (from c.var.pending_effects) to register the promise for test flushing.

deps

query dependencies

token_hash

blake3 hash of the session token

type string

pending_effects

optional array to register the effect for later awaiting

type Promise<void>[] | undefined

log

the logger instance

type Logger

returns

Promise<void>

the settled promise (callers may ignore it — fire-and-forget semantics preserved)

SessionAccount
#

auth/account_schema.ts view source

SessionAccount import type {SessionAccount} from '@fuzdev/fuz_app/auth/account_schema.js';

Account without sensitive fields, scoped to the authenticated user's own session.

id

type Uuid

username

type Username

email

type Email | null

email_verified

type boolean

created_at

type string

SessionAccountJson
#

auth/account_schema.ts view source

ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; username: ZodPipe<ZodString, ZodTransform<string, string>>; email: ZodNullable<ZodString>; email_verified: ZodBoolean; created_at: ZodString; }, $strict> import type {SessionAccountJson} from '@fuzdev/fuz_app/auth/account_schema.js';

Zod schema for SessionAccount — account without sensitive fields.

SessionCookieOptions
#

auth/session_cookie.ts view source

SessionCookieOptions import type {SessionCookieOptions} from '@fuzdev/fuz_app/auth/session_cookie.js';

Cookie options for session cookies.

path

type string

httpOnly

type boolean

secure

type boolean

sameSite

type 'strict' | 'lax' | 'none'

maxAge

type number

SessionId
#

auth/account_schema.ts view source

$ZodBranded<ZodString, "SessionId", "out"> import type {SessionId} from '@fuzdev/fuz_app/auth/account_schema.js';

A session's storage key — the blake3 hash of its raw token, branded so a bare string can't stand in for one. Blake3Hash (hash_schemas.ts) is the shape; this is the meaning. Mint only via hash_session_token, or SessionId.parse at an external boundary.

The raw token itself is never typed — it stays a string that exists only long enough to be hashed.

SessionListInput
#

SessionListOutput
#

auth/account_action_specs.ts view source

ZodObject<{ sessions: ZodArray<ZodObject<{ id: $ZodBranded<ZodString, "SessionId", "out">; account_id: $ZodBranded<ZodUUID, "Uuid", "out">; created_at: ZodString; expires_at: ZodString; last_seen_at: ZodString; }, $strict>>; }, $strict> import type {SessionListOutput} from '@fuzdev/fuz_app/auth/account_action_specs.js';

Output for account_session_list.

SessionOptions
#

auth/session_cookie.ts view source

SessionOptions<TIdentity> import type {SessionOptions} from '@fuzdev/fuz_app/auth/session_cookie.js';

Configuration for a session cookie format.

Apps provide encode/decode to control the identity portion of the cookie payload.

The TIdentity type parameter determines the trust model:

  • string (e.g. a session_id) — the cookie references a server-side session record, enabling per-session revocation and metadata. Use when you need admin controls like "revoke all sessions" or per-session audit trails.
  • number (e.g. an account_id) — the cookie directly encodes the user identity, requiring no server-side session state. Simpler, but individual sessions can only be invalidated by rotating the signing key (which invalidates all sessions).

generics

SessionOptions<TIdentity>
TIdentity

examples

// zap: 3-part format (admin:session_id) const zap_config: SessionOptions<string> = { cookie_name: 'zap_session', context_key: 'auth_session_id', encode_identity: (session_id) => `admin:${session_id}`, decode_identity: (payload) => { const parts = payload.split(':'); if (parts.length !== 2 || parts[0] !== 'admin') return null; return parts[1] || null; }, }; // visiones: 1-part format (account_id) const visiones_config: SessionOptions<number> = { cookie_name: 'session_id', context_key: 'auth_session_id', encode_identity: (id) => String(id), decode_identity: (payload) => { const n = parseInt(payload, 10); return Number.isFinite(n) && n > 0 ? n : null; }, };

cookie_name

type string

context_key

Hono context variable name for the identity.

type string

max_age?

Cookie lifetime in seconds. Single source of truth for both the embedded expires_at (via create_session_cookie_value) and the cookie's HTTP Max-Age attribute (via set_session_cookie). Defaults to SESSION_AGE_MAX (30 days). The cookie_options slot intentionally cannot carry maxAge so the two values can't drift.

type number

refresh_threshold_seconds?

Threshold (seconds) for expiration-based cookie refresh. When a parsed cookie's expires_at - now <= refresh_threshold_seconds, process_session_cookie returns action: 'refresh' with a freshly-signed value (extending the embedded expiration by max_age). Defaults to SESSION_REFRESH_THRESHOLD_S (1 day). Set to 0 to disable.

type number

cookie_options?

type Partial<Omit<SessionCookieOptions, 'maxAge'>>

encode_identity

Encode identity into the cookie payload (before the :expires_at suffix).

type (identity: TIdentity) => string

decode_identity

Decode identity from cookie payload. Return null if invalid.

type (payload: string) => TIdentity | null

SessionRevokeAllInput
#

SessionRevokeAllOutput
#

auth/account_action_specs.ts view source

ZodObject<{ ok: ZodLiteral<true>; count: ZodNumber; }, $strict> import type {SessionRevokeAllOutput} from '@fuzdev/fuz_app/auth/account_action_specs.js';

Output for account_session_revoke_all.

SessionRevokeInput
#

auth/account_action_specs.ts view source

ZodObject<{ session_id: $ZodBranded<ZodString, "SessionId", "out">; }, $strict> import type {SessionRevokeInput} from '@fuzdev/fuz_app/auth/account_action_specs.js';

Input for account_session_revoke. session_id is the blake3 hash.

SessionRevokeOutput
#

auth/account_action_specs.ts view source

ZodObject<{ ok: ZodLiteral<true>; revoked: ZodBoolean; }, $strict> import type {SessionRevokeOutput} from '@fuzdev/fuz_app/auth/account_action_specs.js';

Output for account_session_revoke. revoked is false for IDOR misses.

set_mock_stdin
#

runtime/mock.ts view source

(runtime: MockRuntime, input: string): void import {set_mock_stdin} from '@fuzdev/fuz_app/runtime/mock.js';

Set stdin buffer for simulating user input.

runtime

input

string to provide as stdin input

type string

returns

void

set_session_cookie
#

setup_bootstrap_token
#

dev/setup.ts view source

(deps: FsReadDeps & FsWriteDeps & CommandDeps & EnvDeps, app_name: string, options?: SetupBootstrapTokenOptions | undefined): Promise<...> import {setup_bootstrap_token} from '@fuzdev/fuz_app/dev/setup.js';

Create a bootstrap token file if it doesn't exist.

The token is a one-shot secret used to create the first admin account. Stored at ~/.{app_name}/secret_bootstrap_token by default.

deps

file, command, and env capabilities

type FsReadDeps & FsWriteDeps & CommandDeps & EnvDeps

app_name

application name (used for default state directory)

type string

options?

state_dir override, permissions, logger

type SetupBootstrapTokenOptions | undefined
optional

returns

Promise<SetupTokenResult>

result indicating whether a token was created

mutates

  • filesystem — creates state directory and writes the token file (optionally chmods to `0o700` / `0o600`)

setup_env_file
#

dev/setup.ts view source

(deps: FsReadDeps & FsWriteDeps & CommandDeps, env_path: string, example_path: string, options?: SetupEnvOptions | undefined): Promise<...> import {setup_env_file} from '@fuzdev/fuz_app/dev/setup.js';

Create an env file from its example template, auto-generating SECRET_FUZ_COOKIE_KEYS.

If the file already exists, backfills any empty values that have generators. Idempotent — safe to re-run.

deps

file read, write, and command capabilities

type FsReadDeps & FsWriteDeps & CommandDeps

env_path

path for the env file (e.g. .env.development)

type string

example_path

path to the example template

type string

options?

extra replacements, permissions, logger

type SetupEnvOptions | undefined
optional

returns

Promise<SetupEnvResult>

result indicating whether the file was created or updated

mutates

  • filesystem — writes `env_path` (creating from `example_path` if missing) and optionally chmods to `0o600`

SetupBootstrapTokenOptions
#

dev/setup.ts view source

SetupBootstrapTokenOptions import type {SetupBootstrapTokenOptions} from '@fuzdev/fuz_app/dev/setup.js';

state_dir?

State directory override. Defaults to ~/.{app_name}.

type string

set_permissions?

Optional callback to set file/directory permissions.

type (path: string, mode: number) => Promise<void>

log?

type SetupLogger

SetupEnvOptions
#

dev/setup.ts view source

SetupEnvOptions import type {SetupEnvOptions} from '@fuzdev/fuz_app/dev/setup.js';

Options for setup_env_file.

replacements?

Extra env var replacements beyond the default SECRET_FUZ_COOKIE_KEYS.

Keys are env var names, values are async generators. Replaces ^KEY=$ (empty value) patterns in the env file.

type Record<string, () => Promise<string>>

set_permissions?

Optional callback to set file permissions (e.g. Deno.chmod).

type (path: string, mode: number) => Promise<void>

log?

type SetupLogger

SetupEnvResult
#

dev/setup.ts view source

SetupEnvResult import type {SetupEnvResult} from '@fuzdev/fuz_app/dev/setup.js';

Result of setup_env_file.

created

Whether a new file was created (vs updating existing).

type boolean

updated

Whether any values were generated/replaced.

type boolean

path

The env file path.

type string

SetupLogger
#

dev/setup.ts view source

SetupLogger import type {SetupLogger} from '@fuzdev/fuz_app/dev/setup.js';

Optional logger for setup helpers.

Functions that accept a logger use it for status messages. When omitted, a default bracket-format logger writes to console.

ok

type (msg: string) => void

skip

type (msg: string) => void

error

type (msg: string) => void

SetupTest
#

testing/cross_backend/setup.ts view source

SetupTest import type {SetupTest} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

Per-test fixture-producing function. Invoked once inside every test() body. The implementation captures factory inputs (in-process) or a long-running backend handle (cross-process) and creates a fresh per-test bundle on each call.

(call)

type (): Promise<TestFixtureBase>

returns Promise<TestFixtureBase>

SetupTokenResult
#

dev/setup.ts view source

SetupTokenResult import type {SetupTokenResult} from '@fuzdev/fuz_app/dev/setup.js';

created

Whether a new token was created (false if already existed).

type boolean

token_path

The token file path.

type string

should_allow_origin
#

http/origin.ts view source

(origin: string, allowed_patterns: readonly RegExp[]): boolean import {should_allow_origin} from '@fuzdev/fuz_app/http/origin.js';

Tests if a request source (origin or referer) matches any of the allowed patterns. Pattern matching is case-insensitive for domains (as per web standards).

origin

type string

allowed_patterns

type readonly RegExp[]

returns

boolean

should_validate_output
#

actions/action_event_helpers.ts view source

(kind: "request_response" | "remote_notification" | "local_call", phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"): boolean import {should_validate_output} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

kind

type "request_response" | "remote_notification" | "local_call"

phase

type "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"

returns

boolean

ShouldDeliverFn
#

actions/broadcast_api.ts view source

ShouldDeliverFn import type {ShouldDeliverFn} from '@fuzdev/fuz_app/actions/broadcast_api.js';

Per-connection delivery predicate for subscription ACLs.

Called once per connection for every broadcast send. Returning false skips that connection. Keep it fast — this runs in the broadcast hot path.

input is the already-validated payload (matches the spec's input schema); method is the action method name.

(call)

type (connection: ConnectionIdentity, method: string, input: unknown): boolean

connection

method

type string

input

type unknown
returns boolean

sidebar_state_context
#

SidebarState
#

ui/sidebar_state.svelte.ts view source

import {SidebarState} from '@fuzdev/fuz_app/ui/sidebar_state.svelte.js';

constructor

type new (options?: SidebarStateOptions | undefined): SidebarState

options?

type SidebarStateOptions | undefined
optional

toggle_sidebar

type (value?: boolean): void

value

type boolean
default !this.show_sidebar
returns void

activate

Show the sidebar and enable the toggle. The returned disposer hides and disables on cleanup — pair with $effect for scoped activation.

type (): () => void

returns () => void

enabled

type boolean

gettersetter

show_sidebar

type boolean

gettersetter

SidebarStateOptions
#

ui/sidebar_state.svelte.ts view source

SidebarStateOptions import type {SidebarStateOptions} from '@fuzdev/fuz_app/ui/sidebar_state.svelte.js';

enabled?

Reactive getter that controls whether the sidebar is enabled. When supplied, overrides the internal enabled state — show_sidebar auto-returns false while the getter returns false.

type () => boolean

SignupForm
#

ui/SignupForm.svelte view source

import SignupForm from '@fuzdev/fuz_app/ui/SignupForm.svelte';

redirect_on_signup?

Path to navigate to after a successful signup.

type string
optional default resolve('/')

SignupInput
#

auth/signup_route_schema.ts view source

ZodObject<{ username: ZodPipe<ZodString, ZodTransform<string, string>>; password: ZodString; email: ZodOptional<ZodNullable<ZodString>>; }, $strict> import type {SignupInput} from '@fuzdev/fuz_app/auth/signup_route_schema.js';

Input for POST /signup. email is optional (absent or null = no email) and must match any referenced invite.

SignupOutput
#

auth/signup_route_schema.ts view source

ZodObject<{ ok: ZodLiteral<true>; account: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; username: ZodPipe<ZodString, ZodTransform<string, string>>; }, $strict>; actor: ZodObject<...>; }, $strict> import type {SignupOutput} from '@fuzdev/fuz_app/auth/signup_route_schema.js';

Output for POST /signup.

Session cookie is the operative side effect. The returned account and actor mirror BootstrapOutput so cross-process per-test setup can read the per-test identity straight off the signup response.

SignupRouteOptions
#

auth/signup_routes.ts view source

SignupRouteOptions import type {SignupRouteOptions} from '@fuzdev/fuz_app/auth/signup_routes.js';

Per-factory configuration for signup route specs.

inheritance

signup_account_rate_limiter

Rate limiter for signup attempts, keyed by submitted username. Pass null to disable.

type RateLimiter | null

signup_fail_floor_ms?

Minimum wall-clock time (ms) for signup denial responses (403 / 409). Set to 0 or a negative number to disable (e.g., in tests). Default DEFAULT_SIGNUP_FAIL_FLOOR_MS. 429 responses are not floored.

type number

signup_fail_jitter_ms?

Uniform jitter window (±ms) layered on the floor. Set to 0 to disable jitter while keeping the floor. Default DEFAULT_SIGNUP_FAIL_JITTER_MS.

type number

SignupRouteShapeOptions
#

auth/signup_route_schema.ts view source

SignupRouteShapeOptions import type {SignupRouteShapeOptions} from '@fuzdev/fuz_app/auth/signup_route_schema.js';

Option inputs that shape the signup route metadata (not its handler).

signup_account_rate_limited

Whether a per-account signup rate limiter is wired — toggles rate_limit.

type boolean

socket_status_to_async_status
#

actions/socket.svelte.ts view source

(status: SocketStatus, revoked: boolean): AsyncStatus import {socket_status_to_async_status} from '@fuzdev/fuz_app/actions/socket.svelte.js';

Project SocketStatus onto fuz_util's AsyncStatus — the 5-way → 4-way mapping every consumer re-derives to surface connection state to UI (loading indicators, retry banners). Collapses reconnecting into failure (UI shows "lost, retrying") and splits closed by revoked so a terminal session-revocation read as failure while a clean client- initiated close reads as initial (the "not connected, not trying" state).

status

revoked

whether the session has been permanently revoked (typically FrontendWebsocketClient.revoked)

type boolean

returns

AsyncStatus

SocketCloseContext
#

actions/register_action_ws.ts view source

SocketCloseContext import type {SocketCloseContext} from '@fuzdev/fuz_app/actions/register_action_ws.js';

Context passed to the on_socket_close hook.

Fires before transport.remove_connection runs, so consumer cleanup can still read identity before it's torn down. Fires for both client-initiated closes (Hono onClose) and server-initiated closes via audit revocation (the audit guard calls ws.close(), which triggers Hono's onClose).

ws

The raw WebSocket context at close time.

type WSContext

connection_id

Connection id captured at open time.

type Uuid

identity

Auth identity captured at open time — still valid even if the transport already cleaned up.

type ConnectionIdentity

SocketErrorHandler
#

actions/socket.svelte.ts view source

SocketErrorHandler import type {SocketErrorHandler} from '@fuzdev/fuz_app/actions/socket.svelte.js';

(call)

type (event: Event): void

event

type Event
returns void

SocketMessageHandler
#

actions/socket.svelte.ts view source

SocketMessageHandler import type {SocketMessageHandler} from '@fuzdev/fuz_app/actions/socket.svelte.js';

(call)

type (event: MessageEvent<any>): void

event

type MessageEvent<any>
returns void

SocketOpenContext
#

actions/register_action_ws.ts view source

SocketOpenContext import type {SocketOpenContext} from '@fuzdev/fuz_app/actions/register_action_ws.js';

Context passed to the on_socket_open hook.

Fires after the transport has registered the new connection (so connection_id is valid) but before any client message can dispatch. Consumers use this to bootstrap per-socket domain state — e.g. spawning a per-account unit and pushing an initial state snapshot.

ws

The raw WebSocket context — exposed for edge cases; prefer notify for sends.

type WSContext

connection_id

Connection id assigned by BackendWebsocketTransport.add_connection.

type Uuid

identity

Auth identity registered for this connection.

type ConnectionIdentity

notify

Send a JSON-RPC notification to just this socket. Mirrors ctx.notify on per-message handler contexts — same socket-scoped semantics.

type (method: string, params: unknown) => void

signal

Fires when this socket closes — threaded through to every handler's ctx.signal.

type AbortSignal

SocketStatus
#

actions/socket.svelte.ts view source

SocketStatus import type {SocketStatus} from '@fuzdev/fuz_app/actions/socket.svelte.js';

Client-side WebSocket status.

  • initial — never connected; connect() has not been called.
  • connecting — WebSocket readyState === CONNECTING.
  • connected — WebSocket readyState === OPEN.
  • reconnecting — close fired; waiting out backoff before next attempt.
  • closed — socket is not open. Terminal only when revoked is true or auto-reconnect is disabled; otherwise connect() reopens.

soft_delete_test_actor
#

testing/db_entities.ts view source

(db: Db, actor_id: string): Promise<boolean> import {soft_delete_test_actor} from '@fuzdev/fuz_app/testing/db_entities.js';

Soft-delete (tombstone) one actor via the production query_actor_soft_delete — returns true when an active row flipped. The TS twin of the Rust query_actor_soft_delete the guard tests use, for seeding the latent multi-actor tombstone state directly (no per-actor-delete RPC ships yet).

db

type Db

actor_id

type string

returns

Promise<boolean>

SPACE_CELL_KIND
#

spawn_backend
#

testing/cross_backend/spawn_backend.ts view source

(config: BackendConfig): Promise<BackendHandle> import {spawn_backend} from '@fuzdev/fuz_app/testing/cross_backend/spawn_backend.js';

Spawn config.start_command and return a handle once the binary is health-probe-ready and the daemon-token file is readable.

Errors at any stage SIGTERM the child group before rethrowing — the caller never sees a half-started backend.

config

returns

Promise<BackendHandle>

SpecSource
#

actions/action_codegen.ts view source

SpecSource import type {SpecSource} from '@fuzdev/fuz_app/actions/action_codegen.js';

One source in a multi-source consumer's namespace map. ns is the local alias used inside the generated file; module is the import path; specs is the runtime spec array. create_namespace_qualifier consumes a list of these.

ns

type string

module

type string

specs

type ReadonlyArray<ActionSpecUnion>

SPINE_CELL_EDITOR_ROLE
#

testing/cross_backend/spine_surface_constants.ts view source

"cell_editor" import {SPINE_CELL_EDITOR_ROLE} from '@fuzdev/fuz_app/testing/cross_backend/spine_surface_constants.js';

App role the role-shaped-cell_grant cross suite exercises. Registered with no grant path (grant_paths: []) so it stays a valid registry member without entering the admin / self-service grant flows — holders are seeded directly via extra_accounts. Must match the cell_editor entry in the Rust testing_spine_stub's known_roles (cross-language test contract).

SPINE_EXPECTED_SCHEMA_URL
#

testing/cross_backend/spine_surface_constants.ts view source

URL import {SPINE_EXPECTED_SCHEMA_URL} from '@fuzdev/fuz_app/testing/cross_backend/spine_surface_constants.js';

Committed expected-schema fixture for the spine /ready deploy gate — the column map a fresh full spine bootstrap (auth + cell + cell_history + fact) produces. Resolved relative to this module so the spawned TS binary (which imports this source under its loader) reads it off disk via node:fs. Regenerated + guarded by src/test/cross_backend/spine_expected_schema.db.test.ts.

The Rust testing_spine_stub reads the same committed file (its absolute path passed via env by rust_spine_stub_backend_config) — column-presence is engine-portable, so one fixture is the cross-impl contract.

SPINE_PARTICIPANT_ROLE
#

testing/cross_backend/spine_surface_constants.ts view source

"participant" import {SPINE_PARTICIPANT_ROLE} from '@fuzdev/fuz_app/testing/cross_backend/spine_surface_constants.js';

Admin-grantable app role the role-gated-participation cross suite exercises. Registered with grant_paths: ['admin'] so it enters the admin grant flow — the cross-backend proof that an app-defined role is conferrable (offer / role_grant_assign) admin-only on both spines. Must match the participant entry in the Rust testing_spine_stub's RoleRegistry and its known_roles (the registry feeds the cell vocabulary too) — a cross-language test contract. Distinct from SPINE_CELL_EDITOR_ROLE (no grant path): this one is the *grantable* role, that one is the bootstrap-seed-only cell role.

spine_roles
#

testing/cross_backend/default_spine_surface.ts view source

RoleSchemaResult import {spine_roles} from '@fuzdev/fuz_app/testing/cross_backend/default_spine_surface.js';

The spine's closed role registry: built-ins plus two app roles — SPINE_CELL_EDITOR_ROLE (no grant path; the role-shaped-cell_grant suite's bootstrap-seeded role) and SPINE_PARTICIPANT_ROLE (grant_paths: ['admin']; the role-gated-participation suite's admin-grantable role). Threaded into the cell spec set's role-validity gate and the auth grantability gates; the Rust stub mirrors the same membership in both its RoleRegistry and known_roles. The participant entry also gives the admin suite real app-role grant-path coverage (admin_account_list.grantable_roles carries it on both spines).

spine_rpc_endpoints
#

testing/cross_backend/default_spine_surface.ts view source

(ctx: AppServerContext, options?: SpineRpcEndpointsOptions | undefined): RpcEndpointSpec[] import {spine_rpc_endpoints} from '@fuzdev/fuz_app/testing/cross_backend/default_spine_surface.js';

Factory-form RPC endpoints over the per-test ctx.deps. create_app_server (in the binary) owns live dispatch; the surface builder invokes the factory once with a stub ctx for setup-time path/method lookup, so the handler closures are never called across the process boundary.

Test binaries append their own _testing_reset action to this endpoint's actions (see testing/cross_backend/testing_reset_actions.ts); it is intentionally excluded here so it stays off the declared surface (the harness calls it directly over the daemon-token channel).

options.notification_sender, when supplied, reaches the role-grant-offer sub-factory so the spine emits the WS notification family — see SpineRpcEndpointsOptions.

ctx

options?

type SpineRpcEndpointsOptions | undefined
optional

returns

RpcEndpointSpec[]

SPINE_RPC_PATH
#

spine_session_options
#

testing/cross_backend/default_spine_surface.ts view source

SessionOptions<string> import {spine_session_options} from '@fuzdev/fuz_app/testing/cross_backend/default_spine_surface.js';

Session config — cookie name matches the binary's issued session cookie (fuz_session) so cookie-attribute assertions + jar extraction line up.

SPINE_SSE_PATH
#

testing/cross_backend/spine_surface_constants.ts view source

"/api/admin/audit/stream" import {SPINE_SSE_PATH} from '@fuzdev/fuz_app/testing/cross_backend/spine_surface_constants.js';

Audit-log SSE stream path — /api/admin prefix + the create_audit_log_route_specs /audit/stream route. Matches the default BackendConfig.sse_path and the cross-process SSE suite's default. Only mounted by the TS spine binary (which wires audit_log_sse); the shared surface stub leaves ctx.audit_sse null so the snapshot stays SSE-free.

SpineRpcEndpointsOptions
#

testing/cross_backend/default_spine_surface.ts view source

SpineRpcEndpointsOptions import type {SpineRpcEndpointsOptions} from '@fuzdev/fuz_app/testing/cross_backend/default_spine_surface.js';

Options for .

notification_sender?

WS notification sender threaded into the role-grant-offer sub-factory for server-initiated fan-out (role_grant_offer_received / _accepted / _declined / _retracted / _supersede, flat role_grant_revoke).

Shared-instance trap. Pass the SAME BackendWebsocketTransport instance the WS endpoint registers connections against — the transport *is* the connection registry, so a separate instance would fan out to an empty registry and reach nobody (silently). The TS spine binary constructs one ws_transport and threads it both here and into register_ws_endpoint.

Omitted (the default) for the shared create_spine_surface_spec path — surface generation doesn't depend on it, and it must stay absent there so the declared snapshot is unaffected.

type NotificationSender | null

readonly

SpineStubBackendConfigOptions
#

testing/cross_backend/rust_spine_stub_backend_config.ts view source

SpineStubBackendConfigOptions import type {SpineStubBackendConfigOptions} from '@fuzdev/fuz_app/testing/cross_backend/rust_spine_stub_backend_config.js';

port?

Listening port. Default RUST_SPINE_STUB_DEFAULT_PORT.

type number

readonly

database_url?

Postgres connection URL. Default RUST_SPINE_STUB_DEFAULT_DATABASE_URL.

type string

readonly

binary_path?

Prebuilt binary path. Overrides the FUZ_TESTING_RUST_SPINE_STUB_BIN env var. When neither is set the preset throws.

type string

readonly

enable_login_rate_limit?

Enable the per-IP + per-account login rate limiters on the stub (FUZ_LOGIN_RATE_LIMIT_ENABLED=true). Off by default — the standard cross suites fire many loopback logins a live limiter would 429. Set true only for the dedicated login-security cross project (global_setup_login_security.ts). Pair with trusted_proxies so the limiter keys on the resolved X-Forwarded-For client IP. Mirrors TsSpineBackendConfigOptions.enable_login_rate_limit.

type boolean

readonly

trusted_proxies?

Comma-separated trusted-proxy allowlist passed as FUZ_TRUSTED_PROXIES (e.g. '127.0.0.1,::1'). Unset by default (the stub leaves XFF parsing off, keying on the raw TCP peer). The login-security project sets the loopback set so the limiter keys on the X-Forwarded-For client IP — the TS spine binary wires the equivalent set unconditionally.

type string

readonly

SSE_CONNECTED_COMMENT
#

realtime/sse_constants.ts view source

": connected\n\n" import {SSE_CONNECTED_COMMENT} from '@fuzdev/fuz_app/realtime/sse_constants.js';

The comment line written immediately on SSE stream open. Flushes headers + confirms the connection is live before the first real event. Cross-process SSE tests assert the stream emits this on connect.

SSE_FRAME_READ_TIMEOUT_MS
#

SseFrameReader
#

testing/transports/sse_frame_reader.ts view source

SseFrameReader import type {SseFrameReader} from '@fuzdev/fuz_app/testing/transports/sse_frame_reader.js';

Frame-level reader returned by create_sse_frame_reader.

read_frame

Read one complete SSE frame (up to the next \n\n), without the trailing terminator. Throws if the per-read timeout elapses or the stream ends before a frame arrives.

type (timeout_ms?: number) => Promise<string>

wait_for_close

Drain until the server closes the stream. Resolves true if the stream closes within timeout_ms, false on timeout.

type (timeout_ms?: number) => Promise<boolean>

cancel

Cancel the underlying reader. Safe to call when already closed.

type () => Promise<void>

SseNotification
#

realtime/sse.ts view source

SseNotification import type {SseNotification} from '@fuzdev/fuz_app/realtime/sse.js';

Notification shape aligned with JSON-RPC 2.0.

Uses {method, params} to match the JSON-RPC notification format.

method

Notification method name (e.g. 'run_created', 'host_updated').

type string

params

Method-specific payload.

type unknown

SseRouteTestOptions
#

testing/sse_round_trip.ts view source

SseRouteTestOptions import type {SseRouteTestOptions} from '@fuzdev/fuz_app/testing/sse_round_trip.js';

session_options

Session config for cookie-based auth.

type SessionOptions<string>

create_route_specs

Route spec factory — same shape as production.

type (ctx: AppServerContext) => Array<RouteSpec>

app_options?

Optional overrides for AppServerOptions.

type SuiteAppOptions

db_factories?

Database factories to run tests against. Default: pglite only.

type Array<DbFactory>

on_audit_event?

Backend audit event callback — threaded to create_test_app_server. Use to wire a close-on-revoke guard for consumer SSE registries (e.g., via create_sse_auth_guard) so session_revoke_all actually closes the tested streams.

type (event: AuditLogEvent) => void

rpc_endpoints

RPC endpoint specs — required so the close-on-revoke assertion can dispatch account_session_revoke_all via RPC (there is no REST equivalent). Hard-fails via require_rpc_endpoint_path on setup.

Accepts either an array (eager) or a factory (ctx: AppServerContext) => Array<RpcEndpointSpec> — the factory form is required when action handlers must close over the per-test ctx.deps. The factory must return the same endpoint path regardless of ctx — it is invoked once at setup with a stub ctx for path lookup and again per-test by create_app_server for live dispatch.

type RpcEndpointsSuiteOption

routes

SSE routes to exercise.

type Array<SseRouteTestSpec>

SseRouteTestSpec
#

testing/sse_round_trip.ts view source

SseRouteTestSpec import type {SseRouteTestSpec} from '@fuzdev/fuz_app/testing/sse_round_trip.js';

Config for a single SSE route under test.

path

Full HTTP path of the SSE endpoint (e.g., '/api/zap/subscribe').

type string

trigger

Fire an event matching one of the declared event_specs that should reach the open stream. Called after the : connected comment is observed. The triggered frame must be a JSON-serializable {method, params} payload.

type (ctx: { test_app: TestApp; account: TestAccount }) => Promise<void>

event_specs?

Event specs to validate the triggered payload against. When omitted, the payload is only asserted to be well-formed {method, params}.

type Array<EventSpec>

assert_closes_on_revoke?

Whether to assert the stream closes after session_revoke_all. Default true. Set false for endpoints that don't wire a close-on-revoke guard (leaves a TODO to fix, rather than silently passing).

type boolean

SseStream
#

realtime/sse.ts view source

SseStream<T> import type {SseStream} from '@fuzdev/fuz_app/realtime/sse.js';

Generic SSE stream controller interface.

Transport-agnostic — works with any serializable type.

generics

SseStream<T = unknown>
T
default unknown

send

Send data to the client as a JSON SSE event.

type (data: T) => void

comment

Send a comment (for keep-alive pings).

type (text: string) => void

close

Close the stream.

type () => void

on_close

Register a listener called when the stream closes (client disconnect or explicit close).

type (fn: () => void) => void

SseTransport
#

testing/transports/sse_transport.ts view source

SseTransport import type {SseTransport} from '@fuzdev/fuz_app/testing/transports/sse_transport.js';

A cross-process SSE client: read frames, await server close, cancel.

read_frame

Read one complete SSE frame (up to the next \n\n), without the trailing terminator. Throws if the per-read timeout elapses or the stream ends before a frame arrives.

type (timeout_ms?: number) => Promise<string>

wait_for_close

Drain until the server closes the stream. Resolves true if the stream closes within timeout_ms, false on timeout. The signal for an auth-guard revocation dropping a live stream — mirrors WsClient.wait_for_close.

type (timeout_ms?: number) => Promise<boolean>

close

Cancel the reader (client-initiated close). Safe to call when already closed.

type () => Promise<void>

SseTransportOptions
#

testing/transports/sse_transport.ts view source

SseTransportOptions import type {SseTransportOptions} from '@fuzdev/fuz_app/testing/transports/sse_transport.js';

Construction options for create_sse_transport.

base_url

Base URL the binary is reachable at — e.g. http://localhost:1178.

type string

readonly

sse_path

SSE endpoint path on the binary (e.g. /api/admin/audit/stream).

type string

readonly

cookies

Session cookie values (full Set-Cookie strings as FetchTransport.cookies() returns them) threaded onto the request Cookie header. Without these the stream is anonymous and the connect is refused (the audit stream requires an admin session).

type ReadonlyArray<string>

readonly

origin?

Origin header for the request. Backends running with ALLOWED_ORIGINS=http://localhost:* accept http://localhost:<port>. Defaults to base_url — acceptable because cross-process tests always run against localhost.

type string

readonly

default_timeout_ms?

Default per-read / wait-for-close timeout. Falls back to 2000ms.

type number

readonly

StandardAdminIntegrationTestOptions
#

testing/admin_integration.ts view source

StandardAdminIntegrationTestOptions import type {StandardAdminIntegrationTestOptions} from '@fuzdev/fuz_app/testing/admin_integration.js';

setup_test

Per-test fixture-producing function. The admin suite calls this in every test() body — auth_integration_truncate_tables clears account, so each test re-bootstraps the keeper.

type SetupTest

surface_source

App surface (with route specs + middleware specs) for route iteration and error-coverage scoping. Constructed in TS by the consumer (same shape for in-process and cross-process tests).

type AppSurfaceSpec

capabilities

Backend capability declarations.

type BackendCapabilities

session_options

Session config — needed for cookie_name + factory-form rpc_endpoints resolution.

type SessionOptions<string>

roles

Role schema result from create_role_schema() — used to determine valid/invalid/web-grantable roles.

type RoleSchemaResult

rpc_endpoints

RPC endpoint specs — the source RpcAction arrays. Required; role_grant grant/revoke are RPC-only and the suite hard-fails without them.

type RpcEndpointsSuiteOption

admin_prefix?

Path prefix where admin routes are mounted (e.g., '/api/admin'). Used by the 401/403 error-coverage probe to scope to fuz_app admin routes only, avoiding app-specific admin-gated routes that may use stub deps. Default '/api/admin'.

type string

StandardAttackSurfaceOptions
#

testing/attack_surface.ts view source

StandardAttackSurfaceOptions import type {StandardAttackSurfaceOptions} from '@fuzdev/fuz_app/testing/attack_surface.js';

Options for the standard attack surface test suite.

build

Build the app surface bundle (surface + route specs + middleware specs).

type () => AppSurfaceSpec

snapshot_path

Absolute path to the committed snapshot JSON file.

type string

expected_public_routes

Expected public routes, e.g. ['GET /health', 'POST /api/account/login'].

type Array<string>

expected_api_middleware

Expected middleware names for API routes, e.g. ['origin', 'session', 'request_context', 'bearer_auth'].

type Array<string>

roles

All roles in the app (e.g. ['admin', 'keeper']).

type Array<string>

api_path_prefix?

Path prefix for middleware stack assertion. Default '/api/'.

type string

security_policy?

Security policy configuration. Omit for sensible defaults.

type SurfaceSecurityPolicyOptions

error_schema_tightness?

Error schema tightness assertion config. Defaults to default_error_schema_tightness (ignores 401/403/429, min_specificity: 'enum', allowlist seeded with fuz_app_stock_route_tightness_allowlist).

Consumer-supplied allowlist and ignore_statuses are additive — the suite merges them underneath the stock defaults, so project-specific entries don't need to re-list fuz_app's own stock routes. Pass a narrower config to extend either list or tighten min_specificity; pass null to skip the assertion and keep the audit log informational-only.

type ErrorSchemaTightnessOptions | null

StandardCrossProcessTestOptions
#

testing/cross_backend/standard.ts view source

StandardCrossProcessTestOptions import type {StandardCrossProcessTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/standard.js';

Configuration for describe_standard_cross_process_tests.

Mirrors StandardTestOptions minus the in-process-only knobs (create_route_specs, bootstrap, rate_limiting_app_options, bootstrap_token) — those drive the three omitted suites.

setup_test

Per-test fixture-producing function.

type SetupTest

surface_source

App surface. Constructed in TS by the consumer; same shape for in-process and cross-process tests.

type AppSurfaceSpec

capabilities

Backend capability declarations.

type BackendCapabilities

session_options

Session config — needed for cookie_name + factory-form rpc_endpoints resolution.

type SessionOptions<string>

rpc_endpoints

RPC endpoint specs — required. The standard integration tests drive account_verify, account_session_*, account_token_* through the RPC surface (and admin tests, when wired, drive role_grant grant/revoke through it too).

type RpcEndpointsSuiteOption

roles?

Role schema result from create_role_schema(). When provided, the admin integration suite is included.

type RoleSchemaResult

admin_prefix?

Path prefix where admin routes are mounted. Default '/api/admin'.

type string

error_coverage_min?

Forwarded to describe_standard_integration_tests — overrides the default error-coverage threshold on the scoped REST surface. Set to 0 to skip the assertion entirely.

type number

round_trip_skip_routes?

Forwarded to describe_round_trip_validation as skip_routes ('METHOD /path' keys). For consumer REST routes whose responses aren't JSON-with-an-output-schema and so can't be round-tripped — e.g. fuz_forge's git smart-HTTP routes (git-upload-pack / git-receive-pack / info/refs) which stream git protocol bytes.

type Array<string>

rpc_success_fixtures?

Forwarded to describe_rpc_round_trip_tests as success_fixtures (method name → async params factory). Drives a populated success body for referential RPC reads (*_get, *_log) the nil-id round-trip can only ever error on, and validates it against the method's output schema on each backend — the success-shape parity check. See RpcRoundTripTestOptions.success_fixtures.

type Map<string, (fixture: TestFixture) => Promise<Record<string, unknown>>>

rest_success_fixtures?

Forwarded to describe_round_trip_validation as success_fixtures ('METHOD /path' → async {url?, body?} factory) for referential REST routes. See RoundTripTestOptions.success_fixtures.

type Map< string, (fixture: TestFixture) => Promise<{ url?: string; body?: Record<string, unknown> }> >

StandardIntegrationTestOptions
#

testing/integration.ts view source

StandardIntegrationTestOptions import type {StandardIntegrationTestOptions} from '@fuzdev/fuz_app/testing/integration.js';

setup_test

Per-test fixture-producing function. The integration suite calls this in every test() body — auth_integration_truncate_tables clears account, so each test re-bootstraps the keeper.

type SetupTest

surface_source

App surface (with route specs + middleware specs) for route iteration and error-coverage scoping. The same shape feeds both in-process and cross-process tests — the test process always constructs the spec in TS (via create_test_app_surface_spec or a consumer's equivalent); cross-process-ness is a property of the transport + per-test fixture, not the schema source. The on-disk auth_attack_surface.json is an observability artifact for human inspection + gen-time drift gating, not the source the test runtime reads from.

type AppSurfaceSpec

capabilities

Backend capability declarations for capability-gated cases.

type BackendCapabilities

session_options

Session config — needed to resolve factory-form rpc_endpoints against a stub AppServerContext at setup time and to read cookie_name for manual cookie composition in the session cases.

type SessionOptions<string>

rpc_endpoints

RPC endpoint specs — required. This suite dispatches account_verify, account_session_*, and account_token_* via rpc_call_for_spec (the /api/account/verify REST route is a status-only nginx shim with no payload). Hard-fails via require_rpc_endpoint_path on setup so consumer projects see a clear setup error instead of confusing test failures.

Accepts either an array (eager) or a factory — see testing/rpc_helpers.ts for the union semantics. The factory must return the same endpoint path regardless of ctx — invoked once at setup with a stub ctx for path lookup; the running backend handles live dispatch.

type RpcEndpointsSuiteOption

error_coverage_min?

Minimum error-coverage ratio to enforce on the scoped REST surface (login / logout / password / signup + the shared RPC endpoint). Default DEFAULT_INTEGRATION_ERROR_COVERAGE (0.2). Set to 0 to skip the assertion entirely — useful for consumers with minimal route sets whose declared error codes outpace the suite's denial-path drivers.

type number

StandardRpcActionsDeps
#

StandardRpcActionsOptions
#

auth/standard_rpc_actions.ts view source

StandardRpcActionsOptions import type {StandardRpcActionsOptions} from '@fuzdev/fuz_app/auth/standard_rpc_actions.js';

Options for create_standard_rpc_actions.

Composes AdminActionOptions (roles), RoleGrantOfferActionOptions (roles, default_ttl_ms, authorize), and AccountActionOptions (max_tokens). roles is shared between admin and role-grant-offer — the caller supplies it once and the helper threads the same reference to both.

inheritance

StandardTestOptions
#

testing/standard.ts view source

StandardTestOptions import type {StandardTestOptions} from '@fuzdev/fuz_app/testing/standard.js';

Configuration for describe_standard_tests.

setup_test

Per-test fixture-producing function.

type SetupTest

surface_source

App surface. Constructed in TS by the consumer; same shape for in-process and cross-process tests.

type AppSurfaceSpec

capabilities

Backend capability declarations.

type BackendCapabilities

session_options

Session config — needed for cookie_name + factory-form rpc_endpoints resolution.

type SessionOptions<string>

create_route_specs

Route spec factory — same one used in production. Required by describe_rate_limiting_tests, which builds a fresh TestApp per test (bypasses the shared setup_test fixture) so it can pass tight per-test rate-limiter overrides.

type (ctx: AppServerContext) => Array<RouteSpec>

rpc_endpoints

RPC endpoint specs — required. The standard integration tests drive account_verify, account_session_*, account_token_* through the RPC surface (and admin tests, when wired, drive role_grant grant/revoke through it too).

type RpcEndpointsSuiteOption

roles?

Role schema result from create_role_schema(). When provided, admin integration + audit completeness suites are included.

type RoleSchemaResult

bootstrap?

Bootstrap config — when set to mode: 'live', the bootstrap success suite runs against create_test_app_for_bootstrap. Other modes ('disabled' / 'surface_only' / omission) silent-skip the suite.

type BootstrapServerOptions

rate_limiting_app_options?

Optional overrides forwarded to describe_rate_limiting_tests.

type SuiteAppOptions

admin_prefix?

Path prefix where admin routes are mounted. Default '/api/admin'.

type string

error_coverage_min?

Forwarded to describe_standard_integration_tests — overrides the default error-coverage threshold on the scoped REST surface. Set to 0 to skip the assertion entirely.

type number

bootstrap_token?

Override the bootstrap-success suite's synthetic token.

type string

start_daemon_token_rotation
#

auth/daemon_token_middleware.ts view source

(runtime: Pick<EnvDeps, "env_get"> & Pick<FsWriteDeps, "mkdir" | "write_text_file" | "rename"> & { chmod?: ((path: string, mode: number) => Promise<...>) | undefined; } & FsRemoveDeps, deps: QueryDeps, options: DaemonTokenRotationOptions, log: Logger): Promise<...> import {start_daemon_token_rotation} from '@fuzdev/fuz_app/auth/daemon_token_middleware.js';

Start daemon token rotation.

Generates an initial token, writes it to disk, resolves the keeper account, and sets up periodic rotation. Returns the mutable state object and a stop function.

runtime

runtime with file and remove capabilities

type Pick<EnvDeps, "env_get"> & Pick<FsWriteDeps, "mkdir" | "write_text_file" | "rename"> & { chmod?: ((path: string, mode: number) => Promise<void>) | undefined; } & FsRemoveDeps

deps

query dependencies for resolving keeper account

options

rotation configuration

log

the logger instance

type Logger

returns

Promise<DaemonTokenRotation>

rotation state and stop function

mutates

  • filesystem — writes the token file on each rotation; `stop` removes it

start_testing_server
#

testing/cross_backend/testing_server_core.ts view source

(options: StartTestingServerOptions): Promise<void> import {start_testing_server} from '@fuzdev/fuz_app/testing/cross_backend/testing_server_core.js';

Boot a test-mode server using the supplied runtime adapter.

Mirrors a production start_server at the surface level — stale-daemon check, daemon-info write, bind, graceful drain — but the app is the caller's no-domain (or domain) and the runtime boundary is the . Refuses any non-loopback bind host (the test binary must stay on loopback — see is_loopback_host).

options

returns

Promise<void>

StartTestingServerOptions
#

testing/cross_backend/testing_server_core.ts view source

StartTestingServerOptions import type {StartTestingServerOptions} from '@fuzdev/fuz_app/testing/cross_backend/testing_server_core.js';

Options for .

adapter

Runtime-boundary adapter (Node or Deno).

type TestingServerAdapter

daemon_name

Daemon-info namespace — the cli/daemon key the daemon.json is written under (e.g. 'fuz_app_spine'). The cross-process harness reads the daemon token from the rotation file, not this; daemon.json is for stale-process detection + parity with production daemon lifecycle.

type string

host

Bind host (e.g. 'localhost').

type string

port

Bind port.

type number

app_version?

App version recorded in daemon.json.

type string

build_app

Build the app. Closes over the entry's runtime + connection-IP getter + password deps + resolved config — so this core never touches the domain. Returns the assembled app, a close teardown, and an optional mount_websocket hook.

type () => Promise<BuiltTestingApp>

log?

Optional logger; defaults to a [daemon_name]-namespaced Logger.

type LoggerType

StatResult
#

runtime/deps.ts view source

StatResult import type {StatResult} from '@fuzdev/fuz_app/runtime/deps.js';

Result of a stat operation.

is_file

type boolean

is_directory

type boolean

size?

Byte length of a regular file. Meaningful only when is_file is true; for directories it is runtime-defined (real OS stat reports the directory entry's on-disk size, not 0 — only create_mock_runtime reports 0). Populated by every runtime factory (create_node_runtime / create_deno_runtime / create_mock_runtime); optional so loose test stubs that only assert is_file / is_directory don't have to supply it. Callers that need an exact size (e.g. a streaming upload's Content-Length) read it from a real runtime, where it is always present.

type number

mtime_ms?

Last-modification time in epoch milliseconds, when the runtime reports it. Populated by create_node_runtime / create_deno_runtime; create_mock_runtime omits it (so a mock-backed sweep treats every temp as unknown-age and never reaps). Optional so loose test stubs that only assert is_file / is_directory don't have to supply it. The orphan-temp sweep (db/fact_disk_storage.ts) reads it to age out stale .tmp spill files.

type number

stop_daemon
#

cli/daemon.ts view source

(runtime: Pick<EnvDeps, "env_get"> & Pick<FsReadDeps, "stat" | "read_text_file"> & FsRemoveDeps & CommandDeps & LogDeps, name: string): Promise<...> import {stop_daemon} from '@fuzdev/fuz_app/cli/daemon.js';

Stop a running daemon by sending SIGTERM and cleaning up the PID file.

Returns a result object instead of logging directly, separating lifecycle from presentation. Errors removing the PID file are swallowed (the daemon's own shutdown handler may have removed it concurrently).

runtime

runtime with command, file, and env capabilities

type Pick<EnvDeps, "env_get"> & Pick<FsReadDeps, "stat" | "read_text_file"> & FsRemoveDeps & CommandDeps & LogDeps

name

application name

type string

returns

Promise<StopDaemonResult>

result describing the outcome

mutates

  • filesystem — removes `~/.{name}/run/daemon.json` on success or when corrupt
  • external — process - sends `SIGTERM` to the daemon process via `kill`

StopDaemonResult
#

cli/daemon.ts view source

StopDaemonResult import type {StopDaemonResult} from '@fuzdev/fuz_app/cli/daemon.js';

Result of a stop_daemon operation.

stopped

Whether a daemon was stopped.

type boolean

pid?

PID of the stopped daemon, if any.

type number

message

Human-readable message describing the outcome.

type string

StorageFullError
#

db/fact_store_errors.ts view source

import {StorageFullError} from '@fuzdev/fuz_app/db/fact_store_errors.js';

The disk filled mid-stream (ENOSPC). Thrown by put_stream when the temp-file write fails for lack of space — the real disk-full guarantee that a best-effort free-space preflight can't promise (chunked uploads, TOCTOU races). A consumer route maps this to 507.

inheritance

extends: Error

constructor

type new (cause?: unknown): StorageFullError

cause?

type unknown
optional

stream_fact_to_disk
#

db/fact_disk_storage.ts view source

(deps: Pick<FactDiskStorageDeps, "stat" | "mkdir" | "rename" | "fsync" | "write_file_stream" | "remove">, facts_dir: string | undefined, source: ReadableStream<...>, max_bytes: number, embedded_threshold: number): Promise<...> import {stream_fact_to_disk} from '@fuzdev/fuz_app/db/fact_disk_storage.js';

Stream source to storage with bounded memory: hash BLAKE3 + SHA-256 incrementally in one pass, buffer in memory until the bytes cross embedded_threshold, then spill the buffer + remaining chunks through a temp file and atomically land it in the disk CAS. Peak heap is O(chunk + embedded_threshold), never O(artifact), so a multi-GB upload never buffers in RAM.

  • Embedded vs disk. A body <= embedded_threshold stays in memory and is returned as {kind: 'embedded'} for the PG bytes column. Above it (with a facts_dir), the buffer + remaining chunks spill to <facts_dir>/.tmp/…, then rename into <facts_dir>/<shard>/<rest> once the hash is known — {kind: 'disk'}. A body over the threshold with facts_dir === undefined throws PayloadTooLargeError (matches PgFactStore.put).
  • Cap enforcement. Aborts with PayloadTooLargeError the moment the running byte count passes max_bytes — the mid-stream backstop for a chunked or mis-declared Content-Length.
  • Disk-full. An ENOSPC from the temp-file write surfaces as StorageFullError.

deps

type Pick<FactDiskStorageDeps, "stat" | "mkdir" | "rename" | "fsync" | "write_file_stream" | "remove">

facts_dir

type string | undefined

source

type ReadableStream<Uint8Array<ArrayBufferLike>>

max_bytes

type number

embedded_threshold

type number

returns

Promise<StreamFactToDiskResult>

StreamFactToDiskResult
#

db/fact_disk_storage.ts view source

StreamFactToDiskResult import type {StreamFactToDiskResult} from '@fuzdev/fuz_app/db/fact_disk_storage.js';

Outcome of streaming an upload to storage: the blake3:-prefixed fact hash, the bare-hex SHA-256, the byte count, and where the bytes landed. PgFactStore.put_stream turns this into the fact row insert.

hash

type FactHash

sha256

type string

size

type number

placement

type StreamPlacement

StreamPlacement
#

db/fact_disk_storage.ts view source

StreamPlacement import type {StreamPlacement} from '@fuzdev/fuz_app/db/fact_disk_storage.js';

Where a streamed body landed — embedded carries the in-memory bytes (under the embedded threshold, bound for the PG fact.bytes column); disk means the bytes are already at <facts_dir>/<shard>/<rest> and the row carries the file: URL.

stub
#

testing/stubs.ts view source

any import {stub} from '@fuzdev/fuz_app/testing/stubs.js';

Throwing stub — use for deps that should never be reached.

stub_app_deps
#

testing/stubs.ts view source

AppDeps import {stub_app_deps} from '@fuzdev/fuz_app/testing/stubs.js';

Stub AppDeps for auth surface tests — throws on any method access.

stub_handler
#

testing/stubs.ts view source

(): Response import {stub_handler} from '@fuzdev/fuz_app/testing/stubs.js';

Stub handler that returns a 200 response.

returns

Response

stub_mw
#

testing/stubs.ts view source

(_c: any, next: any): Promise<void> import {stub_mw} from '@fuzdev/fuz_app/testing/stubs.js';

Stub middleware that passes through.

_c

type any

next

type any

returns

Promise<void>

stub_password_deps
#

testing/app_server.ts view source

PasswordHashDeps import {stub_password_deps} from '@fuzdev/fuz_app/testing/app_server.js';

Fast password stub for tests that don't exercise login/password flows.

Hashes are deterministic (stub_hash_<password>) and verify correctly, so auth bootstrap and session creation work without Argon2 overhead.

StubUpgrade
#

testing/ws_round_trip.ts view source

StubUpgrade import type {StubUpgrade} from '@fuzdev/fuz_app/testing/ws_round_trip.js';

The return of create_stub_upgrade — fake upgradeWebSocket + factory capture.

upgradeWebSocket

type UpgradeWebSocket

get_create_events

type () => (c: Context) => WSEvents | Promise<WSEvents>

SubscribeOptions
#

realtime/subscriber_registry.ts view source

SubscribeOptions import type {SubscribeOptions} from '@fuzdev/fuz_app/realtime/subscriber_registry.js';

Options for SubscriberRegistry.subscribe.

channels?

Channels to subscribe to. Empty/absent = all channels.

type ReadonlyArray<string>

scope?

Primary (capped) identity — e.g., session hash. Subject to max_per_scope and matched by close_by_identity.

type string

groups?

Grouping identities — e.g., account id. Matched by close_by_identity but NOT subject to the cap. Use for coarse-targeted close.

type ReadonlyArray<string>

Subscriber
#

realtime/subscriber_registry.ts view source

Subscriber<T> import type {Subscriber} from '@fuzdev/fuz_app/realtime/subscriber_registry.js';

generics

Subscriber<T>
T

stream

type SseStream<T>

channels

Channels this subscriber listens to. null means all channels.

type Set<string> | null

scope

Primary (capped) identity. null when none.

type string | null

groups

Grouping identities for close_by_identity. null when none.

type Set<string> | null

SubscriberRegistry
#

realtime/subscriber_registry.ts view source

import {SubscriberRegistry} from '@fuzdev/fuz_app/realtime/subscriber_registry.js';

Generic subscriber registry with channel-based filtering and identity-keyed disconnection.

Subscribers connect with optional channel filters, a capped scope, and uncapped groups. Broadcasts go to a specific channel and reach only matching subscribers. close_by_identity force-closes all subscribers whose scope or groups contain the given key — use for auth revocation.

generics

SubscriberRegistry<T>
T

examples

const registry = new SubscriberRegistry<SseNotification>(); // subscriber connects (from SSE endpoint) const unsubscribe = registry.subscribe(stream, {channels: ['runs']}); // when a run changes registry.broadcast('runs', {method: 'run_created', params: {run}}); // subscriber disconnects unsubscribe();
// scope = session hash (capped), groups = [account id] (close-only) const unsubscribe = registry.subscribe(stream, { channels: ['audit_log'], scope: session_hash, groups: [account_id], }); // coarse — close all of a user's streams on role revocation registry.close_by_identity(account_id); // fine — close just the stream(s) tied to a specific session registry.close_by_identity(session_hash);

constructor

type new <T>(options?: SubscriberRegistryOptions | undefined): SubscriberRegistry<T>

options?

type SubscriberRegistryOptions | undefined
optional

subscribe

Add a subscriber.

type (stream: SseStream<T>, options?: SubscribeOptions | undefined): () => void

stream

SSE stream to send data to

type SseStream<T>

options?

channel filter and identity slots (scope + groups)

type SubscribeOptions | undefined
optional
returns () => void

unsubscribe function

broadcast

Broadcast data to all subscribers on a channel.

Subscribers with no channel filter receive all broadcasts. Subscribers with a channel filter only receive matching broadcasts.

type (channel: string, data: T): void

channel

type string

data

type T
returns void

close_by_identity

Force-close all subscribers whose scope or groups match the given key.

Closes each matching stream and removes the subscriber from the registry. Use for auth revocation — when a user's permissions change, close their SSE connections so they must reconnect and re-authenticate.

type (identity: string): number

identity

the identity key to match (checked against scope and groups)

type string
returns number

the number of subscribers closed

count

Number of active subscribers.

type number

getter

SubscriberRegistryOptions
#

realtime/subscriber_registry.ts view source

SubscriberRegistryOptions import type {SubscriberRegistryOptions} from '@fuzdev/fuz_app/realtime/subscriber_registry.js';

Options for SubscriberRegistry.

max_per_scope?

Max subscribers sharing a single scope. On subscribe, when the count of subscribers with the same scope reaches this limit, the oldest matching subscriber(s) are closed before the new one is added. null (default) disables the cap. groups identities are never capped.

type number | null

SuiteAppOptions
#

testing/app_server.ts view source

Partial<Omit<AppServerOptions, "backend" | "bootstrap" | "session_options" | "create_route_specs" | "rpc_endpoints">> import type {SuiteAppOptions} from '@fuzdev/fuz_app/testing/app_server.js';

app_options shape accepted by create_test_app and the DB-backed suite helpers. Excludes fields the helpers manage directly — backend / session_options / create_route_specs are constructed by the helper itself; rpc_endpoints and bootstrap live on top-level options so setup-time surface lookup and runtime dispatch read from one source of truth.

allowed_origins?

Parsed allowed origin patterns.

type RegExp[]

proxy?

Trusted proxy options.

type { trusted_proxies: string[]; get_connection_ip: (c: Context<any, any, {}>) => string | undefined; }

ip_rate_limiter?

Shared IP rate limiter for login, bootstrap, and bearer auth. Omit or undefined to use a default limiter (5 attempts per 15 minutes). Pass null to explicitly disable rate limiting. Also available on AppServerContext for route factory callbacks.

type RateLimiter

login_account_rate_limiter?

Per-account rate limiter for login attempts. Omit or undefined to use a default limiter (10 attempts per 30 minutes). Pass null to explicitly disable rate limiting. Also available on AppServerContext for route factory callbacks.

type RateLimiter

signup_account_rate_limiter?

Per-account rate limiter for signup attempts, keyed by submitted username. Omit or undefined to use a default limiter (10 attempts per 30 minutes). Pass null to explicitly disable rate limiting. Also available on AppServerContext for route factory callbacks.

type RateLimiter

bearer_ip_rate_limiter?

Rate limiter for bearer token auth attempts (per-IP). Omit or undefined to use a default limiter (5 attempts per 15 minutes). Pass null to explicitly disable rate limiting.

type RateLimiter

action_ip_rate_limiter?

Per-IP rate limiter for the action dispatchers (HTTP RPC + WebSocket). Consulted for actions whose spec declares rate_limit: 'ip' or 'both'. Same limiter applies across transports — one budget per action. Omit or undefined to use a default limiter (600 attempts per 15 minutes — permissive). Pass null to explicitly disable. Also available on AppServerContext for consumers wiring register_action_ws.

type RateLimiter

action_account_rate_limiter?

Per-actor rate limiter for the action dispatchers (HTTP RPC + WebSocket). Consulted for actions whose spec declares rate_limit: 'account' or 'both'. Keyed on request_context.actor.id (post-auth). Omit or undefined to use a default limiter (1200 attempts per 15 minutes — permissive). Pass null to explicitly disable. Also available on AppServerContext for consumers wiring register_action_ws.

type RateLimiter

max_body_size?

Maximum allowed request body size in bytes. Omit or undefined to use the default (1 MiB). Pass null to explicitly disable body size limiting.

type number

daemon_token_state?

Daemon token state for keeper auth. Omit to disable.

type DaemonTokenState

surface_route?

Set to false to disable the auto-created surface route (GET /api/surface). Default: auto-created (authenticated).

type false

transform_middleware?

Optional: transform middleware specs before applying.

type (specs: MiddlewareSpec[]) => MiddlewareSpec[]

audit_log_sse?

Enable factory-managed audit log SSE.

When truthy, creates an AuditLogSse instance internally, registers the SSE listener via backend.deps.audit.add_listener (composing with the consumer's on_audit_event callback rather than rebuilding AppDeps), and auto-includes audit_log_event_specs in the surface. The result is exposed on AppServerContext (for route factories) and AppServer (for the caller), always typed as AuditLogSse | null — when this option is set, the field is non-null. Use require_audit_sse(ctx) to assert the invariant in route factories that depend on it.

Pass true for defaults (admin role), or {role: 'custom'} for a custom role. Omit to wire audit SSE manually.

type true | { role?: string | undefined; }

event_specs?

SSE event specs for surface generation. Defaults to [] (no SSE events).

type EventSpec[]

upgradeWebSocket?

Hono adapter's upgradeWebSocket helper. Required whenever ws_endpoints resolves to a non-empty array — create_app_server throws at assembly otherwise. Omit (along with ws_endpoints) when the consumer doesn't mount any WS endpoints. The same adapter helper services every WsEndpointSpec mounted from ws_endpoints — one adapter per app.

For Node, import {upgradeWebSocket} from '@hono/node-ws'. For Deno, import {upgradeWebSocket} from 'hono/deno'. Test harnesses use create_stub_upgrade from $lib/testing/ws_round_trip.ts.

type UpgradeWebSocket<unknown, any, WSEvents<unknown>>

ws_endpoints?

WebSocket endpoint specs — single source of truth for both surface generation *and* live dispatch. Each entry is auto-mounted via register_ws_endpoint against the assembled Hono app, so consumers no longer call register_ws_endpoint themselves.

Accepts either an array (evaluated eagerly) or a factory (ctx: AppServerContext) => ReadonlyArray<WsEndpointSpec> (evaluated after the server context is assembled). Use the factory form when action lists depend on ctx.deps / ctx.action_*_rate_limiter — e.g. when spreading create_standard_rpc_actions(ctx.deps, ...) over WS.

When non-empty, upgradeWebSocket must be supplied (throws otherwise). A factory returning [] does NOT trip the check — feature-flag gated WS surfaces stay safe.

Duplicate path values across two WsEndpointSpecs throw at mount time (Hono would silently shadow them otherwise).

Each spec's auth_guard? defaults to true — the factory composes create_ws_auth_guard + create_ws_logout_closer against the mounted transport and registers them via deps.audit.add_listener. Wiring is deduped by transport reference identity so two specs sharing one BackendWebsocketTransport instance get a single pair of listeners; wrapped / proxied transports dedupe as separate entries (set auth_guard: false on duplicates and compose against the underlying transport once).

type readonly WsEndpointSpec[] | ((context: AppServerContext) => readonly WsEndpointSpec[])

env_schema?

Env schema for surface generation. Defaults to BaseServerEnv — pass an extended schema (typically BaseServerEnv.extend({...})) when the consumer adds app-specific env vars.

type ZodObject<$ZodLooseShape, $strip>

post_route_middleware?

Middleware applied after routes, before static serving. Included in surface.

type MiddlewareSpec[]

static_serving?

Static file serving. Omit if not serving static files.

type { serve_static: ServeStaticFactory; root?: string | undefined; spa_fallback?: string | undefined; is_spa_route?: ((path: string) => boolean) | undefined; }

await_pending_effects?

Await all pending fire-and-forget effects before returning the response. Use in tests so audit log assertions don't need polling. Default false (production: true fire-and-forget).

type boolean

on_effect_error?

Called when a pending effect rejects. Use for monitoring, metrics, or alerting in production. Only called when await_pending_effects is false (production mode).

type (error: unknown, context: EffectErrorContext) => void

env_values?

Env values for startup summary logging.

type Record<string, unknown>

SupersededOffer
#

auth/role_grant_offer_schema.ts view source

SupersededOffer import type {SupersededOffer} from '@fuzdev/fuz_app/auth/role_grant_offer_schema.js';

A superseded offer row annotated with the grantor's account_id.

Carried by superseded_offers in accept/revoke query results so callers can fan out role_grant_offer_supersede notifications to the grantor's sockets without a second round-trip. Populated via a CTE join on actor in the supersede UPDATE.

inheritance

from_account_id

type Uuid

surface_auth_summary
#

http/surface_query.ts view source

(surface: AppSurface): { none: number; authenticated: number; optional: number; role: Map<string, number>; keeper: number; other: number; } import {surface_auth_summary} from '@fuzdev/fuz_app/http/surface_query.js';

Summarize route auth distribution across the surface.

Categorical view over the four-axis flat record. Multi-role specs contribute one count per role they admit.

surface

returns

{ none: number; authenticated: number; optional: number; role: Map<string, number>; keeper: number; other: number; }

counts by auth category, with role counts broken out by role name

SurfaceExplorer
#

SurfaceRouteOptions
#

http/common_routes.ts view source

SurfaceRouteOptions import type {SurfaceRouteOptions} from '@fuzdev/fuz_app/http/common_routes.js';

Options for the surface explorer route.

surface

The generated app surface to serve.

type AppSurface

SurfaceSecurityPolicyOptions
#

testing/surface_invariants.ts view source

SurfaceSecurityPolicyOptions import type {SurfaceSecurityPolicyOptions} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Configuration for security policy invariants.

All fields have sensible defaults. Pass overrides for project-specific needs.

sensitive_route_patterns?

Path patterns for routes that should be rate-limited. Default: common sensitive REST patterns (login, password, bootstrap). account_token_create lives on the RPC surface; per-method RPC rate limiting is a separate invariant if consumers want it.

type Array<string | RegExp>

public_mutation_allowlist?

Routes explicitly allowed to be public mutations (e.g., webhooks, bootstrap). Format: 'METHOD /path' (e.g., 'POST /api/account/login').

type Array<string>

keeper_route_prefixes?

Allowed path prefixes for keeper-protected routes. Default: ['/api/']. Catches keeper routes outside expected namespaces.

type Array<string>

sweep_orphan_temps
#

db/fact_disk_storage.ts view source

(deps: Pick<FactDiskStorageDeps, "stat" | "readdir" | "remove">, facts_dir: string, options?: { max_age_ms?: number | undefined; log?: Pick<Logger, "warn"> | undefined; } | undefined): Promise<...> import {sweep_orphan_temps} from '@fuzdev/fuz_app/db/fact_disk_storage.js';

Reap stale temp files left under <facts_dir>/.tmp/ by a hard crash (SIGKILL / OOM / host crash) mid-write — the finally cleanup in the writers above never ran. Removes .tmp entries whose mtime is older than max_age_ms (so an in-flight upload isn't yanked out from under itself). The TS twin of the Rust sweep_orphan_temps; call on startup + on an interval.

Best-effort: a missing .tmp/ dir (no oversize upload has ever run) is a no-op; a runtime that doesn't report mtime_ms (a mock) leaves every temp untouched; a per-file stat/remove failure is logged and skipped rather than aborting the sweep. Returns the count removed.

deps

type Pick<FactDiskStorageDeps, "stat" | "readdir" | "remove">

facts_dir

type string

options?

type { max_age_ms?: number | undefined; log?: Pick<Logger, "warn"> | undefined; } | undefined
optional

returns

Promise<number>

sync_expected_schema_fixture
#

testing/schema_ready_fixture.ts view source

(options: SyncExpectedSchemaFixtureOptions): Promise<SyncExpectedSchemaFixtureResult> import {sync_expected_schema_fixture} from '@fuzdev/fuz_app/testing/schema_ready_fixture.js';

Introspect the live (bootstrapped) DB's columns, write them to the committed fixture when update, then read the committed fixture back. The caller asserts deepEqual(live, committed):

const {live, committed} = await sync_expected_schema_fixture({ db, fixture_url: new URL('../../lib/server/expected_schema.json', import.meta.url), update: process.env.UPDATE_SCHEMA_READY === '1', }); assert.deepEqual(live, committed);

When update writes the fixture it emits raw JSON.stringify (one array element per line); Prettier collapses short arrays inline, so run gro format after UPDATE_SCHEMA_READY=1 before committing or the format check will flag the regenerated file. (The content is identical either way — the regen test compares values, not formatting.)

options

returns

Promise<SyncExpectedSchemaFixtureResult>

the live column map and the committed map (post-write when update)

SyncExpectedSchemaFixtureOptions
#

testing/schema_ready_fixture.ts view source

SyncExpectedSchemaFixtureOptions import type {SyncExpectedSchemaFixtureOptions} from '@fuzdev/fuz_app/testing/schema_ready_fixture.js';

db

A bootstrapped DB — the consumer has run its full migration chain on it.

type Db

fixture_url

Committed fixture location — an import.meta.url-relative URL or a path.

type URL | string

update?

When true, overwrite the fixture with the live column map instead of just reading it. Drive from an env flag (e.g. UPDATE_SCHEMA_READY === '1').

type boolean

SyncExpectedSchemaFixtureResult
#

testing/schema_ready_fixture.ts view source

SyncExpectedSchemaFixtureResult import type {SyncExpectedSchemaFixtureResult} from '@fuzdev/fuz_app/testing/schema_ready_fixture.js';

The live column map and the committed fixture, for a deepEqual assertion.

live

Columns introspected from the live, freshly-bootstrapped DB.

type Record<string, Array<string>>

committed

The committed fixture (re-read after writing when update).

type ExpectedSchema

TABLE_LIMIT_MAX
#

ui/table_state.svelte.ts view source

1000 import {TABLE_LIMIT_MAX} from '@fuzdev/fuz_app/ui/table_state.svelte.js';

Maximum number of rows that can be fetched in a single page.

TableInfo
#

http/db_routes.ts view source

TableInfo import type {TableInfo} from '@fuzdev/fuz_app/http/db_routes.js';

Table metadata from information_schema.

table_name

type string

TableSnapshot
#

testing/schema_introspect.ts view source

ZodObject<{ columns: ZodRecord<ZodString, ZodObject<{ data_type: ZodString; udt_name: ZodString; is_nullable: ZodBoolean; column_default: ZodNullable<ZodString>; is_identity: ZodBoolean; }, $strip>>; indexes: ZodArray<...>; constraints: ZodArray<...>; }, $strip> import type {TableSnapshot} from '@fuzdev/fuz_app/testing/schema_introspect.js';

Per-table structural metadata.

TableState
#

ui/table_state.svelte.ts view source

import {TableState} from '@fuzdev/fuz_app/ui/table_state.svelte.js';

list

type AsyncSlot<void, string>

readonly

table_name

type string

$state.raw

columns

type Array<ColumnInfo>

$state.raw

rows

type Array<Record<string, unknown>>

$state.raw

total

type number

$state.raw

offset

type number

$state.raw

limit

type number

$state.raw

primary_key

type string | null

$state.raw

deleting

type string | null

$state.raw

delete_error

type string | null

$state.raw

showing_start

type number

readonly $derived

showing_end

type number

readonly $derived

has_prev

type boolean

readonly $derived

has_next

type boolean

readonly $derived

fetch

Fetch a page of rows for table_name from GET /api/db/tables/{table_name}. limit is clamped to [1, TABLE_LIMIT_MAX].

type (table_name: string, offset?: number, limit?: number): Promise<void>

table_name

type string

offset

type number
default 0

limit

type number
default 100
returns Promise<void>

go_prev

type (): void

returns void

go_next

type (): void

returns void

delete_row

Delete a row by its primary key via DELETE /api/db/tables/{table_name}/rows/{pk}. Optimistically drops it from rows and decrements total on success; surfaces server errors on delete_error.

type (row: Record<string, unknown>): Promise<boolean>

row

type Record<string, unknown>
returns Promise<boolean>

true when the row was removed; false on missing primary key or server error

TableStatus
#

db/status.ts view source

TableStatus import type {TableStatus} from '@fuzdev/fuz_app/db/status.js';

Table info with row count.

name

type string

row_count

type number

TableWithCount
#

http/db_routes.ts view source

TableWithCount import type {TableWithCount} from '@fuzdev/fuz_app/http/db_routes.js';

Table info with row count.

name

type string

row_count

type number

TerminalDeps
#

runtime/deps.ts view source

TerminalDeps import type {TerminalDeps} from '@fuzdev/fuz_app/runtime/deps.js';

Terminal I/O operations.

stdout_write

Write bytes to stdout.

type (data: Uint8Array) => Promise<number>

stdin_read

Read bytes from stdin, or null on EOF.

type (buffer: Uint8Array) => Promise<number | null>

test_cell_gated_create_authorize
#

testing/cross_backend/test_cell_gated_create_authorize.ts view source

(auth: RequestActorContext, input: CellCreateAuthorizeInput): CellCreateVerdict | Promise<CellCreateVerdict> import {test_cell_gated_create_authorize} from '@fuzdev/fuz_app/testing/cross_backend/test_cell_gated_create_authorize.js';

The directory-model test authorizer — a pure function of the input (it reads the governing root's policy off input.root_data, supplied by the handler). Byte-equivalent with the Rust TestCellGatedCreateAuthorize.

auth

input

returns

CellCreateVerdict | Promise<CellCreateVerdict>

TEST_CLIENT_IP
#

testing/middleware.ts view source

"127.0.0.1" import {TEST_CLIENT_IP} from '@fuzdev/fuz_app/testing/middleware.js';

Default client IP set by the proxy stub in test apps.

TEST_CONTEXT_PRESET_KEY
#

hono_context.ts view source

"test_context_preset" import {TEST_CONTEXT_PRESET_KEY} from '@fuzdev/fuz_app/hono_context.js';

Hono context variable name for the test-harness pre-baked context flag.

Test harnesses (create_test_app_from_specs, create_fake_hono_context, the WS round-trip connect() helper, plus per-test middleware that pre-populates REQUEST_CONTEXT_KEY) set this to true so apply_authorization_phase skips its DB-backed actor resolution and trusts the supplied RequestContext. Production middleware never sets this key — only test code does. The flag is the explicit escape hatch that replaced the implicit "is REQUEST_CONTEXT_KEY already set?" probe, so that future production code consulting REQUEST_CONTEXT_KEY cannot silently bypass the live build.

TEST_COOKIE_SECRET
#

test_if
#

testing/cross_backend/capabilities.ts view source

(cond: boolean, name: string, fn: () => void | Promise<void>): void import {test_if} from '@fuzdev/fuz_app/testing/cross_backend/capabilities.js';

Conditional test() wrapper — registers a vitest case only when cond is true; otherwise registers it as .skip so the run still surfaces the gated coverage in the report.

Thin wrapper around vitest's test.skipIf(!cond) with the argument order flipped to match the more readable test_if(capabilities.ws, ...) call pattern.

cond

type boolean

name

type string

fn

type () => void | Promise<void>

returns

void

TEST_MIDDLEWARE_PATH
#

TestAccount
#

testing/app_server.ts view source

TestAccount import type {TestAccount} from '@fuzdev/fuz_app/testing/app_server.js';

A bootstrapped test account with credentials.

account

type { id: Uuid; username: string }

actor

type { id: Uuid }

session_cookie

Signed session cookie value.

type string

api_token

Raw API token for Bearer auth.

type string

create_session_headers

Build request headers with this account's session cookie.

type (extra?: Record<string, string>) => Record<string, string>

create_bearer_headers

Build request headers with this account's Bearer token.

type (extra?: Record<string, string>) => Record<string, string>

TestAccountFixture
#

testing/cross_backend/setup.ts view source

TestAccount import type {TestAccountFixture} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

Shape returned by TestFixture.create_account. Aliased to the existing TestAccount interface from testing/app_server.ts — same shape, stable name on the cross-backend testing surface so call sites read fixture.create_account(...) returning TestAccountFixture without crossing module boundaries.

account

type { id: string & $brand<"Uuid">; username: string; }

actor

type { id: string & $brand<"Uuid">; }

session_cookie

Signed session cookie value.

type string

api_token

Raw API token for Bearer auth.

type string

create_session_headers

Build request headers with this account's session cookie.

type (extra?: Record<string, string> | undefined): Record<string, string>

extra?

type Record<string, string> | undefined
optional
returns Record<string, string>

create_bearer_headers

Build request headers with this account's Bearer token.

type (extra?: Record<string, string> | undefined): Record<string, string>

extra?

type Record<string, string> | undefined
optional
returns Record<string, string>

TestAccountOverrides
#

testing/entities.ts view source

TestAccountOverrides import type {TestAccountOverrides} from '@fuzdev/fuz_app/testing/entities.js';

Override type for create_test_account — id-like fields accept plain string.

created_at?

type string

username?

type string

email?

type string

email_verified?

type boolean

updated_at?

type string

deleted_at?

Soft-delete tombstone. Non-null means the account is deleted (delete = soft); auth resolution treats it as absent. A hard purge removes the row entirely. See auth/account_queries.ts.

type string

password_hash?

type string

deleted_by?

Actor that performed the soft-delete (initiator: self / admin / keeper). Paired with deleted_at, mirroring role_grant's revoked_at / revoked_by. Plain UUID (no FK, like created_by / updated_by on this table).

type string & $brand<"Uuid">

id?

type string

created_by?

type string

updated_by?

type string

TestAccountWithActor
#

TestActorOverrides
#

testing/entities.ts view source

TestActorOverrides import type {TestActorOverrides} from '@fuzdev/fuz_app/testing/entities.js';

Override type for create_test_actor — id-like fields accept plain string.

created_at?

type string

name?

type string

updated_at?

type string

deleted_at?

Soft-delete tombstone — set alongside the owning account's soft-delete.

type string

deleted_by?

Actor that performed the soft-delete. Paired with deleted_at.

type string & $brand<"Uuid">

id?

type string

account_id?

type string

updated_by?

type string

TestApp
#

testing/app_server.ts view source

TestApp import type {TestApp} from '@fuzdev/fuz_app/testing/app_server.js';

A fully assembled test app — Hono app + backend + helpers.

app

type Hono

backend

type TestAppServer

surface_spec

type AppSurfaceSpec

surface

type AppSurface

route_specs

type Array<RouteSpec>

create_session_headers

Build request headers with the bootstrapped session cookie.

type (extra?: Record<string, string>) => Record<string, string>

create_bearer_headers

Build request headers with the bootstrapped Bearer token.

type (extra?: Record<string, string>) => Record<string, string>

create_daemon_token_headers

Build request headers with the daemon token (keeper auth).

type (extra?: Record<string, string>) => Record<string, string>

create_account

Create an additional account with credentials.

type (options?: CreateTestAppAccountArgs) => Promise<TestAccount>

cleanup

Cleanup resources (delegates to TestAppServer.cleanup).

type () => Promise<void>

TestAppForBootstrap
#

testing/app_server.ts view source

TestAppForBootstrap import type {TestAppForBootstrap} from '@fuzdev/fuz_app/testing/app_server.js';

A fully assembled test app in the pre-bootstrap state — empty DB, bootstrap_lock.bootstrapped = false, no keeper account. Test drives POST /bootstrap itself.

app

type Hono

backend

type AppBackend

surface_spec

type AppSurfaceSpec

surface

type AppSurface

route_specs

type Array<RouteSpec>

create_request_headers

Build host/origin request headers for the anonymous bootstrap POST.

type (extra?: Record<string, string>) => Record<string, string>

cleanup

Release test resources (no-op when DB is injected or factory-cached).

type () => Promise<void>

TestAppServer
#

testing/app_server.ts view source

TestAppServer import type {TestAppServer} from '@fuzdev/fuz_app/testing/app_server.js';

An AppBackend with a bootstrapped account, API token, and session cookie.

inheritance

extends: AppBackend

account

The bootstrapped account.

type { id: Uuid; username: string }

actor

The actor linked to the account.

type { id: Uuid }

api_token

Raw API token for Bearer auth.

type string

session_cookie

Signed session cookie value for cookie auth.

type string

keyring

Keyring used for cookie signing — exposed for forging expired/tampered cookies in tests.

type Keyring

cleanup

Release test resources (no-op when DB is injected or factory-cached).

type () => Promise<void>

TestAppServerOptions
#

testing/app_server.ts view source

TestAppServerOptions import type {TestAppServerOptions} from '@fuzdev/fuz_app/testing/app_server.js';

Configuration for create_test_app_server.

session_options

Session options — needed for cookie signing.

type SessionOptions<string>

db?

Existing database — skips internal DB creation when provided. Caller owns the DB lifecycle.

type Db

db_type?

Database driver type — only used when db is provided. Default: 'pglite-memory'.

type DbType

migration_namespaces?

Extra migration namespaces run after the builtin auth namespace in the auto-created in-memory PGlite, mirroring create_app_backend's migration_namespaces. For suites whose backend needs tables beyond auth — the cell parity suite passes [CELL_MIGRATION_NS]. The harness builds + caches a fresh-per-test factory migrating [auth_migration_ns, ...migration_namespaces]; the reset-on-create gives the same fresh-db isolation as the auth-only default. Mutually exclusive with db (which assumes the caller already migrated).

type ReadonlyArray<MigrationNamespace>

password?

Password implementation. Default: stub_password_deps. Pass argon2_password_deps for tests that exercise login.

type PasswordHashDeps

username?

Username for the bootstrapped account. Default: 'keeper'.

type string

password_value?

Password for the bootstrapped account. Default: DEFAULT_TEST_PASSWORD.

type string

roles?

Roles to grant. Default: [ROLE_KEEPER].

type Array<string>

audit_factory?

Build the bound AuditEmitter used by the test backend. Defaults to default_audit_factory (a no-listener create_audit_emitter over the test backend's {db, log}). Pass a custom factory when a test needs:

  • to capture audit events (compose on_audit_event inside the body)
  • to register consumer event-type schemas (pass audit_log_config)
  • to instrument emit ordering (create_emit_ordering_audit_factory)
  • to wrap or replace the emitter for some other reason

Matches the production shape — create_app_backend requires an audit_factory and create_test_app_server mirrors that contract end-to-end. The earlier on_audit_event / audit_log_config sugar fields were removed alongside the CreateAppBackendOptions rename.

type AuditFactory

TestAuditEventOverrides
#

testing/entities.ts view source

TestAuditEventOverrides import type {TestAuditEventOverrides} from '@fuzdev/fuz_app/testing/entities.js';

Override type for create_test_audit_event — id-like fields accept plain string.

ip?

type string

metadata?

type Record<string, unknown>

created_at?

type string

event_type?

type string

outcome?

type "success" | "failure"

seq?

type number

id?

type string

actor_id?

type string

account_id?

type string

target_account_id?

type string

target_actor_id?

type string

TestBackendPaths
#

testing/cross_backend/build_test_backend_paths.ts view source

TestBackendPaths import type {TestBackendPaths} from '@fuzdev/fuz_app/testing/cross_backend/build_test_backend_paths.js';

Generic per-backend paths every cross-process test binary needs. Consumers extend this with their own domain paths.

  • root — the per-backend subtree under os.tmpdir(). Compose consumer-specific paths under here.
  • bootstrap_token_pathFUZ_BOOTSTRAP_TOKEN_PATH; harness writes the bootstrap token here before spawn.
  • daemon_token_path — where init_daemon_token (Rust) and the TS server's daemon-token writer land the token (under {root}/run/).

root

type string

readonly

bootstrap_token_path

type string

readonly

daemon_token_path

type string

readonly

TestFixture
#

testing/cross_backend/setup.ts view source

TestFixtureBase import type {TestFixture} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

The per-test bundle returned by SetupTest. Every Tier 1 suite body reads exclusively from this shape — no test_app.backend.* reads remain in the suite bodies.

Transport-agnostic: in-process and cross-process producers return the same shape. Behaviors that once needed raw backend access (keyring for forging cookies) are reached through wire-shaped seams instead — mint_expired_session() mints over the _testing_mint_session channel cross-process and directly in-process, so suite bodies never branch on the transport.

transport

Transport for this test's HTTP requests. Typed as FetchTransport so cross-process tests can call transport.cookies() for WS upgrade cookie threading; in-process provides a no-op cookies() returning [] (in-process tests construct cookies via create_session_headers directly and don't thread WS through this channel).

type (url: string, init: RequestInit): Promise<Response>

readonly

url

type string

init

type RequestInit
returns Promise<Response>

fresh_transport

Build a brand-new FetchTransport with an empty cookie jar pinned to the same backend. Use for unauthed assertions (`no cookie on protected route returns 401`, bearer-only calls expected to fall through to the unauthenticated path) where the per-test session cookie carried by transport's jar would otherwise leak into the request and convert a 401 into a 200.

New-per-call, not memoized — each invocation returns a fresh instance. If a call mutates the jar (e.g. an unauthed login attempt returning Set-Cookie) it can't pollute sibling calls.

Pass origin: null for bearer-only probes that must look like non-browser callers — the auth middleware silently discards bearer credentials when Origin/Referer is present, so a default Origin: <base_url> would convert "bearer + no Origin → 200" into "bearer + Origin → discarded → 401" cross-process. In-process the wrapper is stateless and the option is a no-op (no auto-Origin to suppress).

In-process this is functionally identical to transport (the wrapper's cookies(): [] is a no-op already); cross-process the returned transport starts with an empty jar at the same base_url.

type (options?: { readonly origin?: string | null | undefined; } | undefined): FetchTransport

readonly

options?

type { readonly origin?: string | null | undefined; } | undefined
optional

account

The freshly-bootstrapped keeper account.

type { readonly id: string & $brand<"Uuid">; readonly username: string; }

readonly

actor

The actor linked to the keeper account.

type { readonly id: string & $brand<"Uuid">; }

readonly

create_session_headers

Build request headers with the keeper's session cookie.

type (extra?: Record<string, string> | undefined): Record<string, string>

readonly

extra?

type Record<string, string> | undefined
optional
returns Record<string, string>

create_bearer_headers

Build request headers with the keeper's bearer token.

type (extra?: Record<string, string> | undefined): Record<string, string>

readonly

extra?

type Record<string, string> | undefined
optional
returns Record<string, string>

create_daemon_token_headers

Build request headers with the daemon token (keeper auth).

type (extra?: Record<string, string> | undefined): Record<string, string>

readonly

extra?

type Record<string, string> | undefined
optional
returns Record<string, string>

create_account

Mint an additional bootstrapped account for cross-account / multi-user tests. In-process: re-uses create_test_account_with_credentials against the same DB; cross-process: goes through the consumer-supplied DB-admin channel.

type (options?: CreateTestAccountOptions | undefined): Promise<TestAccount>

readonly

options?

type CreateTestAccountOptions | undefined
optional
returns Promise<TestAccount>

extra_accounts

Bootstrap-time-seeded secondaries, keyed by their declared username. Populated from the extra_accounts option passed to default_in_process_setup / default_cross_process_setup. Empty for suites that don't declare any.

type Readonly<Record<string, ExtraAccountFixture>>

readonly

extra_actors

Additional actors seeded on the keeper account (beyond its single bootstrap actor), in declaration order. Populated from the extra_actors option. Empty unless a suite declares any. Use to drive the multi-actor acting-selector branches: with extra_actors non-empty the keeper has >1 actor, so a keeper request omitting acting resolves to actor_required with these ids in its available[] list. Each id is a valid acting value the keeper can supply explicitly.

type readonly { readonly id: string & $brand<"Uuid">; readonly name: string; }[]

readonly

mint_expired_session

Forge an *expired server-side session* for the keeper account and return the ready-to-send Cookie header value (name=value). The minted auth_session row is backdated while the signed cookie payload stays valid — so resolution clears the cookie-payload gate (parse_session) and is refused at the authoritative DB-row gate (query_session_get_validWHERE expires_at > NOW()). Backs the expired_session conformance principal. In-process mints directly via mint_test_session; cross-process drives the _testing_mint_session RPC over the keeper's daemon-token channel (the driver has no keyring).

type (): Promise<string>

readonly
returns Promise<string>

TestFixtureBase
#

testing/cross_backend/setup.ts view source

TestFixtureBase import type {TestFixtureBase} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

Fields shared by every TestFixture regardless of transport. The discriminated union below adds in-process-only fields conditionally.

Keeper ≠ admin. fixture.account / fixture.actor refer to the fresh keeper seeded per test. The keeper account holds ROLE_KEEPER + ROLE_ADMIN by default — matching the production bootstrap_account flow. The ROLE_KEEPER role itself does *not* grant admin reach; the bootstrap account just happens to hold both as separate grants. Tests probing the keeper-vs-admin separation (e.g. "non-admin cannot list accounts") declare a secondary at setup-time via extra_accounts: [{username, roles: [ROLE_KEEPER]}] and read it from fixture.extra_accounts[username].

transport

Transport for this test's HTTP requests. Typed as FetchTransport so cross-process tests can call transport.cookies() for WS upgrade cookie threading; in-process provides a no-op cookies() returning [] (in-process tests construct cookies via create_session_headers directly and don't thread WS through this channel).

type FetchTransport

readonly

fresh_transport

Build a brand-new FetchTransport with an empty cookie jar pinned to the same backend. Use for unauthed assertions (`no cookie on protected route returns 401`, bearer-only calls expected to fall through to the unauthenticated path) where the per-test session cookie carried by transport's jar would otherwise leak into the request and convert a 401 into a 200.

New-per-call, not memoized — each invocation returns a fresh instance. If a call mutates the jar (e.g. an unauthed login attempt returning Set-Cookie) it can't pollute sibling calls.

Pass origin: null for bearer-only probes that must look like non-browser callers — the auth middleware silently discards bearer credentials when Origin/Referer is present, so a default Origin: <base_url> would convert "bearer + no Origin → 200" into "bearer + Origin → discarded → 401" cross-process. In-process the wrapper is stateless and the option is a no-op (no auto-Origin to suppress).

In-process this is functionally identical to transport (the wrapper's cookies(): [] is a no-op already); cross-process the returned transport starts with an empty jar at the same base_url.

type (options?: { readonly origin?: string | null }) => FetchTransport

readonly

account

The freshly-bootstrapped keeper account.

type { readonly id: Uuid; readonly username: string }

readonly

actor

The actor linked to the keeper account.

type { readonly id: Uuid }

readonly

create_session_headers

Build request headers with the keeper's session cookie.

type (extra?: Record<string, string>) => Record<string, string>

readonly

create_bearer_headers

Build request headers with the keeper's bearer token.

type (extra?: Record<string, string>) => Record<string, string>

readonly

create_daemon_token_headers

Build request headers with the daemon token (keeper auth).

type (extra?: Record<string, string>) => Record<string, string>

readonly

create_account

Mint an additional bootstrapped account for cross-account / multi-user tests. In-process: re-uses create_test_account_with_credentials against the same DB; cross-process: goes through the consumer-supplied DB-admin channel.

type (options?: CreateTestAccountOptions) => Promise<TestAccountFixture>

readonly

extra_accounts

Bootstrap-time-seeded secondaries, keyed by their declared username. Populated from the extra_accounts option passed to default_in_process_setup / default_cross_process_setup. Empty for suites that don't declare any.

type Readonly<Record<string, ExtraAccountFixture>>

readonly

extra_actors

Additional actors seeded on the keeper account (beyond its single bootstrap actor), in declaration order. Populated from the extra_actors option. Empty unless a suite declares any. Use to drive the multi-actor acting-selector branches: with extra_actors non-empty the keeper has >1 actor, so a keeper request omitting acting resolves to actor_required with these ids in its available[] list. Each id is a valid acting value the keeper can supply explicitly.

type ReadonlyArray<{ readonly id: Uuid; readonly name: string }>

readonly

mint_expired_session

Forge an *expired server-side session* for the keeper account and return the ready-to-send Cookie header value (name=value). The minted auth_session row is backdated while the signed cookie payload stays valid — so resolution clears the cookie-payload gate (parse_session) and is refused at the authoritative DB-row gate (query_session_get_validWHERE expires_at > NOW()). Backs the expired_session conformance principal. In-process mints directly via mint_test_session; cross-process drives the _testing_mint_session RPC over the keeper's daemon-token channel (the driver has no keyring).

type () => Promise<string>

readonly

testing_action_manifest_action_spec
#

testing/cross_backend/testing_reset_actions.ts view source

{ readonly method: "_testing_action_manifest"; readonly kind: "request_response"; readonly initiator: "frontend"; readonly auth: { readonly account: "required"; readonly actor: "none"; readonly credential_types: readonly [...]; }; ... 4 more ...; readonly description: string; } import {testing_action_manifest_action_spec} from '@fuzdev/fuz_app/testing/cross_backend/testing_reset_actions.js';

_testing_action_manifest — dump the backend's live RPC method set as a normalized ActionManifest (one entry per method: `{method, side_effects, account, actor, roles, credential_types}`) for cross-impl parity diffing. The action-surface twin of _testing_schema_snapshot: where that introspects the live *database*, this introspects the live *RPC registry*. The cross-backend harness calls it on each backend, then assert_action_manifests_equals the results (the Rust mirror is fuz_testing::create_testing_action_manifest_action_spec, whose normalization matches by design). Complements the in-repo spine_method_coverage gate (mounted ⟹ covered) by proving the TS mount-set ≡ the Rust mount-set, method-for-method and auth-shape-for-shape.

Unlike its _testing_* siblings this action is not bundled by create_testing_actions — the manifest must enumerate *every* mounted method, which create_testing_actions (one sub-factory among many) can't see. It's appended at the full-mount layer (build_full_spine_rpc_actions) via create_testing_action_manifest_action, where the complete list exists.

auth gates on the daemon-token credential, matching _testing_reset.

testing_drain_effects_action_spec
#

testing/cross_backend/testing_reset_actions.ts view source

{ readonly method: "_testing_drain_effects"; readonly kind: "request_response"; readonly initiator: "frontend"; readonly auth: { readonly account: "required"; readonly actor: "none"; readonly credential_types: readonly [...]; }; ... 4 more ...; readonly description: "Test-binary only — await in-flight fire-and-forge... import {testing_drain_effects_action_spec} from '@fuzdev/fuz_app/testing/cross_backend/testing_reset_actions.js';

_testing_drain_effects — await in-flight fire-and-forget audit writes so a following audit_log_list is authoritative. The deterministic barrier the cross-backend conformance suite uses in place of a poll/sleep before asserting on audit rows.

On the TS spine the barrier is satisfied by construction: the test binary runs await_pending_effects: true, so every mutation's fire-and- forget audit emits are awaited before its response returns — by the time a later drain call runs, prior emits are already durable. The action still exists so the cross-backend test body calls the same method on every backend; the Rust spine (whose audit writes are detached tokio tasks) does the real await in AuditEmitter::drain_inflight.

auth gates on the daemon-token credential, matching _testing_reset.

TESTING_METHOD_PREFIX
#

testing/surface_invariants.ts view source

"_testing_" import {TESTING_METHOD_PREFIX} from '@fuzdev/fuz_app/testing/surface_invariants.js';

Reserved method-name prefix for the daemon-token-gated test-backdoor actions (_testing_reset, _testing_mint_session, _testing_put_fact, _testing_drain_effects, _testing_schema_snapshot). Test binaries live-mount these on their RPC endpoint but they must never appear on a declared surface.

testing_migration_tracker_action_spec
#

testing/cross_backend/testing_reset_actions.ts view source

{ readonly method: "_testing_migration_tracker"; readonly kind: "request_response"; readonly initiator: "frontend"; readonly auth: { readonly account: "required"; readonly actor: "none"; readonly credential_types: readonly [...]; }; ... 4 more ...; readonly description: string; } import {testing_migration_tracker_action_spec} from '@fuzdev/fuz_app/testing/cross_backend/testing_reset_actions.js';

_testing_migration_tracker — dump the schema_version tracker rows as a normalized MigrationTracker ([{namespace, name, sequence}]) for cross-impl migration-identity diffing. The provenance half of _testing_schema_snapshot: where that captures the resulting *schema* (and excludes the tracker by design), this captures the tracker *itself*, so the cross-backend harness can assert the two spines record byte-identical migration identity. This closes the gap that let the cell/fact migration-name divergence reach the visiones cutover undetected (name-divergence-at-N — same schema, divergent recorded names).

auth gates on the daemon-token credential, matching _testing_reset. The Rust mirror is fuz_testing::create_testing_migration_tracker_action_spec.

testing_mint_session_action_spec
#

testing/cross_backend/testing_reset_actions.ts view source

{ readonly method: "_testing_mint_session"; readonly kind: "request_response"; readonly initiator: "frontend"; readonly auth: { readonly account: "required"; readonly actor: "none"; readonly credential_types: readonly [...]; }; ... 4 more ...; readonly description: string; } import {testing_mint_session_action_spec} from '@fuzdev/fuz_app/testing/cross_backend/testing_reset_actions.js';

_testing_mint_session — mint an expired-by-construction server-side session for an existing account and return its signed cookie value.

expires_in_seconds is constrained negative (z.number().int().negative()) so the action is structurally incapable of minting a *usable* session: it can only produce an already-backdated, already-dead auth_session row. The daemon-token gate + loopback binding already fence the backdoor, but the negative constraint is the make-impossible-states floor — even a misuse can't forge a valid session for an arbitrary account_id. The Rust mirror (fuz_testing::create_testing_mint_session_action_spec) enforces the same floor.

The minted auth_session row's expires_at is backdated while the returned cookie's own signed payload stays valid (future). Cross-process auth resolution therefore passes the cookie-payload gate (parse_session) and is refused by the authoritative DB-row gate (query_session_get_validWHERE expires_at > NOW()) — the gate the in-process payload-expiry tests never reach and the one that structurally needs a server-side mint (the cross-process driver has no keyring / DB access). The expired_session conformance principal drives this.

auth gates on the daemon-token credential, matching _testing_reset — effectively keeper-only. Like its siblings the action is internally privileged (a direct auth_session insert the production wire never exposes); daemon-token auth is the structural fence and the module's assert_dev_env import (TS) plus the Rust cargo xtask check-release dep-graph audit keep the _testing_ surface out of every shipped build.

testing_put_fact_action_spec
#

testing/cross_backend/testing_reset_actions.ts view source

{ readonly method: "_testing_put_fact"; readonly kind: "request_response"; readonly initiator: "frontend"; readonly auth: { readonly account: "required"; readonly actor: "none"; readonly credential_types: readonly [...]; }; ... 4 more ...; readonly description: string; } import {testing_put_fact_action_spec} from '@fuzdev/fuz_app/testing/cross_backend/testing_reset_actions.js';

_testing_put_fact — seed an embedded fact (fact.bytes) for the cross-process fact-serving suite, which drives over real HTTP and has no PgFactStore to call. Hashes the UTF-8 content (blake3, via fact_hash_bytes — the same hash the Rust _testing_put_fact computes), inserts the row idempotently, and returns {hash}. The referencing cell is seeded separately via the cell_create RPC. Embedded-only is enough for the authz assertions (cell-scoped admit, cross-owner-no-leak, 404-mask, bare-hash admin-only); external / X-Accel parity stays covered by the forge's own gate.

auth gates on the daemon-token credential, matching _testing_reset — the action does a direct fact insert the production wire never exposes. The Rust mirror is fuz_testing::create_testing_put_fact_action_spec.

testing_reset_action_spec
#

testing/cross_backend/testing_reset_actions.ts view source

{ readonly method: "_testing_reset"; readonly kind: "request_response"; readonly initiator: "frontend"; readonly auth: { readonly account: "required"; readonly actor: "none"; readonly credential_types: readonly [...]; }; ... 4 more ...; readonly description: "Test-binary only — wipe auth tables, re-bootstrap a fresh... import {testing_reset_action_spec} from '@fuzdev/fuz_app/testing/cross_backend/testing_reset_actions.js';

The _testing_reset action spec.

Input:

  • extra_keeper_roles — roles to grant the fresh keeper *in addition to* [ROLE_KEEPER, ROLE_ADMIN] (matching production bootstrap).
  • extra_accounts — additional accounts to seed at this same bootstrap-equivalent step. Each entry's roles are direct-granted (bypassing offer/accept) because the seed is *part of bootstrap*, not a post-bootstrap action. Use this for accounts whose required roles aren't admin-grantable via offer/accept (e.g. ROLE_KEEPER, whose RoleSpec.grant_paths is bootstrap-only). For admin-grantable roles, prefer fixture.create_account({roles}) (offer/accept production path).

Output: keeper credentials plus a parallel array of seeded extra_accounts (same order as input). The per-test fixture closes over the returned values; subsequent calls in the same test see the fresh keeper and any requested secondaries.

auth gates on the daemon-token credential — the keeper holds it exclusively. The action is internally privileged (it runs direct DB writes the production wire never exposes); daemon-token auth is the structural fence.

testing_reset_wiped_tables
#

testing_schema_snapshot_action_spec
#

testing/cross_backend/testing_reset_actions.ts view source

{ readonly method: "_testing_schema_snapshot"; readonly kind: "request_response"; readonly initiator: "frontend"; readonly auth: { readonly account: "required"; readonly actor: "none"; readonly credential_types: readonly [...]; }; ... 4 more ...; readonly description: "Test-binary only — introspect the live schema i... import {testing_schema_snapshot_action_spec} from '@fuzdev/fuz_app/testing/cross_backend/testing_reset_actions.js';

_testing_schema_snapshot — introspect the live database into a normalized SchemaSnapshot for cross-impl parity diffing. The cross-backend harness calls this on each backend, then assert_schema_snapshots_equals the results (a Rust backend answers from fuz_db::query_schema_snapshot; the shapes match by design). Optional exclude_tables drops documented divergences from both sides before comparison.

auth gates on the daemon-token credential, matching _testing_reset.

TestingBackdoorCrossTestOptions
#

testing/cross_backend/testing_backdoor.ts view source

RpcPathCrossSuiteOptions import type {TestingBackdoorCrossTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/testing_backdoor.js';

Options for the testing-backdoor negative-credential suite.

rpc_path?

RPC endpoint path the methods are mounted on. Default /api/rpc.

type string

readonly

setup_test

Per-test fixture-producing function (fresh keeper + db per call).

type (): Promise<TestFixtureBase>

readonly
returns Promise<TestFixtureBase>

capabilities

Backend capability declarations — each suite gates on its own flag.

type BackendCapabilities

readonly

TestingRateLimiter
#

testing/testing_rate_limiter.ts view source

import {TestingRateLimiter} from '@fuzdev/fuz_app/testing/testing_rate_limiter.js';

RateLimiter plus bucket tracking. Every check/record call adds its key to #seen_keys; reset removes it; reset_all clears every tracked bucket. Drop-in replacement anywhere a RateLimiter is expected — the type is nominally compatible via subclassing.

inheritance

extends: RateLimiter

check

type (key: string, now?: number | undefined): RateLimitResult

key

type string

now?

type number | undefined
optional

record

type (key: string, now?: number | undefined): RateLimitResult

key

type string

now?

type number | undefined
optional

reset

type (key: string): void

key

type string
returns void

reset_all

Clear every bucket this limiter has been asked about. Idempotent; safe to call before any check/record activity. Designed to be invoked from a _testing_reset handler's reset_state callback so the test binary's rate-limit buckets don't leak across test cases.

type (): void

returns void

tracked_keys

Snapshot of every bucket key this limiter has observed via check/record. Doesn't reflect post-cleanup pruning — keys that cleanup() removed remain in tracked_keys until reset/reset_all runs (or the limiter is disposed). Useful for assertions like "limiter saw exactly N IPs" in tests.

type ReadonlySet<string>

getter

TestingServerAdapter
#

testing/cross_backend/testing_server_core.ts view source

TestingServerAdapter import type {TestingServerAdapter} from '@fuzdev/fuz_app/testing/cross_backend/testing_server_core.js';

Runtime adapter contract for the test-binary entry. Each adapter (testing/cross_backend/testing_server_node.ts, testing/cross_backend/testing_server_deno.ts) implements this and hands the shape to .

runtime_label

Human-readable runtime label for log output (e.g. "Node", "Deno").

type string

runtime

type RuntimeDeps

get_connection_ip

Extract the raw TCP connection IP from a Hono context.

type (c: Context) => string | undefined

prepare_websocket

Build the WS upgrade closure after the caller's build_app returns the app.

type (app: Hono) => PreparedWebsocket

serve

Bind app.fetch to port on hostname; return a .

type (options: { fetch: Hono['fetch']; port: number; hostname: string }) => ServeHandle

pid

Current process pid (for daemon.json).

type number

register_shutdown_signals

Register SIGINT/SIGTERM listeners that invoke handler once each.

type (handler: () => Promise<void>) => void

exit

Forceful exit on graceful-shutdown completion or fatal error.

type (code: number) => never

TestMiddlewareStackApp
#

testing/middleware.ts view source

TestMiddlewareStackApp import type {TestMiddlewareStackApp} from '@fuzdev/fuz_app/testing/middleware.js';

app

type Hono

mock_validate

type ReturnType<typeof vi.fn>

mock_find_by_id

type ReturnType<typeof vi.fn>

mock_find_actor_by_id

type ReturnType<typeof vi.fn>

mock_find_actors_by_account

type ReturnType<typeof vi.fn>

mock_find_active_for_actor

type ReturnType<typeof vi.fn>

TestMiddlewareStackOptions
#

testing/middleware.ts view source

TestMiddlewareStackOptions import type {TestMiddlewareStackOptions} from '@fuzdev/fuz_app/testing/middleware.js';

trusted_proxies?

Trusted proxy IPs.

type Array<string>

default `['10.0.0.1']`

allowed_origins?

Comma-separated allowed origin patterns.

type string

default `'https://app.example.com'`

connection_ip?

Connection IP or factory.

type string | (() => string | undefined)

default first trusted proxy

ip_rate_limiter?

Rate limiter for bearer auth.

type RateLimiter | null

default `null`

TestRoleGrantOverrides
#

testing/entities.ts view source

TestRoleGrantOverrides import type {TestRoleGrantOverrides} from '@fuzdev/fuz_app/testing/entities.js';

Override type for create_test_role_grant — id-like fields accept plain string.

created_at?

type string

expires_at?

type string

role?

type string

revoked_at?

type string

revoked_reason?

Optional free-form reason attached on revoke (rides on the role_grant_revoke WS notification to the revokee).

type string

id?

type string

actor_id?

type string

scope_kind?

type string

scope_id?

type string

revoked_by?

type string

granted_by?

type string

source_offer_id?

type string

ThrowingApi
#

actions/rpc_client.ts view source

ThrowingApi<TApi> import type {ThrowingApi} from '@fuzdev/fuz_app/actions/rpc_client.js';

Maps a typed ActionsApi to a throwing variant.

For each method whose return type matches the create_rpc_client shape (Promise<Result<{value: T}, {error: JsonrpcErrorObject}>>), the wrapped method returns Promise<T> directly. Other shapes (notifications typed as => void, sync local_call methods) pass through unchanged — there is nothing to unwrap.

Input + options parameters are preserved verbatim via ...args: infer TArgs so the conditional matches both required-input (input: T) and optional-input (input?: T / nullary) signatures uniformly. Required-input shapes (e.g. admin_session_revoke_all(input: AdminSessionRevokeAllInput)) are not assignable to a (input?: TInput) => … pattern under --strictFunctionTypes, so an earlier (input?, options?) => form silently fell through to TApi[K] and left those methods Result-shaped — create_admin_rpc_adapters(api) would then reject the typed throwing Proxy because half its surface still returned Result<...>. The rest-args form preserves both required and optional parameters and resolves the gap.

generics

ThrowingApi<TApi>
TApi

ThrownJsonrpcError
#

http/jsonrpc_errors.ts view source

import {ThrownJsonrpcError} from '@fuzdev/fuz_app/http/jsonrpc_errors.js';

Error class carrying a JSON-RPC error code — thrown by handlers, caught by apply_route_specs and mapped to HTTP status + JSON-RPC error response.

Named for what it is: an error with a JSON-RPC error code that gets thrown.

inheritance

extends: Error

code

type JsonrpcErrorCode

data?

type unknown

constructor

type new (code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">), message: string, data?: unknown, options?: ErrorOptions | undefined): ThrownJsonrpcError

code

type -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<"JsonrpcServerErrorCode">)

message

type string

data?

type unknown
optional

options?

type ErrorOptions | undefined
optional

to_action_property_key
#

actions/action_codegen.ts view source

(method: string): string import {to_action_property_key} from '@fuzdev/fuz_app/actions/action_codegen.js';

Render a method name as an object/interface property key — bare when it's a valid JS identifier, single-quoted otherwise. Method names like peer/ping aren't legal as bare keys, so quoting keeps the emitted ActionSpecs / ActionInputs / FrontendActionsApi members valid TypeScript. Pairs with the bracketed indexed-access form (ActionInputs['peer/ping']) used for value/type lookups, which already accepts the raw method string.

method

type string

returns

string

to_action_spec_identifier
#

actions/action_codegen.ts view source

(method: string): string import {to_action_spec_identifier} from '@fuzdev/fuz_app/actions/action_codegen.js';

Derive the exported spec identifier for a method. Method names that aren't legal JS identifiers — e.g. the / in the peer/ping protocol action — have each run of illegal characters collapsed to a single _, so peer/ping resolves to peer_ping_action_spec (the export in actions/peer_ping.ts). Already-valid names pass through unchanged.

method

type string

returns

string

to_action_spec_input_identifier
#

actions/action_codegen.ts view source

(method: string): string import {to_action_spec_input_identifier} from '@fuzdev/fuz_app/actions/action_codegen.js';

method

type string

returns

string

to_action_spec_output_identifier
#

actions/action_codegen.ts view source

(method: string): string import {to_action_spec_output_identifier} from '@fuzdev/fuz_app/actions/action_codegen.js';

method

type string

returns

string

to_admin_account
#

auth/account_schema.ts view source

(account: Account): { id: string & $brand<"Uuid">; username: string; email: string | null; email_verified: boolean; created_at: string; updated_at: string; updated_by: (string & $brand<...>) | null; deleted_at: string | null; } import {to_admin_account} from '@fuzdev/fuz_app/auth/account_schema.js';

Convert an Account to an AdminAccountJson for admin listings.

account

the full account record

type Account

returns

{ id: string & $brand<"Uuid">; username: string; email: string | null; email_verified: boolean; created_at: string; updated_at: string; updated_by: (string & $brand<"Uuid">) | null; deleted_at: string | null; }

the admin-safe account with audit fields

to_cell_json
#

auth/cell_actions.ts view source

(row: CellRow): { id: string & $brand<"Uuid">; path: (string & $brand<"CellPath">) | null; data: { [x: string]: unknown; label?: string | undefined; summary?: string | undefined; }; ... 11 more ...; grant_count: number; } import {to_cell_json} from '@fuzdev/fuz_app/auth/cell_actions.js';

row

type CellRow

returns

{ id: string & $brand<"Uuid">; path: (string & $brand<"CellPath">) | null; data: { [x: string]: unknown; label?: string | undefined; summary?: string | undefined; }; kind: string | null; ... 10 more ...; grant_count: number; }

to_field_json
#

auth/cell_field_actions.ts view source

(row: CellFieldRow): { source_id: string & $brand<"Uuid">; name: string; target_id: string & $brand<"Uuid">; created_at: string; } import {to_field_json} from '@fuzdev/fuz_app/auth/cell_field_actions.js';

row

returns

{ source_id: string & $brand<"Uuid">; name: string; target_id: string & $brand<"Uuid">; created_at: string; }

to_grant_json
#

auth/cell_grant_actions.ts view source

(row: CellGrantRow): { id: string & $brand<"Uuid">; cell_id: string & $brand<"Uuid">; level: "viewer" | "editor"; actor_id: (string & $brand<"Uuid">) | null; role: string | null; scope_id: (string & $brand<...>) | null; granted_by: (string & $brand<...>) | null; created_at: string; } import {to_grant_json} from '@fuzdev/fuz_app/auth/cell_grant_actions.js';

row

returns

{ id: string & $brand<"Uuid">; cell_id: string & $brand<"Uuid">; level: "viewer" | "editor"; actor_id: (string & $brand<"Uuid">) | null; role: string | null; scope_id: (string & $brand<...>) | null; granted_by: (string & $brand<...>) | null; created_at: string; }

to_item_json
#

auth/cell_item_actions.ts view source

(row: CellItemRow): { parent_id: string & $brand<"Uuid">; position: string & $brand<"CellItemPosition">; child_id: string & $brand<"Uuid">; created_at: string; } import {to_item_json} from '@fuzdev/fuz_app/auth/cell_item_actions.js';

row

returns

{ parent_id: string & $brand<"Uuid">; position: string & $brand<"CellItemPosition">; child_id: string & $brand<"Uuid">; created_at: string; }

to_jsonrpc_message_id
#

http/jsonrpc_helpers.ts view source

(message_or_id: unknown): string | number | null import {to_jsonrpc_message_id} from '@fuzdev/fuz_app/http/jsonrpc_helpers.js';

Extracts a JSON-RPC request id from a message or raw value. Returns null if no valid id can be extracted.

message_or_id

type unknown

returns

string | number | null

to_jsonrpc_params
#

http/jsonrpc_helpers.ts view source

(input: unknown): Record<string, any> | undefined import {to_jsonrpc_params} from '@fuzdev/fuz_app/http/jsonrpc_helpers.js';

Normalizes input to JSON-RPC params format. Returns undefined for null/undefined, wraps primitives in {value}.

input

type unknown

returns

Record<string, any> | undefined

to_jsonrpc_result
#

http/jsonrpc_helpers.ts view source

(output: unknown): Record<string, any> import {to_jsonrpc_result} from '@fuzdev/fuz_app/http/jsonrpc_helpers.js';

Normalizes output to JSON-RPC result format. Returns empty object for null/undefined, wraps primitives in {value}.

output

type unknown

returns

Record<string, any>

to_max_length
#

cli/help.ts view source

<T>(items: T[], to_string: (item: T) => string): number import {to_max_length} from '@fuzdev/fuz_app/cli/help.js';

Get maximum length from array.

items

array of items

type T[]

to_string

function to convert item to string for length measurement

type (item: T) => string

returns

number

maximum string length

generics

to_max_length<T>
T

to_role_grant_offer_json
#

auth/role_grant_offer_schema.ts view source

(offer: RoleGrantOffer): { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; ... 12 more ...; resulting_role_grant_id: (string & $brand<...>) | null; } import {to_role_grant_offer_json} from '@fuzdev/fuz_app/auth/role_grant_offer_schema.js';

Convert a RoleGrantOffer row to its JSON payload shape.

offer

returns

{ id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; to_actor_id: (string & $brand<"Uuid">) | null; ... 11 more ...; resulting_role_grant_id: (string & $brand<...>) | null; }

to_session_account
#

auth/account_schema.ts view source

(account: Account): SessionAccount import {to_session_account} from '@fuzdev/fuz_app/auth/account_schema.js';

Convert an Account to a SessionAccount by stripping sensitive fields.

account

the full account record

type Account

returns

SessionAccount

the client-safe account

TokenCreateInput
#

auth/account_action_specs.ts view source

ZodPrefault<ZodObject<{ name: ZodDefault<ZodString>; }, $strict>> import type {TokenCreateInput} from '@fuzdev/fuz_app/auth/account_action_specs.js';

Input for account_token_create.

TokenCreateOutput
#

auth/account_action_specs.ts view source

ZodObject<{ ok: ZodLiteral<true>; token: ZodString; id: ZodString; name: ZodString; }, $strict> import type {TokenCreateOutput} from '@fuzdev/fuz_app/auth/account_action_specs.js';

Output for account_token_create. token is returned exactly once.

TokenListInput
#

TokenListOutput
#

auth/account_action_specs.ts view source

ZodObject<{ tokens: ZodArray<ZodObject<{ id: ZodString; account_id: $ZodBranded<ZodUUID, "Uuid", "out">; name: ZodString; expires_at: ZodNullable<ZodString>; last_used_at: ZodNullable<...>; last_used_ip: ZodNullable<...>; created_at: ZodString; }, $strict>>; }, $strict> import type {TokenListOutput} from '@fuzdev/fuz_app/auth/account_action_specs.js';

Output for account_token_list. Hashes are excluded.

TokenRevokeInput
#

auth/account_action_specs.ts view source

ZodObject<{ token_id: ZodString; }, $strict> import type {TokenRevokeInput} from '@fuzdev/fuz_app/auth/account_action_specs.js';

Input for account_token_revoke.

TokenRevokeOutput
#

auth/account_action_specs.ts view source

ZodObject<{ ok: ZodLiteral<true>; revoked: ZodBoolean; }, $strict> import type {TokenRevokeOutput} from '@fuzdev/fuz_app/auth/account_action_specs.js';

Output for account_token_revoke. revoked is false for IDOR misses.

TransitionFunction
#

ui/popover.svelte.ts view source

TransitionFunction import type {TransitionFunction} from '@fuzdev/fuz_app/ui/popover.svelte.js';

Support both Svelte transitions and custom transitions.

(call)

type (node: HTMLElement): TransitionConfig | { destroy?: (() => void) | undefined; }

node

type HTMLElement
returns TransitionConfig | { destroy?: (() => void) | undefined; }

Transport
#

actions/transports.ts view source

Transport import type {Transport} from '@fuzdev/fuz_app/actions/transports.js';

transport_name

type TransportName

send

type (message: { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; }, options?: TransportSendOptions | undefined): Promise<...>

message

type { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; }

options?

type TransportSendOptions | undefined
optional
returns Promise<{ [x: string]: unknown; jsonrpc: "2.0"; id: string | number; result: JSONType; } | { [x: string]: unknown; jsonrpc: "2.0"; id: string | number | null; error: { [x: string]: unknown; code: -32700 | -32600 | -32601 | -32602 | -32603 | (number & $brand<...>); message: string; data?: unknown; }; }>

is_ready

type () => boolean

dispose?

type () => void

TransportForMethod
#

actions/rpc_client.ts view source

TransportForMethod import type {TransportForMethod} from '@fuzdev/fuz_app/actions/rpc_client.js';

Optional per-method transport selector. Return the transport to use for a given method, or undefined to let the peer pick via its fallback rules.

Useful when methods are registered on different backend dispatchers — e.g. a streaming action mounted on the WebSocket endpoint while the rest of the RPC surface lives on HTTP.

(call)

type (method: string): string | undefined

method

type string
returns string | undefined

TransportName
#

Transports
#

actions/transports.ts view source

import {Transports} from '@fuzdev/fuz_app/actions/transports.js';

allow_fallback

Whether to allow fallback to other transports if the current one is not available.

type boolean

default true

register_transport

Registers a transport. The first transport registered also becomes the current.

type (transport: Transport): void

transport

returns void

set_current_transport

Switch the current transport selection by name.

type (transport_name: string): void

transport_name

type string
returns void

throws

  • Error - if no transport with `transport_name` has been registered

get_transport

Resolve a transport. With allow_fallback, walks specified → current → any-ready; without, returns the named transport (or current) only when it's ready.

type (transport_name?: string | undefined): Transport | null

transport_name?

type string | undefined
optional
returns Transport | null

the resolved transport, or null when none is ready

is_ready

type (): boolean | null

returns boolean | null

get_current_transport

type (): Transport | null

returns Transport | null

get_current_transport_name

type (): string | null

returns string | null

get_transport_by_name

type (transport_name: string): Transport | null

transport_name

type string
returns Transport | null

TransportSendOptions
#

actions/transports.ts view source

TransportSendOptions import type {TransportSendOptions} from '@fuzdev/fuz_app/actions/transports.js';

Per-call options accepted by every transport's send. Optional and extensible — adding a field is non-breaking. Source of truth for the shared option shape; ActionDispatcherSendOptions and RpcClientCallOptions extend it.

signal?

Per-call cancellation. Bottoms out at FrontendWebsocketClient.request({signal}) on the WS path (sends the shared cancel notification on abort) and at fetch({signal}) on HTTP. Backend transport has no per-call abort surface to honor.

type AbortSignal

queue?

Per-call durable-queue opt-in. Names the client-authoritative vs server-authoritative distinction — server-authoritative consumers (e.g. zzz completion calls) fail fast with service_unavailable when the transport is down; client-authoritative consumers (games, real-time apps) buffer and replay on reconnect because the user already committed to the action at click time. Honored only by FrontendWebsocketTransport on the request_response path (default false). HTTP and backend transports ignore it; WS notifications also ignore it and always fail-fast when disconnected (fire-and-forget connection.send has no queue semantic).

type boolean

truncate_middle
#

ui/ui_format.ts view source

(str: string, max_length: number, separator?: string): string import {truncate_middle} from '@fuzdev/fuz_app/ui/ui_format.js';

Truncate a string by keeping the start and end, with a separator in the middle.

str

type string

max_length

total length including separator

type number

separator

type string
default '…'

returns

string

the truncated string, or the original if it fits

truncate_uuid
#

ui/ui_format.ts view source

(uuid: string): string import {truncate_uuid} from '@fuzdev/fuz_app/ui/ui_format.js';

Truncate a UUID for display, keeping start and end visible.

uuid

type string

returns

string

a 12-character truncated UUID like a1b2c…7890

ts_default_capabilities
#

ts_default_shape_notes
#

testing/cross_backend/default_backend_configs.ts view source

BackendShapeNotes import {ts_default_shape_notes} from '@fuzdev/fuz_app/testing/cross_backend/default_backend_configs.js';

Shape notes for TS-family backends — wiring facts, not gating flags. trusted_proxy: false (the test binary doesn't enable proxy parsing) and login_rate_limit: false (the TS canonical path leaves the limiter null in test mode); bearer auth is always wired. Documentation only — see BackendShapeNotes.

ts_spine_bun_backend_config
#

testing/cross_backend/ts_spine_backend_config.ts view source

(options?: TsSpineBackendConfigOptions): BackendConfig import {ts_spine_bun_backend_config} from '@fuzdev/fuz_app/testing/cross_backend/ts_spine_backend_config.js';

BackendConfig for the Bun TS spine binary — spawned via bun run. Bun resolves the entry's relative .js.ts source specifiers natively (no flag needed — unlike Deno's --sloppy-imports, and like Gro's loader on the Node path), and Bun.serve + hono/bun need no extra deps.

options

default {}

returns

BackendConfig

TS_SPINE_BUN_DEFAULT_PORT
#

TS_SPINE_BUN_ENTRY
#

ts_spine_deno_backend_config
#

testing/cross_backend/ts_spine_backend_config.ts view source

(options?: TsSpineBackendConfigOptions): BackendConfig import {ts_spine_deno_backend_config} from '@fuzdev/fuz_app/testing/cross_backend/ts_spine_backend_config.js';

BackendConfig for the Deno TS spine binary. The --allow-* set mirrors the cross-process needs (net + read/write for the daemon-token file + env + sys); --unstable-detect-cjs matches the ecosystem's Deno test entries.

--sloppy-imports is required because the binary imports fuz_app source via relative .js specifiers (the src/lib convention) — Deno resolves .js.ts only under this flag, whereas Gro's loader (the Node path) does so natively. (zzz's Deno entry sidesteps it by importing fuz_app as a built package; this binary tests live source instead.)

options

default {}

returns

BackendConfig

TS_SPINE_DENO_DEFAULT_PORT
#

TS_SPINE_DENO_ENTRY
#

TS_SPINE_DIR_ENV
#

ts_spine_node_backend_config
#

TS_SPINE_NODE_DEFAULT_PORT
#

TS_SPINE_NODE_ENTRY
#

TsSpineBackendConfigOptions
#

testing/cross_backend/ts_spine_backend_config.ts view source

TsSpineBackendConfigOptions import type {TsSpineBackendConfigOptions} from '@fuzdev/fuz_app/testing/cross_backend/ts_spine_backend_config.js';

port?

Listening port. Defaults per runtime (1178 Node, 1179 Deno, 1180 Bun).

type number

readonly

database_url?

Database URL. Default 'memory://' (in-memory PGlite).

type string

readonly

enable_login_rate_limit?

Enable the per-IP + per-account login rate limiters on the spawned binary (FUZ_LOGIN_RATE_LIMIT_ENABLED=true). Off by default — the standard cross suites fire many loopback logins a live limiter would 429. Set true only for the dedicated login-security cross project (global_setup_login_security.ts), which drives the 429 + Retry-After path and XFF-keyed bucketing over the wire (login_security.ts). The binary always wires trusted_proxies for 127.0.0.1/::1, so the limiter keys on the resolved X-Forwarded-For client IP. Mirrors SpineStubBackendConfigOptions.enable_login_rate_limit.

type boolean

readonly

ui_fetch
#

ui/ui_fetch.ts view source

(input: URL | RequestInfo, init?: RequestInit | undefined): Promise<Response> import {ui_fetch} from '@fuzdev/fuz_app/ui/ui_fetch.js';

Fetch with credentials included (sends cookies).

input

type URL | RequestInfo

init?

type RequestInit | undefined
optional

returns

Promise<Response>

UncoveredEntry
#

testing/error_coverage.ts view source

UncoveredEntry import type {UncoveredEntry} from '@fuzdev/fuz_app/testing/error_coverage.js';

Uncovered entry — either a status-level row (no code) or a specific-code row.

method

type string

path

type string

status

type number

code?

Declared code value missing, when the status's error schema names specific codes.

type string

UNKNOWN_ERROR_MESSAGE
#

http/jsonrpc_errors.ts view source

"unknown error" import {UNKNOWN_ERROR_MESSAGE} from '@fuzdev/fuz_app/http/jsonrpc_errors.js';

Default message for unknown errors.

update_env_variable
#

env/update_env_variable.ts view source

(key: string, value: string, options: UpdateEnvVariableOptions): Promise<void> import {update_env_variable} from '@fuzdev/fuz_app/env/update_env_variable.js';

Updates or adds an environment variable in the .env file. Preserves existing formatting, comments, and other variables.

Behavior:

  • Duplicate keys: updates the LAST occurrence (matches dotenv behavior)
  • Inline comments: preserved after the value (e.g., KEY=value # comment)
  • Quote style: preserved from original (quoted/unquoted)
  • export prefix: preserved when updating an export KEY=… line

key

the environment variable name (e.g., 'SOME_CONFIGURATION_KEY')

type string

value

the new value for the environment variable

type string

options

file path and optional read/write overrides

returns

Promise<void>

throws

  • Error - if the file read fails for any reason other than `ENOENT`, or if the write fails

mutates

  • filesystem — writes the updated content back to `options.env_file_path`

UpdateAppSettingsInput
#

auth/app_settings_schema.ts view source

ZodObject<{ open_signup: ZodBoolean; }, $strict> import type {UpdateAppSettingsInput} from '@fuzdev/fuz_app/auth/app_settings_schema.js';

Zod schema for updating app settings.

UpdateEnvVariableOptions
#

env/update_env_variable.ts view source

UpdateEnvVariableOptions import type {UpdateEnvVariableOptions} from '@fuzdev/fuz_app/env/update_env_variable.js';

Options for updating environment variables in a .env file.

env_file_path

Path to the .env file.

type string

read_file?

Function to read file contents (defaults to node:fs/promises readFile).

type (path: string, encoding: string) => Promise<string>

write_file?

Function to write file contents (defaults to node:fs/promises writeFile).

type (path: string, content: string, encoding: string) => Promise<void>

Username
#

primitive_schemas.ts view source

ZodPipe<ZodString, ZodTransform<string, string>> import type {Username} from '@fuzdev/fuz_app/primitive_schemas.js';

Username for account creation — starts with letter, alphanumeric/dash/underscore middle, ends with alphanumeric. No @ or . allowed.

Canonicalized to lowercase at parse time. The regex rejects whitespace outright, so .trim() is unnecessary here. Storage is canonical across every creation site (bootstrap, signup, admin-create, invite acceptance) because the schema is the single source of truth — eliminates the per-caller trim().toLowerCase() ritual and keeps the LOWER(username) = LOWER($1) lookup contract simple.

USERNAME_LENGTH_MAX
#

primitive_schemas.ts view source

39 import {USERNAME_LENGTH_MAX} from '@fuzdev/fuz_app/primitive_schemas.js';

Maximum username length (matches GitHub's limit).

USERNAME_LENGTH_MIN
#

primitive_schemas.ts view source

3 import {USERNAME_LENGTH_MIN} from '@fuzdev/fuz_app/primitive_schemas.js';

Minimum username length (must have start + middle + end characters).

USERNAME_PROVIDED_LENGTH_MAX
#

primitive_schemas.ts view source

255 import {USERNAME_PROVIDED_LENGTH_MAX} from '@fuzdev/fuz_app/primitive_schemas.js';

Maximum length for username input on login/lookup — more permissive than USERNAME_LENGTH_MAX for forward-compatibility if the creation limit is raised.

UsernameProvided
#

primitive_schemas.ts view source

ZodPipe<ZodString, ZodTransform<string, string>> import type {UsernameProvided} from '@fuzdev/fuz_app/primitive_schemas.js';

Username submitted for login or lookup — minimal validation for forward-compatibility if format rules change.

Canonicalized via .trim().toLowerCase() at parse time so login's per-account rate-limit key and DB lookup see a uniform value regardless of casing or surrounding whitespace. Mirrors the storage canonicalization on Username so submission and storage agree.

The trailing .refine rejects a whitespace-only identifier: .min(1) runs on the raw string (so " " passes it), and without the post-trim check the value would canonicalize to "" and fall through to a lookup-miss 401 instead of a 400 — an empty identifier is malformed input, not a wrong credential. Keeps the Rust spine's account_login (which rejects empty-after-trim) in parity.

VALID_SQL_IDENTIFIER
#

db/sql_identifier.ts view source

RegExp import {VALID_SQL_IDENTIFIER} from '@fuzdev/fuz_app/db/sql_identifier.js';

Pattern matching valid SQL identifiers: starts with a letter or underscore, followed by letters, digits, or underscores.

validate_daemon_token
#

auth/daemon_token.ts view source

(provided: string, state: DaemonTokenState): boolean import {validate_daemon_token} from '@fuzdev/fuz_app/auth/daemon_token.js';

Validate a daemon token against the current state.

Accepts both the current and previous token (2-token race window). Uses timing-safe comparison.

provided

the token from the X-Daemon-Token header

type string

state

the daemon token state

returns

boolean

true if the token is valid

validate_env_vars
#

env/resolve.ts view source

(runtime: Pick<EnvDeps, "env_get">, refs: EnvVarRef[]): EnvValidationResult import {validate_env_vars} from '@fuzdev/fuz_app/env/resolve.js';

Validate that all referenced env vars exist in the environment.

Returns all missing refs (including duplicates by name). Grouping and deduplication is handled by format_missing_env_vars at display time. Refs marked optional: true (from $$?VAR$$ syntax) are skipped — a deliberately-blank var is contract, not a missing dependency.

runtime

runtime with env_get capability

type Pick<EnvDeps, "env_get">

refs

env var references from scan_env_vars

type EnvVarRef[]

returns

EnvValidationResult

validation result with any missing vars

validate_facts_internal_location
#

server/x_accel.ts view source

(config: string, facts_location: string): NginxFactsValidation import {validate_facts_internal_location} from '@fuzdev/fuz_app/server/x_accel.js';

Assert the nginx location serving the X-Accel facts prefix is internal;.

facts_location is the path the X-Accel redirect prefix points at (e.g. /_facts/). Returns a fatal error when no matching location block exists, or when the matching block is not marked internal; — either is a public facts location that bypasses cell visibility.

config

the nginx config template string to check

type string

facts_location

the facts location path the redirect prefix points at

type string

returns

NginxFactsValidation

{ok, errors}ok is true only when the location exists and is internal;

validate_ip_strict
#

http/proxy.ts view source

(ip: string): "IPv4" | "IPv6" | undefined import {validate_ip_strict} from '@fuzdev/fuz_app/http/proxy.js';

Strict IP validity check.

Defense in depth around Hono's hono/utils/ipaddr helpers, which are lax in two ways:

  1. distinctRemoteAddr classifies anything-with-a-colon as 'IPv6', including 'host:port', 'attacker:controlled', '203.0.113.1:8080'.
  2. convertIPv6ToBinary silently accepts malformed forms like '[::1]:8080' and '::1\n', parsing them as inconsistent binary values that would still serve as distinct rate-limit keys for an attacker rotating the suffix.

Strict validation here is two-layered: a character-set pre-filter (IP_LITERAL_CHARS), then a round-trip through convertIPv*ToBinary to confirm the input parses cleanly. Either layer alone has holes; together they reject every input form we've seen Hono mis-handle.

Used as the security primitive for any code path that takes an IP string from an untrusted source (XFF, query params) and uses it as a key (rate limiting, audit subject) or compares it against trusted proxies via CIDR (where the latent throw would otherwise bubble out).

ip

type string

returns

"IPv4" | "IPv6" | undefined

the address family on success, undefined if the string is not a strictly-valid IP

validate_keyring
#

auth/keyring.ts view source

(env_value: string | undefined): string[] import {validate_keyring} from '@fuzdev/fuz_app/auth/keyring.js';

Validate key ring configuration.

Returns an error when no keys are configured (undefined, empty string, or all-separator input like '____'), and for each key shorter than MIN_KEY_LENGTH characters.

env_value

the SECRET_FUZ_COOKIE_KEYS environment variable

type string | undefined

returns

string[]

array of validation errors (empty if valid)

validate_nginx_config
#

server/validate_nginx.ts view source

(config: string): NginxValidationResult import {validate_nginx_config} from '@fuzdev/fuz_app/server/validate_nginx.js';

Validate an nginx config template string for security properties.

Checks for required security headers, Authorization stripping in /api blocks, and the nginx add_header inheritance gotcha. Designed for fuz_app consumer deploy configs (zap.ts NGINX_CONFIG constants).

Limitations: string pattern matching, not a real nginx parser. Catches common omissions in fuz_app deploy configs but won't catch all possible misconfigurations.

config

type string

returns

NginxValidationResult

validate_phase_for_kind
#

actions/action_event_helpers.ts view source

(kind: "request_response" | "remote_notification" | "local_call", phase: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"): void import {validate_phase_for_kind} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

Validate that phase is one of the phases allowed for kind per action_event_phase_by_kind.

kind

type "request_response" | "remote_notification" | "local_call"

phase

type "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"

returns

void

throws

  • Error - if `phase` is not valid for `kind`

validate_phase_transition
#

actions/action_event_helpers.ts view source

(from: "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute", to: "send_request" | "receive_request" | ... 6 more ... | "execute"): void import {validate_phase_transition} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

Validate that a phase chain is legal per action_event_phase_transitions.

from

type "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"

to

type "send_request" | "receive_request" | "send_response" | "receive_response" | "send_error" | "receive_error" | "send" | "receive" | "execute"

returns

void

throws

  • Error - if `from → to` is not the permitted next phase (or `from` is terminal)

validate_server_env
#

server/env.ts view source

(env: { NODE_ENV: "development" | "production"; PORT: number; HOST: string; DATABASE_URL: string; SECRET_FUZ_COOKIE_KEYS: string; FUZ_ALLOWED_ORIGINS: string; PUBLIC_FUZ_API_URL: string; ... 7 more ...; FUZ_FACTS_X_ACCEL_REDIRECT_PREFIX?: string | undefined; }): ServerEnvOptionsResult import {validate_server_env} from '@fuzdev/fuz_app/server/env.js';

Validate a loaded BaseServerEnv and produce the artifacts needed for server init.

Handles keyring validation, origin parsing, and bootstrap token path extraction. Returns a Result so callers handle errors their own way (exit, logging, etc).

env

a loaded and Zod-validated BaseServerEnv

type { NODE_ENV: "development" | "production"; PORT: number; HOST: string; DATABASE_URL: string; SECRET_FUZ_COOKIE_KEYS: string; FUZ_ALLOWED_ORIGINS: string; PUBLIC_FUZ_API_URL: string; FUZ_FACTS_DIR: string; ... 6 more ...; FUZ_FACTS_X_ACCEL_REDIRECT_PREFIX?: string | undefined; }

returns

ServerEnvOptionsResult

{ok: true, keyring, allowed_origins, bootstrap_token_path} or {ok: false, field, errors}

validate_step_transition
#

actions/action_event_helpers.ts view source

(from: "initial" | "parsed" | "handling" | "handled" | "failed", to: "initial" | "parsed" | "handling" | "handled" | "failed"): void import {validate_step_transition} from '@fuzdev/fuz_app/actions/action_event_helpers.js';

Validate that a step transition is legal per action_event_step_transitions.

from

type "initial" | "parsed" | "handling" | "handled" | "failed"

to

type "initial" | "parsed" | "handling" | "handled" | "failed"

returns

void

throws

  • Error - if `from → to` is not a permitted transition

ValidatedKeyringResult
#

ValidationError
#

http/error_schemas.ts view source

ZodObject<{ error: ZodEnum<{ invalid_json_body: "invalid_json_body"; invalid_request_body: "invalid_request_body"; invalid_route_params: "invalid_route_params"; invalid_query_params: "invalid_query_params"; }>; issues: ZodOptional<...>; }, $loose> import type {ValidationError} from '@fuzdev/fuz_app/http/error_schemas.js';

Input validation error — returned when params / query / body fails Zod parsing, or when the request body is not valid JSON.

error is one of the four validation codes the framework emits. issues carries Zod's validation issues for diagnostic display on the three schema-failure cases (invalid_request_body, invalid_route_params, invalid_query_params). The field is optional: the invalid_json_body case (request body parse failure or non-object root) emits no issues, and the schema-failure cases emit them only in development — production omits them (via dev_only) so error responses don't leak input-schema structure to callers.

verify_dummy
#

auth/password_argon2.ts view source

(password: string): Promise<boolean> import {verify_dummy} from '@fuzdev/fuz_app/auth/password_argon2.js';

Verify a password against a dummy hash for timing attack resistance.

Always returns false, but takes the same time as a real verification. Call when account lookup fails to prevent timing-based user enumeration.

password

the plaintext password to "verify"

type string

returns

Promise<boolean>

always false

verify_password
#

auth/password_argon2.ts view source

(password: string, password_hash: string): Promise<boolean> import {verify_password} from '@fuzdev/fuz_app/auth/password_argon2.js';

Verify a password against an Argon2id hash.

password

the plaintext password to verify

type string

password_hash

the Argon2id hash to verify against

type string

returns

Promise<boolean>

true if the password matches

verify_request_source
#

http/origin.ts view source

(allowed_patterns: readonly RegExp[]): Handler import {verify_request_source} from '@fuzdev/fuz_app/http/origin.js';

Middleware that verifies the request source against an allowlist.

Origin allowlisting (not the CSRF layer — that's SameSite: strict cookies):

  • Checks the Origin header (if present) against the allowlist
  • Allows requests without an Origin header (direct access, curl, etc.)

Origin-only by design — Fetch spec mandates Origin on every unsafe method (POST / PUT / DELETE / PATCH) regardless of Referrer-Policy, so every real browser request on the state-changing surface carries it. Non-browser clients (curl, server-to-server, CLI) don't ship auto- attached session cookies, so CSRF isn't the relevant threat there — auth (bearer / daemon token) is the actual control. A Referer fallback would only widen the accepted-shape envelope without closing a real CSRF hole; mirrors zzz_server::auth::is_request_origin_allowed.

allowed_patterns

compiled regex patterns from parse_allowed_origins

type readonly RegExp[]

returns

Handler

VerifyInput
#

auth/account_action_specs.ts view source

ZodVoid import type {VerifyInput} from '@fuzdev/fuz_app/auth/account_action_specs.js';

Input for account_verify. No parameters — the caller is the subject.

WebsocketConnection
#

actions/transports_ws.ts view source

WebsocketConnection import type {WebsocketConnection} from '@fuzdev/fuz_app/actions/transports_ws.js';

Minimal interface for a WebSocket connection, decoupled from the concrete Socket Cell.

send

type (data: object) => boolean

connected

type boolean

readonly

add_message_handler

type (handler: (event: MessageEvent) => void) => () => void

add_error_handler

type (handler: (event: Event) => void) => () => void

WebsocketRpcConnection
#

actions/transports_ws.ts view source

WebsocketRpcConnection import type {WebsocketRpcConnection} from '@fuzdev/fuz_app/actions/transports_ws.js';

RPC-capable WebSocket connection — a WebsocketConnection that also handles request/response correlation with timeout, queue, AbortSignal cancel, and explicit-id support. Required by FrontendWebsocketTransport so it can delegate the pending-map bookkeeping to one canonical implementation (FrontendWebsocketClient) instead of running a parallel one.

Consumer wrappers around FrontendWebsocketClient (e.g. zzz's Socket) implement this by adding a one-line delegate to the underlying client's request.

inheritance

request

type ( method: string, params: unknown, options?: { signal?: AbortSignal; queue?: boolean; id?: JsonrpcRequestId } ) => Promise<unknown>

write_daemon_info
#

cli/daemon.ts view source

(runtime: Pick<EnvDeps, "env_get"> & Pick<FsWriteDeps, "mkdir" | "write_text_file" | "rename">, name: string, info: { version: number; pid: number; port: number; started: string; app_version: string; }): Promise<...> import {write_daemon_info} from '@fuzdev/fuz_app/cli/daemon.js';

Write daemon info to the PID file, creating directories as needed.

runtime

runtime with file write and env capabilities

type Pick<EnvDeps, "env_get"> & Pick<FsWriteDeps, "mkdir" | "write_text_file" | "rename">

name

application name

type string

info

daemon info to write

type { version: number; pid: number; port: number; started: string; app_version: string; }

returns

Promise<void>

throws

  • Error - if `$HOME` is not set

mutates

  • filesystem — creates `~/.{name}/run/` and atomically writes `daemon.json`

write_daemon_token
#

auth/daemon_token_middleware.ts view source

(runtime: DaemonTokenWriteDeps, token_path: string, token: string): Promise<void> import {write_daemon_token} from '@fuzdev/fuz_app/auth/daemon_token_middleware.js';

Write the current token to disk atomically.

Uses write_file_atomic (temp file + rename) and optionally sets mode 0600.

On-disk format is JSON {"token": "..."} — the wrapper leaves room for future fields (rotated_at, version) without changing every reader. Both the TS cross-backend harness reader (spawn_backend.read_daemon_token) and the Rust daemon-token writer match this shape.

runtime

runtime with file write capabilities

token_path

path to write the token

type string

token

the raw token string

type string

returns

Promise<void>

mutates

  • filesystem — writes `token_path` atomically and `chmod 0600` when supported

write_fact
#

server/fact_write.ts view source

(fact_store: FactStore, embedded_threshold: number, facts_dir: string, bytes: Uint8Array<ArrayBufferLike>, options: WriteFactOptions): Promise<...> import {write_fact} from '@fuzdev/fuz_app/server/fact_write.js';

Write bytes as a fact, choosing embedded (PG) vs external (disk + put_ref) based on embedded_threshold. Returns the canonical blake3: hash either way.

fact_store

the FactStore (typically PgFactStore)

type FactStore

embedded_threshold

bytes ≤ threshold → embedded; > threshold → disk

type number

facts_dir

root of the sharded facts directory tree on disk

type string

bytes

the raw fact bytes

type Uint8Array<ArrayBufferLike>

options

content type for the fact metadata

returns

Promise<string & $brand<"FactHash">>

the fact's blake3:<hex64> hash

write_fact_bytes_to_disk
#

db/fact_disk_storage.ts view source

(deps: Pick<FactDiskStorageDeps, "stat" | "mkdir" | "rename" | "write_file" | "fsync" | "remove">, facts_dir: string, hash: string & $brand<"FactHash">, bytes: Uint8Array<...>): Promise<...> import {write_fact_bytes_to_disk} from '@fuzdev/fuz_app/db/fact_disk_storage.js';

Write fully-buffered bytes for hash to the canonical <facts_dir>/<shard>/<rest> path, then publish via commit_temp_to_cas (fsync'd temp + atomic rename, dedup-aware). The buffering twin of stream_fact_to_disk, used by PgFactStore.put for oversize sync bytes. Returns the file: external_url for the fact row.

deps

type Pick<FactDiskStorageDeps, "stat" | "mkdir" | "rename" | "write_file" | "fsync" | "remove">

facts_dir

type string

hash

type string & $brand<"FactHash">

bytes

type Uint8Array<ArrayBufferLike>

returns

Promise<string & $brand<"FileFactUrl">>

write_file_atomic
#

runtime/fs.ts view source

(deps: Pick<FsWriteDeps, "write_text_file" | "rename">, path: string, content: string): Promise<void> import {write_file_atomic} from '@fuzdev/fuz_app/runtime/fs.js';

Write a file atomically via temp file + rename.

Writes to <path>.tmp then renames over path so readers either see the old contents or the full new contents — never a partial write.

deps

type Pick<FsWriteDeps, "write_text_file" | "rename">

path

type string

content

type string

returns

Promise<void>

throws

  • Error - if `write_text_file` or `rename` rejects (permissions, disk full, cross-device rename, etc.)

mutates

  • filesystem — creates `<path>.tmp` then renames it to `path`

WriteFactOptions
#

WS_CLIENT_DEFAULT_TIMEOUT_MS
#

testing/transports/ws_client.ts view source

1000 import {WS_CLIENT_DEFAULT_TIMEOUT_MS} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

Default wait-for timeout shared across in-process + cross-process impls. Tunable per-call via the timeout_ms parameter.

WS_CLOSE_CLIENT_HEARTBEAT_TIMEOUT
#

actions/transports.ts view source

4002 import {WS_CLOSE_CLIENT_HEARTBEAT_TIMEOUT} from '@fuzdev/fuz_app/actions/transports.js';

WebSocket close code — client timed out waiting for a response.

WS_CLOSE_SERVER_HEARTBEAT_TIMEOUT
#

actions/transports.ts view source

4003 import {WS_CLOSE_SERVER_HEARTBEAT_TIMEOUT} from '@fuzdev/fuz_app/actions/transports.js';

WebSocket close code — server timed out with no incoming activity.

WS_CLOSE_SESSION_REVOKED
#

actions/transports.ts view source

4001 import {WS_CLOSE_SESSION_REVOKED} from '@fuzdev/fuz_app/actions/transports.js';

WebSocket close code for session revocation.

ws_disconnect_event_types
#

actions/transports_ws_auth_guard.ts view source

ReadonlySet<string> import {ws_disconnect_event_types} from '@fuzdev/fuz_app/actions/transports_ws_auth_guard.js';

Audit event types that trigger WebSocket socket closure.

  • session_revoke — close only the socket tied to the revoked session hash.
  • token_revoke — close only the socket(s) authenticated with the revoked api_token.id.
  • session_revoke_all / token_revoke_all / password_change — close every socket for the affected account (all credentials invalidated).

role_grant_revoke is intentionally omitted: the WS transport does not track per-connection role requirements, so role-scoped disconnection would require either closing all sockets (too aggressive) or new tracking (out of scope). Consumers that need it compose their own callback.

WsClient
#

testing/transports/ws_client.ts view source

WsClient import type {WsClient} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

A test WS client: send requests, inspect / await incoming messages.

send

Send a JSON-RPC message (request or notification) to the server.

type (message: unknown) => Promise<void>

throws

  • Error - if called after `close()` resolves — every impl

request

Send a JSON-RPC request and await its response. Resolves with the result; throws with a useful message (code, text, and any data payload) on an error frame — without this, asserting on result.foo for a failed request throws Cannot read property 'foo' of undefined, hiding the real cause. Use send + wait_for(is_response_for(id)) directly when the test needs to assert on the error frame itself.

type <R = unknown>( id: number | string, method: string, params: unknown, timeout_ms?: number ) => Promise<R>

throws

  • Error - if the server returns a JSON-RPC error frame for `id`,

close

Close the connection. Returns a promise that resolves once the transport's own cleanup (and any on_socket_close for the in-process driver) has completed — tests that assert on post-close state should await.

type (code?: number, reason?: string) => Promise<void>

wait_for_close

Wait for the server to close the connection. Resolves true if the socket closed within timeout_ms, false on timeout. The signal for server-initiated close — used by close-on-revoke tests that fire a revocation over a side channel and assert the live socket drops.

Resolves true immediately when the socket is already closed. Distinct from close() (client-initiated): this awaits a close the test did not request. Mirrors wait_for_close on the SSE frame reader in testing/sse_round_trip.ts.

type (timeout_ms?: number) => Promise<boolean>

messages

Every message the server has sent, in arrival order.

type ReadonlyArray<unknown>

readonly

wait_for

Wait until a message satisfies predicate. Matches are checked against already-received messages first, then new arrivals until the timeout (defaults to WS_CLIENT_DEFAULT_TIMEOUT_MS).

When predicate is a type guard (e.g. is_notification_with<P>), the result is narrowed automatically and callers don't need to spell <JsonrpcNotificationFrame<P>> on the call site.

type { <T>(predicate: (msg: unknown) => msg is T, timeout_ms?: number): Promise<T>; // eslint-disable-next-line @typescript-eslint/unified-signatures <T = unknown>(predicate: (msg: unknown) => boolean, timeout_ms?: number): Promise<T>; }

throws

  • Error - if `timeout_ms` elapses before a matching message

WsConnectIdentity
#

testing/ws_round_trip.ts view source

WsConnectIdentity import type {WsConnectIdentity} from '@fuzdev/fuz_app/testing/ws_round_trip.js';

Auth identity for a mock connection.

account_id?

Account id for the connection. Defaults to a fresh uuid per call.

type Uuid

credential_type?

Credential type. Defaults to 'session'. Keeper actions require 'daemon_token'.

type CredentialType

session_id?

Session id (any string). Defaults to a fresh uuid. Hashed by the dispatcher.

type string

api_token_id?

Api token id; set for bearer connections, null otherwise.

type string | null

roles?

Roles to grant via active role_grants. Pass [ROLE_KEEPER] for keeper actions.

type Array<string>

WsEndpointSpec
#

actions/ws_endpoint_spec.ts view source

WsEndpointSpec import type {WsEndpointSpec} from '@fuzdev/fuz_app/actions/ws_endpoint_spec.js';

Declarative description of a WebSocket endpoint to be auto-mounted by create_app_server.

Single source of truth for mount + surface — the same array drives register_ws_endpoint-style upgrade wiring AND the surface.ws_endpoints slot emitted into AppSurface, so consumers cannot drift their declared actions from what dispatch actually serves.

path

Hono mount path (e.g. /api/ws).

type string

allowed_origins

Origin allowlist regexes — typically parsed via parse_allowed_origins. Passed straight to verify_request_source on upgrade.

type ReadonlyArray<RegExp>

actions

The actions registered on this endpoint. Spread protocol_actions from actions/protocol.ts first to complete the disconnect-detection + per-request cancel pairing with the frontend client.

type ReadonlyArray<Action>

required_roles?

Roles permitted to upgrade — any-of disjunction. Omit (or pass []) to skip the upgrade-time role gate; per-action auth on each spec still applies at dispatch time via perform_action. Pass [ROLE_ADMIN] for a zap-style admin-only WS endpoint.

type ReadonlyArray<RoleName>

transport?

Existing transport to register connections with. Auto-created when omitted. Either way the mounted transport is reachable on AppServer.ws_endpoints[path] for broadcast / fan-out.

type BackendWebsocketTransport

heartbeat?

Server-side heartbeat policy. Default-on (60s receive-silence timeout). Set false only when an upstream stack (TCP keepalive, Cloudflare idle timeout) already owns disconnect detection.

type boolean | ServerHeartbeatOptions

artificial_delay?

Optional per-message delay for testing loading states.

type number

on_socket_open?

Called once per socket after transport.add_connection but before the first message dispatches. See RegisterActionWsOptions.on_socket_open.

type (ctx: SocketOpenContext) => void | Promise<void>

on_socket_close?

Called once per socket on close, before transport.remove_connection. See RegisterActionWsOptions.on_socket_close.

type (ctx: SocketCloseContext) => void | Promise<void>

auth_guard?

Default true — auto-composes create_ws_auth_guard + create_ws_logout_closer against this endpoint's transport and registers them via deps.audit.add_listener. Wiring is deduped by transport reference identity (WeakSet<BackendWebsocketTransport>), so two WsEndpointSpecs sharing the exact same instance get a single pair of listeners.

Shared-transport OR-semantics. When multiple WsEndpointSpecs share one transport, the guard is wired iff any of those specs has auth_guard !== false. To opt out for a shared transport, every sibling spec must pass auth_guard: false. The default is "fail safe" — easier to enable than disable, and predictable regardless of spec order.

Reference-identity dedupe means wrapped or proxied transports dedupe as separate entries — a consumer threading every transport through a tracing / DI / metrics shim will get a fresh pair of listeners per shimmed reference, even when the underlying transport is the same. If you wrap or proxy, set `auth_guard: false on the duplicate WsEndpointSpec`s and compose create_ws_auth_guard / create_ws_logout_closer against the underlying transport once.

Set false when a consumer needs to compose their own callback from scratch — or to opt out of the auto-wiring entirely.

NOTE: does NOT close sockets on role_grant_revoke — that omission is deliberate (per-connection role tracking is out of scope). A user whose admin role is revoked keeps their socket open; the next message gets forbidden from the per-message authorization phase. Consumers wanting role-revoke disconnection use extra_audit_handlers.

type boolean

extra_audit_handlers?

Extra audit-event handlers registered via deps.audit.add_listener AFTER the standard auth_guard wiring (when enabled). By the time these run, the standard guards may have already closed sockets. Use for role-revoke disconnection, custom analytics, etc.

Never deduped — consumer-owned; pass the same handler twice and it fires twice.

type ReadonlyArray<AuditEventHandler>

WsRequestResponder
#

testing/transports/ws_client.ts view source

WsRequestResponder import type {WsRequestResponder} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

Answers a server-initiated request (the server→client direction ActionPeer adds). Passed at client construction so it's attached before the upgrade completes — otherwise it races a connect-time ping. Receives the parsed request frame and returns the reply outcome (or a promise of it).

(call)

type (request: { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; }): WsResponderOutcome | Promise<WsResponderOutcome>

request

type { [x: string]: unknown; jsonrpc: "2.0"; id: string | number; method: string; params?: { [x: string]: unknown; } | undefined; }
returns WsResponderOutcome | Promise<WsResponderOutcome>

WsResponderOutcome
#

testing/transports/ws_client.ts view source

WsResponderOutcome import type {WsResponderOutcome} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

What a WsRequestResponder returns for a server-initiated request:

  • {result} → reply with a JSON-RPC success
  • {error} → reply with a JSON-RPC error envelope
  • undefined → send nothing (models a never-replying peer — exercises the server-side Timeout)

WsTestHarness
#

testing/ws_round_trip.ts view source

WsTestHarness import type {WsTestHarness} from '@fuzdev/fuz_app/testing/ws_round_trip.js';

A harness instance — transport handle + connection factory.

transport

type BackendWebsocketTransport

connect

Open a mock connection. Resolves after on_socket_open (and the transport's register_ws) completes, so broadcasts issued immediately after the await reach the connection. Earlier revisions returned synchronously and required a settle_open() microtask drain — no longer necessary.

Returns the shared WsClient interface — same surface the cross-process driver in transports/ws_transport.ts implements, so assertion helpers and suite bodies work against either impl.

type (identity?: WsConnectIdentity) => Promise<WsClient>

WsTransportOptions
#

testing/transports/ws_transport.ts view source

WsTransportOptions import type {WsTransportOptions} from '@fuzdev/fuz_app/testing/transports/ws_transport.js';

Construction options for create_ws_transport.

base_url

Base URL the binary is reachable at — e.g. http://localhost:8788. Converted to ws:// for the upgrade.

type string

readonly

ws_path

WebSocket endpoint path on the binary (e.g. /api/ws).

type string

readonly

cookies

Session cookie values (full Set-Cookie strings as FetchTransport.cookies() returns them) threaded onto the upgrade Cookie header. Without these the upgrade is anonymous and per-action auth fails on the first message.

type ReadonlyArray<string>

readonly

origin?

Origin header for the upgrade. Backends running with ALLOWED_ORIGINS=http://localhost:* accept http://localhost:<port>. Defaults to base_url — acceptable because cross-process tests always run against localhost.

type string

readonly

default_timeout_ms?

Optional per-call default for wait_for timeouts. Falls back to WS_CLIENT_DEFAULT_TIMEOUT_MS if omitted.

type number

readonly

on_request?

Optional responder for server-initiated requests (the server→client direction ActionPeer adds). Attached before the upgrade so a connect-time or on-demand peer/ping is answered as soon as it arrives. Omit for the default observe-only client — a server-initiated request is then surfaced as a normal message (today's behavior).

type WsRequestResponder

readonly

WsWaiter
#

testing/transports/ws_client.ts view source

WsWaiter import type {WsWaiter} from '@fuzdev/fuz_app/testing/transports/ws_client.js';

A pending wait_for entry — predicate + its resolver.

predicate

type (msg: unknown) => boolean

resolve

type (msg: unknown) => void

XAccelConfig
#

server/x_accel.ts view source

$ZodBranded<ZodObject<{ redirect_prefix: ZodString; }, $strict>, "XAccelConfig", "out"> import type {XAccelConfig} from '@fuzdev/fuz_app/server/x_accel.js';

A validated X-Accel redirect configuration — the only handle that enables the X-Accel-Redirect serving path in server/serve_fact_route.ts.

The redirect prefix can be obtained only by passing the nginx config through validate_facts_internal_location (via create_x_accel_config), so X-Accel serving is impossible to enable without proving the facts location is internal; at boot — a public facts location would bypass every cell-visibility check. A Zod-branded type: the brand can't be forged without an explicit cast, so the factory is the only ordinary construction path.

XAccelConfigError
#

server/x_accel.ts view source

import {XAccelConfigError} from '@fuzdev/fuz_app/server/x_accel.js';

A misconfigured X-Accel facts location — a fail-loud boot error thrown by create_x_accel_config.

inheritance

extends: Error

errors

The validator errors that made the location unsafe.

type Array<string>

readonly

constructor

type new (errors: string[]): XAccelConfigError

errors

type string[]

xfail_until
#

testing/cross_backend/xfail.ts view source

(tracking_id: string, reason: string, name: string, fn: () => void | Promise<void>): void import {xfail_until} from '@fuzdev/fuz_app/testing/cross_backend/xfail.js';

Register fn as an expected-failure test. Passes while fn throws / rejects; fails once fn succeeds (signalling the gap closed and the marker should be removed).

tracking_id

descriptive id of the tracked gap (e.g. 'audit-log-sse-rust-spine') — a feature/behavior name, not a process/milestone label.

type string

reason

why the case is deferred-by-design.

type string

name

the assertion / test label.

type string

fn

the test body (expected to throw/reject until the gap closes).

type () => void | Promise<void>

returns

void