Skip to content
Backend and platform engineer in Nairobi. Everything on this page is running now, and every figure is measured by the site itself. Saturday 10 October 2026 · Nairobi
The LighthouseThe engineering portfolio of Martin Omwenga
Projects / Redacted / Architecture

How Redacted is put together

One server process, one PostgreSQL database. The browser talks to the server two ways: ordinary requests for everything that changes permissions, and an open connection per section for live editing. Both ask the same policy code what a member may do.

The parts and the walk-through are listed in full below.

Every part

The page

Shows the briefing and its controls, and makes every request that changes something.

A React app. It only draws what the server sent: a redaction bar on screen is a bar in the data, and there is nothing behind it for the developer tools to find.

Code web/src/Room.tsx web/src/api.ts

More about this in the story →

Audit log

Records who did what, in the same transaction as the change itself.

Each row carries a hash of the row before it, so a deleted or edited row breaks the chain. The head of the chain is signed, so a copy of the log can be checked away from the server.

Code src/audit/audit.service.ts src/audit/chain.ts src/audit/checkpoint.ts

Policy

One file of plain functions that answers "may this member do this to that?".

Roles, departments, clearance levels and shares all meet here. The REST API and the live connections call the same functions, so a browser tab and an open connection can never disagree about what someone may see.

Code src/policy/policy.ts src/briefings/access.ts

Collaboration server

Holds the open connections, merges edits, and re-checks every connection when access changes.

A Hocuspocus server inside the same process. A connection is checked when it opens and again whenever a permission change is announced. A read-only connection is read-only on the server: edits it sends are dropped, whatever the page shows.

Code src/realtime/realtime.service.ts

More about this in the story →

Redacted copies

Writes a separate copy of a section for each clearance level, with classified words replaced by bars.

Readers below a word's classification connect to a copy that never contained it. The bar's length is rounded so it doesn't give away the length of the word behind it.

Code src/realtime/projection.ts

More about this in the story →

Notifications

PostgreSQL's own broadcast (LISTEN/NOTIFY), used to tell every server instance that access changed.

A notice is sent inside the transaction that makes the change, so it is delivered only if the change commits. No message broker to run, and no notice for a change that didn't happen.

Code src/realtime/access-changes.ts

More about this in the story →

Walk-through, step by step

A Director demotes an editor who is mid-sentence

The hard case for any live editor: the connection was allowed when it opened, and it is still open. Redacted refuses edits first and sorts out who keeps access second.

  1. The requestThe page → REST API

    The Director changes the member's role. The page sends one ordinary request.

  2. May the Director do this?REST API → Policy

    The policy checks that this Director may manage this member and may hand out the new role. If not, the request stops here.

  3. Lock firstREST API → Collaboration server

    Before anything is saved, the member's open section connections turn read-only on the server. From this moment no edit from them is accepted.

  4. Save, record, announceREST API → Tenant tables

    One transaction changes the role, writes the audit row and queues a notification. Either all three happen or none do.

  5. Every instance hearsNotifications → Collaboration server

    The transaction commits and PostgreSQL delivers the notification to every server instance, including ones the Director's request never touched.

  6. Re-checkCollaboration server → Policy

    Each affected connection is put through the same policy code as a fresh request, with the member's new role.

  7. The editor locksCollaboration server → Section editors

    The demoted member's editor goes read-only, or closes if they lost access entirely. Anyone who kept access is restored, and whatever they typed during the lock is resent.

How we know: A test sends an edit the instant the demotion request returns and checks it never lands; another checks an edit refused during the lock is recovered (test/realtime.e2e-spec.ts).