Skip to main content

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.

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.

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_KEY in env) or pre-hashed (ENGINE_KEY_HASH in env, plaintext lives upstream).
  • Single value per Engine. There are no scopes; the key has full access to this Engine.
What the key is not:
  • 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).
For rotation see Secrets. For the conceptual auth model see Authentication.

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.
When user X makes a request, BAP:
  1. Authenticates the user via its own auth (sessions, OAuth, API key, etc.).
  2. Authorizes the operation per BAP’s RBAC.
  3. Routes to the right Engine, attaching X-Engine-Key.
The Engine never sees user identity; it sees a pre-validated 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).
The sandbox is per-task. The agent’s bash in task A cannot see files written by the agent’s bash in task B (within the same Engine), beyond what’s persisted to 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

Same X-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 /execute and HITL).
  • Path.
  • HTTP status.
  • Latency.
Logs ship to your logging pipeline. The pipeline’s retention is your audit retention. For specific compliance auditing of permission decisions and admin actions, the observability_events table holds:
  • Permission grants and denials.
  • Admin endpoint invocations.
  • Migration runs.
  • API key validation failures.
These are SQLite rows; export periodically for long-term retention.

What multi-user products usually do

For a typical multi-user product running on the Engine:
  1. BAP authenticates the user (web session, OAuth, etc.).
  2. BAP authorizes based on its own RBAC.
  3. BAP looks up the user’s Engine (fixed mapping or dynamic provisioning).
  4. BAP forwards the request to the Engine with the Engine’s API key.
  5. The Engine processes without knowing which user — its job is to serve whoever sent the right X-Engine-Key.
This separation matters: BAP can enforce complex multi-user policy without each Engine needing to understand it.

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