TRUST

Security

How access is decided, what cannot be changed after the fact, and what we have actually tested rather than intended.

Permission is decided by the runtime, not by the screen

Every action checks the permission model before it runs. The console hides what you cannot do, but hiding is a convenience — the same request made directly is refused by the same check, and the refusal is written to the audit trail with who asked and what for.

  • Deny by default. A capability nobody was granted is held by nobody.
  • Permission reaches individual fields. A role can be allowed to see a record and not one column of it, and a field hidden from a role is absent from what the API returns rather than hidden in the browser.
  • A workspace role never grants the ability to approve. Approving is granted per process, by name, and somebody cannot approve their own submission.
  • A respondent following a resume link is scoped to one record. Without that scope the link would open every record in the workspace.

Credentials

  • Passwords are hashed with scrypt. A sign-in for an unknown address does the same work as a real one, so response time does not reveal whether an account exists.
  • Sessions are opaque tokens; only a hash is stored, so a dump of the session table cannot be replayed as a login.
  • Invitation, verification and reset links are single-use, expire, and are stored only as hashes. Spending a reset link signs the account out everywhere else.
  • A password reset answers identically for an address with an account and one without. Anything that varied would let a stranger enumerate who works here.
  • Deactivating a member revokes their live sessions in the same transaction.

What cannot be changed after the fact

  • A published process version cannot be edited — not by an administrator, not by a support engineer, not by a migration script. The database refuses the write. A finished record keeps the rules it was decided under.
  • History is appended, never overwritten. A correction is a new entry beside the old one.
  • Every action carries a key tied to the occurrence that caused it, so a retried job does not send a second email or generate a second document.
  • Generated documents are stored with a checksum, and an export is reproducible: the same record exported twice produces the same checksum.

What has been tested, not intended

The claims above are each backed by an executable proof that runs against a real database and fails the build if it stops holding. The current run is twenty-three of them.

Recovery is a drill rather than a document: it takes a backup, destroys the database, restores it, and replays the jobs that were in flight — then reports what was lost inside the recovery window, including messages that had already reached real inboxes and will be sent again when the work is redone.

Accessibility is checked the same way, across seventeen flows on every run. The findings and the gaps are in the repository rather than summarised here.

What is not met

Named rather than implied.

  • No multi-factor authentication yet. It is the last unmet row in the authentication requirements.
  • No SSO provider is wired up. The identity model accommodates one — external identities match on the provider’s subject, never the email address — but nothing is connected.
  • No uploaded-file malware scanning. Files are recorded as metadata; the bytes are not scanned.
  • No penetration test, and no third-party audit or certification.
  • No point-in-time recovery. Restores go to the last backup, and the drill reports exactly what that costs.
  • The effect log lives in the same database as the data it describes.

Privacy, the data map and erasure are on the privacy page; who processes what is on subprocessors.

Back to the overview