Trust & Security

Your members' money. Our first problem.

Gym software holds the roster, the card tokens and the takings. This page says how Anchor protects all three — and which of them we have not finished yet.

How it is built

Security is the foundation, not the last coat.

None of the four below is a policy. Each is a decision made in the schema or the middleware, where it holds even when somebody writes a bug — which somebody eventually will.

Isolation

One gym cannot see another, even if we write a bug

Every table carries the gym it belongs to, and Postgres row-level security enforces it — policies forced on, with the application connecting as a non-superuser role that cannot bypass one. A forgotten WHERE clause in application code returns nothing rather than somebody else’s members. That is the difference between isolation as a rule and isolation as a habit.

Held to a cross-tenant test matrix that runs against real Postgres on every commit, not a mock.

Money

Every cent is an integer, and nothing is ever deleted

Money is integer cents throughout — no floating point anywhere near a balance. Every financial movement is a double-entry transaction written through a single chokepoint, and the database itself rejects a transaction whose entries do not sum to zero, at commit time. Financial records are append-only: there is no UPDATE and no DELETE path to a posted entry.

Held to an unbalanced-transaction-rejected-at-commit test and append-only immutability suites.

Cards

Card numbers never reach our servers

Payments run on Stripe Connect into your own connected account. Card details are tokenised in the browser and never transit or rest on Anchor infrastructure, which keeps us in the lightest PCI tier — SAQ A — and keeps Stripe as the regulated money party. Your merchant relationship and your card tokens are yours, and they leave with you if you go.

Money enters the ledger only through Stripe’s confirmed-webhook path, processed exactly once.

Access

Hiding a button is not security

Every action re-checks the acting user’s permission on the server, scoped to their own gym — five roles, keyed by permission rather than by role name. Passwords are hashed with argon2id, sessions are opaque and held server-side, and two-factor is TOTP with recovery codes. Every action lands in an append-only audit log that is itself row-level-security scoped.

Held to a permission matrix per module and a row-level-security sweep across every table.

The request path

What every request passes through.

TransportTLS everywhere. In production the service fails closed at startup rather than serving plaintext.
Rate limitingPer-IP and per-account, so one noisy client cannot spend everybody else’s capacity.
CSRFBrowser-origin checking on state-changing requests, using the standard library rather than a bolt-on.
HeadersA security header set on every response, plus a hard cap on request body size.
ObservabilityRequest IDs through the whole chain, JSON access logs, and panic recovery that never leaks a stack trace to a caller.
Client IPsResolved only through proxies we explicitly trust, so a header cannot forge an origin.
SecretsHeld behind a secrets source, never in the repository. CI scans every commit for secrets and known vulnerabilities.
AnomaliesIn-process detectors watch the audit log for patterns that should not happen, and raise them.
Where we actually stand

11 live today. The rest, dated when it's done.

A trust page that lists only wins is marketing. The amber half is the part worth reading — and we would rather you found these gaps here than three weeks into a procurement questionnaire.

Tenant isolation by row-level securityLive
Append-only financial ledger, integer centsLive
PCI SAQ A — cards never touch our serversLive
argon2id password hashingLive
TOTP two-factor with recovery codesLive
Server-enforced permissions across five rolesLive
Append-only audit log with anomaly detectionLive
Encryption in transit and at restLive
Secret and dependency scanning in CILive
Offline-first door access during an outageLive
Full data export on demand, no exit feeLive
Public status page with uptime historyNot yet
SOC 2 Type IIThe audit has not begun. We will publish the report date when it has, and not before.Not yet
Single sign-on (SAML / OIDC)Not yet
Data processing agreement & subprocessor listNot yet
Published penetration-test summaryNot yet
Leaving

The best proof your data is yours is how easily you can take it.

One click exports everything — members, plans, billing history, visits, the Kids Club roster and notes, all of it — at any time, without asking us, without an exit fee and without a retention call. Your Stripe connected account and your card tokens stay yours, so your members never re-enter a card in order to leave us either.

We would rather compete on being worth staying with than on being difficult to leave.