This page names the actual mechanism, not a badge. If you're the one who has to sign off on this before your company gives FORA any data, this is written for you.
React (Vite) on the frontend. Every read and write goes through a Vercel serverless function in api/, never straight from the browser to the database. Supabase-hosted Postgres for storage, with its own SOC 2 Type 2 report. No ORM abstracting away what a query actually touches.
Multi-tenant isolation here is not a Postgres policy doing the heavy lifting. It's application code, with Postgres itself as a deny-by-default backstop underneath it. Both matter, and they're not the same thing.
Every request must carry a session token signed server-side with HMAC-SHA256. The signing secret lives only in the server environment, so a token can't be forged from the browser or replayed after tampering with its payload.
The signed session carries the caller's company id. Every query in every serverless function scopes explicitly to it. This, not a database policy, is where one company's data actually gets kept out of another's.
Row Level Security is enabled on every table with no policies defined. A request using the public/anon key is refused unconditionally by Postgres itself, before any application code runs at all — a backstop under the real boundary above, not a substitute for it.
The service-role credential that can bypass RLS exists only inside the Vercel serverless environment. It is never in a client bundle, never in a repo, never shipped anywhere a browser can read it.
Documents and photos sit in private storage buckets. Every link the app hands out is signed and expires; there is no permanent public URL for a private object, and code that would build one is checked for on a recurring schedule.
Checkout runs entirely on Stripe's hosted page. FORA never receives, sees or stores a card number, and an incoming Stripe webhook is refused unless its signature verifies against our signing secret.
A security page written once at launch and never revisited is a claim, not a control. This one gets re-verified against the live system on a fixed schedule, across six areas:
| Check | What it verifies |
|---|---|
| Storage exposure | Every storage bucket except the one deliberately public asset bucket is confirmed private, tested live against unauthenticated requests, not just read from config. |
| RLS coverage | Row Level Security is confirmed enabled with zero policies on every table in the schema, including any added since the last pass. |
| Secret hygiene | Tracked files are scanned for hardcoded credentials, and env-file coverage in .gitignore is confirmed. |
| Signed-URL discipline | Code paths that build a storage URL are checked for one that constructs an unsigned public URL for a private object — a specific bug class, checked because it has recurred before. |
| External surface | Vercel preview-deployment access control and Stripe webhook signature verification, since this exposure lives in connected services, not in this repository's source. |
| Adversarial review | A broader pass across authentication, authorization, injection, cross-tenant data access, file-upload handling, rate limiting and dependency vulnerabilities, not limited to one checklist. |
Findings that only ever tighten access and are trivially reversible get fixed on the spot. Anything that's a real access-control judgment call, touches production availability, or means a credential needs rotating gets reported for a human decision, not silently patched.
Full detail on what's collected, why, and who it's shared with lives in the actual documents. This is the short version.
The Postgres provider FORA runs on is independently audited to SOC 2 Type 2. That's a third party checking the checker.
Your records and uploaded files live in a database hosted in Canada, not wherever a default region happened to land.
Personal information is handled under Alberta's Personal Information Protection Act and the federal PIPEDA, not whichever rule happens to be convenient.
No summary replaces the real documents. Read the Privacy Policy and Terms of Use in full.
Export any time. Nothing is sold, and nothing is used to train a model outside your own account's Brain.
Hosting and AI processing run through the specific providers named in our Privacy Policy, not left vague.
If you've got a specific question about where your data lives or how it's protected, you'll get a straight answer by email, not a canned one from a security questionnaire template.