Guardrails & policy
The backend runs a chain of guardrails at defined points in every agent turn. They are part of the api-backend, not the runtime — so they apply no matter how the conversational loop behaves.
What guardrails do
Budgets. Bound work per turn with model steps, wall-clock time, and parallel tool calls. A per-member daily model-token ceiling is optional; no daily ceiling applies until an operator sets a positive limit.
Citation discipline. Keep answers grounded in retrieved sources rather than in the model's recall. This is enforced, not requested.
Redaction. Remove sensitive content where configured, before it reaches a member response or a model context.
Loop control. Stop runaway cycles and out-of-scope actions.
Where they sit
Guardrails run on the callback path — the point where the runtime asks the backend to actually do something — and again before a result is published.
That placement matters. A guardrail inside the runtime would be advice to a system that is designed to be flexible. A guardrail in the backend is a condition of execution.
The budget table
| Setting | Default | Bound |
|---|---|---|
AGENT_LOOP_MAX_CYCLES | 10 | General tool-bearing model rounds; staged artifact protocol calls use a separate derived ceiling while byte, sequence, ownership, and execution fences remain enforced |
AGENT_LOOP_BUDGET_MS | 120000 | Maximum inactivity between healthy model or tool events |
AGENT_LOOP_MAX_PARALLEL_TOOLS | 1 | Concurrent tool calls |
LLM_PER_USER_DAILY_TOKENS | 0 (unlimited) | Optional per-member daily token cap |
LLM_LATENCY_BUDGET_SECONDS | 5.0 | Assist-path latency |
SEARCH_BRANCH_LATENCY_BUDGET_SECONDS | 8.0 | Per retrieval branch |
SANDBOX_MAX_WALL_SECONDS | 30 | Per code execution |
SANDBOX_MAX_OUTPUT_BYTES | 1 MiB | Per code execution |
When an operator enables the daily cap, the token check, model call, and audit
row run under one per-member database lock, so concurrent turns cannot
overspend together. The lock is released after completed streams, refused task
authority (503 authority_unavailable), upstream errors, and disconnections
before the first streamed token. Without a daily cap, no daily-budget lock or
usage lookup runs; model calls are still audited.
Hashline tags never grant write authority
The four-hex tag in a workspace header is not authority. It resolves only inside trusted state keyed by the authenticated actor and conversation workspace. The retained full text and seen-line provenance stay outside the untrusted workspace tar. A tag from another member, workspace, expired history, or colliding content cannot widen a write.
edit_workspace enters the ordinary write guard once for the complete patch.
The sandbox worker rejects absolute paths, traversal, symlinks, unauthorized
tag-based path recovery, unseen anchors, ambiguous recovery, oversized files,
and duplicate destinations before committing. Multi-file PUT, CUT, REM,
and MV operations stage together and roll back together. The pinned Hashline
helper receives bounded text and snapshot metadata but no identity envelope,
bearer, object-store key, or direct filesystem access.
Artifact Hashline edits use a separate authority path rather than aliasing an
artifact to a workspace file. read_artifact and edit_artifact resolve the
exact artifact under the forwarded member identity, immutable version, and
content tag. A foreign source is readable or remixable only when the active
room projection authorizes that exact version; the completed result becomes a
new actor-owned root and the creator's root remains unchanged.
The edit enters WriteToolGuard, rejects overlapping or out-of-range hunks,
and relies on the artifact store's locked version check before appending one
version. Streamed replacement text and hidden React/Mermaid validation frames
are disposable pane projections over the committed base. They are not
persisted or shared as completed artifacts when syntax, authorization, or
concurrency checks fail.
Exhaustion is a policy outcome, not a crash
When model-step budget runs out, the turn does not simply fail. The engine grants one forced-synthesis step: answer from the evidence already gathered.
The member gets a bounded, cited answer with a clear limitation instead of an error, and the run still records exactly one terminal outcome. Failing loudly at the budget edge would have been easier to implement and worse for everyone.
Conditional correction is not authorization
An operator-authored stream-correction rule may discard a matching response before it becomes visible or durable, inject a fixed reminder, and recapture a new provider step. Rules are manifest-bound, attempt-bounded, and record only their ID.
They improve model behavior; they do not protect a resource. A model can produce wording that no pattern anticipated, and no correction rule can grant, approve, or execute a tool. The backend's snapshot validation, capability ceiling, member authorization, approval gate, and write guard remain the security controls.
Capability ceilings
Every agent has a maximum set of tools it may use. A read-only capability ceiling can inspect tasks but cannot mutate them, regardless of what a prompt asks for.
Two ceilings must both allow a call: the agent's capability ceiling, and the member's own permissions resolved at call time. Neither is evaluated inside the model, so neither is negotiable from inside a prompt.
Prompt injection
Connector content, uploaded documents, tool results, and remote agent responses are all untrusted text. Any of them may contain instructions aimed at the model.
Kaizen's defense is structural rather than detective. Untrusted content:
- arrives as evidence, subject to citation discipline;
- cannot grant a tool, widen a scope, or raise a capability ceiling;
- cannot approve an action or alter the turn's authority bundle;
- cannot supply an identity or a role.
A successful injection changes what the model says. It does not change what the backend permits. That is the whole design: the boundary is placed so that model compromise is contained rather than catastrophic.
Grounding
An answer that cannot find support should say so rather than guess. Grounding instructions join the shared system-prompt composition chain, and citation discipline is checked on the backend side.
Because composition is shared, every agent gets the same grounding treatment. An individual agent definition cannot opt out of it.
Structured artifact output
Remote tools and reviewed skills may return a versioned renderable-artifact envelope. Kaizen validates and canonicalizes it at the shared tool-result boundary before the normal owner-scoped Artifact writer runs. The envelope must retain security markings plus cluster, dataset, or job provenance; missing, oversized, non-JSON, or path-bearing download metadata fails closed.
The reserved Artifact language is not model authority. Direct
create_artifact and update_artifact calls reject it. Only a validated trusted
tool result or declared MCP artifact contract receives the call-scoped server
attestation required by the writer, so an LLM cannot label invented provenance
or markings as governed output. A vendored package's declared side outputs are
rebuilt from allowlisted fields at the package boundary, which drops an
envelope, a materialization key, and the reserved language, so package code
cannot route its own output down the attested path either.
That attestation protects the model-to-writer boundary; it is not independent proof that a producer told the truth. For a generic MCP version 2 artifact, the administrator-approved registration of the server and tool is the trust root. Kaizen proves that the selected producer's output passed the bounded envelope contract and was written through the authorized path, not that its asserted source, provenance, or markings are factually correct. A careless or compromised producer can mint false metadata that remains schema-valid. Administrator review, controlled enablement, and prompt revocation of suspect producers are therefore part of the security boundary.
The streamed-create boundary also fails closed while that reserved pair is
still possible. No incomplete create_artifact opens a durable checkpoint,
even when an early language looks non-reserved: a later duplicate JSON key can
replace it in permissive JSON parsers. The checkpoint adapter instead rejects
duplicate keys and reads only the completed top-level object. Ordinary unique-key
calls still materialize after that parse. If a turn is interrupted first, the
call is terminally fenced and there is intentionally no partial checkpoint to
resume; preserving one would allow later argument bytes to claim the reserved
language after model-authored content was already stored. Updates keep their
live checkpoint behavior because they target an already owner-resolved artifact.
An envelope carries either the exact authorized JSON result or an opaque,
SHA-256-bound result reference. References are never browser-fetchable URLs.
This release validates and preserves result_ref, but does not resolve it.
Every referenced result remains inert: the browser labels it, displays the
producer's text_fallback, and retains the opaque reference only in the JSON
view and download. No backend resolver or browser fetch runs. A future reviewed
server-side adapter may resolve a reference only under the current member and
room authority; a missing or changed result must fail closed, and resolution
must never launch a replacement remote job or infer data removed upstream.
Structured portion markings remain a map through persistence and rendering.
A missing markings object or upstream markings_error prevents artifact
creation; no renderer may flatten, infer, or default the markings.
Updates revalidate the entire envelope and reject any change to its provenance
or markings, so a later payload cannot relabel itself onto a prior binding.
Server-mediated fork and remix operations may copy a governed Artifact only by
cloning its stored language and exact envelope bytes. The derived library root
preserves the original markings and provenance, grants no new producer
attestation, and remains subject to the same revision guard. Copy requests
cannot provide replacement content or transformed envelope fields.
The database stores a separate server-attestation marker on every Artifact
version. A check constraint rejects the reserved renderable language unless
that marker is present, so model-authored staged drafts and old application
replicas cannot mint governed rows by choosing the reserved kind and
language pair. Trusted creation stamps the marker once; later versions,
forks, and member remixes can preserve it only while copying the exact stored
governed bytes. A shared-room collaborator cannot use the ordinary update path
to substitute model-authored content into a governed copy.
No renderable-artifact iframe adapter or READY/DELIVER flow ships in this
release. Governed artifacts use the native table or inert text fallback;
the existing general-purpose Artifact frame is not a governed renderer. A
future iframe adapter is admitted only with a fixed same-origin route and a
fresh base64url channel carried only in the URL fragment. Uppercase READY and
one-shot DELIVER messages must bind to the exact frame window, exact origin,
and channel. Each side must reject mismatched source/origin/channel, replay,
and unexpected schema or type literals. The parent must never use a wildcard
target origin or broadcast artifact data to other frames.
Producer data never becomes HTML. The reviewed table view uses native browser text nodes, unknown renderer names show the envelope's text fallback, and the download is the inert canonical JSON envelope. A renderer name is data, not a plugin or capability grant. The member and room ACL remain authoritative; markings cannot widen them.
Member metadata routes cannot revise, rename, publish, or attach a governed
Artifact to a conversation. Those operations fail with validation_failed
before changing its attested bytes or title or creating a conversation or share
grant. Owner resolution happens first, so a foreign governed root remains a
not_found response instead of revealing its type. Ordinary Artifacts retain
those operations, and an already-published governed result can still be
unpublished to reduce access. A governed Artifact produced inside a shared chat
may already be scoped to that room by the server-attested producer path; the
member attach route cannot move an existing library result into another room.
On upgrade, migration 0116_legacy_artifact_quarantine removes false
attestation from rows that predate this boundary. Every historical
member_artifact version whose kind and language happen to form the
reserved pair is reclassified as ordinary JSON code. Its exact content, title,
identity, lineage, version history, ownership, timestamps, and publication
state remain unchanged. The migration never interprets those bytes as a
governed envelope, and its downgrade is intentionally a no-op: promoting
arbitrary JSON back to the reserved language would fabricate attestation.
Migration 0125_renderable_artifact_attestation stamps the explicit marker on
the already-quarantined trusted rows and installs the persistent check
constraint without scanning the table under its brief creation lock. Migration
0126_validate_renderable_artifact_attestation validates that constraint in a
separate transaction under PostgreSQL's lower validation lock. The constraint
closes the rolling-upgrade window: an older replica that does not know how to
stamp attestation fails closed instead of creating a reserved row after the
one-time quarantine has run.
Uniformity is the point
Streaming, journal projection, retries, timeout salvage, durability, grounding, lifecycle, policy, and error handling are implemented once in the shared pipeline.
An agent definition may declare only genuinely domain-specific persona, capability, tool-grant, and source-selection behavior. This is enforced by boundary tests that walk the source, so "every agent behaves the same way" is a structural property rather than a convention.
The practical consequence: there is no corner of the product where a guardrail is quietly weaker, and no agent that is accidentally more privileged because its feature landed on a different code path.