Back to home

Security

Concrete controls — what we do, not what we wish we did.

Last updated

1. Tenant isolation

  • Every database row that holds tenant data carries a user_id foreign key referencing user_sessions(id). Every query path filters by that id; cross-tenant joins are not possible at the schema level.
  • Object storage is namespaced by user id (<uid>/<filename>). The file server refuses any path whose first segment does not match the requesting user.
  • Inter-service requests carry an HMAC-signed X-User-Token header. Each Go service verifies the signature and only ever uses the embedded uid for downstream queries.
  • SSE event streams filter every event to the connection's authenticated uid before flushing.

2. Data protection

  • TLS 1.2+ on all public endpoints, terminated at the edge proxy with HSTS enabled.
  • AES-256 at rest for both the primary database and object storage.
  • Database role split — schema migrations run as the owning role at boot only; runtime queries use a DML-only role with no DDL privilege.

3. Authentication and access

  • User sign-in via OAuth (Google today; additional providers on a near-term roadmap).
  • Session cookies are HTTP-only, Secure, SameSite=Lax, and signed with a rotating secret.
  • Production infrastructure access requires hardware-backed multi-factor authentication.
  • Admin promotion in the application is a deliberate operator action via direct SQL — there is no self-serve admin escalation flow.

4. Infrastructure and operations

  • All services run as containers behind an edge proxy with per-IP rate limiting.
  • Persistent jobs use SELECT ... FOR UPDATE SKIP LOCKED for safe multi-replica execution and a stale-lock reaper for crash recovery.
  • Per-user SSE connection caps prevent a single account from monopolising server file descriptors.
  • Observability stack is always on: logs (Loki), metrics (Prometheus), traces (Tempo), dashboards (Grafana), alerts (Alertmanager).

5. Backups and disaster recovery

  • Postgres point-in-time recovery via pgBackRest with daily fulls and continuous WAL archive to encrypted off-site storage.
  • Backup integrity is verified by a periodic restore drill into an isolated environment; the most recent successful drill is documented internally and surfaced on request to enterprise customers.
  • Recovery objectives: RPO ≤ 15 minutes, RTO ≤ 4 hours.
  • Backups expire 30 days after creation. Account-deletion requests purge live data within 30 days; backups containing the deleted data expire on the same retention schedule.

6. Secret management

  • Secrets are file-mounted into the runtime via the orchestrator's native secret store (Docker Secrets / Kubernetes Secrets / Vault). They are never written to environment variables baked into images.
  • Two rotation classes are documented internally — one set requires a coordinated restart, the other is rolling.
  • API keys for upstream providers (e.g. ByteDance) are scoped to the minimum permissions required.

7. Supply chain

  • CI runs govulncheck on every Go change, bun audit on every JS change, and Trivy SARIF scanning on every built container image.
  • Dependency updates are opened weekly by Dependabot and grouped by ecosystem so reviews stay focused.
  • Reproducible container builds via pinned base images and lockfile-only dependency installs.

8. Responsible disclosure

If you believe you've found a security vulnerability, please email security@example.com with reproduction steps. We acknowledge within 2 business days, triage within 5, and aim to remediate critical severity issues within 30. We do not pursue legal action against good-faith security research that respects user privacy and follows this policy.

Questions? Email legal@example.com.

This document is provided as a starting template and has not been reviewed by counsel. Replace before going live.