api #

fullstack app library

350 modules · 2048 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_COLUMNS
#

auth/account_queries.ts view source

readonly ["id", "username", "email", "email_verified", "password_hash", "created_at", "created_by", "updated_at", "updated_by", "deleted_at", "deleted_by"] import {ACCOUNT_COLUMNS} from '@fuzdev/fuz_app/auth/account_queries.js';

The full account column set, named explicitly so a row read fails loud on schema drift.

SELECT * silently omits a dropped column, which the login lookups then misread: query_account_by_username_or_email filters its result with account.deleted_at === null, so a missing deleted_at column reads back as undefined, undefined === null is false, and *every* login resolves to "not found" (401) — a silent, total auth outage instead of an error. Selecting named columns turns that drift into a hard Postgres column "..." does not exist. Mirrors the Rust side (fuz_auth/src/account_queries.rs), which selects named columns and decodes them positionally. Keep in sync with Account and the account DDL in auth/auth_ddl.ts.

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: ZodObject<{ name: ZodDefault<ZodString>; scope: ZodDiscriminatedUnion<...>; lifetime: ZodDiscriminatedUnion<...>; }, $strict>; output: ZodObject<...... 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; ... 6 more ...; scope: 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

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

Options for the account-lifecycle parity suite. The standard capability-gating RPC-dispatched cross-suite shape (setup_test / capabilities / rpc_path); aliases RpcPathCapabilityGatedCrossSuiteOptions 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_ip_rate_limiter

Rate limiter for login + password-change attempts, keyed by client IP. Pass null to disable. The distributed-spray backstop: login is the one surface that guesses *other* accounts' credentials, so this bucket is never refunded on success (see RateLimiter.reset). Password change shares it — it is password-bearing on the same account grain, and the Rust spine shares the same instance across both.

type RateLimiter | null

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: SessionId }) => 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 & $brand<"SessionId">): Promise<void>

id

type string & $brand<"SessionId">
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; required_scope?: string | undefined; }; readonly side_effects: boolean; readonly rate_limit?: "both" | ... 2 more ... | undefined; }): { ...; } 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; required_scope?: string | undefined; }; readonly side_effects: boolean; readonly rate_limit?: "both"...

returns

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

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-authorization 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, 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 3 — 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<...>; rate_limit: ZodNullable<...>; }, $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<...>; rate_limit: ZodNullable<...>; }, $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. rate_limit is nullable rather than optional for the same reason — an unthrottled action serializes an explicit null, so a missing key is drift, not a default.

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 shape from http/auth_shape.ts is shared verbatim between action specs and route specs, so no mapping is needed).

The bridge fills in required_scope on whichever shape it ends up with, so an override still gets the token-scope gate; declare required_scope here to name a different capability (e.g. surface:<name> when the bridged route is a stream rather than a request/response call — see the token-scope note on create_action_route_spec).

Overriding to *widen* (admitting a credential the action's own gate refused) makes this route the consumer's to audit, not the spine's.

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_COLUMNS
#

auth/account_queries.ts view source

readonly ["id", "account_id", "name", "created_at", "updated_at", "updated_by", "deleted_at", "deleted_by"] import {ACTOR_COLUMNS} from '@fuzdev/fuz_app/auth/account_queries.js';

The full actor column set — the same fail-loud discipline as ACCOUNT_COLUMNS (this module owns both tables). Keep in sync with Actor and the actor DDL in auth/auth_ddl.ts.

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 / rpc_path); aliases the shared RpcPathCrossSuiteOptions rather than minting a duplicate. No case here is capability-gated, so the flag bundle is not on the 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>

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>

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; 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; 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>

skip_routes?

Routes to skip, in 'METHOD /path' form (the surface key) — the escape hatch every sibling suite carries. Reach for it only when a route cannot be driven generically at all (a handler needing real seeded state, a path segment no schema can describe); a route whose params merely have a format is handled by the synthesizer.

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_COLUMNS
#

auth/api_token_queries.ts view source

readonly ["id", "account_id", "name", "token_hash", "expires_at", "last_used_at", "last_used_ip", "created_at", "scope"] import {API_TOKEN_COLUMNS} from '@fuzdev/fuz_app/auth/api_token_queries.js';

The full api_token column set, named explicitly so a row read fails loud on schema drift (see ACCOUNT_COLUMNS in auth/account_queries.ts for the outage class; the Rust twin names columns at every token site). Keep in sync with ApiToken and the migration chain's end state.

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

scope

The token's authority narrowing (api_token.scope, NOT NULL). Stored as JSONB; read back through parse_token_scope, which is fail-closed — an unreadable document refuses the credential rather than widening it.

type unknown

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_COLUMNS
#

auth/app_settings_queries.ts view source

readonly ["id", "open_signup", "updated_at", "updated_by"] import {APP_SETTINGS_COLUMNS} from '@fuzdev/fuz_app/auth/app_settings_queries.js';

The full app_settings column set — the singleton id (always 1) plus the settings the row type carries — drift-guarded like every other *_COLUMNS. Reads project APP_SETTINGS_ROW_COLUMNS, which omits the constant id since AppSettings doesn't carry it. Keep in sync with AppSettings and the app_settings DDL in auth/auth_ddl.ts.

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).

read_secure_file

Hardened secret-file read — used for the bootstrap token (the file that mints the keeper account). Production wiring passes the runtime's read_secure_file (FsSecureReadDeps), which rejects symlinks, group/other-accessible modes, and oversized files; both the boot-time availability probe and the request-time read go through this one capability so the probe can never be laxer than the read it gates.

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

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; required_scope?: 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-authorization 401 → authorization phase → post-authorization 403 → input validation 400.

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-authorization 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; required_scope?: 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-authorization auth guards (401 + rule-3 scope) → authorization phase → post-authorization auth guards (403) → input validation (400) → handler. Body validation runs behind every authority gate, so a caller any of them refuse never sees route-shape information from a parse failure — a 400 there would confirm the route exists and describe how to call it. Params and query still validate first (they address the route rather than carry its payload, and the authorization phase reads acting off the query on GETs).

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 the pre-authorization guards and before the post-authorization guards

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>

login_ip_rate_limiter

Per-IP login + password-change rate limiter (from options). null when not configured. One instance per auth surface — see AppServerOptions.login_ip_rate_limiter for why these aren't shared.

type RateLimiter | null

signup_ip_rate_limiter

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

type RateLimiter | null

bootstrap_ip_rate_limiter

Per-IP bootstrap 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; }

login_ip_rate_limiter?

Per-IP rate limiter for login + password change — the distributed-spray backstop. 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.

One instance per auth surface, not one shared across all four. These buckets are monotone within their window — a success never refunds them (see RateLimiter.reset) — so a shared instance let a failure on any surface spend the budget that bounds guessing on every other one, and let one caller's exhaustion deny four routes at once. Pass the same limiter to two of these fields to opt back into a shared budget.

type RateLimiter | null

signup_ip_rate_limiter?

Per-IP rate limiter for signup. Omit or undefined to use a default limiter (5 attempts per 15 minutes). Pass null to explicitly disable. Separate from login_ip_rate_limiter because an open-signup deployment lets any unauthenticated caller spend it. Also available on AppServerContext for route factory callbacks.

type RateLimiter | null

bootstrap_ip_rate_limiter?

Per-IP rate limiter for bootstrap. Omit or undefined to use a default limiter (5 attempts per 15 minutes). Pass null to explicitly disable. Separate from login_ip_rate_limiter so a fumbled bootstrap token can't spend the operator's login budget. Wired directly into the factory-managed bootstrap route; also on AppServerContext for symmetry.

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

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>

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[]; rate_limit: "both" | ... 2 more ... | null; }[]; }, 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[]; rate_limit: "both" | ... 2 more ... | null; }[]; }

b

type { methods: { method: string; side_effects: boolean; account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles: string[]; credential_types: string[]; rate_limit: "both" | ... 2 more ... | null; }[]; }

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_columns_match_live
#

testing/db.ts view source

(db: Db, table: string, columns: readonly string[]): Promise<void> import {assert_columns_match_live} from '@fuzdev/fuz_app/testing/db.js';

Assert a *_COLUMNS projection const names exactly the live columns of table — the drift guard for an exported const (ACCOUNT_COLUMNS, CELL_COLUMNS, a consumer's own).

This is the reverse of the fail-loud read: a named projection makes a *dropped* column fail loud at query time, but a column *added* to the table and not to the projection would silently vanish from every row read. Both directions are covered by the equality here. It guards one const; it can't see a table that has *no* const — for that, keep a table → const registry and assert its key set against query_public_columns (fuz_app's own is src/test/db/column_projections.db.test.ts).

db

a bootstrapped test database

type Db

table

the public-schema table the const projects

type string

columns

the projection const's column names

type readonly string[]

returns

Promise<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_iso8601_seconds
#

testing/cross_backend/wire_shapes.ts view source

(value: unknown, label: string): void import {assert_iso8601_seconds} from '@fuzdev/fuz_app/testing/cross_backend/wire_shapes.js';

Assert value is a timestamp in the spine's canonical wire shape: second-precision UTC, exactly 20 characters. The TS spine emits it from the iso8601_timestamp_column SQL projection (db/sql_columns.ts) and to_iso8601_seconds (timestamp.ts); the Rust spine from fuz_db::iso8601_timestamp_column and fuz_sys::rfc3339_now.

value

the field read off a wire response

type unknown

label

the field's name, for the failure message

type string

returns

void

assert_iso8601_seconds_nullable
#

testing/cross_backend/wire_shapes.ts view source

(value: unknown, label: string): void import {assert_iso8601_seconds_nullable} from '@fuzdev/fuz_app/testing/cross_backend/wire_shapes.js';

Assert value is either null or a canonical second-precision UTC timestamp — the shape of every nullable timestamp on the wire (expires_at, deleted_at, updated_at, …).

value

the field read off a wire response

type unknown

label

the field's name, for the failure message

type string

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; required_scope?: 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; required_scope?: 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_secure_mode
#

runtime/secure_file.ts view source

(path: string, mode: number): void import {assert_secure_mode} from '@fuzdev/fuz_app/runtime/secure_file.js';

Refuse any group/other-accessible mode (only 0600/0400 pass).

Callers own the platform gating: the real runtimes skip the check where modes aren't meaningful (Node on Windows, a null Deno mode); the mock checks its simulated modes unconditionally.

path

type string

mode

type number

returns

void

throws

  • Error - naming the path, the offending mode, and the `chmod` fix

assert_secure_size
#

runtime/secure_file.ts view source

(path: string, size: number): void import {assert_secure_size} from '@fuzdev/fuz_app/runtime/secure_file.js';

Refuse a byte count over MAX_SECURE_FILE_SIZE.

path

type string

size

type number

returns

void

throws

  • Error - naming the path, the size, and the cap

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", ... 16 more ..., "db_admin_row_delete"] 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_COLUMNS
#

auth/audit_log_queries.ts view source

readonly ["id", "seq", "event_type", "outcome", "actor_id", "account_id", "target_account_id", "target_actor_id", "ip", "created_at", "metadata"] import {AUDIT_LOG_COLUMNS} from '@fuzdev/fuz_app/auth/audit_log_queries.js';

The full audit_log column set, named explicitly so a row read fails loud on schema drift — audit rows ride the audit_log_list / role-grant-history RPC responses and the SSE broadcast raw, so a SELECT * would silently carry a dropped or leftover column into the strict-validated wire shapes (see ACCOUNT_COLUMNS in auth/account_queries.ts for the outage class; the Rust twin names columns at every audit site). Keep in sync with AuditLogEvent and the migration chain's end state. Exported for the per-cell timeline read in db/cell_audit_queries.ts.

AUDIT_LOG_DEFAULT_LIMIT
#

audit_log_event_specs
#

audit_log_expr
#

auth/audit_log_queries.ts view source

(alias: string) => ColumnExpr import {audit_log_expr} from '@fuzdev/fuz_app/auth/audit_log_queries.js';

The audit_log timestamp override, by row qualifier ('' bare, al for the username JOINs). Exported for the per-cell timeline read in db/cell_audit_queries.ts, which projects the same table.

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>>; ... 24 more ...; db_admin_row_delete: 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

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" | ... 26 more ... | "db_admin_row_delete" ? (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" | ... 26 more ... | "db_admin_row_delete" ? (AuditMetadataMap[T] & Record<...>) | null : Record<...> | null) | undefined; outcome?: "success" | ... 1 more ... ...
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 by default: the bound emitter fires the pool write immediately and pushes the in-flight Promise<void> here. Attempt/failure audits never go through emit_after_commit — pool-routed writes are already rollback-resilient because they run outside the request transaction, so deferring them would only delay forensic visibility without any safety benefit. The exception is a success-only event paired with a state mutation (the db_admin_row_delete emit in http/db_routes.ts): there the caller wraps the emit in emit_after_commit so the trail can't claim a mutation whose transaction failed at COMMIT.

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"; ... 21 more ...; db_admin_row_delete: "db_admin_row_delete"; }> 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; }

db_admin_row_delete

type { [x: string]: unknown; table: string; pk_column: string; id: string; }

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_COLUMNS
#

auth/session_queries.ts view source

readonly ["id", "account_id", "created_at", "expires_at"] import {AUTH_SESSION_COLUMNS} from '@fuzdev/fuz_app/auth/session_queries.js';

The full auth_session column set, named explicitly so a row read fails loud on schema drift — SELECT * would silently carry a dropped or leftover column into the strict-validated wire shapes (see ACCOUNT_COLUMNS in auth/account_queries.ts for the outage class this discipline exists to prevent; the Rust twin already names columns at every session site). Keep in sync with AuthSession and the migration chain's end state — not the frozen v0 DDL in auth/auth_ddl.ts, which still creates last_seen_at for the appended drop migration to remove.

AUTH_SESSION_INDEXES
#

AUTH_SESSION_LIFETIME_MS
#

auth/session_queries.ts view source

number import {AUTH_SESSION_LIFETIME_MS} from '@fuzdev/fuz_app/auth/session_queries.js';

Session lifetime in milliseconds (30 days).

An absolute cap: expires_at is set once at mint and never extended — there is deliberately no touch/renewal query on either spine (a sliding window renews a leaked cookie forever; see docs/security.md §Session Security). The cookie's SESSION_AGE_MAX mirrors this value.

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; required_scope?: string | undefined; }): AuthGuards

auth

type { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; required_scope?: 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_authorization runs before the authorization phase — the 401 check and the rule-3 token-scope refusal live here, so a caller either one turns away never reaches the actor resolution or the route's body. post_authorization runs after the authorization phase has populated RequestContext — credential / role checks live here because they read c.var.request_context.role_grants.

Both phases run ahead of body validation, which is why neither is named for it: route-shape information stays behind every authority gate. The names say which side of the authorization phase a guard sits on, the only axis that still distinguishes them.

pre_authorization

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

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 the pre-authorization auth guards and before input validation; resolves the acting actor (when `auth.actor !== 'none') from the acting selector — c.var.validated_query.acting` on GETs, read off the raw body on mutations — and sets the request context on the Hono context. Per-route order in apply_route_specs: params → query → pre-authorization auth (401 + rule-3 scope) → authorization phase → post-authorization auth (403) → input validation (400) → 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

{ id: string & $brand<"SessionId">; account_id: string & $brand<"Uuid">; created_at: string; expires_at: string; } import type {AuthSession} from '@fuzdev/fuz_app/auth/account_schema.js';

Server-side auth session row, keyed by blake3 hash of the session token. Structurally identical to the wire shape AuthSessionJson — kept as the DB-row name so query signatures keep saying which side of the boundary they read.

id

type string & $brand<"SessionId">

account_id

type string & $brand<"Uuid">

created_at

type string

expires_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; }, $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.

Extended by AccountRouteOptions and SignupRouteOptions. Consumers can destructure these from AppServerContext once and spread into multiple factories.

The per-IP limiter is deliberately *not* here. Each auth surface names its own (login_ip_rate_limiter, signup_ip_rate_limiter, bootstrap_ip_rate_limiter) because the buckets are monotone within their window — see RateLimiter.reset. A shared base field made one instance the path of least resistance, which let a failure on any surface spend the budget that bounds guessing on every other one.

session_options

type SessionOptions<string>

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 / rpc_path); aliases the shared RpcPathCrossSuiteOptions rather than minting a duplicate. The limit is on every spine, so no case is capability-gated and the flag bundle is not on the 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>

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_secure_file

Hardened secret-file read (see FsSecureReadDeps.read_secure_file) — the token mints the keeper account, so a symlinked, group/other-readable, or oversized file must refuse rather than be honored. Throws on refusal; every throw reads as TOKEN_FILE_MISSING upstream.

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

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>

bootstrap_ip_rate_limiter

Rate limiter for bootstrap attempts, keyed by client IP. Pass null to disable. Its own instance, not login's: bootstrap is one-shot and its bucket is never refunded on success (see RateLimiter.reset), so a fumbled token would otherwise leave the operator's *login* budget nearly spent on a deployment where their new account is the only one that exists. The Rust spine rate-limits bootstrap not at all (the token is 32 bytes of CSPRNG compared in constant time); this is the tighter side of that divergence, kept because the token also sits in a file whose read path an operator can misconfigure.

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" | ... 2 more ... | "rate_limit">[]): { ...; } 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[]; rate_limit: "both" | ... 2 more ... | null; }[]; }

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)

CapabilityGatedCrossSuiteOptions
#

testing/cross_backend/setup.ts view source

CapabilityGatedCrossSuiteOptions import type {CapabilityGatedCrossSuiteOptions} from '@fuzdev/fuz_app/testing/cross_backend/setup.js';

CrossSuiteOptions for a suite that reads capability flags — the ones whose cases are test_if-gated on a per-backend declaration. The field is on this variant rather than the base so a suite's option type states whether it consults capabilities: a caller cannot pass a flag bundle to a suite that ignores it, and a suite cannot quietly stop reading one while still demanding it.

inheritance

capabilities

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

type BackendCapabilities

readonly

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[]; rate_limit: "both" | ... 2 more ... | null; }[]; }> 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[]; rate_limit: "both" | ... 2 more ... | null; }[]; }>

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_COLUMNS
#

db/cell_queries.ts view source

readonly ["id", "data", "kind", "visibility", "path", "refs", "parent_id", "root_id", "moderation", "created_at", "updated_at", "deleted_at", "created_by", "updated_by"] import {CELL_COLUMNS} from '@fuzdev/fuz_app/db/cell_queries.js';

The full cell column set, named explicitly so a row read fails loud on schema drift — a SELECT * silently omits a dropped column and the deleted_at IS NULL-shaped predicates on the hydrated row then misread undefined (see ACCOUNT_COLUMNS in auth/account_queries.ts for the outage class). Column order mirrors the Rust twin's cell_columns(alias) (fuz_cell), which decodes the same projection positionally. The derived grant_count is not a column — cell_row_projection appends it. Keep in sync with CellRow and the cell DDL in db/cell_ddl.ts.

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_COLUMNS
#

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_COLUMNS
#

db/cell_grant_queries.ts view source

readonly ["id", "cell_id", "level", "actor_id", "role", "scope_id", "granted_by", "created_at"] import {CELL_GRANT_COLUMNS} from '@fuzdev/fuz_app/db/cell_grant_queries.js';

The full cell_grant column set, named explicitly so a row read fails loud on schema drift (see CELL_COLUMNS in db/cell_queries.ts). Column order mirrors the Rust twin's grant_columns(alias) (fuz_cell). Keep in sync with CellGrantRow and the cell_grant DDL in db/cell_ddl.ts.

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_COLUMNS
#

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_row_projection
#

db/cell_queries.ts view source

(alias: string): string import {cell_row_projection} from '@fuzdev/fuz_app/db/cell_queries.js';

The full CellRow projection — CELL_COLUMNS qualified by alias plus the derived grant_count (a correlated subquery against cell_grant, served by idx_cell_grant_cell) — used by every cell-row SELECT / RETURNING. alias is the row qualifier: the table name ('cell') for single-table reads + RETURNING, the query alias ('c') for query_cell_list. Twin of fuz_cell's cell_columns(alias).

::int narrows the count from bigint so the JS row hydrates a number rather than a bigint primitive — counts on a single cell are trivially within int32.

Exported for consumers writing their own cell-row reads: CELL_COLUMNS alone hydrates a row without grant_count, which typechecks as a CellRow but fails at to_cell_json — this is the projection that actually produces one.

alias

type string

returns

string

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 string

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 string

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 string

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 string

updated_at

type string | null

deleted_at

type string | 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 passes the secure read (exists, not a symlink, mode 0600/0400, within the size cap)
  3. The bootstrap_lock table shows bootstrapped = false

The probe reads through the same read_secure_file the request-time read uses, so "bootstrap is available" and "the token file can actually be read" can't drift apart — an availability check laxer than the read it gates is the misconfiguration that reports green at boot and fails at request time. Twin of the Rust spine's is_bootstrap_available.

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.

read_secure_file

Hardened secret-file read — the same capability the request-time read uses.

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

db

Only the single-row bootstrap_lock read — narrower than the full Db.

type Pick<Db, 'query_one'>

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; scope: 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';

ColumnExpr
#

db/sql_columns.ts view source

ColumnExpr import type {ColumnExpr} from '@fuzdev/fuz_app/db/sql_columns.js';

Per-column expression override for a projection — return the SQL to project for column, or undefined to keep the bare column reference.

Twin of the expr closure fuz_db::qualify_columns takes. The Rust side decodes rows positionally, so its overrides need no output name; TS reads rows by name, so columns_sql / qualify_columns alias every override back to its column (… AS created_at) — otherwise a to_char(…) projection would arrive under Postgres' derived name and read as undefined.

undefined is the *only* decline sentinel. An empty string is a returned expression, not a decline: it renders AS created_at and the query fails to parse. This is documented rather than guarded — every override in the repo is a literal lookup returning a fragment or nothing, and a malformed projection failing loud at the first query beats silently swallowing an expression the caller meant to supply.

(call)

type (column: string): string | undefined

column

type string
returns string | undefined

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']

columns_sql
#

db/sql_columns.ts view source

(columns: readonly string[], expr?: ColumnExpr | undefined): string import {columns_sql} from '@fuzdev/fuz_app/db/sql_columns.js';

Render a *_COLUMNS const as a SQL select list, in projection order.

columns

the column names

type readonly string[]

expr?

optional per-column expression override; an overridden column is aliased back to its own name

type ColumnExpr | undefined
optional

returns

string

a, b, c

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"; ... 8 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"; ... 7 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"; scoped_token: "scoped_token"; ... 4 more ...; 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).
  • scoped_token — the keeper authenticated via a bearer api-token minted with a narrowed TokenScope (`{kind: 'methods', methods: [SCOPED_TOKEN_ADMITTED_METHOD]}`), non-browser context. Minted through the production account_token_create path over the keeper's session, not seeded — _testing_reset deliberately seeds a full token, and a harness that narrowed the seed would silently re-scope every other bearer case. The scope gate sits between the credential gate and the role gate, so this principal is what distinguishes token_scope_required from the credential-type and role denials on either side of it.
  • 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, login_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; required_scope?: 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; requi...

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; required_scope?: 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.

The bridge carries the token-scope gate across for you. A bridged route runs options.handler through the REST pipeline and never reaches perform_action, so the dispatcher's per-method scope check cannot fire on it — which would leave a bearer-reachable route that a narrowed api token walks straight through. So the derived spec declares auth.required_scope: 'rpc:<method>', and fuz_auth_guard_resolver mounts the same refusal ahead of the role gate. A token that lists the method reaches the bridged route; one that doesn't gets the 403 it would have gotten over RPC.

Per-method rather than rule 3's blanket refusal because a bridged route *has* a method identity — which is exactly what the non-RPC surfaces rule 3 covers (the db browser, a bare-hash read, a stream) do not, and why that rule is all-or-nothing there. Bridge something with no request/response shape — an SSE stream, a file download — and rule 3's reasoning applies instead: pass options.auth with required_scope: 'surface:<name>', naming your own surface. See docs/security.md §Token scoping.

Skipped for a public action (account: 'none', actor: 'none'), where the same holder reaches the route by dropping the credential, so the guard would enforce nothing — the shape fuz_auth_guard_resolver refuses outright.

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; requi...

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, 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-authorization / 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.

There is deliberately no rate limit on this path, and no 429 — every failure soft-fails to "no credential". An API token is 32 bytes of CSPRNG output resolved by a blake3 hash lookup, so guessing is bounded by entropy, not by throttling; a limiter here would buy nothing measurable while costing availability (the check/record has to precede the async lookup to close its own TOCTOU window, so concurrent requests bearing a *valid* token race each other into a 429). The Rust spine never had one here; this is the converged shape. See docs/security.md §Why bearer auth is not rate limited.

deps

query dependencies (pool-level db for middleware)

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): { 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

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
#

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 the acting selector via read_route_actingc.var.validated_query on GETs, the raw body on mutations, since input validation now runs after the authority gates. 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 the selector is present on exactly the specs that read it, and input validation is what rejects a malformed one.

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>, options?: CreatePgliteFactoryOptions | undefined): 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 the PGlite factories this module builds. Subsequent create() calls reset the schema via DROP SCHEMA public CASCADE instead of paying the WASM cold-start cost again.

Returns the installed substitute's factory instead when one is installed and options.substitutable is not false — see set_substitute_db_factory.

init_schema

callback to initialize the database schema

type (db: Db) => Promise<void>

options?

substitutable: false pins this call site to PGlite

type CreatePgliteFactoryOptions | undefined
optional

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" | ... 17 more ... | "db_admin_row_delete">[] | 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" | ... 16 more ... | "db_admin_row_delete">[] | 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, 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)

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_route_skip_filter
#

testing/auth_apps.ts view source

(skip_routes: string[] | undefined): (route: { method: string; path: string; }) => boolean import {create_route_skip_filter} from '@fuzdev/fuz_app/testing/auth_apps.js';

Build the skip predicate an adversarial suite applies from its skip_routes option — routes named in 'METHOD /path' form, the surface key. undefined skips nothing.

skip_routes

type string[] | undefined

returns

(route: { method: string; path: string; }) => boolean

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-authorization auth — short-circuit unauthenticated when no account is on the request, before any later phase 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 by read_acting, which parses it as a UUID and treats anything else as omitted.
  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 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 testing/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).

read_secure_file

Hardened secret-file read for the bootstrap token — pass the runtime's read_secure_file (see FsSecureReadDeps). Rejects symlinks, group/other-accessible modes, and oversized files.

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

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

CreatePgliteFactoryOptions
#

testing/db.ts view source

CreatePgliteFactoryOptions import type {CreatePgliteFactoryOptions} from '@fuzdev/fuz_app/testing/db.js';

substitutable?

Whether an installed substitute (set_substitute_db_factory) may stand in for the PGlite factory at this call site. Defaults to true; pass false where PGlite itself is the subject under test.

type boolean

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

test_timeout?

Per-test timeout in ms. Default: 30_000 — see DEFAULT_TEST_TIMEOUT for why the 5 s vitest default does not fit a spawned-backend suite. Always emitted, so a consumer's root testTimeout never leaks in.

type number

readonly

hook_timeout?

Per-hook timeout in ms for beforeAll / afterAll etc. Default: 30_000. Always emitted, so a consumer's root hookTimeout never leaks in.

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 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.), and a suite that actually gates cases on a capability flag extends CapabilityGatedCrossSuiteOptions instead. Lives here in the neutral fixture-types home rather than in any one domain helper, so a non-cell suite never reaches into the cell helpers for its option shape.

setup_test

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

type SetupTest

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
#

testing/daemon_token_rotation.ts view source

DaemonTokenRotationOptions import type {DaemonTokenRotationOptions} from '@fuzdev/fuz_app/testing/daemon_token_rotation.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
#

testing/daemon_token_rotation.ts view source

DaemonTokenWriteDeps import type {DaemonTokenWriteDeps} from '@fuzdev/fuz_app/testing/daemon_token_rotation.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. mode applies at creation (no-op where modes don't apply).

type (path: string, options?: { recursive?: boolean | undefined; mode?: number | undefined; } | undefined): Promise<void>

path

type string

options?

type { recursive?: boolean | undefined; mode?: number | undefined; } | undefined
optional
returns Promise<void>

write_text_file

Write text to a file. See WriteFileOptions for mode / exclusive.

type (path: string, content: string, options?: WriteFileOptions | undefined): Promise<void>

path

type string

content

type string

options?

type WriteFileOptions | 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>

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

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_ADMIN_STATEMENT_TIMEOUT_MS
#

http/db_routes.ts view source

5000 import {DB_ADMIN_STATEMENT_TIMEOUT_MS} from '@fuzdev/fuz_app/http/db_routes.js';

Per-statement timeout applied (SET LOCAL statement_timeout) inside the table-list, table-detail, and row-DELETE transactions — no single statement outlives it. (Per statement, not per request: the table listing's one-COUNT(*)-per-table loop can still take several in sequence.) Milliseconds.

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

DbFactoryBuilder
#

testing/db.ts view source

DbFactoryBuilder import type {DbFactoryBuilder} from '@fuzdev/fuz_app/testing/db.js';

Builds a DbFactory from a schema initializer — create_pglite_factory's own shape.

(call)

type (init_schema: (db: Db) => Promise<void>): DbFactory

init_schema

type (db: Db) => Promise<void>
returns DbFactory

DbRouteDeps
#

http/db_routes.ts view source

DbRouteDeps import type {DbRouteDeps} from '@fuzdev/fuz_app/http/db_routes.js';

Capabilities the db routes need — a narrow structural slice of AppDeps (auth/deps.ts), declared here so http/ stays auth-free. The bound auth/audit_emitter.ts AuditEmitter (and therefore AppDeps.audit / RouteFactoryDeps.audit) satisfies audit structurally.

audit

Pool-routed fire-and-forget audit emit. The row-DELETE handler defers the call via emit_after_commit, so the success-only trail row can never claim a delete whose transaction failed at COMMIT — the pool routing means the write itself never rides the request transaction.

type { emit: ( ctx: { pending_effects: Array<Promise<void>> }, input: { event_type: 'db_admin_row_delete'; account_id: Uuid | null; ip: string; metadata: { table: string; pk_column: string; id: string }; } ) => void; }

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

browsable_tables

The tables the browser exposes — an explicit allowlist gating the table list, table detail, and row-DELETE alike. Required with no "all" escape hatch, so a future secret-bearing table stays unlisted until a consumer names it, and NON_BROWSABLE_TABLES is subtracted even when named. An unlisted table 404s as table_not_found — the same answer as a table that doesn't exist.

type ReadonlyArray<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

non_deletable_tables?

Consumer tables to exclude from row deletion, unioned with the builtin NON_DELETABLE_TABLES (which this never replaces).

type ReadonlyArray<string>

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 auth rate limiting: 5 attempts per 15 minutes. The default for each of the three per-surface IP limiters (login + password change, signup, bootstrap) and the shared cap the Rust spine's DEFAULT_LOGIN_IP_RATE_LIMIT pins.

Deliberately not widened when the buckets became monotone (a success no longer refunds them — see RateLimiter.reset). Widening would have bought NAT'd egress headroom by loosening the one bound that caps credential guessing from a single address; splitting one shared bucket into three per-surface ones buys the same headroom without touching that bound.

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
#

auth/account_route_schema.ts view source

5 import {DEFAULT_MAX_SESSIONS} from '@fuzdev/fuz_app/auth/account_route_schema.js';

Default maximum sessions per account.

The Rust twin pins the same 5 as a const (fuz_auth's DEFAULT_MAX_SESSIONS) rather than a route-state field, because its AccountRouteState has no Default and a new field would break every exhaustive literal in every consumer workspace. So this stays the configurable side of the pair (null disables), and the two ship the same number — retuning means editing both.

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[]): 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[]

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: RpcPathCapabilityGatedCrossSuiteOptions): 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_query_shape_cross_tests
#

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 2 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.

There is deliberately no bearer-auth group: the bearer path carries no rate limiter on either spine, because an API token's entropy — not throttling — is what bounds guessing it. See auth/bearer_auth.ts.

Each test group asserts that required routes exist, failing with a descriptive message if the consumer's route specs are misconfigured.

options

returns

void

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_session_cap_cross_tests
#

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
#

describe_token_lifetime_cross_tests
#

describe_token_scope_surface_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[]; rate_limit: "both" | ... 2 more ... | null; }[]; }, 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[]; rate_limit: "both" | ... 2 more ... | null; }[]; }

b

type { methods: { method: string; side_effects: boolean; account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles: string[]; credential_types: string[]; rate_limit: "both" | ... 2 more ... | null; }[]; }

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 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
#

http/pending_effects.ts view source

EmitAfterCommitContext import type {EmitAfterCommitContext} from '@fuzdev/fuz_app/http/pending_effects.js';

Minimal structural context required by emit_after_commit — just the deferred queue. Both RouteContext and ActionContext satisfy this. (No log here: the flush middleware owns the per-thunk try/catch + log.error, with a logger of its own.)

post_commit_effects

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

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 single-column primary key, so there is no column the row-DELETE can target: either no primary key at all, or a composite one (a single-column WHERE on one member of a composite key over-matches).

ERROR_TABLE_NOT_DELETABLE
#

http/error_schemas.ts view source

"table_not_deletable" import {ERROR_TABLE_NOT_DELETABLE} from '@fuzdev/fuz_app/http/error_schemas.js';

Table is excluded from row deletion by policy — the audit trail and the framework's singleton bookkeeping rows (bootstrap_lock, app_settings, schema_version), whose invariants live in the domain layer rather than in a generic storage endpoint. See http/db_routes.ts's NON_DELETABLE_TABLES.

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.

ERROR_TOKEN_SCOPE_REQUIRED
#

http/error_schemas.ts view source

"token_scope_required" import {ERROR_TOKEN_SCOPE_REQUIRED} from '@fuzdev/fuz_app/http/error_schemas.js';

The credential is admitted on this channel, but the specific token's scope does not grant the requested capability.

The body carries required_scope: string in the stable <section>:<id> capability format — rpc:<method> for an action, surface:<name> for a non-RPC surface. Distinct from credential_type_required (which is about the *channel*) and from insufficient_permissions (about the *account's* roles): this one says the account could do it but this token was minted narrower.

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_COLUMNS
#

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. mode applies at creation (no-op where modes don't apply).

type (path: string, options?: { recursive?: boolean | undefined; mode?: number | undefined; } | undefined): Promise<void>

path

type string

options?

type { recursive?: boolean | undefined; mode?: number | 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