Skip to main content
Switch answers the following access questions in separate places, with separate data.
  • Authorization asks may this user touch this resource. It covers references, documents, packages and rooms.
  • Addressing asks may this sender make this agent respond. It covers @name mentions, aliases, roles and targeted messages.
Neither answer implies the other. A person entitled to read a room may still get nothing back when they name an agent in it, and an agent may answer someone who can’t open a document that agent owns. Both run inside one workspace.

Workspace isolation

A workspace is the unit of isolation. Every room, agent, message and resource belongs to one workspace, and PostgreSQL row-level security restricts each scoped table to the workspace the request is bound to. The policy applies to reads and writes alike, so a row from another workspace is never loaded and can’t be written.
  • The credential supplies the workspace. An agent API key or OAuth client resolves to its workspace before any user is known. A Gateway session is bound to one workspace.
  • Nothing writes unbound. A write with no workspace bound raises an error instead of falling back to a default, so there is no cross-workspace write. Background tasks that start unbound bind the workspace of the row they act on before they act, and a test pins which modules may do that.
  • People are global. Users and their sign-in identities aren’t scoped to a workspace. What’s scoped is a person’s membership in each workspace and the role that membership holds.
  • Isolation covers every table that holds workspace data, not only messages: attachment files, the agent event buffer, bridge connections and the puppet identities of people on messaging platforms included. The one deliberate exception is resolving a credential. An API key is looked up before any workspace is bound, which is how it supplies the workspace.
  • The exceptions are fixed. A short list of server-wide tables, such as users, sign-in identities and server audit events, is exempt from isolation, and a test pins that list. A table that is neither scoped nor on the list stops the process at import, so a new table can’t ship without an isolation policy.

Entry points

switch-core is one process with several doors. Each accepts its own credential. The agent-facing surface is the HTTP API plus the event connection: a WebSocket, or an SSE stream where the client or the server predates it. A session’s host presents Switch operations to the agent as MCP tools. Switch Console or its sidecar makes the calls to Switch over that same surface.

Unauthenticated prefixes

The agent bridge skips its bearer-token check on the following prefixes: /health, /.well-known, /oauth, /gateway, /deeplink and /messaging. Nothing is mounted at /.well-known or /oauth. They’re reserved.
/gateway is on that list because the Gateway authenticates its own requests with a session cookie, not because the Gateway is open. The bearer-token middleware steps aside so the cookie check can run.

Gateway sign-in

The Gateway serves operators, through either a browser or Switch Console, over ordinary web session mechanics.
  • A successful login sets an HS256 JWT in a session cookie marked httponly, samesite=lax and, by default, secure, expiring after 24 hours.
  • OIDC login is optional. When configured, the round trip uses its own short-lived cookie, separate from the session cookie it produces.

The authorization chokepoint

Every resource decision goes through one module. It is pure policy: no I/O, no database access, no queries. It takes a user and a resource and returns a verdict, so the answer to “who can delete this” lives in exactly one place and tests with plain values. The subject of every decision is a user. Not an agent, not a session, not a connection. Everything else resolves to a user before the check runs. The resource side is structural. Anything with an owner plus read and write visibility satisfies the policy. References, library documents, packages, and rooms all qualify by having those fields, with no base class and no registration step.

Decision order

A user administers a workspace when they’re the administrator of the Switch server or hold the owner or admin role in that workspace. The server administrator administers every workspace, one at a time. An owner or admin administers only that workspace. Every check below runs inside the workspace the request belongs to. A resource from another workspace never reaches these checks. read consults public read visibility; write consults public write visibility. The ordering has the following properties:
  • The administer check is a full bypass, delete included. The policy doesn’t distinguish the server administrator from a workspace owner or admin: both are resolved into one check before the policy runs.
  • delete is owner-or-administrator only, and is never reachable through visibility. No visibility setting lets a non-owner delete a resource. Write visibility governs a resource’s contents, not whether it exists.
  • Write-public implies read-public, and the pairing is validated where visibility is set rather than assumed where it’s checked. A resource can’t end up writable by everyone and readable by nobody.
owner and admin differ only in managing ownership: granting owner, demoting an owner and removing one need owner, and the last owner can’t be removed. For resources, the two roles are the same. The Gateway’s room-access check mirrors the protocol layer’s, so a human going through the dashboard and an agent going through the bridge are held to the same rule.

An agent works within its owner’s permissions

An agent’s request is checked as its owner’s. Switch resolves the agent to its owner before the policy runs, so every check in Decision order runs against that user, in the workspace the agent’s credential is for. The owner’s roles in other workspaces don’t apply, and an agent has no permissions of its own on Switch resources. An agent can end up with less than its owner, never more:
  • It can’t manage agents unless Can manage agents is turned on for it, and that’s off by default.
  • An agent can’t change its own settings, services, or permissions, even where its owner can.
Addressing is a separate gate. Whether a message reaches the agent at all is decided by its addressing policy, not by this check. Services are the exception. A service turned on for an agent gives the agent its own access, no wider than its owner’s connection and optionally narrower. See Let the agent use your services. An agent with no owner resolves to a user who owns nothing. It passes no ownership check and administers no workspace, so it can access only public resources. A role changes what an agent is briefed to do, not what it’s allowed to do. This check covers Switch resources only. On the machine where it runs, an agent also has the tools and access available there and in its working directory. See Add a custom agent. For what else is up to you when you run agents, see What Switch does, what you set, and what you decide. If you run your own server, Host Switch for your team covers your part of running it, such as TLS, secrets, and backups.

Addressing policy

An agent’s addressing policy is a set of rules, evaluated against the sender and the room rather than against a resource. A rule scopes the following dimensions:
  • rooms — the room the message was sent in.
  • room_groups — the group that room belongs to. Moving a room between groups can change which agents answer in it.
  • users — matched when the sender is a human.
  • agents — matched when the sender is another agent.
The following subjects are symbolic, resolving at evaluation time rather than naming anyone:
  • owner — the agent’s owner, whoever that currently is.
  • owner_agents — any agent owned by that same person.
Each dimension is * for any, a list for those named, or an empty list for none. A sender is exactly one kind, human or agent, so “people only” is a users list of * and an empty agents list. A rule admits an attempt when the room matches and the group matches and the sender matches on its own kind. Addressing is permitted when any rule admits it, so rules widen rather than narrow each other.

Precedence

  • No rules at all means open. Agents registered before addressing policies existed were deliberately left permissive.
  • Once there’s any rule, everything not admitted is denied. Adding the first rule flips the agent from open to closed, a larger change than adding the second.
  • A newly registered agent admits owner and owner_agents. New agents don’t inherit the permissive default, and other people and their agents are refused until the owner permits them. Agents that already exist keep the policy they have.
An agent’s owner sets its policy, in Switch Console or the Gateway. The API also accepts a change from anyone who administers the workspace, though neither app offers one. Policies aren’t agent-facing, so an agent can’t widen its own.

Identity claims

A claim links a platform account to a Switch user. Addressing rules name Switch users; a message from Slack arrives from a Slack account, and the claim connects the two. The relationship is deliberately many-to-many. An exclusive claim would let whoever claimed an account first lock everyone else out of it permanently, with no recovery that doesn’t involve an admin. An unclaimed platform account matches nobody: not the owner, not a listed user, not owner_agents. As far as policy is concerned it is a non-user, not an unknown user.
A newly registered agent answers only its owner and the owner’s agents, and it can’t recognize its own owner until that owner links their messaging account. Until then, the agent ignores the person who created it. If the setup looks correct and the agent still ignores its owner, check the claim.

How a refusal appears

Refusal takes a different form for each kind of action:
  • A message from a sender who isn’t admitted is demoted to ordinary room chatter, and the agent posts one reply saying it can’t act on it. The refusal is visible in the room rather than silent.
  • A command is gated the same way. A command that names the agent gets a reply. A room-wide command gets a quiet decline, so agents in a full room don’t each announce a refusal.
  • A targeted message reports a per-target not_permitted status rather than failing the call. Addressing several agents at once doesn’t fail because one declined.

Event scoping

Addressing decides what an agent may act on. Membership decides what it is shown. An agent receives events only for rooms it belongs to, and membership is checked per subscribe rather than once when the connection opens. Cross-room resource access is checked on the server. An agent doesn’t read documents directly: it sends a request through the room, and a server-side participant handles it. Before loading anything, that participant checks that each document is attached to the room the agent is asking from, so the agent can’t skip the check.

Audit log

Switch records changes to the membership and credentials of a workspace in an audit log, which any owner or admin of that workspace can read. It records invitations and membership, workspace roles, API keys, messaging apps and service connections.

Next steps

Rooms and resources

The resource library, ownership and visibility in practice, and how a room gets built

The agent protocol

Registration, connections, the event stream, and the operations registry