Someone else has to sign off on this.
Wire transfers, production access, price changes, content going live. Every serious application ends up needing a second pair of eyes, and every team builds the sign-off flow again. Quorum is that second pair of eyes, as one self-hosted service.
Open source under MIT. Written in Go. Runs on PostgreSQL or SQL Server.
- From account
- ACC-001
- Amount
- $50,000.00
- Destination
- IBAN-12345
You have written this before
Section titled “You have written this before”A status column, a couple of endpoints, an audit table that is never quite complete, a UI only one app can use. Then the next application needs the same thing and it starts again.
| Rolling your own | With Quorum | |
|---|---|---|
| State | Approval state lives in a column on your own table, with transitions scattered across handlers. | Requests, stages, votes and expiry are a first-class model with enforced transitions. |
| Guards | “Not the maker”, “one vote per stage”, “only managers” are re-implemented, and re-forgotten, per app. | Layered guards are enforced centrally and configured per policy. |
| Changes | Changing who approves what means a code change and a deploy. | Policies are data. Edit them at runtime in the console or the API. |
| Audit | The trail is whatever logging you remembered to add. | Every action is recorded with actor, timestamp and details, in the same transaction as the change. |
| Screens | Each app builds its own approval pages. | Three themeable Web Components, or the admin console. |
How a request moves
Section titled “How a request moves”- Your app creates a request
One call with a type and a JSON payload. The caller is the maker.
- Quorum finds the policy
Stages, thresholds, who may act and when to expire, all read from data.
- Checkers sign
Self-approval, roles, permissions, scope and duplicate votes are checked before a signature lands.
- Your app acts on the decision
Approved, rejected or expired arrives by webhook, event stream or poll. Your app performs the write.
The rules are a document, not a deploy
Section titled “The rules are a document, not a deploy”- Stages in order
- Each with its own threshold, allowed roles or permissions, and rejection rule.
- Scope
- Only the compliance officers in the maker’s branch may sign the second stage. Attributes come from your identity provider’s claims or your gateway’s headers.
- Bypass with a velocity limit
- Amounts under 1,000 skip review, but the fourth one in a day comes back for a human to look at. Limits stop a threshold from being gamed.
- Expiry
- A request nobody decides on does not stay open forever.
{ "name": "High-value wire transfer", "request_type": "wire_transfer", "stages": [ { "name": "Manager approval", "required_approvals": 1, "allowed_checker_roles": ["manager"] }, { "name": "Compliance review", "required_approvals": 2, "allowed_checker_roles": ["compliance_officer"], "scope": [{ "attribute": "branch_id" }] } ], "bypass_rules": [ { "name": "petty cash", "payload": [{ "path": "amount", "op": "lt", "value": 1000 }], "limits": [{ "window": "24h", "max_count": 3 }] } ], "auto_expire_duration": "24h"}What is included
Section titled “What is included”- Layered permission guards
- The maker cannot approve their own request. Per-request eligible reviewers, per-stage roles and permissions, reviewer scoping by attributes such as branch, an optional authorization endpoint you control, and one vote per checker per stage. Evaluated in order on every decision.
- An audit trail you can hand to an auditor
- Every create, approve, reject, cancel, bypass and expiry is permanently logged with actor, timestamp and details. One place to answer who approved this, and when.
- Webhooks that cannot be lost
- Deliveries are written to an outbox in the same transaction as the state change, signed with HMAC-SHA256 and retried with backoff. A review_requested event tells your application exactly who is allowed to act next, so you can notify them.
- Approval screens for any front end
- Three Lit Web Components: an approval panel, a request list and a stage progress bar. Shadow DOM isolation, theming through CSS custom properties, live updates over Server-Sent Events. Load them from a script tag or npm.
- Your identity provider stays in charge
- Validate JWTs against any OIDC provider’s JWKS endpoint with claim mapping and automatic key rotation, or trust headers from your gateway. Quorum never becomes a user directory.
- Built to be operated
- PostgreSQL or SQL Server, with everything in its own schema so Quorum can share your database. Prometheus metrics, a health endpoint that reports its version, multi-arch Docker images, an OpenAPI contract validated against the real server in tests.
The console
Section titled “The console”Manage policies, browse requests, read every audit entry and watch webhook deliveries. It ships inside the server binary when you opt in at build time.




Three apps, one engine
Section titled “Three apps, one engine”Three sample applications on three stacks share one hosted Quorum. Switch personas in the navigation bar to walk a request from maker to checker.
- BankingGo, html/template, built on the Go client
Multi-stage wire transfers that execute when the approved webhook arrives.
- Expense trackerNode.js and Express
Single-stage expense approval with status polling.
- Access portalPython and Flask
Threshold voting: two of three security reviewers must approve.
Start in a few minutes
Section titled “Start in a few minutes”One command brings up the database, the migrations and a server with the console and widgets. Then create a policy and your first request.
- Quick start build from source and point it at a database
- Walkthrough a two-stage wire transfer from policy to webhook
- Go client typed, with webhook verification and wait-for-decision
- Source on GitHub MIT licensed
- Why Quorum exists the design story, on the author’s blog
git clone https://github.com/lawale/quorum.git && cd quorumdocker compose up# PostgreSQL, migrations, Quorum with console and widgets at localhost:8080Quorum is built by Olawale Lawal.