OCTW assumes tenants are mutually untrusted. Tenants may be adversarial through prompt injection, may install compromised skills or MCP servers, and should never be able to access another tenant's data or network.
| Threat | Mitigation |
|---|---|
| Cross-tenant data access | Per-tenant volumes with 0700 permissions, separate Docker networks |
| Cross-tenant network movement | Dedicated bridge network per tenant (internal=true), no inter-tenant connectivity |
| Host compromise via container breakout | Non-root execution, all capabilities dropped, no-new-privileges, optional gVisor |
| Secret leakage to disk | AES-256-GCM encryption at rest, env var injection at runtime, SecretRef in OpenClaw config |
| Ingress auth bypass | JWT validation at edge proxy, forwarded header sanitization |
Master KEK (file at /etc/octw/master.key or OCTW_KEK env var)
│
└─► encrypts per-tenant DEK (stored in DB as ciphertext)
│
└─► encrypts each secret value (AES-256-GCM, unique nonce per value)
- KEK — 32-byte AES key, stored outside the database (file or env var). Single point of trust.
- DEK — one random 32-byte key per tenant, encrypted by KEK and stored in
tenant_dekstable. - Secret values — encrypted by the tenant's DEK using AES-256-GCM with a unique 12-byte nonce.
- Secrets are decrypted only when starting or waking a tenant container.
- Decrypted values are injected as environment variables into the container process.
- Secrets are never written to the tenant's state volume or config files by OCTW.
- OpenClaw config uses SecretRef (
{"$ref": "env:ZAI_API_KEY"}) to reference env vars without embedding values.
| Category | Example | Storage |
|---|---|---|
| Provider API keys (shared) | ZAI_API_KEY |
Server config (OCTW_ZAI_API_KEY), injected at runtime |
| Per-tenant secrets | Channel tokens, custom keys | Encrypted in DB, injected at runtime |
| Platform secrets | JWT secret, DB password | Server environment only |
| Infrastructure secrets | KEK, TLS keys | File or env, never in DB |
Every tenant container runs with:
user: "1000:1000"
cap_drop: ["ALL"]
security_opt: ["no-new-privileges:true"]
pids_limit: 512
mem_limit: "1536m"
cpu_quota: 100000Additionally:
- No Docker socket mount
- No host port publishing
- No privileged mode
- Restart policy:
unless-stopped - Health check:
curl -sf http://localhost:18789/healthevery 30s
| Mode | Runtime | Description |
|---|---|---|
standard |
Default Docker (runc) | seccomp + AppArmor profiles |
hardened |
gVisor (runsc) | User-space kernel, stronger syscall isolation |
Set per tenant at creation: "isolation_mode": "hardened".
- Each tenant gets a dedicated Docker bridge network (
octw_tenant_<id>). - Networks are created with
internal=true— no outbound internet access by default. - Only the edge proxy (octw-edge) is connected to tenant networks.
- Tenant containers never join the default bridge network.
- No ports are published to the host.
- Authenticates every request via JWT Bearer token or
octw_sessioncookie. - Sanitizes forwarded headers (
X-Forwarded-Foris overwritten with client IP). - Injects
x-octw-tenant-slug,x-octw-user-id,x-octw-user-emailheaders.
- JWT-based auth on all endpoints (except
/auth/loginand/auth/verify). - RBAC: every tenant operation checks membership and role.
- Roles:
tenant_admin>tenant_user>tenant_viewer.
All sensitive actions emit audit events stored in the audit_events table:
- Tenant create / delete
- Secret set / rotate / delete
- Container start / stop / pause / wake
- Member add / remove
- Route mapping changes
- Backup / restore
Each event includes tenant_id, user_id, action type, and detail metadata.