Bonus Drop #119 (2026-06-21): Are You Being Served?

Pong in a Favicon; Website in a Favicon; zeroserve

Three webby-thingies this weekend, with a nod to the coming week when we’ll be covering some CLI data visualization tools, a new tool to turn PDFs into usable data. We may even dig into some practical applications of the third resource from today.

I’m also trying a new thing where the main + (if present) repo links are right at the top of each section to make it easier to skip the blather.


TL;DR

(This is an LLM/GPT-generated summary of today’s Drop. Ollama and ibm/granite4.1:8b.)

  • A fully playable Pong game rendered entirely within a browser’s 32×32 favicon, driven by a shared physics/rasterizer pipeline with CLI scriptability and persistent game state (https://pong-in-a-favicon.franzai.com/)
  • An HTML document encoded as pixel color values inside a favicon image, bootstrapping itself from its own icon via canvas decoding — a boundary test exposing favicons as arbitrary binary containers with unexamined security implications (https://www.timwehrle.de/labs/favicon-site/)
  • zeroserve is a minimal static file server where eBPF programs replace configuration files entirely, featuring io_uring I/O, TLS 1.3 with ECH, userspace uBPF JIT, and built-in JA4 fingerprinting available as a scripting variable (https://su3.io/posts/introducing-zeroserve)

Pong in a Favicon

Photo by P. L. on Unsplash

pong-in-a-favicon.franzai.com · github

Franz Enzenhofer looked at the 32×32 pixel favicon slot – that little brand mark sitting in your browser tab that everyone treats as a read-only decoration – and decided it was a display surface. The result is a fully playable Pong game that lives entirely in the tab icon. You scroll the page to move your paddle and the score updates in the title bar. The page itself is blank on purpose, as there’s nothing to see there.

The engineering is admirably clean: underneath it all, there’s a physics engine that drives game state, combined wioth a single rasterizer which converts that state to a pixel grid, and separate renderers that consume the same grid for the favicon; there’s even a mobile tab-preview fallback (TIL pretty much all tiny mobile glowing rectangles tail to show tab icons, so there’s a workaround), and a headless terminal mode. Every surface reads from the same source, so nothing drifts; and, the CLI side is particularly spiffy – game state persists in a JSON file between invocations, so you can drive it one command at a time with node bin/pong.js up 4 && node bin/pong.js show. That makes it trivially scriptable or agentable if you’re feeling creAItive.


Website in a Favicon

colorful stamp collection in spiral notebook
Photo by Sóc Năng Động on Pexels.com

timwehrle.de/labs/favicon-site · github

Tim Wehrle started from a different angle: a PNG is just bytes, and a browser doesn’t particularly care what those bytes represent. He encoded an entire HTML document as pixel color values inside a favicon – red, green, and blue channels carry one byte of payload apiece, with a four-byte length header prepended so the decoder knows where the content ends and unused pixels begin. The page bootstraps itself from its own icon.

You will not be rendering “War and Peace” with this technique, as the practical capacity is tiny – a few kilobytes at most – and the page needs JavaScript to read the favicon back via canvas, decode the bytes, and render the HTML. Wehrle’s upfront about the absurdity here; there are obviously better ways to distribute a small document. The point’s the boundary test. Favicons are assumed to be inert 16×16 brand marks, nothing more. This project pokes at that assumption by treating them as what they actually are: arbitrary binary containers that a browser will fetch, cache, and hand back to you. The security implications of that are left as an exercise for the reader (which is a very diplomatic way of putting it).


zeroserve

su3.io/posts/introducing-zeroserve · github

If you’ve ever reached for nginx + Lua because you needed just a bit of programmable request handling without standing up a full app server, zeroserve is def worth your attention. The creator’s central idea is that an eBPF program is the entire configuration layer — no config file, just a C program that sees every request and decides what happens next: routing, headers, auth, rate limiting, proxying, all of it.

Deployment is deliberately spartan as the entire site ships as a single tarball (a super clever idea!); zeroserve indexes it on load, builds a path-to-byte-range map, and serves files straight out of the archive without unpacking anything to disk. Hot reload on SIGHUP, all I/O through io_uring, TLS 1.3 only via BoringSSL with Encrypted Client Hello, and it hardens itself with Linux namespace dropping on startup — in other words, it sheds CAP_SYS_ADMIN (the notorious “basically root” capability that lets a process mount filesystems, reconfigure namespaces, and perform all kinds of kernel-level admin tasks) and its powerful friends (CAP_NET_ADMIN, CAP_SYS_PTRACE, CAP_DAC_OVERRIDE, and the rest of the dangerous capability cabal that a root-owned process usually inherits) before the first request hits. By dropping these privileges during startup rather than after it begins serving traffic, the process is already locked inside a low-privilege sandbox when a client connection arrives, so even a later exploit would be trapped without the keys to remount filesystems, escape the container, or otherwise act as root.

The server can also compile and serve a Caddy config directly if you want that migration path. One of the best parts of this setup is that the dreaded path traversal (i.e., ../) hack is completely nullified.

For the eBPF curious out there, those scripts run in userspace via a JIT-compiled uBPF runtime — no CAP_BPF, no kernel involvement — with a preemption timer so a slow script can’t stall the event loop (which matters more than it sounds). The scripting surface is fairly complete: request rewriting, HMAC-SHA-256, JSON construction, per-key token-bucket rate limiting, AWS SigV4 signing, JA4 client fingerprinting exposed as a script variable, and a full OIDC relying-party implementation storing session state in sealed XChaCha20-Poly1305 cookies.

I haven’t pushed it into production yet, but I spent a couple hours with it locally and the cold-start and reload story is genuinely nice. Per-core throughput beats nginx on small static files and small-response proxying at a 10ms preemption interval; large-body proxying is still nginx’s game to lose (FWIW Caddy “loses” on almost all benchmarks, but I’m never going back to nginx since I’m not a fan of 0/n days).

The pre-computed JA4 components of zeroserve are intriguing, and I have some ideas as to how they + zeroserve might fit in a honeypot/deception context that I’m hoping to write about further this coming week.


FIN

Remember, you can follow and interact with the full text of The Daily Drop’s free posts on:

  • 🐘 Mastodon via @dailydrop.hrbrmstr.dev@dailydrop.hrbrmstr.dev
  • 🦋 Bluesky via https://bsky.app/profile/dailydrop.hrbrmstr.dev.web.brid.gy

☮️

Leave a Reply

Discover more from hrbrmstr's Daily Drop

Subscribe now to keep reading and get access to the full archive.

Continue reading