Most multi-tenant software keeps companies apart with a WHERE clause and good intentions. One forgotten filter in one query and a competitor sees your customer list. Scanified pushes the boundary down into the database, where forgetting is not possible.
Every tenant table has RLS enabled with FORCE, so the policy applies to the table owner too. There is no privileged path that skips it.
The application connects as a role created without BYPASSRLS and without schema rights. Even a full SQL injection cannot read another company’s rows, because the role is not permitted to.
The tenant is set inside the transaction and cleared with it. A connection returned to the pool carries nothing from the request before it.
Every table that holds customer data carries an organisation column and a policy that compares it to a value set on the connection at the start of each request. The policy is declared FORCE ROW LEVEL SECURITY, which is the part most implementations miss: without it, the role that owns the table is exempt from its own rules.
The application does not connect as an administrator. It uses a dedicated role created without BYPASSRLS, without CREATEDB, and without rights to alter schema. This is deliberate and it is the reason the guarantee holds: an attacker who achieves arbitrary SQL execution through the application still cannot read a row belonging to another company, because the role executing the query has never been permitted to.
This holds by construction, not by promise. Row-level security isFORCEd on every tenant table, so it binds even the table’s owner, and the application role has never been granted the right to bypass it — the refusal comes from the database, not from application code remembering to check. An automated two-organisation crossing suite is listed below with the other things we have not done yet, because a test we have not written is not something we will describe as if we run it.
Sign-in is handled by Supabase Auth. Passwords are hashed with bcrypt and never reach our application code. Sessions are carried in httpOnly, secure, SameSite cookies which JavaScript on the page cannot read.
Permissions are read from the database on every request rather than trusted from the token. Deactivating a user takes effect on their next action, not whenever their token happens to expire.
The mobile apps hold a token in the operating system’s secure storage — Keychain on iOS, EncryptedSharedPreferences on Android — and present it as a bearer token. That token is validated against the auth server on every call. No shared secret and no service key is ever shipped to a device.
Four roles: owner, admin, manager and user. A driver can scan and see their own work. A manager can settle discrepancies. Administrative actions — pricing, team, deleting anything — require admin or owner. Role is checked on the server for every action, not hidden in the interface, so a modified client gets a refusal rather than a result.
Invitations are single-use tokens tied to one email address and one organisation. Creating an account without an invitation or a company name gets you an account with no company attached, which can see nothing at all.
Scans and custody changes are append-only. A correction is a new entry that references the entry it corrects, and both remain. This is a security property as much as an accounting one: there is no operation in the product that makes a past event disappear, so a dispute in November can still be settled with the record from March.
Every scan carries who submitted it, when the device recorded it, when the server received it, and — where the device allows it — where. Duplicate submissions are recognised and discarded, so a replayed offline batch cannot inflate a bill.
Data is stored in Postgres on Supabase, encrypted at rest. Traffic is TLS 1.2 or better, end to end. The web application runs on Netlify. Secrets live in the platform’s encrypted environment store and are not present in the repository. The full list of who else touches your data, and what each one sees, is at /legal/subprocessors.
Scanified is a young product and it would be easy to imply more than is true, so plainly: we do not hold a SOC 2 report, we have not commissioned a third-party penetration test, we do not yet offer single sign-on, and we have not yet automated the two-organisation isolation test described above — the policies are live; the suite that proves them on every build is still on the list. A nightly encrypted database backup is built and scheduled, but we do not yet have a verified successful run or a completed restore drill, so we are not calling backups solved until we can. The mobile app’s on-device token encryption is written and tested but has not finished rolling out to every installed copy of the app yet. We are not going to put badges on this page that we have not earned.
What we can offer today is a description precise enough to be checked, an isolation model that fails closed, and a straight answer to any question your IT team wants to send.
Email security@scanified.com. Tell us what you found and how to reproduce it. We will confirm receipt within two business days, and we will not pursue anyone who reports a genuine issue in good faith and gives us a reasonable window to fix it before telling anyone else — 90 days is reasonable unless we ask you for more, and we will tell you why if we do.
In scope: scanified.com, the Scanified mobile app, and the API behind both. Out of scope: social engineering against our team, physical access, and anything that disrupts the service for other people — a proof of concept against your own test account is worth more to us than a load test against production.
If your IT team has a security review form, we will fill it in properly rather than leaving it to a salesperson.
security@scanified.com