Security · Architecture

Architecture brief

A single page a CIO or security architect can read in ten minutes. Shorter than your standard enterprise security questionnaire. Expands every control on /security/controls.

Section 1

Tenancy model

The top-level tenancy boundary is the Environment. Every System, Workflow, Goal, Document, Signal, Execution, Meeting, Project, and Memory row is scoped to one Environment. Cross-Environment reads are explicitly disallowed at the ORM layer — every handler filters by environmentId + caller identity.

Users hold an Identity. A user joins an Environment through an EnvironmentMembership with a role: OWNER (1 per Environment), ADMIN, CONTRIBUTOR, or VIEWER. Role escalation to Environment actions is enforced by lib/auth/roles.ts::requireRole(). A RoleAccessDenied error returns a 403 at the route boundary.

Section 2

Encryption posture

In transit: TLS 1.2+, HSTS with preload. Strict CSP with connect-src 'self' https://api.anthropic.com and frame-ancestors 'none'; production script-src drops unsafe-eval.

At rest: The primary Postgres is encrypted by the managed provider. Application-layer envelope encryption for every integration credential (Integration.credentialsEnc) and every AI key (Identity.anthropicKeyEnc). Long-lived tokens (email verification, invitations, public share) are stored as SHA-256 hashes — the plaintext only ever lives in the link sent to the user.

Customer-managed keys: on the roadmap for the first enterprise contract that requires it. The envelope-encryption layer is factored so CMK slots in without touching application code.

Section 3

Autonomy + approval gates

Every Workflow and System carries an AutonomyConfigwith one of five levels: Observe, Suggest, Act & Notify, Autonomous, and Self-Direct. The level is set per-scope, not globally; a user can have their Inbox Triage at Autonomous while their Finance System stays at Suggest.

Tool calls that would have user-visible effect (send email, post to Slack, create a Notion page, stage an ad campaign) are routed through the ApprovalRequest queue at any level below Autonomous. The kernel emits a PendingActionrow caching Claude’s tool_use_id so the conversation can resume after approval without re-prompting. Args are never editable between approval and execution.

Reversible-by-default: autonomous actions write a 24-hour undo window. Undoing creates a compensating AuditLog entry and a AtriumMemory row of type user_correction so future Atrium calls factor in the correction.

Section 4

Audit chain

Every mutation writes one or more rows across three tables:

  • AuditLog — high-level changes (workflow.created, member.added, approval.approved) with before/after JSON diffs and actor identity.
  • IntelligenceLog — every Atrium action with input, output, reasoning excerpt, tokens, cost, success flag, and the System id.
  • KernelTrace— lower-level tool-call traces used to render the “why did Atrium do this?” drawer.

The monthly ROI report signs its contents with a SHA-256 hash of the AuditLog entries in the window so the PDF is verifiable after the fact. Customers can export the full AuditLog as CSV or JSONL at any time via GET /api/audit/export.

Section 5

Claude / Anthropic integration

BYOK per Environment. Every Environment can hold its own Anthropic API key. The key is validated against Anthropic at connect time (a 1-token Haiku ping), stored envelope-encrypted, and used for all Atrium calls made on behalf of that Environment. No cross-tenant key leakage.

Platform key fallback. For users on the free tier who have not brought a key, a platform key with strict budget caps routes their requests during a time-boxed trial. lib/cost/user-budget.tspreflights every request; exceeding the cap returns a connect-your-key prompt rather than silently failing.

Model routing. lib/kernel/router.ts maps abstract tier names (FAST / BALANCED / DEEP) to concrete Claude model slugs; tier assignment lives in the skill registry so swapping models is a one-line change. Current mix: Haiku for narrative + planner, Sonnet for reasoning-dense executors.

Section 6

Data residency

Primary data plane is US-East by default. Regional data planes for EU / UK / US will land for the first enterprise contract that requires it; the architecture is factored for it (no hard-coded region, all secrets are referenced via the Environment row, migrations are region-agnostic).

Subprocessors: Anthropic (model inference), Vercel (hosting), Neon / Turso (Postgres), Resend (transactional email), OAuth providers per integration. Consent is surfaced at sign-up and re-collected on policy-version bump via the ConsentLog model.

Section 7

Isolation + blast radius

Per-identity rate limits on every authenticated API route (lib/rate-limit.ts). Per-Environment budget caps prevent one team’s runaway Atrium call from starving another.

Destructive operations (Environment delete, System delete, Workflow delete) cascade through Prisma onDelete. A cascading delete of a high-level row fires a grid:{entity}-changed event so every subscribed surface (sidebar, environment switcher, project board) updates live rather than showing stale references.

Questions a procurement team needs answered before signing? Email security@grid.app. Public disclosure flow at /security. Capability inventory at /capabilities.