Security overview
How Oxagen decides every call at one boundary, keeps tenant data apart, holds credentials, and records every answer.
Security sets the access clause of every agent's mandate, and this page shows how Oxagen enforces it. An agent has its own identity and mandate. When a task needs a capability, the agent asks. A rule answers at the invoke() boundary (allowed, denied, or routed to a person), and the audit stores keep the answer. Connection credentials stay in Oxagen, encrypted, and the agent never sees them.
Four layers
Four layers each enforce independently:
| Layer | Control | Enforcement point |
|---|---|---|
| Network | Login or Oxagen API key authentication, TLS in transit everywhere | Caddy on the application host |
| IAM | Role-based access control, per-capability default effect | invoke() kernel boundary |
| Data | Row-level security, tenant-scoped seams for Neo4j and ClickHouse | Postgres RLS, scoped query guards |
| Audit | Two independent append-only stores, chain-hash tamper evidence | Every invoke() allow and deny |
One request through all four layers. A denied call stops at IAM with an audit record. An allowed call reaches data only through a tenant-scoped seam. See Tenant isolation and RLS for the policy SQL behind each seam.
The layers are independent. A misconfigured application-layer predicate does not leak data, because the database enforces isolation by itself. IAM behavior depends on the organization tier and principal. Machine-purpose checks apply separately. See RBAC and roles.
The single capability chokepoint
Capability routes and tools call invoke(). Contracts register metadata with registerCapability, and handlers register separately. The kernel governs calls routed through it. Authentication endpoints and other surface infrastructure are separate paths.
At the invoke() boundary:
- Oxagen identifies the calling principal (a signed-in login, an Oxagen API key, or a service token).
- The IAM resolver evaluates the principal's role grants, then the system org Owner role, then falls back to the capability contract's
defaultEffect. The resolver records each step of its decision. - If the answer is deny, Oxagen writes a
capability.invoke_deniedsecurity event to both audit stores and returns a structured 403. - If the answer is allow, the handler runs inside a tenant scope (
runInTenantScope) that feeds the database seams. - On completion, Oxagen meters credits, records latency, and writes a
capability.invoke_allowedevent.
This is the SOC 2 completeness argument: no path through the boundary skips the audit write.
Encryption at rest
Oxagen encrypts every store at rest:
- Postgres (Aurora PostgreSQL): Aurora storage encryption.
- Neo4j and ClickHouse: encrypted EBS volumes on the application host.
- Blob storage: server-side encryption.
Oxagen also encrypts plugin OAuth tokens and API key secrets at the application layer (AES-256-GCM, with a KMS-managed key) before writing them to Postgres.
Encryption in transit
Client connections use TLS 1.2 or higher, terminated by Caddy on the application host. The application reaches Neo4j and ClickHouse over the host's loopback interface, which the security group opens to nothing. Postgres runs on a separate Aurora cluster that admits connections from the application host only.
Data residency
Oxagen runs in AWS us-east-1 (US East). The application host, Postgres, Neo4j, and ClickHouse all run there. External processors and model providers have their own processing locations. See Data handling.
Roles and access control
Eight built-in roles govern what people can do. Four are org-scoped:
- Owner: full control, including billing and org deletion.
- Admin: all operations except destructive org-level actions.
- Compliance: security and privacy policy management. Cannot reach operational surfaces or billing.
- Billing: billing and subscription management only. Cannot reach operational surfaces.
Four are workspace-scoped:
- Owner: everything in the workspace, and nothing outside it.
- Admin: the same as Owner.
- Member: run agents, resolve approvals, and edit memories.
- Viewer: read-only across the workspace.
Agents get three roles of their own. See Agent roles.
The resolver also defines direct workspace grants and org-enforced policies that rank above role grants, but that tier is not active today. Migration 0027 removed its tables, so every live decision comes from role grants, the system org Owner role, a workspace's Owner or Admin role, or the contract default. RBAC and roles documents the resolution order.
Sub-processors
Oxagen relies on these third-party processors for data handling:
| Sub-processor | Purpose | Data involved |
|---|---|---|
| Amazon Web Services | Compute, Postgres (Aurora), the host that runs Neo4j and ClickHouse, and runtime configuration (Parameter Store) | All application data in transit and at rest |
| Model gateway (OpenRouter or Vercel AI Gateway) | Model inference | Messages and system prompts sent to a model |
| Stripe | Billing | Subscription and payment data |
| Inngest | Background job orchestration | Job payloads referencing org and workspace IDs |
Full sub-processor details, including DPA availability, are in Data handling.
Security incident response
There is no incident register in the app. Organization → Audit holds the security event record, and MCP authentication alerts can be configured with set_auth_alerts.
For vulnerability disclosures, contact security@oxagen.sh.