Permissions
Permissions define who may act and who may read. They belong in the model, not only in custom page code or external clients.
Building blocks
Roleless Runtime API keys
Runtime API keys can be created before an app defines custom roles. Leave the
role list empty to create an authenticated, least-privilege API-key principal
with roles: []. Cessy does not inject the built-in user role, an admin role,
or a custom fallback role.
The existing opt-in authorization model still applies: Runtime behavior without
authRules remains available, while a rule that requires a role denies the key
until that matching role is assigned. Adding roles to the app later does not
silently change an existing roleless key.
Command permissions
Command permissions protect business decisions. Check them before publish when a new command is added or an existing command changes meaning.
Projection permissions
Projection permissions protect read data. A user who cannot execute a command may still be allowed to read a projection, or the reverse. Model both explicitly.
Auth-rule composition
Multiple authRules on the same command, projection, page, or exposed adapter
operation are OR-combined: any matching expression grants access. Conditions
that must all hold belong in one expression. For example, use
"'knowledge-user' in $principal.roles && $principal.attr.suspended != true",
not two array entries that would allow the role or any non-suspended
principal.
External principal assertions and effective access
A Custom Page adapter principal assertion authenticates the server-resolved
principal to an external service. Its verified sub is identity, not an
authorization grant. The assertion intentionally omits roles, attributes,
groups, and application-specific tags.
When the downstream needs current fine-grained access, model an app-owned
EffectiveKnowledgeAccess projection keyed by principal ID:
Create a dedicated Runtime API key with a narrow role such as
knowledge-access-reader. Add one projection rule that permits that role and
do not grant it on unrelated projections. The external service verifies the
assertion first, then uses only its sub to call:
For signed runtime_source = workspace:<workspaceId>, append
?workspaceId=<url-encoded-workspaceId> (or send the equivalent runtime-source
header); omit the selector for production. Derive it only from the verified
claim. The key remains in the external service and never enters Custom Page
code. Missing, inactive, malformed, or empty access, a 404, an
authorization/read error, or a source mismatch all deny. Never fall back to
browser-provided identity or tags.
Keep three authorities separate: the human/Page principal, the dedicated Runtime API-key principal that may read only this projection, and the HS256 signing key. HS256 gives the verifier co-signing power, so isolate that shared key per adapter trust domain/audience and environment and never reuse it as the Runtime API key. See Adapters for the verifier contract and Build Custom Pages for setup and tampering tests.
Agents
Runtime agents need scoped authority. Give an agent only the command and projection access required for its job, and make user-visible actions auditable in Activity or channels.
App agents are controlled by app-level Agent Scope Profiles in addition to command and projection auth rules. Runtime MCP-only agents default to the built-in runtime-operator profile, which allows Runtime MCP workspace and production commands plus production projection reads. Design-capable agents default to safe-builder, and platform-managed agents default to platform-admin.
A profile can allow Workspace authoring while denying Production promotion, allow Workspace command execution while denying Production command execution, and separately allow or deny Production projection/log/debug reads.
Admins can inspect effective agent access and manage versioned scope profiles in Design → Security → Agent Scopes. Built-in profiles can be assigned directly, so an existing Runtime MCP-only agent pinned to safe-builder can be repaired by assigning runtime-operator. Scope profile, assignment, and approval-policy changes are stored as Change Requests and follow the app’s central approval policy.
Design MCP exposes Agent Scope discovery and proposal tools through the agent-scopes capability group. For a builder that may edit Workspaces and ChangeSets but may not promote to Production, omit promote from the ChangeSet action list, leave promotion environments empty, and deny the promotion tools.
File permissions
Durable Platform files persist one server-owned access rule. Browser uploads
default to the authenticated principal; callers cannot choose another
principal. projection-document delegates to the referenced projection’s
document authorization. The broad app compatibility scope must be selected
explicitly and applies to every authenticated operator with app access.
Policy file writes must choose one of those scopes explicitly. File metadata,
roles, tags, MIME types, filenames, request fields, and copied URLs are not
authorization inputs. Runtime MCP and browser GET/HEAD evaluate the same
tenant, app, runtime-source, and stored-scope contract. Unknown and known-denied
objects both return a non-revealing not-found response.
Native chat uploads are uploader-only until the uploader commits the exact stored attachment in a channel message. Other humans additionally need current membership in that exact channel; agents use the corresponding exact channel participation and message-proof boundary. Standalone chat attachments remain uploader-only.
Verification
Test at least one allowed actor and one denied actor for sensitive commands,
projections, and files. File tests should exercise both GET and HEAD, an
unknown id, two principals, another tenant, and channel membership where chat
attachments are involved. For assertion-backed adapters, use two authenticated
principals with disjoint effective-access documents and prove that forged
browser IDs, tags, and reserved headers cannot cross their access boundary.