Security Verifiable, not certified.
There is no SOC 2 report, no ISO 27001 certificate and no third-party
penetration test. Saying otherwise would be the easiest lie on this
site. What exists instead is every enforcement point written down, in a
repository you can read — which is worth more than a badge if you intend
to run this yourself, and worth checking before you do.
Sign-in
Google or GitHub OIDC, verified at the API boundary. No session means a 401 before any handler runs — there is no anonymous read path to forget about.
ADR-0002 ↗ Sessions
The cookie carries a random server-side id and nothing else — no JWT, no signed envelope, nothing a leaked cookie tells an attacker about you. HttpOnly, Secure on https, SameSite=Lax.
ADR-0002 ↗ Who gets in
An email allowlist, re-read on every single sign-in. Removing someone takes effect on their next request — no restart, no cache to wait out — and every change lands in an append-only audit log on the box.
Secure hosting ↗ No inbound admin door
Editing the allowlist is the most dangerous operation there is, so it has no public endpoint at all. The box reaches out over a locked channel instead. Stated as one rule: a control-plane change never has an inbound door open to the internet.
ADR-0004 ↗ The perimeter
A Cloudflare tunnel proxies exactly one hostname to loopback and returns 404 for everything else. The app binds 127.0.0.1. Nothing listens for inbound connections — no ports forwarded, no static IP, nothing to scan.
Access model ↗ Tenant isolation
Structural, not a permission check. Every store is rooted at the tenant’s own directory, so a store object cannot address another tenant’s data even if the code asked it to. Wrong owner and does-not-exist both return 404, so the API never confirms a stranger’s campaign is real.
Access model ↗ Delegation attenuates
You can hand a colleague part of your own access — never more than you hold, with a spend ceiling per grant, enforced at a single gate. Rights shrink as they are passed on; they cannot grow.
ADR-0005 ↗ Headers and budgets
nosniff, frame-ancestors none, HSTS, a strict CSP on the API and no-store on every API response. Per-user quotas and spend caps return 429 rather than a surprise invoice.
Access model ↗