Scope is not a string you pass around
In Zephr, a connection's scope is not a mutable field that the client can update on a whim. It is a binding: a project, a repository, and a worktree that are admitted before any tool call and then locked. The binding is represented by an immutable ScopeSnapshot and a binding_epoch that advances by exactly +1 on each legal transition.
This matters because most MCP servers treat scope as a runtime argument. Zephr treats it as a security boundary. If a client could change scope mid-session, it could read memories from a repository it was never admitted to.
First-tool-call binding is forbidden
The MCP launcher admits a connection before the first tool call. A tool cannot bind its own scope as a side effect. This closes the most common scope-escape path: an agent that 'helpfully' expands its scope when it sees a file outside the admitted worktree.
The hierarchy is strict: org → project → scope (repo/worktree). Every admission object names its full ancestry. A request without a resolvable, validated admission receives a structured refusal — never a synthesized default scope.
Governed mutation
Every durable write runs inside a governed transaction. Lease finalize revalidation checks the binding_epoch before commit; admin purge and migrate refuse to operate over admitted connections unless the caller holds the right capability.
In cloud mode the admission record lives in Postgres and is validated per request, statelessly. In local mode the same compare-and-operate pattern runs against SQLite. The shape changes; the invariant does not.
Why immutability is the product
Scope binding is not infrastructure plumbing. It is the property that lets a developer open Claude Code and Cursor on the same multi-codebase workspace without the two agents colliding or leaking context. Each connection stays inside its admitted boundary; no amount of tool traffic can rewrite that boundary.
The result is a memory system that behaves like a filesystem permission: you admit a scope once, and every subsequent operation is checked against it.