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 user 65532, 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.