auth/cell_actions.ts

Generic cell RPC action handlers.

Six request_response actions bound to the specs in auth/cell_action_specs.ts:

  • Mutations: cell_create, cell_update, cell_delete, cell_clone.
  • Reads: cell_get, cell_list.

Authorization model:

  • cell_create is authenticated at the spec level. The handler stamps created_by from auth.actor.id. path writes are admin-only — non-admin callers supplying path get ERROR_CELL_PATH_ADMIN_ONLY.
  • cell_get is optional auth at the spec level. Per-row authorization via can_view_cell(auth, cell). Misses + unauthorized reads both 404, so private-cell existence doesn't leak through the wire. Bundled relations are filtered to viewable targets (strict target-visibility).
  • cell_update / cell_delete are authenticated at the spec level with per-row can_edit_cell enforcement. path writes on update are admin-only; visibility writes require the manage tier (can_manage_cell).
  • cell_list is optional auth at the spec level. The SQL-side visibility predicate in query_cell_list admits null auth to public-only rows and authed callers to owned + public + grant-admitted rows; admin sees all. SQL-side because post-filtering in JS would silently truncate pages. The handler rejects the created_by filter for null auth (account-id enumeration guard).

Mutations emit cell_create / cell_update / cell_delete audit events via deps.audit.emit(...). The AuditLogConfig threaded through the consumer's audit_factory (see create_app_backend) must declare the cell event types (see auth/cell_audit_metadata.ts).

App vocabulary (e.g., collection / entry kinds) lives in client-side helpers and per-app validate_data deps — this layer is generic-only by construction.

view source

Declarations
#

6 declarations

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

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

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.

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

to_cell_json
#

auth/cell_actions.ts view source

(row: CellRow): { id: string & $brand<"Uuid">; path: (string & $brand<"CellPath">) | null; data: { [x: string]: unknown; label?: string | undefined; summary?: string | undefined; }; ... 11 more ...; grant_count: number; } import {to_cell_json} from '@fuzdev/fuz_app/auth/cell_actions.js';

row

type CellRow

returns

{ id: string & $brand<"Uuid">; path: (string & $brand<"CellPath">) | null; data: { [x: string]: unknown; label?: string | undefined; summary?: string | undefined; }; kind: string | null; ... 10 more ...; grant_count: number; }

Depends on
#

Imported by
#