Skip to content

Security

Client tax records carry weight. We handle them like they do.

Four lines we won't cross, six controls underneath, and a published stack so "where does our client's row actually live?" has a one-line answer for the firm partner asking.

A close-up of a fingertip on a fingerprint reader, lit by soft daylight.

What we lock in

Four lines, plainly stated.

No bank-login scraping.

We never ask for client bank passwords or proxy a banking app. The only input is the PDF the bank already sends.

PDFs aren't persisted.

Statements are parsed in-request and dropped. Only the encrypted categorised rows live on.

Column-level encryption.

AES-256-GCM at the column for tax numbers, ID numbers, amounts, descriptions. Not TDE-only.

Firm-wide audit log.

Every action your staff take lands in a sealed log scoped to your firm. Open it any time.

Underneath the four

Six guards keeping each line honest.

Column-grade AES-256-GCM at rest

Tax numbers, ID numbers, transaction amounts, descriptions, counterparties, and tax workings are encrypted column-by-column. A leaked database dump on its own is useless. The master key lives in Vercel project secrets, never in the source tree.

Per-user query isolation

Each read against the database is scoped by the authenticated user id from the session. An internal bug can't quietly cross account boundaries; the sealed log catches any read that tried.

Sealed log, joined by hash

Every move on your account is logged with timestamp, actor, target, and a chained hash. Updates and deletes raise an exception at the database, not a silent overwrite. The chain is what makes 'we sealed this' a verifiable claim, not a marketing line.

Passkey-only sign-in

No passwords to lose. WebAuthn passkeys via Better Auth — device-bound, biometric-gated — with an email OTP fallback. Sensitive in-app moves (uploading a new client statement, exporting an income statement) force a fresh authentication.

Statement PDFs never persisted

PDFs are processed in-request and dropped before the response returns — the parser pulls the lines, then the buffer is discarded. Only the derived, encrypted rows live on. Nothing PDF-shaped sits in our infrastructure waiting to leak.

Logging without your numbers

Sentry and our app logs run a scrubber over every payload before it leaves the process: ID numbers, tax numbers, amounts, descriptions — all wiped at the boundary. Stack traces never carry your data with them.

The stack

The exact rooms your row sits in.

Most local SaaS shops either quietly send tax data offshore or stay deliberately fuzzy about it. We'd rather post the actual stack — including where it is now, and where it's moving to.

Database
Postgres on Neon · eu-central-1 today, moving to the SA region the moment Neon opens it
Statement PDFs
Processed in-request, never persisted — no long-term object store
Application runtime
Next.js 15 on Vercel · region fra1 (Frankfurt)
Transport
TLS 1.3 only
Sign-in
Better Auth · WebAuthn passkey + email OTP
Bucketing engine
OpenAI gpt-4o (Responses API) · not trained on your data

Spotted a hole?

Send it to security@taxup.app. Forty-eight-hour acknowledgement promise. A formal bug-bounty programme follows public launch; in the meantime we'll thank you by name on this page if you say it's okay.

Check us. Then sign up.

We publish the model on purpose — read it, poke at it, and if you're comfortable, open a firm account.