Skip to content

Security

What actually protects your data.

Every claim on this page names the mechanism behind it. A security page that says “bank-grade encryption” and moves on is telling you nothing you could check.

What is actually in place

All of the following is built and running today, and each one is covered by a test that tries to break it.

Customer data is stored only in the United Kingdom

The database is in London (eu-west-2) and it is the only place customer data is held. That is a constraint on the whole system rather than a hosting preference: every service we add has to be able to process data in the UK or EU, or we do not use it, and it has already ruled things out. It applies to error tracking, analytics, email delivery and support tooling equally.

Passwords are hashed with Argon2id

At the parameters OWASP currently recommends: 19 MiB of memory, two iterations. Argon2id is deliberately expensive in memory as well as time, which is what makes a stolen hash dump expensive to attack with specialised hardware. There is no path by which we could show you your own password, because we do not have it.

Session tokens are stored only as a peppered hash

Your browser holds a 256-bit random value; we hold an HMAC of it, keyed with a secret that lives in the environment rather than in the database. A stolen copy of the database therefore contains nothing that can be replayed as a login, because the key needed to check it was never in the database to steal.

Revocation is immediate, not eventual

Sessions are looked up on every request rather than trusted from a signed token. That costs one indexed read and buys the ability to actually end a session — changing your password signs out every other device on the spot, and tells you how many there were.

Tenant isolation is structural

The function that answers “which organisation is this?” takes no arguments at all. There is nothing a request could contain that would change the answer. Where an identifier does come from the address bar, it is never used alone — the query filters on it and on your organisation together, so somebody else’s identifier matches no row.

A request for someone else’s data returns 404, never 403

“Forbidden” would confirm the thing exists, which lets an outsider work out which app identifiers and link aliases are real, and therefore who our customers are. “Not found” says nothing at all.

API keys are shown once and stored hashed

A key belongs to one app and one environment and cannot be moved between them. We keep a hash and a short display prefix, so we genuinely cannot show you a key again — which is why rotation exists. Rotation issues the new key and kills the old one inside a single transaction.

IP addresses are never stored

An IP address is personal data under UK GDPR. We need to tell one visitor from another for rate limiting and, later, click counting; we do not need to know who they are. Addresses are salted and hashed as they arrive and the raw value is never written to disk — not in a log, not in an error report, not in a trace.

Two-factor authentication on every plan

TOTP from any authenticator app, with ten single-use backup codes you can download, copy and replace at any time. Included on the free plan, because the control that most reduces account takeover should not depend on what you pay.

Rate limiting on both the address and the account

Per-address alone is beaten by an attacker with many addresses; per-account alone is beaten by one common password tried against thousands of accounts. Both counters are always incremented, so tripping one cannot be used to avoid the other.

An append-only audit log

Sign-ins, failed sign-ins, password and address changes, two-factor enrolment, key and app lifecycle, and every membership change. The code that writes it exposes one function and that function inserts: there is no update helper and no delete helper anywhere in the codebase.

A nonce-based Content Security Policy

Only scripts carrying a single-use value issued for that one page load may run, so a script injected into a page is refused by the browser rather than by us noticing. Getting this right cost us static page caching, which we judged the better trade for a dashboard that holds API keys.

Nothing customer-facing is ever hard-deleted

Organisations, accounts and apps carry a deletion timestamp rather than being removed, and links are archived rather than dropped. A link printed on packaging has to keep resolving. The database enforces it: the foreign keys refuse a hard delete outright.

Security and compliance questions

Ask us anything on this page and anything that is not.

If you are evaluating us and need detail — how a specific control works, what a sub-processor does, what our answer is to a question on your own security questionnaire — write to [email protected] and a person will answer it directly. We would rather tell you exactly where we stand than have you guess from a page.

The complete list of third parties who could touch customer data is published as our sub-processor list. It is short, because the product is small and we would like to keep it that way.

Reporting a vulnerability

Write to [email protected] with enough detail to reproduce it. We acknowledge within two working days and tell you what we are doing about it. We will not threaten you.

Please do not run automated scanners against the service, access an account that is not yours, or test anything that would degrade the service for other people. Everything else, we would genuinely rather know.