oxagenDocs
Security

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:

LayerControlEnforcement point
NetworkLogin or Oxagen API key authentication, TLS in transit everywhereCaddy on the application host
IAMRole-based access control, per-capability default effectinvoke() kernel boundary
DataRow-level security, tenant-scoped seams for Neo4j and ClickHousePostgres RLS, scoped query guards
AuditTwo independent append-only stores, chain-hash tamper evidenceEvery invoke() allow and deny
Loading diagram

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:

  1. Oxagen identifies the calling principal (a signed-in login, an Oxagen API key, or a service token).
  2. 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.
  3. If the answer is deny, Oxagen writes a capability.invoke_denied security event to both audit stores and returns a structured 403.
  4. If the answer is allow, the handler runs inside a tenant scope (runInTenantScope) that feeds the database seams.
  5. On completion, Oxagen meters credits, records latency, and writes a capability.invoke_allowed event.

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-processorPurposeData involved
Amazon Web ServicesCompute, 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 inferenceMessages and system prompts sent to a model
StripeBillingSubscription and payment data
InngestBackground job orchestrationJob 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.

On this page