Docs / Security in depth
Security in depth
This page describes controls that exist in code and tests today. Each section names the mechanism and where it's enforced.
Secret derivation & storage
-
Gateway API keys (
zf_…) are hashed with scrypt (N=216) plus a server-side pepper loaded from a separate secret file. The pepper and the seed never live in the same store. - Management credentials (console login) are hashed with salted SHA-256.
- The shared seed file that both services hydrate from contains digests only. The bootstrap tool prints plaintext secrets exactly once and writes nothing recoverable to disk.
- There is no decryption path: verification always re-derives the digest and compares.
Fail-closed authentication
-
Authentication runs before route resolution: an unauthenticated request to
any path — known or unknown — gets the same terse
401. Route enumeration without a key is impossible by construction. - The edge schema-enforces a loopback bind and rejects non-local hosts at startup, so a config mistake cannot expose it.
- Console static hosting carries strict path-traversal guards (encoded and nested traversal forms are tested to 404).
Tenant isolation
- Every management and inference record is scoped to a tenant; cross-tenant reads and writes are rejected in the service layer.
- A dedicated tenant-isolation suite drives both services over real HTTP and asserts cross-tenant access fails in every direction.
- Contract tests pin every management route to the frozen OpenAPI snapshot, so behavior can't drift silently.
Container & host hardening
-
All services: read-only root filesystem,
cap_drop: ALL,no-new-privileges, non-root user65532, tmpfs-only scratch. -
Images build from a digest-pinned base; build contexts exclude
.git,node_modules, and all secrets via.dockerignore. - Host: services bind loopback; the single public component is the TLS proxy. Inbound traffic is gated twice — host iptables (default-REJECT) and the cloud security list.
- TLS terminates at Caddy with auto-HTTPS; HSTS is set on every response.
Operational safety
- Credentials survive restarts via seed re-hydration (covered by integration tests and verified on the live deployment).
-
Panic surfaces exist in the contract:
POST /management/v1/sessions/revoke-all, emergency controls, cache purge. - Management mutations are idempotent — safe retries can't double-apply.
Known limitations (stated plainly): single-VM deployment, in-memory
domain state (Phase 1), no DDoS absorption beyond the fair-admission layer, and no
third-party compliance attestations. These are the boundaries of the early-access
deployment, tracked on the roadmap.
Reporting
See Responsible disclosure on the security overview — in short: security@zerofare.dev, private first.