Security Model
Encryption at Rest
All secrets are encrypted with AES-256-GCM before being written to the database. The encryption key is never stored in the database itself. Decryption happens only at the point of use -- secrets are not cached in memory after decryption.
Ciphertext format: arcan:v1:AES256GCM:<base64-ciphertext>
Key Hierarchy
Master Key (stored in KMS or local file)
│
├── HKDF("arcan-dek-v1")
│ └── Data Encryption Key (DEK) — encrypts secrets at rest
│
├── HKDF("arcan-audit-hmac-v1")
│ └── Audit HMAC Key — integrity chain for audit log entries
│
├── HKDF("arcan-plugin-auth-v1")
│ └── Plugin Auth Key — authenticates plugin runtime sessions
│
└── Separate: Registry Signing Keys (Ed25519, not derived from master key)
├── Official key — public key embedded in core binary
└── Enterprise key — distributed with activation
All derived keys use HKDF-SHA256 (RFC 5869) with distinct info strings. Compromising one derived key does not compromise others. Rotating the master key rotates all derived keys.
Master key rules:
- Never stored on disk in plaintext in production (KMS reference only)
- Stored as
[]byte, zeroized after use -- neverstring - Standalone mode: auto-generated file at
~/.arcan/data/master.key(0600 permissions) or KMS - Multi-node mode: KMS required (sharing a file-based key across nodes is a security risk)
TLS Everywhere
TLS is required in all deployment modes with no exceptions. On first startup, Arcan generates:
- An internal CA certificate (
~/.arcan/data/ca/ca.crt) - A server certificate signed by that CA (
~/.arcan/data/tls/server.crt)
Multi-node deployments can optionally enable mTLS for node-to-node communication.
Plugin Signing
All plugin packages (.arcanpkg) are verified with Ed25519 signatures before loading. The verification flow:
- Download package from registry
- Verify SHA-256 checksum
- Verify Ed25519 signature against the registry's public key
- Load into sandboxed runtime
Unsigned or tampered packages are rejected. The official signing key is embedded in the core binary. Enterprise packages use a separate key distributed with the activation license.
RBAC
Three built-in roles, scoped per realm:
| Role | Capabilities |
|---|---|
| admin | secrets:read, secrets:write, secrets:delete, realms:manage, policy:manage, audit:read, tokens:manage |
| member | secrets:read, secrets:write, audit:read |
| viewer | secrets:read, audit:read |
The realm owner (creator) is automatically treated as admin. Policy middleware evaluates permissions on every request.
Threat Model
| Attack Surface | Threat | Mitigation |
|---|---|---|
| API endpoints | Unauthorized access | Auth middleware on all routes except /api/v1/health |
| API endpoints | Brute force | Rate limiting per token and per IP |
| Master key | Extraction from memory | Stored as []byte, zeroized after use; envelope encryption via KMS |
| Master key | Extraction from disk | KMS reference only; file key uses 0600 permissions |
| Plugin package | Supply chain attack | Ed25519 signature verification before loading |
| Plugin package | Tampering after download | SHA-256 checksum on every load |
| Plugin runtime | Network/filesystem access | Sandboxed -- plugins can only call declared host functions |
| Plugin runtime | Cross-plugin data access | Store scoping by engine_id + realm_id |
| Plugin runtime | Privilege escalation | Capability model -- core grants only declared capabilities |
| Stored secrets | Database compromise | AES-256-GCM encryption; key never in DB |
| Stored secrets | Memory dump | Decrypted only at point of use, not cached |
| Audit log | Tampering | Append-only table, optional HMAC chain, SIEM export |
| API in transit | Man-in-the-middle | TLS required in all modes; mTLS option for multi-node |
| CLI credentials | Token theft | Token file with 0600 permissions; token expiry |
| Config file | Credential leakage | No plaintext secrets -- env vars or KMS references only |