Security
Facts, not badges.
How the product is built, what we record and never record, and where compliance stands. There is no certification logo here, because we have not earned one yet.
How it is built.
- Encryption at rest
Envelope encryption, one key per document.
Each uploaded file is encrypted with AES-256-GCM under its own data key, wrapped with a key derived for its organisation. The plaintext data key exists in memory only, for one operation; the wrapped key is kept in the database, apart from the encrypted file on disk. The page images and page text made for the reading room are not encrypted with the document’s key. Removing a document takes it out of the deal at once; its stored files stay, and nothing deletes them automatically yet.- Tenant isolation
Postgres row-level security, enforced through a database role created as
NOSUPERUSER NOBYPASSRLS.Every request sets that role for the transaction, so a policy cannot be silently bypassed by connecting as a superuser or as the table owner. There is a standing test that asserts the superuser bypass exists, so nobody can “fix” it by reconnecting as the owner. Our own test organisation lives on the same database, walled off the same way every customer is walled off from every other: it can be invited into nothing real, and nothing real can be invited into it.- Authorization
Every sensitive route goes through one central
authorize(actor, action, resource)call and denies by default.A route never resolves its own tenant: the owning organisation comes from a single database function, so a route cannot accidentally compare an actor’s organisation with itself. A request for another organisation’s deal or document returns nothing distinguishable from “it does not exist”, which avoids confirming that it does. Row-level security is the second, independent layer beneath this, not the first one.- The reading room
Documents are rendered to raster tiles on the server.
The original file’s bytes never leave the server; readers receive rendered, watermarked page images only. A full recipient session is driven end to end in a real browser in the test suite and every response body is checked for a%PDF-header: none has appeared. Tiles are served over short-lived signed URLs withCache-Control: no-store, and watermarked output is never cached.- Staged disclosure
Every page and every region within a page carries a minimum disclosure level, checked on the server on each render request, and unmapped content fails closed at the highest level.
Requesting a level-2 page as a level-1 reader by editing the address returns a hard refusal, not a page with something hidden on it. A redaction is a block composited into the raster before it is sent, never a mask over pixels the browser already has.- Revocation
Entitlement is re-checked on the server for every tile request, independently of how long any signed URL has left to live.
Cutting a reader off is measured at 115 ms from revoke to refusal in our test environment against a 5-second production budget, and is tested against it.- Identity
Passkeys.
Verification happens on the reader’s own device and we receive a cryptographic assertion, never a biometric image or template. Opening any disclosure level requires a fresh, server-generated, single-use challenge verified with user-verification required, which is a stronger check than signing in, and we do not describe signing in as proof that a person was present. There is no camera in this product.- The evidence chain
Every disclosure-relevant event is appended to a hash-chained log, never edited, never deleted, and batched into a Merkle tree for external anchoring.
Every page a reader opens is recorded with the level actually shown and the document version, and once an hour closes for a reader the rows from it are folded into one more chained entry, so a closed hour of reading is checkable too, not only the events around it. Altering a past entry breaks the chain and the verifier detects it and names the entry. This is tamper-evident, which means detection; it is not prevention, and there is no write-once store for the rendered pixels themselves yet.- Time anchors
Three, independent of each other and of us: RFC 3161 tokens from DigiCert and FreeTSA, and an OpenTimestamps commitment.
Anchoring runs on the live service: on 12 September 2026 it anchored every batch the service held, each with both RFC 3161 tokens and an OpenTimestamps commitment, within seconds. The certificate still counts the batches with and without one, because a batch is anchored after it closes, not before. The verifier and the certificate name exactly what happened to each proof, never a bare “verified”: an RFC 3161 token is checked, chained to a root pinned inside the verifier — DigiCert Trusted Root G4 or FreeTSA Root CA, SIDR’s own copies, not one you obtained yourself — or it printschecked, signer not trustedand is not counted. The OpenTimestamps proof is a calendar commitment, pending until a calendar aggregates it into a Bitcoin block; we have since observed that happen, at which point the certificate prints a Bitcoin attestation with the block itself not checked by the tool. To check a token against a root you obtained independently, or an attestation’s block against a Bitcoin node you trust, the package’s ownHOW-TO-VERIFY.mdshows how, using nothing of ours.
What we log, and what we never log.
The evidence chain and the application logs are two different things with two different rules. The chain records decisions and acts, such as an agreement accepted, a level granted, a page served, because those are the product. The application logs record operations, and they are redacted at the serializer, which is a code-level control rather than a policy reminder.
The list on the right is not a summary of a longer internal list. It is the rule, and fields whose names match *token*, *secret*, *key*, *password* are stripped before a line is ever written.
Never logged, in any service, at any level
- Document content: page text, agreement body text, file names beyond the generated storage key
- Watermark payloads
- Tokens, secrets, plaintext data keys, full session cookies
- Raw biometric or identity-check payloads, which never reach us at all
- Payment details, which we do not collect
The evidence chain itself holds hashes and metadata. Document content is never in the evidence log: only what was served, to whom, when, and its hash.
The verifier does not talk to us.
An evidence package is a file you keep. It ships with a standalone verifier, built from the same source that checks the chain in production, bundled into it: one command, nothing to install, no connection to our servers and no dependency on our code being honest. It recomputes the hash chain from genesis, replays the Merkle inclusion proof, checks the RFC 3161 signatures, rejects a token that claims to predate the event it covers, and rejects a package with no events at all.
It checks every byte of a timestamp token’s signature, not only the time it claims, measured against both anchoring authorities, byte for byte. At the last full measurement (2026-09-07), 497 tests ran green across four tiers, and the ones that mattered were the negative tests: every guard added that week was proven to fail closed first, by removing the fix and watching it catch the defect.
One limit stated plainly: a counted token chains to a root we pinned in verify.js, our own copy, not one you obtained independently. Check it yourself with openssl ts -verify -CAfile against a root you trust, or ots verify against a Bitcoin node you trust; the verifier says so on its own output rather than leaving you to discover it.
Open tools, your machine, no account. If we disappeared tomorrow, a package issued today would still verify.
Subprocessors.
Every vendor that can touch confidential or restricted data, what it touches, and why it is there. A vendor that would touch restricted data without a clear architectural reason is a rejected default, not a fast-tracked one.
- Hosting, rendering and file storage
Fly.io: runs the website, the service and the page renderer, in Ashburn, Virginia.
Documents are decrypted on these servers to be rendered. Its disk holds each uploaded file, encrypted under its own key, and the page images made from it for the reading room, which are not encrypted with that key.- Database
- Neon: accounts, organisations, deals, reader lists, agreements and acceptances, the evidence log, viewing records, the wrapped document keys, and the text extracted from each page, which is not encrypted with the document’s key.
- Key management
No separate vendor.
Each organisation’s key is derived inside our own service from one secret held in Fly.io’s secret store.- Transactional email
- Resend: sends invitations (the reader’s address, the deal’s name, the inviter’s and organisation’s names, and a single-use link), sign-in codes, and a reader’s own copy of an agreement they accepted, in full.
- Sign-in, if chosen, and our mailboxes
Google: if you choose Continue with Google, Google signs you in and shares your name, email address and profile picture with us.
Mail to our support, contact and security addresses is held in Google Workspace.- Subscription billing
Paddle: the merchant of record for every paid plan.
Paddle holds your card details, charges the subscription, collects tax and sends the receipt; we receive your email, the plan you bought and Paddle’s own subscription id, never a card number.- Time-stamping
DigiCert, FreeTSA and OpenTimestamps: when a batch of evidence is anchored, each receives one hash for that batch and nothing else.
No document content, no personal data.- Visit counts, if you allow them
Google Analytics: counts visits to our public pages, and only after you press Accept analytics.
It never runs inside the app, a deal room or sign-in, and it receives no document, deal or account data. Google signals and advertising features are off, and the data is kept for 14 months. Cookie settings at the foot of every public page changes your answer.- Not used
- No advertising, error-tracking or content-delivery provider is connected to SIDR.
Data residency
There is one deployment for every customer. The service, uploaded files and page images are in the United States, in Fly.io’s Ashburn, Virginia region, and the database is hosted by Neon. The data region an organisation records at setup does not change where its data is kept. Ask us before you assume a region: we would rather answer than have you guess from a map on a marketing page.
Responsible disclosure
If you find a vulnerability, write to security@sidr.so. We will acknowledge within 72 hours and tell you what we are doing about it. We will not threaten you, and we will credit you if you want the credit.
Compliance
SOC 2 Type 1 is scheduled with our first enterprise pilot, and is not in place today. That is the whole status. We are not going to put a badge in the header for a certification we have not been through, and we are not going to describe the controls above as “audited” when what they are is built and tested. When the report exists, it will be named in this section with its date and its scope, and available under NDA.
Deletion is manual today. There is no organisation-wide key we can destroy to erase an organisation’s documents at once; that needs a managed key service, which is not built. A data-processing agreement is available for any customer who needs one.
The honest half
This page describes what the product does. The list of what it does not do is a separate page of equal length, and it is the one we would read first if we were you.
Bring your security reviewer.
We will answer a questionnaire, walk your team through the architecture, and hand over an evidence package they can verify themselves without an account. Or set up a room and read the code paths from the outside.