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 on a shelf in a London flat, watched by two observer units that paint what the swarm is doing.

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 the microcontroller answering you is not 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 polls /roster on the machine that sent you this page, every three seconds. The blades gossip over encrypted ESP-NOW radio once a second, and each heartbeat carries the whole picture: who is alive and serving, how strong each link is, what every 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 are Cossack. The otaman is the elected leader: it wears the mask and hands every arriving connection to a kozak, which answers it. Every serving blade carries both programs; which one it runs depends only on who holds the address. The pysar is the scribe keeping the record; the kobzar, named for the bards who carried Cossack history in song, is the big screen keeping the chronicle.

The elected node serves no pages itself: it takes every incoming connection and splices it to a kozak, which does the TLS work. Working at layer 4, it never sees where one request ends and the next begins, so it counts connections while the blades count requests. Those differ because your browser pulls this page, its favicon, the og image and every roster poll down one connection it keeps open. The blades' figures in the table are requests; the otaman's is their sum.

Connections received arrived at the door since this node was elected; relayed reached a clean finish; refused were turned away at once because every backend was busy. Broken mid-transfer died in flight, usually a reader closing the tab; closed after stalling went silent and were 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 how long until that kozak answers with its first TLS byte. The response times come from the blades: each kozak times every request it answers and reports its own p50 and p95, averaged here across the serving ones.

die temperature · last 24 hours · 5-minute buckets · utc

Every heartbeat carries the sender's die temperature. The blade serving you folds all six into five-minute buckets and keeps a day of them; your browser draws that memory from /temps once a minute. Die temperature, not air: it runs well above the room, and only the differences mean anything. A gap is a bucket this blade did not hear - it remembers only since its own last boot. Hour marks are UTC.

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

What makes a swarm of disposable microcontrollers behave like one durable server is that the identity is attached to none of them. The blade holding it acts as a layer 4 TCP splicer, relaying your connection to a serving blade behind it: one machine to the world, the work 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 jacked 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 an SK6812 rail carries one pixel per member: lime for the otaman, white for the kozaky, blue for the pysar, purple for the kobzar. 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 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 in line writes the same address into itself, sends a gratuitous ARP so the switch relearns the port, and carries on. 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: six swipeable tiles - mission control with the chronicle and its reboot coroner, the refusal ledger, refusals by the minute, conversions, a day of die temperatures, and the radio's own gossip tail.

Two hundred and ninety-two joints

None of this arrived assembled (unintentionally). Every header, breakout and shifter wire was soldered on a kitchen table by someone who had barely held an iron in July 2026, and every joint met a multimeter before it met 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, each carrying all four wires: power, ground and both data lines into the ESP32-S3’s native USB. The data pairs were an afterthought until the night a bad update bricked the fleet’s own update door; since then every blade flashes from its own port, no dongle, no harness-swapping. 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

The naked boards get walls: each blade slides like a drawer into its own 3D-printed kurin, a ventilated barracks with the RJ45 nose out and a 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 others receive their visitors through it over the LAN and send nothing outward on their own, so from outside the swarm is one 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 or CDNs. The one third-party script is a Meta pixel for the Facebook page. A first-time visitor pays for one TLS handshake and one response.

Deterministic election

The lowest-numbered candidate healthy on both radio and wire leads. Every node computes the same answer from the same heartbeats: no votes, no coordinator.

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

SpaceXAI

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