Skip to content

Roadmap

Bonuspunkt edited this page Feb 28, 2026 · 4 revisions

Roadmap

Where CaseDropper is headed. Spoiler: it already arrived, burned the map, rebuilt the entire thing in Zig, embedded a scripting language, and is now sitting in a FROM scratch container wondering what to conquer next.

v0.x — The Dark Ages (C#/ASP.NET)

  • Serve static files
  • Case-insensitively
  • Using the .NET runtime like it's 2019 and we don't know any better
  • Dictionary<string, string> that grew forever like an unattended Kubernetes cluster
  • FileSystemWatcher that occasionally noticed files existed
  • Container image that included an entire operating system because .NET said so

This version worked. We are not grateful. It carried a garbage collector and a runtime and it served files at the speed of "managed code." It was the mass-produced furniture of static file servers. Functional, soulless, and full of hidden dependencies you didn't ask for.

v1.0 — The Great Rewrite (Zig)

  • Rewrite the entire project in Zig 0.15.2 because we saw the light
  • Zero external dependencies. Not "few." Zero. The number between -1 and 1.
  • PathMap using std.StringHashMap — a hash map that doesn't need a garbage collector to survive
  • std.Thread.RwLock for concurrent reads — reader-writer locks, like Zig intended
  • std.Thread.Pool sized to CPU count — threads managed by people who understand threads
  • Linux inotify for file watching — direct kernel API, no wrapper, no abstraction, no nonsense
  • 1-second debounce on rebuilds — the map is torn down and reconstructed, not incrementally patched like a coward
  • Full rebuild on changes — deletions actually work now, unlike the old C# version that hoarded paths like emotional baggage
  • Single static binary — no runtime, no shared libraries, no questions asked
  • FROM scratch container — nothing in it except the binary and the void
  • Cross-compilation to 9 architectures from a single host, because Zig doesn't need QEMU to acknowledge that ARM exists

The project is spiritually complete. The binary is pure. The container is empty. Everything below is scope creep dressed up as ambition, except the scope creep turned out to be kind of incredible.

v1.1 — The Victory Lap

  • Directory browsing behind ENABLE_DIRECTORY_BROWSING env var — for the nostalgic IIS admin who misses seeing file listings in a table
  • Multi-arch Docker builds: amd64, arm64, armv7, i386, riscv64, ppc64le, s390x, mips64le, loong64 — nine architectures, one build host, zero excuses
  • Hot reload via inotify — drop files in, the map rebuilds. No restart. Zig watches. Zig knows.
  • CI/CD with GitHub Actions — the binary builds itself in the cloud, tests itself, and publishes itself to GHCR. We just watch.

All of this is done. Shipped. In production. Running on scratch containers across nine CPU architectures. The roadmap for v1.1 is a victory lap.

v2.0 — The Unhinged Era (Lua)

  • Embedded Lua 5.4.7 scripting engine — we put a scripting language in a static file server and we'd do it again
  • Vendored 32 Lua C source files into deps/lua/src/ — zero external dependencies, compiled by Zig, cross-compiled to all 9 architectures
  • LUA_SCRIPT env var — point it at a Lua script, the server generates URL aliases at index time
  • Lua rewrite(path) function called during index build — not per-request, because we have taste
  • Aliases stored in the same StringHashMap — the hash map doesn't know they came from Lua and it doesn't need to know
  • musl libc linkage — Lua needs libc, musl keeps it static, the binary remains a single file, the container remains FROM scratch, purity is nuanced
  • URL percent-decoding in normalizePath — browsers percent-encode Unicode, the server decodes it, the hash map resolves it, the file is served
  • Example Lua scripts — shipped in scripts/, discoverable by the curious, regrettable by the cautious
  • smallcaps.lua — Latin → Unicode small caps, because after Cyrillic homoglyphs, small caps felt restrained
  • Hot reload with Lua — filesystem changes trigger a rebuild that re-runs the Lua script on every path, regenerating all aliases from scratch
  • First-write-wins collision handling — real file paths always beat Lua aliases, Lua aliases don't overwrite each other, the hash map is fair

The project has transcended its original purpose. What started as "serve files case-insensitively" is now "serve files case-insensitively via a hash map populated by an embedded Lua scripting engine running character substitution functions decoded from obfuscated JavaScript, accessible via Cyrillic URLs that get percent-decoded by a Zig HTTP server running in an empty Docker container on nine CPU architectures." This is not scope creep. This is scope achievement.

v3.0 — Plausible But Unmotivated

  • Health check endpoint (return 200 if the hash map exists, which it always does, because Zig doesn't lose hash maps)
  • Metrics endpoint (count how many times someone requested favicon.ICO instead of favicon.ico — the only metric that matters. Also count Lua alias hits vs direct path hits, because we want to know if anyone actually uses Cyrillic URLs, and we're afraid of the answer.)
  • Graceful shutdown (currently it just stops, which is arguably the most graceful thing a process can do)
  • Custom 404 page (serve a file when a file isn't found — the irony is not lost on us. Bonus: serve the 404 page via its Lua-rewritten alias.)
  • Lua script hot reload (currently the script is loaded once at startup; changing it requires a restart. We could watch the script file too, but then we'd be watching the watcher, and that's the kind of recursive architecture decision that ends careers.)

None of these are started. None of these are planned. They exist here so the roadmap has a future section, which makes the project look alive. The project is alive. It just doesn't need features. It needs to serve files case-insensitively with optional Lua-generated URL aliases, and it does that already, in Zig, perfectly.

v4.0 — Fever Dream

  • Multiple Lua scripts (chain them — Cyrillic-ify the smallcaps of the original path, creating URLs in tiny Ukrainian. No one asked for this. No one should.)
  • Web UI dashboard (to monitor the static file server that has four configuration variables, one hash map, and a Lua engine that runs once at startup. The dashboard would need more infrastructure than the server.)
  • WASM support (compile CaseDropper to WebAssembly and serve files from inside a browser serving files to itself — we've gone too far but Zig could actually do this, and the Lua engine would probably survive the transition)
  • AI-powered case prediction (predict what casing the user meant to type — it's always lowercase, the model has 100% accuracy, we don't need AI for this but we do need a bullet point that mentions AI for SEO)
  • LuaJIT integration (replace PUC Lua with LuaJIT for faster index builds. The index build takes milliseconds. JIT compilation takes longer than the thing it's compiling. This is a solution searching for a problem and not finding one.)
  • Rewrite in C (just kidding. Zig or nothing. Andrew Kelley didn't forge a language so we could go back to header files and undefined behavior.)

Long-Term Vision

CaseDropper will continue to serve files case-insensitively, with optional Lua-generated URL aliases in Cyrillic, small caps, or whatever other alphabet someone writes a rewrite() function for, until:

  1. The sun engulfs the earth
  2. The maintainer discovers that nginx can do this with two lines of config (it can't do the Lua part — well, OpenResty can, but it does it per-request, like an animal)
  3. Zig reaches 1.0 and we have to update the build.zig (this will happen first)
  4. Someone writes a Lua script that generates aliases in Wingdings and we have to seriously reconsider what we've built

Whichever comes first. In the meantime, the binary sits in its empty container, statically linked, watching the filesystem with inotify, lowercasing paths, running Lua scripts at index time, decoding percent-encoded Cyrillic URLs, serving files. No garbage collector. No runtime. No regrets. Just Zig, Lua, and a hash map that knows too much.

Clone this wiki locally