Most apps should use `defineAdmin`, `createFlowpanel`, and the generated UI.
This page documents the lower-level `@flowpanel/core` contracts for adapter
authors, custom framework integrations and security-sensitive extensions.

```ts excerpt
import {
  accessAllows,
  assertWritableInput,
  authorizeOperation,
  bindAdapterScope,
  resolveScopeApplier,
  errorResult,
  filterReadableProjection,
  reportUnexpectedError,
  resolveOperationAccess,
  resultResponse,
  FLOWPANEL_ERROR_STATUS,
  type AccessContext,
  type AccessRule,
  type AdminDefinition,
  type AdminPaths,
  type AdminPathsInput,
  type AnyResourceConfig,
  type AdapterScopeContext,
  type BoundAdapterScope,
  type ErrorContext,
  type FieldAccess,
  type FieldAccessMap,
  type FieldWriteContext,
  type FlowpanelErrorCode,
  type FlowpanelResult,
  type FlowpanelResultError,
  type FlowpanelResultMeta,
  type FlowpanelWarning,
  type FlowpanelWarningCode,
  type JsonValue,
  type ResourceAccess,
  type ResourceOperation,
} from "@flowpanel/core";
```

## Definitions and paths

`AdminDefinition<Resources>` is the immutable public input accepted by
`defineAdmin`; `AnyResourceConfig` is its generic resource constraint.
`AdminPathsInput` is the partial `{ admin, api }` input and `AdminPaths` is the
normalized resolved pair. Both paths have a leading slash and no trailing
slash.

Application code normally relies on inference and never writes these types
explicitly. They are useful when a factory returns a definition:

```ts excerpt
function companyAdmin(): AdminDefinition {
  return {
    adapter,
    auth,
    paths: { admin: "/ops", api: "/api/ops" },
  };
}
```

## Access and field policy

`AccessRule` is a boolean, role string, role list, or predicate receiving
`AccessContext` (`session`, `role`, `scope`). `ResourceAccess` maps those rules
to the four `ResourceOperation` values: `read`, `create`, `update`, `delete`.

- `accessAllows(rule, context)` evaluates one normalized rule.
- `resolveOperationAccess(access, requireRole, operation)` applies precedence:
  the operation-specific rule wins, then the compatibility `requireRole`.
- `authorizeOperation(rule, context)` throws the safe access error when the
  resolved rule rejects the request.

`FieldAccess<Row>` adds `read`, `write` and `sensitive` policy. A
`FieldAccessMap<Row>` maps it by field name; write predicates receive
`FieldWriteContext<Row>` with both the current row and submitted input.

- `filterReadableProjection(fields, policy, context)` removes fields the
  caller may not read before the adapter query is built.
- `assertWritableInput(input, knownFields, policy, context)` rejects unknown,
  sensitive or forbidden writes. It does not silently drop them.

## Bound adapter scope

A tenant policy crosses the runtime/adapter boundary as an opaque
`BoundAdapterScope`. Create it with `bindAdapterScope(apply)`. Adapters must
apply it to list, get, update and delete operations and fail closed when a
resource requires scope but no binding is present.

`resolveScopeApplier(ctx)` is that fail-closed rule, shared by the first-party
adapters: it returns the bound predicate, `null` when the resource declares
none, and throws `FlowpanelAccessError` when one is required but missing. An
adapter that resolves scope by hand must refuse the query in the same case.

## Post-commit effects

Post-commit audit, realtime or revalidation failures do not turn a committed
mutation into a false failure. They appear as a `FlowpanelWarning` in result
metadata; `FlowpanelWarningCode` is `audit_failed`, `realtime_failed`, or
`revalidation_failed`.

## Result envelope

Every protected controller and v1 JSON CRUD endpoint returns
`FlowpanelResult<T>`:

```ts excerpt
type FlowpanelResult<T> =
  | { ok: true; data: T; meta: FlowpanelResultMeta }
  | { ok: false; error: FlowpanelResultError };
```

`FlowpanelResultMeta` carries the request id and optional warnings.
`FlowpanelResultError` carries a stable `FlowpanelErrorCode`, a safe message,
optional field errors and request id. `FLOWPANEL_ERROR_STATUS` is the canonical
code-to-HTTP-status table.

- `errorResult(error, requestId)` converts known errors to the envelope and
  redacts unexpected failures to `internal`.
- `resultResponse(result)` produces a JSON `Response` with the canonical status.
- `reportUnexpectedError(error, context, onError)` invokes `hooks.onError` at
  most once for the same error object. `ErrorContext` includes request,
  operation, actor, IP and user-agent diagnostics but no submitted secrets.

These helpers are public for runtime authors. Ordinary actions should throw a
documented FlowPanel error or return an `ActionResult`, then let the built-in
runtime normalize it.

Generated-UI form, drawer, action, import, restore and SSE routes predate the
v1 client protocol and keep their compact, route-specific response shapes for
compatibility. Use `createFlowpanelClient` or protected controllers when a
custom surface needs the structured envelope above.
