zenvault
Security

The defensive primitives are the product.

Custody is a small surface with a high blast radius. Every claim below is implemented today, not on a roadmap. There is no security tier — what's here is what zenvault is.

Layered defense

Five rings between a request and a private key.

A request enters at the outer boundary. It clears the auth and scope check, then session integrity (no replayed tokens), then the cosigner policy. The audit row commits alongside the mutation. Only then does anything touch the key material at the center.

Defensive layers5 active
  1. L1Key material
  2. L2Audit · in-transaction, append-only
  3. L3Cosigner policy · workspace-scoped match + cap
  4. L4Session integrity · refresh family + reuse detection
  5. L5Request boundary · JWT + scoped API keys
Per-layer guarantees

What each layer actually does.

The list below maps directly to control-framework language. We're happy to walk your security team through the source for any of them.

Key material

  • Provider signing keys mount from the host secrets directory and never touch the application database.
  • Reads are contained to a configured base path. Symlinks rejected. Escapes refused.
  • Master encryption key for at-rest secrets is pulled at boot, validated to 32 bytes, and never logged.

Request boundary

  • Admin endpoints sit behind JWT and a per-route role guard. Workspace endpoints sit behind a workspace API key and a scope guard.
  • Scopes are enforced per route. Empty array means wildcard for legacy keys. Non-empty lists must include every required scope.
  • Rate limits key on the authenticated workspace, falling back to the real client IP behind a trusted proxy.

Session integrity

  • Refresh tokens are family-scoped. Replaying a known-revoked token revokes every session in the family and emits a security event.
  • Admin passwords are argon2id. TOTP recovery codes are hashed per code.
  • Forgot-password is constant-time on the miss branch — no enumeration via response timing.

Cosigner integrity

  • Decisions deduped on (provider, externalId, kind). A retried callback returns the prior decision verbatim.
  • Customer refs are namespaced workspaceId:idempotencyKey. Cross-workspace collisions are impossible by construction.
  • Unsupported callback types reject honestly. The signature is reported as valid because it was — the rejection is policy.

Audit

  • Every mutation records actor, target, before, after, IP, and user agent. Append-only.
  • Security-sensitive writes commit alongside their audit row in the same database transaction.
  • Aggregated audit view across every workspace, filterable by action and target.

Webhook integrity

  • Inbound signatures verify against raw request bytes. Re-serialized verification was the bug that prompted this guarantee.
  • Outbound signing secrets are stored AES-GCM ciphertext, never plaintext. The plaintext is returned exactly once.
  • Delivery retries roughly three days, with capped exponential backoff and jitter. State writes are CAS-guarded.
Compliance roadmap

What we're working towards.

We don't claim certifications we don't hold. The list below is honest about where we are.

SOC 2 Type IIin scoping
ISO 27001in scoping
Penetration testannual cadence
Bug bountyprivate, invite only
GDPR / data residencyregional deployment
Incident responseon-call rotation