Skip to main content
Work

Built In 24 Days So That The Whole Country Can Check Their Records

Montenegro's 2023 census results could not be published until every person counted had a way to inspect the four identity answers recorded about them, so Parliament legislated the service and we built it without ever needing to know who anyone was.

The census lookup form over a photo of a crowd crossing a street: the statistical ID first, then one of four further codes

Project snapshot

Location
  • Montenegro
Development period
  • 23 Jul – 16 Aug 2024
Public window
  • 1 – 7 Oct 2024

Project

MONSTAT is Montenegro's Statistical Office, the body responsible for producing official statistics for the country and for reporting them internationally.

In 2024 it had to publish something it had never published before: a service that let members of the public read back part of their own census record. Not a report, not aggregate tables, an individual lookup, open to anyone, running for a fixed window. We designed and built it, and it was live to the public from 1 to 7 October 2024.

The challenge

In November 2023 the Parliament of Montenegro amended the census law. It did so because the census had become contested: there were public allegations that enumerators had not recorded people's answers objectively, and the four questions in dispute were the ones that decide how a country describes itself. National or ethnic identity. Religion. Mother tongue. The language a person normally speaks.

Parliament's answer was to legislate the software into existence. Article 26a required the state to establish a system so that every person covered by the census could inspect those four answers as recorded. Article 26b limited each person to their own record and nothing beyond it. The same law required the government to form a commission to oversee the build, and to procure an independent code audit from an IT company based in an EU member state. MONSTAT put the work out to open public procurement, and we won it.

That produced an unusual engineering constraint. The system had to satisfy people who had already decided not to take the state's word for it, which meant it could not ask to be trusted. It had to be checkable instead: checkable by citizens, who needed to see their own answers and be certain nobody could see theirs, and checkable by outsiders, who would read the source code line by line and try to break the running system. Meanwhile the data it served was among the most sensitive a state holds, and the deadline was set by law rather than by us.

The census brand board: the 2023 census mark, Inter type specimens, the bronze, black, red, Cyprus and grey palette, the form on a laptop and the three error and support messages

Design and UX

One screen, two steps, large fields.

The audience for this interface was everybody. Not a segment, not a persona, everybody who had been counted, including people who had never used a government service online and would arrive once, on a phone, holding a paper questionnaire.

So the interface does one thing. A single screen, two steps, large fields, and nothing else competing for attention: no navigation, no account, no marketing, no cookie theatre. The paper questionnaire stayed in the user's hand, so the form asks for the codes in the order they appear on it, with the label wording taken from the questionnaire itself rather than from the database schema. Errors say which code was wrong and what to do next, and the support phone numbers and email sit on every state the application can reach, including the blocked ones. An accessibility widget runs on every page.

Two codes, no identity

Show me my record. Prove you cannot show me anyone else's.

The lookup takes two things. First a statistical ID, a nine-character code printed on a sticker on the person's own copy of the questionnaire. Then one of four further codes from the same sheet: municipality, enumeration area, dwelling or person. Both must match the same record before anything is returned, and the record returns exactly four fields, the four the law named.

What the system never asks for is who you are. There is no login, no account, no email, no name, no national identification number, no date of birth. The database it queries holds nothing that identifies a person, only randomly assigned codes and the four answers. That is what makes the design defensible: even a full compromise of the running system yields a set of answers attached to codes, and the sheet that connects a code to a human being was never in it.

  • Statistical ID
  • Municipality code
  • Enumeration area code
  • Dwelling code
  • Person code
  • No login
  • No name
  • No national ID
The result screen on the census red: the statistical ID and municipality the data refer to, then the four ethnic and cultural characteristics as recorded

The system could show you your own record. It could never show you anyone else's.

Twelve languages, two scripts

A service about identity should not be available in one identity's language.

The questions being verified were about ethnicity, religion and mother tongue. A verification tool for those questions, offered only in the majority language, would have undercut its own purpose for exactly the communities most likely to want to use it. The application shipped in twelve fully translated locales: Montenegrin in Latin and Cyrillic, Serbian in Latin and Cyrillic, Bosnian, Croatian, Albanian, Turkish, Romanian, Russian, Ukrainian and English. Every string, not a partial translation with fallbacks, including error messages, blocked-state pages and the support notice.

Rate limiting as a privacy control

Three looks at a record. Then that record closes for the day.

Throttling here is not about server load, it is about what an attacker with a stolen list of codes could accomplish. Each statistical ID can be checked three times in twenty-four hours, and after that the record itself is closed regardless of who is asking or from where. A single address gets five attempts a minute at validating an ID before it is held off, and twenty requests a minute overall before it is served a block page with a countdown instead. reCAPTCHA sits in front of the form, and Cloudflare sits in front of the application.

Each of those limits was written into the tender, and each one costs a legitimate user something. Three checks a day is enough for a person confirming their own record and not enough to walk a list. That trade was the point.

  • 3 checks per record per day
  • 5 ID attempts per minute per address
  • 20 requests per minute per address
  • reCAPTCHA
  • Cloudflare
The block page on the census Cyprus blue: you have reached the request limit, with a countdown to the next available request

Verify, do not trust. That was the requirement, and it went all the way down to the footer.

The Seq dashboard for the verification window: successful lookups, distinct codes, events by type and lookups over time by outcome
The Seq event stream on the census bronze, one structured line per lookup with its timestamp, statistical ID, outcome and address
The Seq dashboard for the verification window: successful lookups, distinct codes, events by type and lookups over time by outcome
The Seq event stream on the census bronze, one structured line per lookup with its timestamp, statistical ID, outcome and address

An audit trail that records access, never answers

Every lookup is logged. What the lookup returned is not.

MONSTAT needed to be able to show exactly how the service had been used, both to run it and to answer questions afterwards. So every event is logged in structured form to Seq: which code was queried, from which address, at what time in local Podgorica time, and what the outcome was, categorised as success, failure, blocked, blocked attempt or error. Administrators could watch the whole thing live and query it after the fact.

What the logs do not contain is any person's answers. The four fields the service exists to display are written to the screen and nowhere else, never into a log line, never into an event property. The audit trail can prove that a record was accessed. It cannot reveal what that record said. Building it the other way round would have been easier and would have quietly turned a transparency tool into a surveillance one.

  1. UsersAcross the country
  2. HTTPS onlyEncrypted connection
  3. WAFWeb application firewall
  4. ApplicationSecure by design and by default
  5. DatabaseRead-only account

Hardened for a window that could not slip

One week live, on a date fixed by law, with the whole country able to arrive at once.

The application ran on infrastructure inside a government environment, published only over HTTPS behind a web application firewall with every other port closed. On the application itself: a content security policy with a per-request nonce, frame denial, content type enforcement, cookies marked secure without exception, anti-forgery tokens on every state-changing request, input sanitisation and validation duplicated on the client and the server, and a database account restricted to read-only. On the host: full disk encryption and two-factor authentication on every account.

None of it is exotic. All of it is the list you write down when you know a stranger will check, and when the consequence of getting it wrong is a headline about census data rather than a support ticket.

Shipped for a date set by law

Twenty-four days from contract to delivery.

The tender did not treat the schedule as a detail to agree later. It set a build window of twenty to thirty days from signature, made the number a scored award criterion worth ten points, and gave the most points to whoever committed to the shortest. We signed on 23 July 2024 with a delivery date of 16 August 2024. Twenty-four days to design, build and hand over a national government system holding the most sensitive answers in the census.

What followed was not a victory lap. The independent audit ran at the end of August, findings came back, and they were fixed and revalidated in early September. The service opened to the public on 1 October 2024 and closed on 7 October exactly as planned. A week later MONSTAT published the final results of the 2023 census, which it could not legally do until this step was complete.

A team around a laptop showing the census lookup form
Secnora

Source code and running system reviewed by Secnora, the independent auditor the census law required.

Built to be audited

We did not ask anyone to trust the code. Someone else read all of it.

The law required an independent code audit by a firm from an EU member state, and MONSTAT procured one separately from the build. It went to Secnora, a security team based in Estonia, who read the source, tested the running system and reported to MONSTAT rather than to us. That changed how the software was written. Every control had to be legible to someone who had never met us and had no reason to be generous: no clever indirection, no undocumented behaviour, no security resting on being hard to find.

The audit ran over a week at the end of August 2024 against OWASP secure coding guidelines, combining manual review with tooling. It found four issues, two medium and two low, nothing critical or high. We fixed them, Secnora revalidated the fixes before the service opened, and the system was graded A, the auditor's highest mark, for security that exceeds industry best practice. Separately, the application prints the running build's commit hash in its own footer, so anyone could confirm that the code serving the public was the code that had been delivered and reviewed. That requirement came from the tender, and it is the small feature that carries the whole idea: verify, do not trust.

Independent auditWritten testimonial

Grade A. Security grade, exceeding industry best practice

Their developers engaged constructively with our findings and moved quickly on remediation rather than treating the review as a box-ticking exercise, something that is not a given on projects of this sensitivity. … We would be pleased to work with Codepixel again.

Secnora, the independent security auditor appointed under the census law

Legislated in November, live by October

Checked their record on the first day
20,684
People counted in the census, any of whom could check
623,633
Days from contract signature to delivery
24
Languages the service shipped in
12
Independent EU code audit result
Grade A
Critical or high severity findings
0

Links

The client says...

Custom Software Development, UX/UI Design, Web DesignMontenegro

They solved all doubts immediately and without delay.

5.0
Boro DurkovicInformation Department Head, MONSTAT
Work with us

Ready to build a public service that has to be checkable?

Whether you're working to a deadline set by law, holding records a whole country cares about or preparing for an independent audit, we'll help you choose the smallest sensible starting point and a delivery model that fits your team.

See our work

Build faster with AI

Our playbook for integrating AI into product design and development workflows.

Download the playbook