Security
The walls are in the database, where the application cannot forget them.
Payroll is the most sensitive data a small company owns. Most systems protect it in the interface, which means a single missed check is a leak. Momiji puts the boundary one layer lower, in the database itself, and then runs a suite against production to re-prove it after every change. Here is exactly how, and exactly where the limits are.
- 01
Tenancy enforced in the database
Row-level security is enabled on every table in the application schema, without exception. Even the handful that hold no tenant data at all have it switched on and carry no policy, which means the database refuses them to every signed-in user rather than being trusted to go unasked. Your company's rows are separated by the database itself, not by a filter the application remembers to apply. References between companies are impossible by schema: keys are composite over the record and the company together, so a row in one company cannot point at a record in another, even by mistake.
- 02
Salary is revoked, not hidden
Salary and pay rate are revoked at the database column level. A direct read of the employee table fails for every signed-in user, including your HR admins. Compensation is released only through a gated call that checks People or Payroll authority first and records who read it and when, never the amount. If that record cannot be written the release still goes ahead, because a logging failure must never stop a pay run, and the system raises an integrity flag an administrator has to clear. New columns on that table are unreadable until explicitly granted: a fail-closed default we have had break a payroll run rather than leak a field.
- 03
Numbers we never hold
There is no SIN column and no bank account, transit, or institution column in the schema. They were removed early and never returned, so we hold no payment destination for anyone: net pay leaves your own bank against a printed wire sheet. A SIN is keyed in only at the moment a T4 slip is generated, appears on that slip in your private vault, and is never written to a database field.
- 04
An audit log with no eraser
The audit log carries a read policy and nothing else. There is no update path and no delete path from the application, for any role, including your own owner account. Entries are written only by a privileged database function that verifies membership before it inserts. What happened stays on the record.
- 05
A payroll register that cannot be edited
Once a run is committed, its register rows are immutable. There is no update or delete path from the application: an owner attempting either changes nothing, which our test suite asserts directly rather than assuming. A correction is a new reversing run that negates the original and leaves both on the record, the way an accountant would expect.
- 06
Every figure carries its own provenance
Each run and each row is stamped with the calculation engine version and the CRA tax table edition in force when it was produced, so a figure can be reproduced years later against the rules that actually applied. Preparer and approver are recorded separately, and where one person is both, the system forces a written self-approval note instead of allowing a silent one.
Every claim above is re-proven against production, on every change.
Security that is only reviewed once is security as of that day. Momiji ships with 66 automated suites, 2,987 assertions, that run against the production schema itself rather than a copy: tenant isolation, privilege escalation, confidential record visibility, and the payroll lifecycle. They run inside a transaction that is rolled back, so they test the real thing without touching it.
One of them signs in as your owner account, attempts to edit and then delete a committed payroll row, and asserts that nothing changed. That is the difference between a policy and a proof.
100%
of tables under row-level security
66
automated security suites
2,987
assertions every run
0
SIN or bank columns
How we review ourselves
Five rounds of adversarial review, run internally.
Before each major surface shipped, we ran a security round: a reviewer with no knowledge of how the thing was built, briefed only to break it. Five rounds so far, across tenancy, onboarding, expenses, payroll, and the people record.
None of them passed cleanly the first time, and we think that is the point. The third round returned a conditional failure: an ownership check that fell open when an identifier was null, which would have let a confidential record be written by someone who could not see it. It was fixed in a versioned migration with 42 assertions covering it, and the assertions still run today. The fourth round found compensation readable as a plain column, which is why salary is now revoked at the column level.
Every finding is recorded with a severity and closed by a migration, not a patch note. We say internal because that is what it is. It is a real practice and it has caught real defects, but it is not an external assurance report, and we will not let the word “audit” do work it has not earned.
A promise you cannot check is just marketing.
What we do not claim
The gaps, before you have to find them.
Every security page lists strengths. This is the part that tells you what we have not built, because the fastest way to lose your trust is to have you discover one of these yourself.
We have not been audited by anyone else
Momiji holds no SOC 2 report and has not had an external penetration test. Our security reviews are run internally, and we would rather say so than let a phrase imply otherwise. When a customer's own auditor wants to test us, we will take the engagement and publish what it finds.
Two-factor is available, not mandatory
Time-based one-time-password authentication is enabled and your people can enrol today, and every sign-in, setup, and invitation form is behind a CAPTCHA. We do not yet force two-factor on administrators, so we do not claim enforced MFA.
Some records are deliberately permanent
Receipts and generated T4 slips are retained on purpose, because expense and payroll records need to survive the person who filed them. The consequence is real and worth stating: a document filed in error cannot be deleted through the product today. If you need something removed, it is a conversation with us, not a button.
Documents are read by a model, off our servers
Setup and receipt extraction send your uploaded files to Anthropic, our named subprocessor, using our own enterprise key. Commercial API data is not used to train models. Spreadsheets and CSV files are scanned first: any column holding a SIN, bank account, transit, institution, routing number, or IBAN has its values replaced with a placeholder before the file leaves our servers. PDFs and photographs are sent whole, because reading them is the entire job we are asking the model to do. The extractor is also instructed to return none of those fields. So the honest summary is that structured files are redacted in advance and scanned ones are not, and you should know which of the two you are uploading.
Subprocessors
Who else touches your data.
Momiji is not the only company involved in running it. These are every one of them, what each holds, and why it is there. If a vendor is not on this list, it does not receive your data.
| Subprocessor | What they hold | Why |
|---|---|---|
| Supabase | The application database and the files kept with it: your directory, time, leave, expense and payroll records, and every uploaded document, pay stub, T4 slip, receipt, avatar and signature. Sign-in itself runs here too, for both the web app and the phone. | It is the database, the authentication provider, and the file store the product is built on. |
| Vercel | No stored records. Requests to both the product and this site pass through its network, and it runs the scheduled jobs on time: the nightly document backup, the location retention sweep, the weekly receipt cleanup, usage reporting, filing obligations, and the notification sender. | Hosting and the scheduler. |
| Cloudflare R2 | A complete second copy of every document in the product. Every pay stub, T4 slip, receipt and signature that exists in Momiji exists here as well, refreshed daily. This is the broadest access on the list and we would rather say so plainly than let it sit inside a line about hosting. | Supabase's own backups cover the database and exclude stored files, so a restore would otherwise leave your records and your documents disagreeing about what exists. The copy is deliberately held at a different vendor: a backup that shares a failure domain with the thing it protects is not a backup. |
| Cloudflare Turnstile | A challenge token and the connecting IP address at the moment someone signs in, sets up an account, or accepts an invitation. Nothing about the account itself. | The invisible CAPTCHA in front of every sign-in, setup, and invitation form, on the web and on the phone. |
| Anthropic | The documents you upload during setup and receipt extraction, for the length of the request. Spreadsheets are redacted before they are sent; PDFs and photographs are sent whole, which is set out in full above. | Reads uploaded documents and proposes structured data for you to confirm. Commercial API data is not used to train models. |
| Resend | Recipient email addresses and the wording of the notifications we send, and the sign-in mail the authentication system issues, such as a password reset. Payroll notifications are written to carry no pay figures at all: they say a statement is ready and link to it behind a sign-in, so the amount stays in the vault rather than travelling through an inbox. | Delivers invitations, sign-in mail, and notifications. |
| StripePayments and subscription billing | Your name, email, and payment method when you subscribe, plus, once metered billing is switched on for a subscription, four usage quantities per period: employees, sites, provinces, and a weekly-frequency uplift. A reported meter event is a customer id and a number, and nothing else. Never an employee's name, pay, or records of any kind. Card details never touch Momiji servers; they go straight to Stripe. | Takes the subscription payment and bills usage above the plan's included allowance. |
| SentryMobile crash reports | Crash and error reports from the iOS and Android app, scrubbed on the device before they leave it. Identity is an allowlist of exactly three fields, a user id, an organization id and a role, so a crash report carries no name, no email, and no network address as the person it belongs to. Screenshots, view hierarchies, session replay and captured request bodies are all off. Money-shaped strings, coordinates and URL query strings are redacted by pattern, values are redacted by field name, and console, touch and typing breadcrumbs are dropped outright. | Tells us the app crashed and where, without telling us whose pay was on the screen when it did. |
| PostHogAnalytics, app and marketing site | Two separate streams, and the difference is the point. On these public marketing pages: anonymous pageviews, clicks, and recordings of the page itself, with query strings stripped and no identify call anywhere in the code. In the mobile app: a signed-in user's id, their organization id, their role, the names of screens they open, and the standard app and handset properties any analytics tool collects. Screen names only, never the parameters inside them; touches are not captured and no screen is replayed. No employee record content, no pay figures, no punch data, no document content, and no analytics of any kind inside the web app. One honest edge, since we would rather state it than have you infer it: we send PostHog no location, but it resolves the network address an event arrives from to an approximate city and country at its end, and we do not use the result. | Shows which parts of this site people read and which parts of the app get used. |
| ExpoMobile app delivery | Over-the-air updates to the app's JavaScript, and the push token your device is issued. Notification content passes through Expo's push service on its way to Apple or Google. | Ships app fixes without waiting on a store review, and relays notifications. |
| Apple and GooglePush delivery | The notification itself, on its way to a lock screen: a title, one short sentence, and a route into the app. Never a pay amount, a balance, a day count, or even the title of a document waiting for a signature. | Their push services are the only way to reach a locked phone. A lock screen can be read by anyone standing near it, which is why the payload is written the way it is. |
| Microsoft 365Our inbox | Whatever you choose to send to hello@mymomiji.com, and our reply. | It is our email. |
All of them are established providers under their own security and privacy terms. We hold no other processor, and we do not sell or share your data with anyone. If we add one, this table changes before the vendor does.
Asking harder questions
If you are running a security review, or your auditor is, write to hello@mymomiji.com and ask for the detail behind any line on this page. We will show you the migration, the policy, or the test that backs it. If the honest answer is that something is not built, that is the answer you will get.
Proof covers how the rest of Momiji is checked, including the defect that checking found in our own payroll engine. Privacy covers what this site collects. Terms covers the agreement.