Ivan Bohun · a self-hosted swarm

This page is not
in the cloud.

It is being served to you, right now, by four ESP32-S3 microcontroller blades, sitting on a shelf in a London flat. They are watched over by two more observer units that witness and visualize the swarm.

The router forwards every request to one immortal virtual MAC address: the mask, worn by whichever blade won the election. The wearer hands your connection to the least busy blade, so you are answered by a different microcontroller from the one you connected to.

Every blade carries both programs, balancer and server, and the mask moves between them by election, held over encrypted radio, off the wire.

The page you are reading lives in flash memory, brotli-compressed, behind a TLS 1.3 handshake that terminates on a 240 MHz microcontroller.

No AWS. No landlord. No rent.

Serving blades 4
Balancing now otaman
Answering you ·
Packet loss 0%
Total draw ~22 W
Observer blades 2

The fleet, as you read this

This table is not a screenshot. Your browser is polling /roster on the same machine that sent you this page, every three seconds. The blades gossip over an encrypted ESP-NOW radio mesh once a second, and each heartbeat carries the fleet's whole picture: who is alive and serving, how strong every link is, what each blade has answered and how fast, and what the balancer has relayed.

swarm roster connecting
NodeRoleStatusLinkServedTempUptime
reading the roster…
Quorum
·
Active backends
·
Availability
·
Connections received
·
Connections relayed
·
Connections refused
·
Broken mid-transfer
·
Closed after stalling
·
p50 connect to backend
·
p50 time to TLS first byte
·
p50 response time
·
p95 response time
·

The names come from the Cossacks: the otaman is the elected leader, wears the mask and hands every arriving connection to somebody else, while each kozak does the actual work of answering it. Every serving blade carries both programs, so which one it is doing depends only on who currently holds the address. The pysar is the scribe that watches and keeps the record, and the kobzar is named for the wandering bards who carried Cossack history in song - here, the big screen that keeps the swarm's chronicle.

Since the fleet started balancing, the elected node stops serving pages itself: it holds the mask, takes every incoming connection, and splices it to a kozak that does the TLS work. It works at layer 4, TCP, and never sees where one request ends and the next begins, so it counts connections while the blades count requests - those are different numbers, since your browser pulls this page, its favicon, the og image and every roster poll down a single connection it keeps open. The blades' figures in the table are requests; the otaman's is their sum.

Connections received is what arrived at the door since this node was elected, relayed is what reached a clean finish, and connections refused is what was turned away instantly because every backend was already busy. Broken mid-transfer is a connection that died in flight, usually a reader closing the tab; closed after stalling is one that went silent and was cleaned up rather than left squatting a slot. In the second row, connect to backend is how long the balancer waits to reach a kozak, and time to TLS first byte is how long until that kozak sends its first TLS byte back. The response times come from the blades instead - each kozak times every request it answers and reports its own p50 and p95, and these are the average across the serving ones.

Ivan Bohun broke his sabre rather than swear the oath,
and legend says the shards return when the land is free.

Cossack colonel · kharakternyk

Distributed system, in miniature

The trick that makes a swarm of disposable microcontrollers behave like one durable server is that the identity is not attached to any of them. The blade holding the identity acts as a Level 4 TCP splicer, relaying your connection to one of the downstream serving blades. The fleet behaves like a single machine, but the work is spread across all of them.

UGREEN 200 W · 6C + 2A one mains lead in IVAN BOHUN 4.3″ LCD touchscreen Kobzar · Waveshare ESP32-S3 2.8″ TFT touch Pysar · diymore ESP32-S3 DevKitC N16R8 drives the LED rail through the shifter N1 N2 N3 N4 wears the mask 4× LILYGO T-ETH-Lite ESP32-S3 Otaman + three Kozaky esp-now · 1 Hz TENDA SG108 the world 74AHCT125 · 3.3→5 V sk6812 rgbw · six pixels, one per member N1 · N2 · N3 · N4 · Display · Mission Control

The machine itself. Four serving blades on their pads, each with its own jack into the six-port fabric. The small screen is the pysar keeping the roster; the 4.3 inch touch panel is the kobzar keeping the chronicle. Along the front edge runs an SK6812 rail with one pixel per member: lime for the otaman, white for the kozaky, blue for the pysar, purple for mission control. Your request enters at the switch, rides the lime cable up to the mask holder, and is spliced back down the green one to the kozak that answers it.

you a browser the mask one static address n1 otaman · wears it n2 kozak · ready n3 kozak · ready n4 kozak · ready display pysar · only watches mission control kobzar · only watches ESP-NOW heartbeats 1 Hz on death, mask moves

The mask. The address you connect to is a locally administered MAC that no factory ever issued. The elected leader writes it into its own Ethernet controller and claims the static IP. Lose that node and the next one in line writes the same address into itself, sends a gratuitous ARP so the switch relearns the port, and carries on answering. The visitor never learns that anything died.

one tls session, browser to kozak you a browser tls client tcp :443 the mask one static ip one vmac the router forwards 80/443 worn by the otaman the otaman layer-4 tcp splicer never decrypts never parses http raw bytes only least-connections round-robin on ties 4 in flight per backend 12 relay slots total past that, it waits kozak 1 tls handshake here kozak 2 tls handshake here kozak 3 tls handshake here page in flash page in flash page in flash usable backends the radio gossip esp-now encrypted 1 hz not on the wire the backend set alive · serving · holding an address rebuilt once a second otaman kozak 1 kozak 2 kozak 3 real-time backend discovery the splicer keeps no list of backends in config. it learns them from the radio, and drops any blade that goes quiet for three seconds.

One request, end to end. The otaman splices the raw bytes to the least busy kozak, so the encrypted session runs from your browser straight past the balancer. The backend set comes from the radio - every blade broadcasts its state once a second.

failover ladder four states, one blade lost at a time the mask: one vmac + one ip, the only forwarded address 1 healthy one otaman splices, three kozaky serve. least-connections, round-robin on ties. the mask otaman b1 splices :443 kozak b2 serves tls kozak b3 serves tls kozak b4 serves tls the otaman relays raw bytes 2 otaman lost b1 stops beating. b2 wins the election and takes the mask. declared lost after 3 s the mask lost b1 no heartbeat otaman b2 splices :443 kozak b3 serves tls kozak b4 serves tls relays in flight die with it. the next request lands on b2 3 and again b2 goes too. b3 wins and wears the mask. one backend left. the mask lost b1 no heartbeat lost b2 no heartbeat otaman b3 splices :443 kozak b4 serves tls roles swap by election 4 solo b4 wears the mask and splices to its own server over loopback. nobody left to balance. the last line of defence the mask lost b1 no heartbeat lost b2 no heartbeat lost b3 no heartbeat solo b4 mask + server 20 of 20 requests still served loopback otaman - wears the mask kozak - serves tls lost - silent for 3 s esp-now roster, 1 hz

The ladder down. A blade is declared lost 3 seconds after its last heartbeat; the next in line wins the election, takes the mask and rebuilds its backend set from the gossip roster. A leader that stands down deliberately closes its relays first; one that dies takes its relays with it. The last blade standing splices to its own server over loopback.

The swarm heals itself

Each blade watches its own health, probing its own public port every 7 seconds. The blade that balances watches twice: itself the same way, and the whole fleet through every relay it opens. Either signal can pull a blade out of rotation.

self-probing runs on every blade a blade every 7 seconds opens a tcp connection to its own public port, over loopback 2 in a row 14 seconds reports itself as not serving and does not participate in leader election. the load balancer benches the blade within 2 s. 6 in a row 42 seconds it crashes on purpose, so the coredump records where its server was stuck, then reboots. circuit breaker runs on the balancer the otaman · splicer counts what comes back n2 serving n3 benched n4 serving the bench in: 3 empty replies out: any byte, at once 15 s, doubling, 4 min cap benched blades are skipped when picking a backend if every backend is benched, one is picked anyway - the bench never turns a visitor away

Two health checks. Each blade opens a TCP connection to its own public port every 7 seconds: two failures and it stands down, six and it crashes on purpose so the coredump shows where its server was stuck. The balancer separately benches any backend that accepts connections and returns nothing three times running - 15 seconds, doubling to a 4 minute cap. One byte back clears it.

The swarm watches itself

Two touchscreens and a strip of light, all fed by the same 1 Hz heartbeat the blades already send each other. No polling, no server, no dashboard in a browser somewhere - the instruments are part of the machine.

The Pysar’s dashboard

2.8″ TFT · the chronicler
IVAN BOHUN Pysar @ the mask loss 0% the mac address N1 otaman-27100%5h N2 kozak-26100%5h N3 kozak-3299%5h N4 kozak-24100%5h Display pysar----2h Mission kobzar-20100%1h 6 / 6 up gen 1 1284 req 8 ms avg rssi -26 RAIL DMA 6px cap25% io4

Identity, the vMAC, every member with signal strength, delivery ratio and uptime, then quorum, request count, latency and average RSSI. The six chips along the bottom mirror the physical rail, pixel for pixel.

The rail tells the truth

SK6812 RGBW · six pixels
N1N2N3 N4DisplayMission lost firmware release wears the mask

Green follows the mask wherever the election moves it. Pull a blade’s power and its pixel turns red inside three seconds; push a release and it holds amber for the length of the transfer.

the boot parade · white → green → blue → red

Boot walks four dots - white, green, blue, red - proving every joint is functional before the roster paints.

The Kobzar’s panel

4.3″ touch · keeps the chronicle
IVAN BOHUN HEALTHY RELEASE ONGOING 64% N1 otaman 100% N2 kozak 100% N3 kozak 64% N4 kozak 99% THE CHRONICLE 18:42 N3 firmware release 18:39 reign record 6h 12m 17:02 N1 claimed the mask

A wider glass for wider truth: health badge, live release progress, sixty-sample sparklines per node and a scrolling chronicle of everything the swarm has done.

this very page, baked to a strip · scrolled by finger

It also renders this very page, baked to a pixel strip and scrolled by finger.

Two hundred and ninety-two joints

None of this arrived assembled (unintentionally). Every header, every breakout, every wire in the level shifter was soldered on a kitchen table by someone who had barely held a soldering iron in July 2026, and every joint was proven with a multimeter before it was ever given power.

186header pins across six blades
44joints on the display node
44on the level-shifter board
16USB-C breakout joints
2solder-bridge board mods
01

Headers, six times over

Fifteen-pin strips down both flanks of every blade, soldered from the jack side so the pins rise where the Duponts land. Continuity beeped pin by pin, and a diode-drop baseline taken on all nine boards - 0.486 to 0.503 V, tight enough to prove no cold joints.

02

Power, broken out

Four USB-C breakouts pinned by hand, and every one of them carries all four wires now: power, ground, and both data lines into the ESP32-S3’s native USB. The data pairs were an afterthought that became doctrine the night a bad update bricked the fleet’s own update door - since then each blade flashes from its own port, no dongle, no harness-swapping, and one board’s reversed pin order is why every pair met a multimeter before it met a computer.

03

The screens, plugged not soldered

A rainbow of female-to-female Duponts carries SPI, backlight and touch from the DevKitC to the 2.8″ glass. Nothing on a display is permanent: every link pulls apart, so replacing a dead panel needs no soldering iron.

04

The shifter board

The blades speak 3.3 volts; the LEDs listen for five. A 74AHCT125 quad buffer on a solderable breadboard translates - fourteen chip legs, eleven jumpers routed hole by hole, a 330 Ω resistor to soften the edge, and six header pins so every wire beyond it stays push-fit.

05

Measured, then powered

Thirty measurements before the first electron: no rail shorted to another, eleven grounds beeping to one net, five volts where five volts belonged, and 325.6 Ω on the signal path. The board was correct by instrument before it was ever plugged in.

06

Two deliberate bridges

The display board’s 5 V pin turned out to be isolated by design - a clone quirk that starved the rail at 2.6 volts and kept it dark. Two blobs of solder across the manufacturer’s own jumper pads joined it to USB power, and the rail lit on the first try afterwards.

07

A kurin for every blade

Next, the naked boards get walls: each blade moves into its own 3D-printed kurin - a ventilated barracks it slides into like a drawer, RJ45 nose out, a wire bay for the breakout and the slack. Case by printed case, the swarm becomes a frankenstein imitation of manufacturing.

08

The Sich, racked to spec

A ten-inch rack drawn to our own spec: kurins side by side on one shelf, jacks aft toward the switch, both screens facing forward through a CNC-cut smoked-acrylic face.

Rules the swarm lives by

Wire silence

Only the node wearing the mask answers the world. The other blades receive their visitors through it over the LAN and send nothing outward on their own, so from outside the swarm looks like a single quiet machine.

Crash only

Nothing shuts down gracefully because nothing needs to. There is no state worth preserving on any node. Pulling the plug is a supported operation and the only recovery procedure is to boot.

Own the identity

The address is a locally administered MAC chosen by hand and passed between blades by election. No provider issued it and none can revoke it.

One resource

The entire site is a single brotli-compressed HTML file with no external fonts, scripts, trackers or CDNs. A first-time visitor pays for one TLS handshake and one response.

Deterministic election

The lowest numbered candidate that is healthy both on radio and network leads. Every node computes the same answer from the same heartbeats, making leader election deterministic.

Reachable forever

Every blade boots into its own identity before it is eligible to lead, so a bad release can always be replaced remotely. The swarm can be rebuilt over the air without anyone touching a cable.

Prove you can serve

Every blade routinely opens a TCP connection to probe its own public port. Two failures and it stands down from the election; six and it reboots. A blade that cannot reach itself has no business receiving visitors.

Judge by outcomes

The balancer ignores what a blade claims about itself. A backend that goes silent on three relays in a row is benched for 15 seconds, and each repeat doubles the bench, up to four minutes.

Degrade to solo

Lose the leader and the next blade becomes one in seconds. Keep losing blades and the backend set shrinks with the fleet, down to the last survivor splicing to its own server over loopback.

Kyrylo Novotarskyi

Principal Engineer in London, fifteen years in distributed systems, performance engineering and the kind of reliability work that “only goes in one direction” ↑. The swarm above is the sort of thing I do for fun. Below is what I have done at work.

2026 → now

TBA

Member of Technical Staff
2024 → 2026

Pump.fun

Founding / Principal Engineer

Tenth engineer in. Shaped hiring and grew the team past seventy. Built the Community Integrity vertical from the ground up. Led the first Platform Engineering group to rebuild the mission-critical Coin infrastructure carrying most of the traffic.

$10m+Annual savings
3 to 7xPerf gains
99.999%Availability
10 to 70+Team scale
2022 → 2024

Deliveroo

Senior Staff Engineer

Led Trust engineering against fraud and abuse at scale, led the company-wide compute efficiency programme, architected and delivered the real-time ML feature platform behind novel Fraud Detection models into production.

2019 → 2022

Kobalt Music Group

Principal Engineer

Sole Principal across an eighty-person engineering org. Took the Royalty Processing Engine from on-premise Oracle PL/SQL to a fully streamed AWS pipeline, designed, built and delivered the Chain of Title system that the business runs on. Primary technical IC for due diligence through the sale to Francisco Partners.

2018 → 2019

Amazon Prime Video

Senior Software Engineer

Performance and reliability for video delivery across forty thousand device types. Profile-guided and post-link binary optimisation of the rendering framework on living room hardware, which is where the taste for squeezing constrained devices came from.

2016 → 2018

Wise

European Expansion Lead

Led European Expansion across product, engineering, operations and support, a group of more than a hundred people, and re-architected core payment processing along the way.

Before

HappyCar staff engineer on early AWS microservices (2014-2015) · ironSource high-load realtime billing pipelines, among the first AWS Redshift adopters worldwide (2013-2014) · Sixt / DriveNow backends behind the car-sharing launch across European capitals · Softheme engineering lead.

Foundations

Kyiv Polytechnic Institute, MSc and BSc in Computer Science, summa cum laude. Computer Systems and Networks, with post-graduate research in cryptanalysis on resilient S-Box construction meeting the Strict Avalanche Criterion.

Tools

Python - mother tongue · Java - father tongue · Go (it grows on you) · TypeScript (regretfully) · Lua (when possible) · C++ · Rust (when going deep) · terrible C on bare metal (when the occasion calls for it) · ASM (for occasions that never call)
Stateful stream processing, distributed tracing, fleet-wide continuous profiling, performance engineering from datacentre scale all the way down to a microcontroller with 8 MB of RAM.

Let's connect