Vault
Command software for a campaign of twenty-two thousand people.
- Role
- Lead Engineer: architecture, backend and product
- Period
- Aug 2026 to present
- Status
- In development
The operations platform for a 2027 senatorial campaign in Nigeria: registration and voter-card verification, a six-tier command structure down to the polling unit, a WhatsApp assistant, and election-day command. Five services, one database that enforces who can see what.
Built for a client campaign. This page covers the engineering only. No strategy, data or people from the live system are shown. Every screen uses synthetic data.

- polling units, seeded from the electoral commission's own file
- 731polling units, seeded from the electoral commission's own file
- row-level security policies, so access is enforced in the database
- 147row-level security policies, so access is enforced in the database
- commits in the first six weeks
- 317commits in the first six weeks
Overview
Vault is the internal operations platform for a senatorial campaign: a command structure of roughly twenty-two thousand people, from a general commander through sector, ward and polling-unit leadership down to squad leaders, each responsible for their own people. It handles registration and voter-card verification, the organisation tree, field operations, a WhatsApp assistant, and command on election day.
I designed the architecture and lead the build: data model, access control, services and the product surfaces each role works in.
The problem
A field campaign is an organisation that exists for a few months, grows to tens of thousands of people, and has to work on the cheapest phones on the worst connections. Every person may only see the people below them. Leaders change, step away and get covered by someone else. And on election day, everything has to work at once.
Spreadsheets and group chats break at that scale, not because people are careless, but because nothing enforces who owns what.
The solution
One structure, enforced by the database
The hierarchy is modelled once and enforced in PostgreSQL itself with row-level security. An API bug can't leak a ward's roster to another ward, because the database won't return those rows to that actor. Acting arrangements and "view as" are first-class: a commander can see exactly what a subordinate sees, through the same policies, without a separate admin backdoor.
The real map, not a guess
The electoral areas (5 local governments, 56 wards and 731 polling units) were parsed from the electoral commission's own source file and validated against the published totals before seeding. Unit codes are checked by a database constraint against the official state/LGA/ward/unit format. Every placeholder code in the early designs was thrown away.
A screen for every role
Each tier has its own workspace, designed around one question it has to answer each day. A unit captain sees today's work and the squad leaders who've gone quiet. A ward commander sees which units have stopped. A sector commander decides approvals with evidence and a reason.




Meeting people on WhatsApp
Most of the organisation will never open a dashboard. A WhatsApp service runs an assistant for registration and day-to-day reporting, with outbound messages queued so a slow network never drops a reply. The interface ships in English and Yoruba.
Verification at scale
A worker service runs OCR on uploaded voter cards, scheduled sweeps and SMS: the slow, heavy work that should never block a request.
Architecture
People
- Public campaign site
- Vault web app (EN · YO)
- WhatsApp assistant
- SMS
Services
- API · NestJS + Fastify
- WhatsApp · NestJS + Fastify
- Worker · OCR, sweeps, SMS
Shared packages
- db · migrations & types
- shared · Zod schemas & role rules
- ui · Vault design system
Data
- PostgreSQL
- 147 RLS policies
- 137 SQL migrations
Infrastructure
The infrastructure was designed for one date: election day. Everything about how Vault is built, tested and released assumes that a mistake made that week can't be rolled back.
Edge
- Public site on Vercel
- Vault web app on Vercel
Compute on Render, Frankfurt
- API, a health-checked web service
- WhatsApp service
- Worker daemon for OCR, sweeps and SMS
Data
- PostgreSQL
- Row-level security
- Separate database credentials per service
Messaging
- WhatsApp Cloud API
- SMS provider for OTP delivery
A pipeline that tests the rules, not just the code
Every push is type-checked, linted and unit-tested, and then the database's access policies are tested on their own with pgTAP, before anything builds. A merge migrates the database, then deploys the API, the worker and the web app in that order, and finishes with a smoke test.
Additive-only migrations
Migrations run before the API deploys and only ever add: a new column, a backfill, then the switch. Dropping anything is a separate, later, deliberate change, because there is no rolling back a dropped column on election week.
Keys that can be rotated without losing anyone
The keyrings that protect account recovery and PINs support additive rotation (a new key is added before an old one is retired), and restores are rehearsed, not assumed. Each service connects to the database with its own credentials, so a leak in one is not a leak in all.
A calendar for the last week
The runbook fixes the final week in advance: feature work stops a week out, extra capacity is switched on two days before, and on polling day there are no deploys at all. If something breaks badly enough to need one, the runbook's fallback is the answer.
Engineering challenges
Designing for the sixty-thousandth request, not the first
The system had to be correct at the scale of an election day from the beginning, so the hard rules live at the lowest layer: access in the database, shapes in shared Zod schemas, role rules in one package that the API, the WhatsApp service and the web app all import.
A decision log that outranks the plan
A twenty-two-chapter build guide and thirteen designed sections describe the system. Where they disagree, a single decisions file is the tiebreaker, and anything that needs a campaign decision rather than an engineering one is marked open and modelled as data, so the answer can change without a migration.
Reviewing every role, on every screen size
Each role's workspace was audited in a real browser at phone, laptop and desktop sizes, with screenshots captured for review. It is the only way to know that a ward commander on a 360-pixel phone sees the same truth as one on a desktop.

Outcome
Vault is in active development: five services, three shared packages, 137 migrations and 54 test files in the first six weeks, with each role's workspace designed, built and audited. The public site and the internal platform share a single door: joining is Vault's own registration path.
Lessons learned
- Put the rules where they can't be skipped. Access control in the database is the only kind that survives every future bug.
- Seed from the source of truth. Validating against official totals caught errors that would have shaped every downstream decision.
- Model open questions as data. When the answer belongs to someone else, don't hard-code a guess.
Technology stack
- Next.js
- next-intl
- NestJS
- Fastify
- PostgreSQL
- Row-Level Security
- Zod
- Tesseract.js
- WhatsApp Cloud API
- pnpm workspaces
- Vercel
- Render