Access control in the Engine is layered. The Engine itself has a single binary auth boundary — you have the API key or you don’t. The multi-user complexity lives upstream (in the routing layer) and below (in MCP per-user OAuth). This page maps the layers.Documentation Index
Fetch the complete documentation index at: https://internal.september.wtf/llms.txt
Use this file to discover all available pages before exploring further.
The layers
Each layer has its own access concept. This page covers each.Engine API key
The most basic boundary. Every request except/health requires the
X-Engine-Key header.
What the key is:
- Either a static value (
ENGINE_API_KEYin env) or pre-hashed (ENGINE_KEY_HASHin env, plaintext lives upstream). - Single value per Engine. There are no scopes; the key has full access to this Engine.
- Not a per-user identifier. The Engine doesn’t know which user is calling; it knows whether the caller is authenticated.
- Not OAuth. Plain token in a header.
- Not encrypted in the request body (HTTPS encrypts the transport; always use HTTPS in production).
Upstream routing (BAP)
The Engine is single-tenant, so multi-user logic lives in the upstream router. BAP (the public product’s backend) holds:- User accounts.
- The user-to-engine mapping (which Engine instance serves user X).
- Application-level RBAC (which users can perform which operations).
- Billing.
- Authenticates the user via its own auth (sessions, OAuth, API key, etc.).
- Authorizes the operation per BAP’s RBAC.
- Routes to the right Engine, attaching
X-Engine-Key.
X-Engine-Key. If the routing fails open or routes to the wrong
Engine, user X’s request hits user Y’s brain. This is a BAP-level
concern.
MCP per-connection access
MCP credentials are per-connection. When user X connects Slack, the OAuth flow grants the Engine specific scopes against user X’s Slack workspace. The credentials are encrypted at rest (AD_ENCRYPTION_KEY)
and used only for outbound calls to the corresponding MCP server.
Key properties:
- Per-user. Each Engine instance’s brain has its own connections.
- Per-scope. Each connection grants specific scopes; calls outside those scopes fail at the provider.
- Revocable. Disconnecting (
DELETE /assets/connections/{ref_id}) revokes the stored credentials.
Sandbox access
The sandbox is the access boundary for tool execution. See Code execution for the guarantees. In summary:- The agent (and tool subprocesses) see only paths in
ALLOWED_ROOTS. - Dangerous syscalls are blocked by
seccomp. - Filesystem ACLs are enforced by
landlock. - Permission prompts surface destructive operations to the user (HITL).
ALLOWED_ROOTS.
Per-feature access
Some Engine features have additional access:Admin endpoints
/admin/* endpoints (catalog reload, soul reload, LC trigger) are
authenticated by the same API key. There’s no separate admin role.
In multi-user deployments, BAP gates which users can call admin
endpoints based on its own RBAC.
/feedback
Same X-Engine-Key. No separate authorization. The submitted feedback
is recorded as a learning signal regardless of who submitted it; in
practice BAP gates this to authenticated users.
Memory endpoints
SameX-Engine-Key. Read access to memory is full; write access goes
through /memory/import only. There is no partial-access mode for
memory.
Audit
For every request, the Engine logs:- Timestamp.
request_id.task_id(for/executeand HITL).- Path.
- HTTP status.
- Latency.
observability_events table holds:
- Permission grants and denials.
- Admin endpoint invocations.
- Migration runs.
- API key validation failures.
What multi-user products usually do
For a typical multi-user product running on the Engine:- BAP authenticates the user (web session, OAuth, etc.).
- BAP authorizes based on its own RBAC.
- BAP looks up the user’s Engine (fixed mapping or dynamic provisioning).
- BAP forwards the request to the Engine with the Engine’s API key.
- The Engine processes without knowing which user — its job is to
serve whoever sent the right
X-Engine-Key.
What this access model can’t enforce
- The model’s behavior. The agent can decide to do things that the access model permits but that you’d consider wrong (e.g. sending a Slack message in an awkward tone). Defense lives in the agent’s system prompt and the permission layer.
- Cross-user data leaks via routing bugs. If BAP routes user X’s request to user Y’s Engine, user X reads user Y’s data. The Engine trusts that the request was correctly routed.
- The user’s own actions. A user can ask their own Engine to do things; the access model can’t tell them no. (The permission system catches the destructive operations specifically.)
See also
- Threat model — the full picture.
- Authentication —
X-Engine-Keydetails. - Permissions — the HITL gate for risky operations.
- Data handling — what’s stored, who can read it.

