Security
A tax return is one of the most sensitive documents a person owns. This page describes what we actually do to protect it — specifically enough that each statement could be shown to be false.
Every claim below is tied to an automated test in our codebase. If a control stops working, the test fails and the change cannot ship. We hold no security certification of our own, and nothing here should be read as one.
Your data at rest
Sensitive fields are encrypted individually, under a key that belongs to your household alone.
- Field-level encryption, per-family keys
- Social Security numbers, dates of birth, names and account numbers are encrypted with AES-256-GCM under a data key unique to your family, which is itself wrapped by a key held in Google Cloud KMS. One family's key cannot read another family's data.
- Decryption fails loudly, never silently
- If an encrypted value cannot be decrypted, the request fails or the field is visibly marked unavailable. It is never replaced with a plausible-looking placeholder that could be mistaken for your data.
Separation between households
Your return is reachable only from your own account — enforced in the database, not only in application code.
- Every endpoint declares who may reach it
- Each of our API routes must declare an authentication posture from a fixed set. A route that declares none fails our build and cannot ship.
- Queries are anchored to your household
- Database reads and writes against household-scoped tables must name the household they belong to. Exceptions are enumerated in a file and reviewed, not assumed.
- Tested by attempting the attack
- We run an automated suite that signs in as one household and attempts to reach another's records across every endpoint. It also verifies that a household can still reach its OWN data, so a service that simply errored could not pass by refusing everything.
What we never write down
The cheapest data to protect is the data that was never stored.
- No sensitive data in application logs
- Log lines and error reports are scrubbed of anything shaped like an SSN, EIN, card number, address, phone number or email before they leave the process. An automated check inspects real API responses for those shapes.
- No card data, ever
- Payment details are handled entirely by Stripe and never reach our servers. Our database has no column capable of holding a card number.
- Uploaded documents are not kept
- A PDF you upload is parsed and deleted. We keep the figures we extracted, not the file.
Getting in
Sign-in is the door, so it is the part we hold most tightly.
- No passwords exist to steal
- Sign-in is Google OAuth only. YearWise never receives, stores or verifies a password, so there is no password database to breach.
- Multi-factor is required, not offered
- Every account must enrol an authenticator app. Household-scoped data cannot be reached by a session that has not completed the second factor.
- Idle sessions end on their own
- A session left idle for 15 minutes is signed out automatically — a threshold NIST associates with its highest assurance level, where 30 minutes would be permitted.
How we run it
Controls that live in a runbook get forgotten. These are enforced by the pipeline.
- Development shortcuts cannot reach production
- Settings that are convenient locally and wrong in production — placeholder addresses, local URLs, sandbox mail senders — are refused at build time rather than shipped.
- Each service runs with only what it needs
- Our services run under dedicated, least-privilege identities rather than a shared default account, and a deploy that would use the default account fails.
- Documents are decoded apart from our credentials
- The PDFs and images you upload are opened in a separate service that has no permissions, holds no secrets, accepts requests only from our own application, and has no outbound network access — so a file built to exploit a document decoder lands somewhere that leads nowhere.
- Access to the beta is on an explicit list
- During the closed beta, sign-in is refused for any address not explicitly invited — and refused if the list itself cannot be read, rather than opening the door.
What is not yet in force
These are real gaps, described as they are. Each was checked against the running system rather than copied from a document — a written posture goes stale in the direction that flatters us, so an entry leaves this list only when a measurement says it can.
- Row-level security is not yet the last line
The database enforces per-household row security on every request that carries a household context, and that enforcement is on in production. The shared application role can still bypass it, so the application layer — not the database — remains the primary boundary between households. Making the database the final authority is a planned, one-way change we have not made yet.
What protects you meanwhile
- Every request that reaches household data is checked against the households your sign-in is authorised for, in the application, before any data is read.
- Attempts to reach another household's data are exercised as tests on every merge, so the separation is re-proven rather than assumed.
What is next: Making the database the final authority, after a monitored period with the enforcement watched in production.
- Uploads are not virus-scanned
We have chosen not to run anti-virus scanning on uploaded files. An uploaded file is a tax form we read and discard, never a file we keep or hand to anyone, so the protections that matter are the ones below — on how a file is opened, and on where it can go afterwards.
What protects you meanwhile
- The PDFs and images you upload are decoded only inside a separate service with no permissions, no secrets and no outbound network — never in the application that holds our credentials.
- The uploaded file itself is never stored. Fields are extracted and the temporary copy is deleted in the same operation.
- An uploaded file is never served back to anyone — there is no download of what you uploaded, only of documents we generate.
- Every uploaded file passes size, file-type signature and active-content checks before any decoder is allowed to open it.
- There is no web application firewall
No WAF or geographic restriction sits in front of the application. Rate limiting and authentication are enforced by the application itself.
What protects you meanwhile
- Sensitive endpoints are rate-limited by the application itself.
- Multi-factor authentication is mandatory: a session that has not completed a second factor cannot reach household data at all.
- The service is invitation-only during the closed beta. An address that has not been invited is refused at the point an account would be created, not merely at the sign-in page.
What is next: A dedicated filtering layer in front of the application, pending a capacity increase from our cloud provider.
- Application database access is not independently logged
Reads and writes made by the application are not recorded by the database itself today. Engineer access is: the two roles a person can use are audited by the database, with statement values masked, so that trail does not depend on our code being honest about what it did. The application roles are exempted because of a defect in our database provider's auditing, which fails any audited statement of 8 KB or more; the provider has confirmed the cause and has a fix in development.
What protects you meanwhile
- An engineer can reach your data only through a consent-gated path that writes a record before the access happens, and that path is audited by the database.
- Every such access is recorded in an append-only log that survives account deletion and holds identifiers, never your data.
What is next: Re-enabling database-level logging for the application roles when the provider ships the fix, proven by effect rather than by the setting.
- We hold no third-party security certification
YearWise has not completed any third-party security audit or attestation. Our infrastructure providers hold their own certifications; those cover their platforms and say nothing about this application.
Reporting a security problem
If you believe you have found a vulnerability, or anything that looks like one, email security@taxmint.ai. We would rather hear it from you than from an incident.
- What happens after you email
- We acknowledge every report within three business days. We are a small team handling taxpayer data, so a report reaches the people who can act on it rather than a queue.
- Safe harbor for good-faith research
- We will not pursue action against good-faith research that stays within your own account's data. Testing that reaches another person's data is outside it, even when the intent is good.
- Credit, not a bounty
- We do not currently run a paid bounty. If you would like credit for a report, we will give it.
The same policy is published in machine-readable form, for scanners and for anyone who wants the canonical record, at /.well-known/security.txt (RFC 9116).