# Chapter 8 — Security and tenancy

*Who can do what, enforced where. Rosetta's security story is layered rather than
monolithic: an organizational boundary, a key-management system, a tenancy model, a
tool policy, and a set of structurally human-only acts. This chapter walks the layers
from the outside in.*

## 8.1 The organizational boundary

Repository access itself is governed (DT-RX-024, ratified and shipped): the GitHub
organization runs base-permission **none**, with a single insiders team as the
boundary — a small maintainer core across the constellation repos, named outside
collaborators with read access on a subset, and the evidence archive restricted to
insiders only. The docs site mirrors the same boundary: the internal build is gated on
the same insiders membership (DT-RX-029). One boundary definition, applied to code,
evidence, and documentation alike.

## 8.2 Identity and the KMS

Humans and agents authenticate differently, on purpose:

- **Humans** register at the console under a **fail-closed, data-driven domain
  allow-gate** (DT-RX-031): registration is permitted only when the applicant's
  verified email domain is admitted in the tenancy data, resolved through the API
  server — if the gate cannot be evaluated, registration refuses. Admitted users are
  auto-assigned to their tenant account by verified domain. Sessions exist **only** in
  the console BFF; the API server never sees a cookie.
- **Agents and tools** carry `rsk_` API keys minted by the human in the console:
  40-hex-char secrets stored only as bcrypt hashes with a short prefix index, scoped
  (`read`, `context:retrieve`, `plugin-install`, and a privileged `impersonate` for
  service keys acting on behalf of users), revocable individually or wholesale, with
  every authenticated request asynchronously audited. The API surface is Bearer-only;
  `/health` is the single anonymous route.
- **Distribution is part of the perimeter**: the install routes serving plugin bundles
  and MCP binaries require a `plugin-install`-scoped key with no session fallback —
  the supply chain rides the same KMS as the API.

## 8.3 The tenancy model

Tenancy lives in MySQL and is enforced in one authorization package on the API server:

- **Accounts** hold users; account roles rank `owner > admin > member > viewer`, with
  owner/admin granted account-wide sight.
- **Engagements** partition knowledge. Access is an account-wide role *or* an explicit
  per-engagement grant (by user or account); the engagement creator is atomically
  granted `lead` at birth. **Denial is always 403, never 404** — the API does not
  leak existence through error codes.
- **Graph-side partition isolation** is enforced independently by schema and gates:
  engagement-scoped nodes `BELONGS_TO` their engagement, intra-engagement edge
  families require both endpoints in the same engagement, a foreign-owner apply draws
  a 409, and the client↔engagement linkage is invariant-gated. Tenancy is thus
  checked both at the door (authz) and in the data (invariants).
- **The platform tier** (cross-tenant administration: domain admission, account and
  member mutation, admin grants, stranded-user reassignment) is a separate gate on a
  dedicated table, with last-admin revocation refused under row lock — and the entire
  platform-admin API family rides tool-policy-denied tags, so none of it is reachable
  from the agent surface.

## 8.4 The agent boundary

The MCP tool policy is the security layer most architectures don't have: a
deny-by-default allowlist over the *generated* tool surface, with a completeness guard
that fails the build when a new API tag appears in neither the allow nor the deny list.
At snapshot the denied set spans the tenancy and operator families (`accounts`,
`account-members`, `platform-admin`, `engagement-access`, `export`, `presence`,
`twin-ratification`) plus three explicitly denied operations. Above the policy sits
the plugin write-safety contract (confirm-before-destructive, idempotency, the
simulated-content routing rule, no raw API access); below it, the server enforces the
same boundaries again (scope checks, authz, 422 enum validation). An agent that
misbehaves has to defeat three independent layers, each recorded.

The clearest expression of the design is **ratification** (Chapter 5, stage 6): the
one act that turns simulated into confirmed and agent-authored into human-ratified is
absent from the tool surface, refused to API-key principals by the console routes, and
stamped with a human identity — a *structural* human gate, not a procedural one.

## 8.5 Data honesty as a security property

Rosetta treats provenance as part of its trust boundary, and the assessment endorses
that framing: a governed-knowledge platform whose simulated content could masquerade
as confirmed would be *insecure in its actual threat model* — which is bad knowledge
reaching a customer document with the platform's authority behind it. The lanes
(Chapter 2 §2.5), the rehearsal quarantine, the customer-tone hard-fail, and the
ratification gate together form the integrity half of the security story, and the open
bitemporality work (DT-RX-033) extends it to *when* a fact was believed — invalidate,
never overwrite.


*The measurement system next: [Chapter 9](09-quality-evidence.md).*
