Infrastructure and security
Every chapter runson its own system.
Its own server, its own database, its own files, its own keys. No other chapter shares any of them.
Three chapters, three separate systems. Each one reports only its version and health to Mission Control.
One chapter, one system
Nothing is shared between chapters.
Most software keeps every customer in one database and separates them with rules in the code. Quorum gives each chapter a separate system.
- Its own serverCloudflare Worker
- Its own databaseCloudflare D1
- Its own filesCloudflare R2
- Its own keysDerived per chapter
- Its own addressHost-only sessions
- Its own regionSix to choose from
Technical detail
- Compute
- One Cloudflare Worker per chapter. The server is written without frameworks and has no third-party packages in production.
- Database
- One Cloudflare D1 (SQLite) database per chapter. No query can span chapters because no chapter’s server holds a connection to another chapter’s database.
- Files
- One Cloudflare R2 bucket per chapter.
- Keys
- Each chapter’s encryption keys and its service token are derived with HKDF-SHA-256 from a platform root, with the chapter’s name in the derivation. A chapter’s server never holds the root, so one chapter’s token is refused by every other chapter.
- Sessions
HttpOnly,Secure,SameSite=Lax, and host-only on platform addresses. A browser never presents one chapter’s session to another chapter’s server.- Region
- The database and the bucket are created with a location hint read from the chapter’s time zone:
wnam,enam,weur,eeur,apacoroc. - Provisioning
- An automated pipeline creates a new chapter’s server, database, bucket and keys. Nothing is typed on a chapter’s server by hand.
Between chapters
What passes between chapters, and what never does.
A member can belong to two chapters, and two chapters can host one event. A separate system, Mission Control, carries a short, fixed list of facts between them. If it is unreachable, every chapter keeps working.
One sign-in, two chapters
The second chapter does not accept the first chapter’s session. It accepts a pass signed by Mission Control, then opens a session of its own.
Sign-in pass
- From
- Chapter A
- For
- Chapter B and no other
- Expires
- 2:00
- Works
- once
Used
The pass names the email, the chapter that checked it and the one chapter it is for. It expires in two minutes and works once.
What Mission Control knows
Never
What a shared event passes
Never
What Quorum’s support staff see
- Standing in the chapter
- Forums
- App version
- Email delivery status
- Messages
- Email contents
- Passwords
Every operator action is logged for 400 days.
Technical detail
- Control plane
- A separate Worker and D1 database. It holds the chapter registry, release and health records, deploy pins, the operator activity log and the discovery index. It is off every member request path.
- Discovery index
- HMAC-SHA-256 of each sign-in email and mobile number, used to send a sign-in or a text reply to the right chapter. Raw emails and numbers are not stored.
- Sign-in pass
- Format
qp1.payload.signature, ECDSA P-256 over SHA-256. The payload names the email, the vouching chapter, the target chapter, the issue time, a two-minute expiry and a one-time id. The target chapter verifies with Mission Control’s public key and records the id on first use, so a replay is refused. A refused pass counts against the same limit as a wrong password. - After the pass
- The phone holds one session per chapter, reads each chapter from that chapter’s own server and combines them on the phone. The chapters never read each other.
- Co-host broker
- A chapter posts to Mission Control with its own bearer token. Every message is numbered, and a chapter ignores a number older than the one it holds. A reply carries a name, a title and company, the answer, the party size, the guests’ names and dietary needs. Replies are relayed and not kept on Mission Control. Each chapter bills its own members through its own Stripe account.
- Support access
- Mission Control reaches a chapter’s support routes with that chapter’s own derived token. The chapter’s server answers with the support card, which also says whether a password is set and lists recent emails by subject and status. Mission Control displays the card and stores none of it. Operators sign in with a password or a passkey.
Encryption
Private conversations can be read only by the people in them.
Three layers of encryption. The third is made on the member’s own device, with keys the servers never hold.
Try it
Type a message. The middle box shows what Quorum’s servers would store. This is real AES-256-GCM, running in your browser with a key made on this device.
1 · The sender’s phone
Encrypted here, before it leaves the phone.
2 · Quorum’s servers
- chat
- forum chat 31
- from
- member 214
- sent
- 10:42 AM
- size
- 62 bytes
- message
- q7Hk0wZ3vX1mPf2a.Jx8tQ0dYb2uV5nC1rL9sKe4hWm7pT3aZo6gUiB0yNfXc2RvMq8EdL1jSw5kHt9PzOa4GuY7bC3nVxI6mQe0
Visible to the servers: who, when, how large. Never the words.
3 · A phone in the chat
Opened with the chat’s key, which only the devices in this chat hold.
Who can read a message
1 A private chat turns end-to-end encrypted once everyone in it has a version of Quorum that can read one, or after the chapter’s waiting period, once someone in it has. Earlier messages stay as they were.
2 The key is on the chapter’s server, so this is a rule in the software and not a missing key.
A member’s keys on a new device
- New iPhoneArrive through iCloud Keychain
- New AndroidArrive from the old phone or its backup
- New browserScan a code, or use a passkey
- Every device lostA 28-character recovery code
- No code eitherReset. Quorum cannot recover the history
Technical detail
- Key agreement, signatures
- NIST P-256 ECDH and ECDSA with SHA-256 (FIPS 186-5, SP 800-56A).
- Key derivation
- HKDF-SHA-256 (RFC 5869).
- Content encryption
- AES-256-GCM with a 96-bit random nonce and a 128-bit tag (SP 800-38D).
- Key wrapping
- HPKE base mode: DHKEM(P-256, HKDF-SHA256), HKDF-SHA256, AES-256-GCM (RFC 9180).
- Photos and files
- Encrypted on the device before upload, as segmented AES-256-GCM in one-megabyte segments (the STREAM construction). A file altered or cut short fails to open.
- Implementations
- WebCrypto in browsers, CryptoKit on iOS and the platform providers on Android. No primitive is implemented by hand.
- Chat keys
- A 32-byte secret per chat, replaced at every membership change and wrapped with HPKE to each member’s public key, so a person who leaves cannot read what follows. Each key announcement is signed by its creator and chained to the one before it.
- Devices
- Each device has its own P-256 key and a certificate signed by the member’s account key. The server cannot add a device, because it cannot produce that signature.
- Messages
- Each message is signed by its sender. A server can delay or withhold a message. It cannot alter or invent one.
- Private chats
- Direct messages, forum chats, alumni chats, groups a member started and groups with Confidentiality Covered on.
- In a browser
- The phone apps take new code only through a store release. A web page is sent on each visit, so in a browser the encryption depends on the server sending honest code. Every web messenger has this limit, and the app says so in the chat.
- What stays visible
- That a chat exists, who is in it, when a message was sent and how large it is. A record made from a chat, such as an event or a payment, is a chapter record.
- In transit
- TLS on every host. Plain http is redirected, and
Strict-Transport-Securityholds a browser to https for one year. - Stored encryption
- Per-chat key = HKDF-SHA-256(chapter key, chat id). The chat and author ids are bound as additional data, so a ciphertext moved to another row fails to open. With no usable key the server refuses the write and never stores plain text.
- Review
- The way the app combines these is written down as a specification and has had outside review.
Signing in
How members sign in.
A passkey or a password, with an optional code by text. A stolen copy of the database cannot be used to sign in as anyone.
Passkey
Face ID, Touch ID or the device PIN. It answers only to the real site, so a fake page gets nothing.
Password
Stored only as a salted hash. Nobody at the chapter or at Quorum can read it.
Code by text
An optional second step. Six digits, good for ten minutes.
Eight wrong guesses in 15 minutes pause sign-in for that email.
The database holds only hashes. A copy of it signs nobody in.
- A password reset signs out every device
- Face ID lock in the phone apps
Technical detail
- Passkeys
- WebAuthn, on the web and in the phone apps, verified on the server with WebCrypto: the challenge, the origin, the relying-party hash, user presence, the signature, and a counter that only moves forward.
- Password hashing
- PBKDF2-SHA-256, 100,000 iterations, a random salt per password. Sign-in does the same work for an unknown email, so neither the answer nor its timing reveals who has an account.
- Sessions and links
- Random tokens stored as SHA-256 hashes. Emailed links are single use.
- Two-step code
- Five wrong tries cancel a code. The check of the code and the new session are written in one database transaction, so a sign-out of every device cannot be raced.
- Rate limit
- Eight failures for one email, or thirty from one network address, in 15 minutes. Each attempt is recorded before it is counted, so parallel guesses cannot pass the limit together.
- Email change
- A new password alone cannot move an account to another email. It takes the established password or a code sent to the proven phone.
- App lock
- Face ID or the passcode when the app opens, and again after two minutes in the background, when the member turns it on.
Inside your chapter
Who can see what inside a chapter.
A forum opens only to its own members. The chapter office cannot open it, and neither can the top administrator account.
- EventsOpen
- Dues and paymentsOpen
- The rosterOpen
- Forum AIts members only
- Forum BIts members only
- Groups members startNot shown
- Officers see only what they are grantedView or manage rights for each console area, checked on every request.
- Every console change is recordedWho and when, in a log that cannot be edited or deleted.
- Screenshots can be blockedIn the phone apps, by the chapter’s own switch.
- Your records stay yoursExport everything at any time. A member can ask for their account to be deleted.
Technical detail
- Forums
- Membership is checked on every request. An account with no seat in a forum is refused, whatever its role.
- Audit log
- One append-only row per successful change made in the console, never updated and never deleted. Finance keeps a second ledger of its own.
- Deleting an account
- A member asks from the app. When the office approves, sign-in ends on every device and the member’s contact details are removed.
- The app switcher
- The phone apps cover their content in the app switcher.
- If a chapter leaves
- The export stays available for 60 days. Then the chapter’s system and its data are deleted, and the backups age out.
The application
How the software is protected.
Every request passes the same four checks before any data is read.
- No third-party code on the server
- No outside scripts in the app, except Stripe’s payment form
- Logs record no private values
- Phone apps update only through the App Store and Google Play
Technical detail
- Response headers
Strict-Transport-Security: max-age=31536000; includeSubDomains·X-Content-Type-Options: nosniff·Referrer-Policy: same-origin·X-Frame-Options: SAMEORIGINwithframe-ancestors 'self'·object-src 'none'·base-uri 'self'- Script policy
- Every script element needs a per-response nonce (
strict-dynamic). The sign-in pages also forbid inline event handlers. - Cross-site requests
SameSite=Laxcookies, plus a check ofOriginandSec-Fetch-Siteahead of routing.- Webhooks
- Stripe HMAC signatures, Resend (Svix) signatures and Twilio request signatures, compared in constant time. A route with no secret configured refuses every request.
- Photos and files
- The session and the member’s section are checked on every media request. An upload uses a single-use, short-lived grant for one object of one declared type, and the grant token is stored as a SHA-256 hash.
- Request size
- JSON bodies are capped at 256 KiB unless a route names a larger budget.
- Logs
- Logs record which route failed and why. They do not record request addresses, which can contain private links, or the values a member typed.
- Dependencies
- The server has none in production. Advisories for the build tools and the phone app are checked on every change and again at release, and an unreviewed advisory stops the release.
- Phone apps
- New code arrives only through a store release. There is no over-the-air code update. Quorum sets the oldest app version a chapter accepts, so a version with a known flaw can be retired.
Payments
Card and bank numbers never reach Quorum.
Payments run on your chapter’s own Stripe account. Quorum never holds your members’ money.
Backups and recovery
How data is backed up and restored.
A chapter’s database can be returned to any minute of the last 30 days. A separate copy is kept every night for 90.
30daysRestore to any minute
90daysA copy every night
12a yearRestore drills that prove the copies work
- Backups hold no sessions or keys
- Clean-up waits a week before it deletes
Technical detail
- Point in time
- The database keeps a continuous history for 30 days. A restore returns it to the minute before a mistake.
- Nightly export
- Every chapter’s database is exported each night and kept for 90 days, outside the live system. Sign-in sessions, emailed links and service keys are removed from the copy, so a backup cannot be used to sign in.
- Restore drill
- Each month the newest export of Quorum’s first chapter is restored into an empty database, with every link between records checked.
- Clean-up
- A file the system believes is unused is listed for a week before it is deleted.
- Mission Control
- Chapters do not depend on it to serve their members.
Releases
How a change reaches your chapter.
Quorum releases changes every day. No change goes to every chapter at once.
Technical detail
- Database changes
- Additive only. A new version adds tables or columns and never removes or renames one, so the previous version always runs against the newer database and a rollback is safe.
- Rollback
- Automatic after three failed checks of the live product. The pipeline then raises an alarm.
- The order
- The first chapter, at least six hours there, then the demo system, then every other chapter in groups of four. A failure on the demo system stops the release, and a failure in the first group stops the second.
- Production access
- A release reaches production only through the pipeline, from the main branch. The workflows that touch production refuse any other branch.
- Version pins
- A chapter can be held on a version for at most 14 days, with a recorded reason.
Providers
The other companies that handle chapter data.
- CloudflareRuns each chapter’s server, database and files. Holds SOC 2 and ISO 27001 certifications.
- StripeCard and bank payments
- ResendEmail delivery
- TwilioText messages
- Apple, Google, ExpoPush notifications
- AnthropicThe AI model behind the assistants
- Intuit QuickBooksAccounting, when connected
What each one receives
- Cloudflare
- The chapter’s data, encrypted in storage with AES-256. Cloudflare holds SOC 2 and ISO 27001 certifications for these services.
- Stripe
- Payment details, typed directly into Stripe’s form.
- Resend
- The emails the chapter sends.
- Twilio
- A mobile number and the text, for members who turned texts on.
- Apple, Google, Expo
- The notification. For an end-to-end encrypted chat that is encrypted bytes and the words “New message”.
- Anthropic
- Only what a task needs. Anthropic does not train its models on it, and a chapter can connect its own account.
- Intuit QuickBooks
- Invoices and payments, only when the chapter connects its book.
Technical summary
The system on one page.
For the person who reviews the engineering
Architecture
- Tenancy
- One isolated stack per chapter: Worker, D1 database, R2 bucket, secrets and domain. No shared data tier.
- Compute
- Cloudflare Workers. One codebase, no frameworks, no third-party packages on the server.
- Data
- Cloudflare D1 (SQLite) per chapter, created in one of six regions. Cloudflare R2 for media.
- Live updates
- One Durable Object per chapter over WebSocket. It carries a signal that something changed. The data itself is read from the database.
- Control plane
- Mission Control, a separate Worker and database: registry, health, releases, the discovery index. Off the member request path.
- Clients
- iPhone and Android apps released through the stores, and the web app. One API.
Cryptography
- Transport
- TLS on every host, plain http redirected, HSTS for one year with subdomains.
- At rest
- AES-256 by Cloudflare for D1 and R2. AES-256-GCM by the application for chat text and travel documents.
- End to end
- P-256 ECDH and ECDSA, HKDF-SHA-256, AES-256-GCM, HPKE (RFC 9180). Per-chat keys replaced at every membership change. Signed messages, signed device certificates.
- Passwords
- PBKDF2-SHA-256, 100,000 iterations, per-password salt.
- Tokens
- Sessions, emailed links and upload grants stored as SHA-256 hashes.
- Cross-chapter
- ECDSA P-256 sign-in pass: two-minute expiry, single use, bound to one target chapter.
Operations
- Recovery
- Point-in-time restore for 30 days. Nightly exports kept 90 days, with credentials removed. A monthly restore drill.
- Release
- More than 11,000 automated tests, then the first chapter, a six-hour hold, the demo system, and the other chapters four at a time. Automatic rollback. Additive database changes only.
- Audit
- A permanent, append-only log of console changes in each chapter. A 400-day log of operator actions on Mission Control.
- Access
- Passkeys, optional two-step sign-in, per-area roles in the console, forums closed to everyone but their members.
Questions from your security reviewer?
Send them to us. We answer in writing, and we can go over any part of this page with your reviewer on a call.