Sterryk
Security & Trust
How the platform protects your data and your money. Every control described here is enforced in code and continuously tested — not aspirational. Where a claim rests on a specific mechanism, we name it.
Your data is isolated at the database, and fails closed PostgreSQL FORCE row-level security · dual-key isolation · gate: check_rls.py
Every household's data lives behind PostgreSQL row-level security set to FORCE, so the isolation policy binds even the table owner. Two independent keys scope every query — one for the household and one for the individual member — so member-private information stays private to that member. The design is fail-closed: if the request context is ever missing, a query returns zero rows rather than leaking. No service connects to the database as a superuser (which would bypass row-level security); they connect as least-privilege roles that cannot bypass it.
This isn't left to convention. A blocking test (check_rls.py) refuses to let a
household-scoped table ship without its isolation policy, and a second copy of that check runs
continuously against the live production database.
Money is exact, append-only, and tamper-evident integer minor units · REVOKE UPDATE/DELETE + trigger · hash chain · gate: check_audit_chain.py
Financial records are stored as exact integer amounts (never floating-point) in an append-only ledger: the runtime role has no permission to update or delete a posted entry, a database trigger rejects mutation, and a correction is recorded as a new offsetting entry rather than an edit. On top of that, entries are linked in a per-period hash chain, so any out-of-band tampering is detectable. A verifier runs both in our tests and continuously against the live ledger, and alarms on any break.
One trust boundary controls who you are gateway strips + re-injects identity headers · gate: check_gateway_trust.py
Identity is decided in exactly one place. The gateway strips every inbound identity header from an incoming request and only re-injects them after verifying the caller against the identity service — so a client can never assert who it is by forging a header. Authentication is bearer-token based (no ambient session cookie), which removes a whole class of cross-site request attacks, and the edge additionally rate-limits sign-in and sign-up traffic. A test asserts that the strip list and the re-inject list stay in lockstep, so this boundary cannot silently regress.
Backups are off-host, and restores are rehearsed off-host encrypted backups · dedicated offline role · docs/DEPLOY_FIRST_BRINGUP.md
The database is backed up on a schedule and copied off the host, so a lost server does not mean lost data. Backups run under a dedicated, read-only, offline role — never a role used to serve live traffic. We rehearse restores against a fresh target so recovery is a practiced procedure, not a hope.
Secrets and signing keys are rotated on a practiced runbook encrypted secret store · documented rotation · docs/KEY_ROTATION_RUNBOOK.md
Secrets live only in an encrypted store or injected environment — never in source control — and a secret-scanning check runs over the codebase to keep it that way. Signing keys rotate on a schedule, and key rotation follows a written runbook so it can be done safely and repeatably.
Our security rules are enforced by tests that must be able to fail CI fitness functions with known-bad self-tests
The controls above are guarded by automated checks in our continuous-integration pipeline. Each such check ships with a deliberately broken fixture it must catch — because a test that cannot fail proves nothing. If a change were to weaken data isolation, the money ledger, the trust boundary, or secret hygiene, the build fails before it can reach production.
Who we share data with (sub-processors)
We rely on a small number of vetted third parties to run the service. We share only what each needs for its function.
| Provider | Purpose | Data | Location |
|---|---|---|---|
| Paddle | Merchant of Record — billing, checkout, payment processing, tax remittance | Billing contact; subscription & transaction records. Paddle handles card data directly — we never store it. | United Kingdom / global |
| Hetzner | Cloud hosting — the servers and primary database | All platform data at rest | Germany / Finland (EU) |
| Cloudflare R2 | Object storage — off-host backups and product assets/uploads | Encrypted backups; uploaded content | Global |
| Optional "Sign in with Google" | Email, display name, account identifier | United States / global | |
| Microsoft | Optional "Sign in with Microsoft" | Email, display name, account identifier | United States / global |
| Anthropic | AI processing for AI-assisted features (behind our egress chokepoint) | The text you submit to an AI feature. Images/documents never egress. | United States |
| AWS SES | Transactional email (password reset, email verification) | Email address; transactional message content | United States / configurable |
Reporting a vulnerability
If you believe you've found a security issue, we want to hear from you. Please email security@sterryk.com with enough detail to reproduce it. We ask that you give us a reasonable chance to investigate and fix before public disclosure, and that you avoid privacy violations, data destruction, and service disruption while testing. We will acknowledge your report and keep you updated as we work through it.