Skip to main content

Security posture

The controls below are implemented end-to-end. This is still a young codebase: we publish our threat model and our disclosure process, and we will earn the rest with time and operator feedback.

HMAC-chained audit log

Per-row HMAC-SHA256 signed with Z4J_AUDIT_CHAIN_SECRET, a dedicated key separate from the application secret, plus a prev_row_hmac chain and a separately authenticated head and row count. Detects modification and deletion by the application, a downgraded adapter, or any writer that skips the audit service. Not a defense against a hostile database role: see the threat model.

Argon2id password hashing

64 MiB memory, 3 iterations, parallelism 4. Minimum 12 characters; passwords shorter than 16 characters require 3-of-4 character classes, and every password is checked against the denylist.

Wire-protocol HMAC v2

Every WebSocket frame signed. Replay guard (±60s + nonce + monotonic seq). Session-bound frames. SignatureError closes the socket with code 4403.

CSRF double-submit

__Host-prefixed CSRF cookie + X-CSRF-Token header required on every mutating endpoint. Constant-time comparison.

Secure cookies

HttpOnly session cookie, Secure + SameSite=Lax in prod, __Host- prefix. No JS access to session tokens.

IP rate limits

Per source IP per brain worker: login (20/min), invitation preview plus accept (30/min shared), password-reset request plus confirm (10/min shared). Multi-worker fleets multiply the effective ceiling; use a shared reverse-proxy limit when required.

Account lockout

10 failed login attempts cause a 15-minute account lockout by default. Combines with IP rate limits for layered defense.

Redaction pipeline

Rule-based recursive scrubber at the agent, with built-in key and value patterns plus operator extensions. Defense-in-depth re-scrub on z4j.

SSRF hardening

Private and special-use destinations are blocked for webhooks, channel URLs, and SMTP. HTTP targets use scheme allow-lists and DNS-pinned dialing; SMTP is re-resolved before send.

HSTS + CSP

Strict-Transport-Security (1yr) plus a Content-Security-Policy with hashed inline scripts and style blocks, required inline style attributes, and no unsafe-eval. X-Frame-Options: DENY; Referrer-Policy: strict-origin.

RBAC everywhere

Every mutating endpoint checks membership + role. UI mirrors the server policy (useCan hook). Cross-project access = 403.

Secret storage

Tokens are stored as keyed hashes rather than plaintext. Production deployments provide server secrets through environment or a secret manager; packaged SQLite deployments persist generated secrets in /data/secret.env. No secret-recovery backdoor.

transparency

What we publish

A control plane is worth what its weakest documented boundary is worth, so we write the boundaries down rather than only the controls.

disclosure

Reporting a vulnerability

Please report security issues privately to [email protected]. Include a clear reproduction, expected vs. actual behavior, and a severity estimate.

Response targets: acknowledgment within 48 hours; preliminary assessment within 5 business days; fix and advisory within 30 days of confirmation, or within 7 days when the issue is critical. Coordinated disclosure runs 90 days from your first report, so triage time never comes out of your window. These numbers are copied from the security policy in the repository, which is the version that binds us.

What we already know about is listed too. Those targets cover something you report to us. A weakness we found ourselves and decided to defer gets no clock, because nobody is waiting on us; it gets a public entry, a stated mitigation, and the release it is scheduled for. One is open today, and we rate it critical for anyone whose threat model includes the database role: such a role can defeat both in-database boundaries, so treat those credentials as equivalent to full control of z4j's integrity guarantees. The fix is a design change rather than a patch, so it is scheduled behind a major boundary. The known-limitations list and the threat model give both paths in full, along with the mitigation and what the fix has to prove before the entry comes off the list.

We credit reporters in the advisory (opt-in). We do not offer a paid bounty at this time.

Read the full security docs

Threat model, controls, and the CVE disclosure policy: all on z4j.dev.