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.

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.
