When you write code for a municipal ballot server, a segfault isn't just a bug. It's a constitutional crisis waiting to happen.
Last season, our engineering collective took on the task of deploying an open-source ranked-choice voting prototype for a local civic district. We thought the hard part would be the cryptography. We were wrong. The hard part was wrestling the Linux kernel, systemd states, and strict hardware constraints into something that a skeptical board of elections could audit without needing a computer science degree.
Developers love abstraction. We build microservices, spin up containers, and abstract away the metal until the operating system feels like an infinite playground. But public infrastructure doesn't care about your abstraction layers. When we deployed our initial daemon on a hardened Debian distribution, we hit our first real-world wall: ephemeral storage. Most cloud architectures rely on ephemeral disks and auto-scaling groups. Voting machines, or the servers tallying their outputs, require determinism. If a node drops, the state must recover instantly, perfectly, and without phoning home to a proprietary telemetry server.
We stripped our stack down to the bare essentials. No Kubernetes orchestration layer adding unnecessary network overhead. Just systemd managing a statically compiled Rust binary communicating directly with a local SQLite instance mounted on encrypted disk partitions. Every state transition had to be written to an append-only log that could be read by a parish clerk holding a flashlight and a printed hash manifest.
Here is a snippet of the core state machine logic we used to ensure votes are processed sequentially without race conditions:
use std::sync::mpsc::{sender, receiver};
use std::fs::OpenOptions;
use std::io::Write;
pub struct BallotLedger {
log_path: String,
}
impl BallotLedger {
pub fn record_vote(&self, encrypted_vote: &[u8]) -> Result<(), std::io::Error> {
let mut file = OpenOptions::new()
.create(true)
.append(true)
.open(&self.log_path)?;
file.write_all(encrypted_vote)?;
file.sync_all()?;
Ok(())
}
}
Notice that sync_all call. In standard web development, writing to disk asynchronously is fine for performance. In civic tech, if the power cuts out the millisecond a voter hits submit, that write must be on the physical platter or flash chip before the UI renders success. Performance optimization took a back seat to durability.
Another harsh lesson was dependency management. Developers pull in hundreds of crates or npm packages with a single command. For a government contract, every third-party dependency is an attack surface and an audit liability. We spent three weeks auditing our dependency tree, eventually cutting out all external network libraries entirely. If the system can't talk to the internet, it can't be exfiltrated. Air-gapping forces you to write cleaner, more self-contained code.
The deployment taught us that civic technology is fundamentally an exercise in empathy. We spent days arguing about cryptographic blinding factors, but the real breakthrough came when we watched a non-technical poll worker interact with our command-line recovery script during a dry run (our notes). If the humans operating the hardware don't understand what the machine is telling them, the technology has already failed.
We need more engineers building public infrastructure, but we have to leave our Silicon Valley habits at the door. Design your next system assuming failure means losing the public trust instead of just losing a user session.
Top comments (0)