Security and tenant separation
HR data is among the most sensitive a business holds. This page describes what CirroPeople actually does — the mechanisms, not adjectives.
Tenant separation
CirroPeople is multi-tenant by design. Separating one customer from another is the single property we treat as non-negotiable, and it is enforced in more than one layer.
Ownership in the schema
Every business-owned record carries a required organization relation. There is no table where a row can exist without an owner.
Scoped data access
All reads and writes pass through a repository that mechanically applies the organization filter. A record fetched by ID is looked up by ID and organization, so another tenant’s ID simply returns "not found".
Membership proven per request
The organization address in the URL is used only to look the organization up. Access requires an active membership row, re-read from the database on every request.
Database rules deny by default
The browser is never given a database token. PocketBase’s own API rules are closed to everyone but the service account, so no cross-tenant read is reachable over the database API at all.
Isolation is tested
The test suite creates two organizations and asserts that one cannot reach the other’s records, documents, payroll, searches or exports. A failure there blocks release.
Access control
Two independent role systems that never blur into one another.
Per-organization roles
Owner, HR administrator, manager, finance, employee, intern and auditor — held per organization. The same person can hold different roles in different businesses.
Separate platform roles
Platform administration is a distinct set of roles on the user account. Being an owner of a customer organization grants no platform access whatsoever.
No privilege escalation
An administrator can never grant a permission they do not themselves hold, and the last remaining platform owner and organization owner cannot be removed or demoted.
Least privilege on approvals
Nobody can approve their own leave, and finalized performance reviews and payroll runs become immutable.
Sensitive data
Salary, banking, identifiers and health information are treated differently from a name and a job title.
Split storage
Sensitive identifiers and banking details live in a separate record behind their own permission, so they never ride along on a directory listing or an export.
Masked by default
Without the specific permission, sensitive fields are masked on the server. The real values are never sent to the browser and then hidden with CSS.
Elevated access is deliberate
Platform support staff searching across tenants see masked data unless they hold an explicit elevated permission and supply a reason — and every such access is recorded.
Protected downloads
Files are never served from a guessable URL. Each download is authorised against the caller’s organization and permissions at request time, then logged.
Application security
The ordinary web-application defences, applied consistently.
Session handling
Signed, HTTP-only, same-site cookies. The browser never holds a credential the database would accept, so a stolen cookie cannot be replayed against the data layer.
CSRF protection
Origin checks plus a double-submit token on every state-changing request.
Input validation
Every input is validated with a schema on the server. Database filters use parameter binding, so user input cannot alter a query.
Security headers
A nonce-based Content-Security-Policy, plus X-Content-Type-Options, Referrer-Policy, Permissions-Policy and frame-ancestors restrictions.
Upload validation
File size and MIME type are checked against configured limits before anything is stored.
Safe errors
Users see a helpful message; stack traces and internal detail never leave the server.
Auditability
If something happened, you should be able to find out who did it and when.
Broad coverage
Registrations, sign-ins and failures, invitations, role changes, business creation, payment exemptions, plan changes, suspensions, impersonation, employee changes, payroll and document access, exports, billing webhooks and platform settings.
Secrets are redacted
Audit payloads pass through a redaction layer, so no password, token, cookie, authorization header or provider secret can be written to the trail.
Append-only
Audit entries are written once and never edited by the application.
Exportable
Organizations on the appropriate plan can export their own audit log; platform auditors can export across the platform.
Billing integrity
Money-related state changes are only trusted from a verified source.
Webhooks are verified
Provider signatures are checked before a payload is read, with a replay window enforced.
Idempotent processing
Each provider event is recorded under a unique key, so a redelivered webhook cannot apply twice.
Redirects prove nothing
A subscription is never activated because a browser returned from a checkout page. Only a verified webhook activates a paid plan.
Exemptions are recorded
A business created without payment always records who authorised it, why, and against which subscription.
Reporting a vulnerability
If you believe you have found a security issue, please contact us before disclosing it publicly. Include the steps to reproduce and what you were able to access. We will acknowledge your report, keep you updated while we investigate, and credit you if you would like us to.