Workspace isolation
Every company gets its own workspace, and every row of data carries its workspace's ID. Isolation is enforced by PostgreSQL row-level security inside the database itself, not just by the application code:
- The application connects as a database role with no superuser rights and no way to bypass row-level security.
- Each request sets the signed-in user's workspace for its own database transaction. With no workspace set, queries return nothing and writes are refused.
- The workspace comes only from the verified sign-in token, never from anything the browser sends.
- Links between records include the workspace ID, so data can't be attached to another company's records.
- An automated test fails the build if any table holding workspace data isn't protected.
Passwords and sign-in
- Passwords are at least 12 characters and are stored only as Argon2id hashes.
- Sign-in is rate limited. Repeated failures trigger a CAPTCHA (Cloudflare Turnstile) and alert us; failed sign-ins don't reveal whether an account exists.
- Sessions use a short-lived access token held in memory and a refresh token in an
HttpOnly,Secure,SameSite=Strictcookie that's replaced on every use. If an old refresh token is ever replayed, that session is shut down. - Sessions end after 30 days at most, however active they are.
- Password reset links are single use, expire after 30 minutes and are kept out of server logs. Resetting a password signs out every session.
- Disabling a user, changing their role or suspending a workspace takes effect on the very next request.
Roles and the audit log
Five roles (field, lead, office, editor and admin) decide what each person can see and do. Permissions are checked on the server for every request. Field users are never sent costs, prices or stock values, and labour costs and pay rates are limited to admins and editors.
Changes and sign-ins are written to an audit log that admins can search. The application can add entries but cannot edit or delete them.
Encryption and hosting
- All traffic uses HTTPS. Browsers are told to use HTTPS only (HSTS for two years).
- The service runs on Google Cloud in the us-east1 region (South Carolina, United States). Google Cloud encrypts stored disks by default.
- The web servers and the application run in containers as non-root users, on read-only file systems, with no extra privileges. The database is not reachable from the internet.
Files and photos
- Uploads are checked by their actual content, not their name. Executables, web pages, SVG and script files are refused.
- Photos are re-encoded on upload, which removes hidden metadata such as GPS location and camera details.
- File storage is per workspace, and downloads go through the same permission checks as the record they belong to.
Application hardening
- A strict Content Security Policy: no inline scripts, and the only outside script is Cloudflare's CAPTCHA.
- No analytics, advertising or tracking scripts, on this website or in the app.
- Every input is validated on the server, and database queries are parameterised.
- Errors never return internal details; each one carries a reference ID we can look up.
Backups and monitoring
- The database and uploaded files are backed up every night. Each backup is checked after it's made, and a test restore runs every week.
- We keep daily backups for two weeks, weekly backups for two months and monthly backups for six months.
- Health checks, error alerts and unusual-activity alerts (such as repeated failed sign-ins) go to the people who run the service.
Payments
Card payments are handled by Stripe. Your card number goes straight to Stripe and never touches our servers; we only keep the card brand and last four digits so you can tell which card is on file.
What we don't have yet
We'd rather you hear it from us:
- No independent certification such as SOC 2 or ISO 27001.
- No single sign-on (SSO) or two-factor authentication for workspace users yet.
- No hosting in Canada yet: data is stored in the United States.
- No uptime guarantee (SLA).
Reporting a vulnerability
If you think you've found a security problem, please email security@projectsystem.ca with the details and steps to reproduce it. Our security.txt has the same contact.
- Only test against a workspace you created yourself, and never access, change or delete other people's data.
- Don't run denial-of-service, spam or social-engineering tests.
- Give us a reasonable amount of time to fix the issue before telling anyone else.
We'll confirm we've received your report, keep you posted while we fix it and, if you'd like, thank you by name once it's resolved.