The server side of a Bounded app: the policy that governs documented supported
mutation surfaces, the runtime-enforced invariants, the functions that run
trusted code, and the actor model that decides who is acting.
bounded deploy validates, compiles, and pushes; runtime rule and invariant
checks reject violations before commit on their documented supported mutation
surfaces. Check a policy with concrete policy tests (bounded tests run) and by
deploying it. Do not generalize that coverage to an unsupported plane,
undocumented storage path, or inherited corpus. For CLI/deploy see the
bounded-deploy skill; for the client SDK and auth UI see
bounded-frontend; to route across the family, see the root bounded skill.
Write the actor model in mind from the start: know who @user is, which principal
a function acts as (runAs/actAs), and where authorization comes from
(@origin) before you write a rule.
Reference Router
Building one of the five most common app shapes? Take the quick path - one target page per build - instead of scanning this table. Otherwise read only the row matching the current task or term.
| Task or term | Read |
|---|---|
| One-screen syntax: field types, tiers, variables, operators, where logic goes | policy cheat sheet |
| Generate or repair a policy from an app description | policy generation |
| Complete policy examples by intent (marketplace, escrow, vaults, staking, tokens, ...) | examples index |
| Worked multi-collection policies (team SaaS, marketplace, realtime game) | examples |
| Rule recipes: owner-only, admin-or-owner, membership, time windows, validation, immutable fields, atomic batches | access patterns |
Require or copy a user's verified X account in a rule, get(/__connections__/@user.id).xHandle, testing linked-X rules, handles typed before linking, onchain apps |
connected accounts |
Rules, fields, expressions, @user, @data, @newData, @time, get(), getAfter(), ownership fields |
policy reference |
Caps, balances, supply, tenant isolation, hard bounds; rollingSum, windowSum, flowBound, conserve, tenantTag, tenantEdge, bound |
invariants |
Trending feeds, leaderboards, ranked windowSum reads |
trending feeds |
| Browser CSP / restrict what app pages may reach | browser boundary |
Constants, reusable rules, @const, @def |
constants and defs |
| Choose rule vs invariant vs hook vs function | when to use functions |
Functions; declare, auth, entry, secrets, actAs, ctx.user, ctx.bounded, ctx.env, ctx.secrets, invoke, deploy |
functions |
Public functions: HTTP routes without a Bounded session at <slug>-api.bounded.page, public, methods, cors, public JWKS, machine callers, webhook and browser-public modes |
public functions |
ctx.ai.run, ctx.ai.generateImage, ctx.ai.generateVideo, getJob, AI without API keys |
ctx.ai |
ctx.services, managed third-party APIs, bounded services |
ctx.services |
ctx.browser, headless browser from a function, driving your own app signed in, @const.AGENT, agent identity |
ctx.browser |
ctx.enqueue, background jobs, queues, replay identity |
ctx.enqueue |
ctx.build, functions that originate governed app builds, image and video builds, mediaSlots |
ctx.build |
| Start simple and graduate to functions | function graduation |
| User-owned provider API keys | secrets |
Schedules, dueRows, hooks, webhooks, verifyWebhook |
scheduled hooks and webhooks |
| Recurring fleet sweeps without full scans | scheduled sweeps |
| Anti-cheat proof limits | hooks and anti-cheat |
Atomic writes, subset attacks, requiresInBatch, incomplete_batch |
data plane |
Queries, pagination, queryAggregate, count, filters, missing versus null, $exists, sort, cursor |
queries |
Files, setFile, storage, full-text search |
files and search |
Realtime rooms; session.tick, settleTo, settleFrom, fog-of-war views |
realtime and games |
Native live modules; session.live, tick, views, @effect, live.intent |
live runtime |
| Input cadence, interpolation, prediction | realtime netcode |
| AI NPCs / AI players | AI NPCs |
| Long-running backend runtime or managed services | backend runtime |
| Multi-step Flue agents | Flue agents |
| Owners, collaborators, scoped admins | admin and ownership |
Top-level roles, members, read:"*", read/write scopes |
roles |
access, custom/external roles, __owners__, __admins__, __developers__, __viewers__ |
access control · identity and logs |
Service keys, payout bots, backend identities, runAs, actAs, @origin, ctx.origin |
service keys · principals and origins |
Concrete allow/deny tests; policy-tests/*.json, bounded tests run/push/list/pull |
policy tests |
| End-to-end tests for authenticated apps | testing authed apps |
| Completion review | quality checklist |
| Agent-facing or backend-only app | building for agents · building a backend |
Error Router
| Error/status | Meaning |
|---|---|
403 |
A write or function invoke failed a rule. Check auth, ownership, roles, or function auth. Denied reads are hidden as 200 with empty data, not 403. |
500 rule_evaluation_failed |
The rule was reached and could NOT be evaluated - no rule denied you, and nothing was read or written. Not a denial, not a retryable conflict. Read bounded decisions for the cause; the row is recorded with decision: error. |
409 + invariant name |
The transaction would violate an invariant. Fix state or policy. |
403 incomplete_batch |
A collection's requiresInBatch declaration names companion paths missing from the atomic batch. Submit the complete setMany. |
| Deploy validation error | Deploy refused the policy before anything changed. Fix policy syntax, field types, tier/invariant pairing, constants, or expression use as the message names. |
Rules Of Thumb
-
Use
@user.idfor normal ownership and membership checks;@user.addressonly for wallet/onchain semantics. -
Denied reads return empty
200responses.get()inside a policy-testexpectreads sandbox state with read rules skipped, so prove a denied read with a second real session. Write and delete denials stay onshouldFail. -
Use
conservefor fixed totals,rollingSumfor caps over time, andflowBoundfor cumulative per-partition outflow ≤ inflow; use one atomicset-manywhen correctness spans multiple writes. -
When one write is invalid without companion writes, declare
requiresInBatchso a hostile client cannot submit only the individually valid subset. -
Put provider API keys in Bounded secrets, not frontend code.
-
Know the acting principal before writing a rule: a function's
runAs/actAsand@origindecide who@useris and whether the call is authorized. -
For authenticated app actions, use the client SDK
functions.invoke, declare the function auth rule, and usectx.user. Do not send a Bounded session token in function arguments or build a JWT verifier inside a public HTTP function. Keep intentionally public data, such as today's prompt, in a separate public function without user-state access. -
Verify private function flows as a non-owner user, including an anonymous session when the app supports guests. Save state, reload, and resume it; owner CLI calls can mask data-read permission failures. A denied read may appear as missing data, so investigate authorization before recreating state or weakening rules.