Read the last section first if you are evaluating Calcutab. It says what we do not have, which is the part a vendor usually makes you dig for.
This page is written for the person who has to sign off on us. It describes what is actually built, not what is planned.
The one that matters: your data cannot reach another customer
Every record carries the workspace it belongs to, and every query is scoped by it. That is the ordinary answer, and on its own it is only as good as the developer who last wrote a query.
So the database enforces it as well. Time entries, rates, Jira credentials and sign-offs reference their parent by a composite foreign key of (id, workspace). Postgres itself rejects a row that points into another customer's workspace: not a WHERE clause somebody can forget, but a constraint that makes the mistake impossible to store.
Three of the automated checks below exist to prove this, by trying it.
Access, and money
Four levels: platform operator, workspace owner, manager, member. Every page and every action checks on the server. Hiding a menu item is not access control, and we do not treat it as such; the checks run again even when the link was never shown.
Money is omitted, not hidden. A member cannot see any cost, including their own rate. The figures are not sent to their browser at all, so there is nothing to find in the page source.
Credentials and secrets
| What | How it is held |
|---|---|
| Jira API tokens, Slack tokens | AES-256-GCM, encrypted before reaching the database, versioned so keys can rotate. Never returned beyond the last four characters, never logged, never written into an audit row |
| Passwords, where a workspace uses them | Salted scrypt, with the cost stored alongside each hash so it can be raised. Never recoverable, by us or anyone |
| Invitation and password-reset links | Only the SHA-256 of the token is stored. A leaked database completes neither |
| The encryption key itself | Google Secret Manager, never in the repository |
Most workspaces have no password at all: sign-in is Google or Atlassian, so their provider's two-factor and offboarding rules apply and there is nothing for us to lose.
In transit, and in the browser
All traffic is TLS. Session cookies are Secure, HttpOnly, SameSite=Lax.
Every page carries a Content-Security-Policy built per request, with a fresh nonce: scripts are allowed by nonce rather than by unsafe-inline. frame-ancestors is none, so the app cannot be framed.
What is recorded
An append-only audit trail: rate changes, membership changes, settings, period sign-offs, and any timesheet edited on somebody else's behalf, with who, what, from what to what, and when. Exportable as CSV.
Rates are never edited. A change inserts a row with a new start date, so a report run last year still produces last year's figure.
Abuse
Writes and sign-in attempts are rate limited. Eight consecutive failed password attempts shut an account for fifteen minutes: passwords only, because locking the Google and Atlassian paths would let anyone deny a colleague access by guessing at their address.
Where your data is
Google Cloud Run in europe-west1 (Belgium); PostgreSQL on Neon in AWS eu-central-1 (Frankfurt). Backups stay in the EU. The complete sub-processor list is in the [DPA](/legal/dpa), section 7; it is the whole list, and changing it changes that document.
Checked before release
Fourteen automated checks run against a real deployment, not a mock, covering:
- a manager reaching into another workspace's membership, projects, reports and CSV export
- a member reaching the rate card, the audit export, or the operator's page
- a session signed with the wrong key
- unauthenticated access to pages that require one
- stack traces, deployment details and policy headers
The script refuses to run at all unless a minted session is first accepted: an earlier version reported eleven passes against what turned out to be empty redirect bodies, which is the kind of result that is worse than no result.
Alongside these: 936 unit tests, 233 end-to-end tests against a real browser and database, and 216 integration tests.
If something goes wrong
A written incident process covers who is told, in what order, and within what time. Health and error alerting reaches the operator if the service or its database becomes unreachable from two regions.
Report a vulnerability to support@calcutab.com. We will acknowledge within one working day. We do not currently offer a bounty.
What we do not have
Stated here rather than left for you to discover:
- No independent penetration test. The checks above are ours. A third-party test will be commissioned when a customer needs the report; we will not pretend one exists.
- No SOC 2 or ISO 27001. Neither is in progress. Both are a year's work and a five-figure cost, and we would rather say so than imply otherwise.
- No on-call rota. Alerting reaches one person. Outside working hours, recovery depends on that person.
- No contractual uptime or recovery-time commitment. The terms say this too.
- No customer-managed encryption keys, no SAML, no SCIM. OIDC per workspace is feasible and unbuilt; SAML needs a broker. Neither exists today.
- The legal documents have not been reviewed by external counsel. They are published as version 1.0 and describe what the software actually does; their legal sufficiency in your jurisdiction is not something we have had tested.
If one of these is a blocker for you, say so; it is better to know before you buy than after.