@personaai/runtime’s routes split into two trust tiers, and this is a deliberate design choice,
not an oversight:
- Always on — things an end user does in their own chat session: send messages, manage their
own conversations/files/memory, connect their own MCP account. Scoped entirely to whichever
user
resolveUser returns for that request.
- Opt-in via
capabilities — Project-level configuration: LLM provider credentials, skill
authoring, knowledge base and vector store management, security audit logs, an agent-building
co-pilot, and full Agent/MCP-server CRUD. Every one of these defaults to false. Upgrading
this package never silently exposes new surface to whoever resolveUser accepts.
The capability flags
Each flag gates exactly the routes listed against it in the Routes
opt-in table. GET /agents (read-only discovery) is always on regardless of agentsWrite —
only the write/mutate routes need the flag.
providers is the one to think hardest about: enabling it exposes routes that create/update/
read LLM provider credentials (API keys) for your Project. Only enable it on a mount that
resolveUser locks down to your own team.
Who should enable these
Most hosts should never turn any of these on. The intended pattern is to call
@personaai/sdk directly from your own admin backend, CLI, or setup
script for Project configuration — that’s what “belongs to the host application” means in
practice. This runtime’s job is the end-user-facing surface (/chat, /threads, /files,
/memory), not necessarily your team’s internal tooling.
The two-mount pattern
If you do want an admin surface reachable over HTTP — e.g. building your own internal admin
tool on top of this runtime — the right pattern is two separate createRuntime() calls mounted
at two different paths, each with its own resolveUser:
resolveAdminUser is where the actual access control lives — e.g. checking a role claim on the
session, or a hardcoded allowlist of internal user ids. The runtime itself doesn’t know what
“admin” means; it only knows whether a given mount’s capabilities are on or off.
Coarse-grained by design
This is intentionally coarse: a capability is either fully on or fully off for whoever
resolveUser accepts on that mount. There is no per-user or per-action permission model inside
the runtime itself — “this user can update Agents but not delete them” isn’t expressible as a
capability flag.
If you need that level of granularity, enforce it in resolveUser (reject the request before it
reaches the route) or in a lifecycle hook (e.g. throw from beforeRun for
a disallowed action), not by asking this runtime for finer-grained capability flags than “on this
mount, for this resolved identity, is the capability on.”