The Code

My Use Case Link to heading

This was designed for a custom JWT validation system I wrote for one of my projects. The main API runs on AWS Lambda, and I needed a super fast caching layer for basic data. I literally needed a bare-bones key/value store, no need for persistence, replication, modules, Lua scripting, eviction policies, fine-grained ACLs, or the universe of configuration flags that comes with general-purpose data stores.

The combination of small values, tight latency, and owned endpoints made existing options feel like a mismatch. Systems like Redis and Valkey are excellent; they’re also designed to solve larger, broader problems than mine. Even with the features turned off, we still inherit the operational surface.

What I actually needed Link to heading

My workload is very read-heavy. Think on the order of 10,000 reads for every write. My data is also small and boring, a handful of bytes to a few hundred characters. The consumer and the producer are both mine. That single ownership is the key enabler; it lets me dictate the wire format and API contract without worrying about third-party clients.

I don’t need data to survive a process restart, and I don’t need cross-region fail-over. What I do need is low and predictable latency.

Feature Requirements Link to heading

  • Read-heavy: ~10,000 reads for every write

  • Small data: a handful of bytes to a few hundred characters

  • Single ownership: both the consumer and producer are mine, so I dictate wire format and API contract

  • No persistence: data doesn’t need to survive restarts

  • No cross-region failover

  • Requirement: low, predictable latency

Why UDP and fixed frames? Link to heading

The first architectural decision was the transport layer. I chose UDP for one reason: one datagram equals one operation. There’s no connection handshake or connection state to manage, and for tiny messages the kernel does less work. The usual caveats about UDP, packet loss and reordering, are real, but in our environment the network is stable, the messages are small, and the consumer is prepared to timeout and retry. I place a strict requirement on callers; they must provide a context with a deadline. If the request doesn’t complete within those bounds, the call fails. That simple rule eliminates an entire class of mystery lags.

On top of UDP, I defined a compact, fixed-size frame. I encrypt the entire frame using AES-256-GCM with a unique per-message nonce.

The Frame Link to heading

  • 1 byte per command limits us to 256 commands ever

  • 4 bytes for flags limits us to a maximum of 32 flags

  • 128 bytes for the key

  • 863 bytes for the value (chosen so the full frame fits within 1024 bytes)

This gives us a total of 996 bytes

The Encryption Link to heading

  • 12 bytes for the nonce

  • 16 bytes for the tag

This gives us a total encrypted packet size of 1024 bytes, comfortably below the typical 1500-byte Ethernet MTU. That ensures a single packet per operation without fragmentation.

One note is that putting the value at the end means in the future if I decide I need an additional field I can carve it out of the value.

Because I own both ends, I can keep security simple and non-optional. Every request and response is encrypted with AES-256-GCM using a 32-byte key.

The developer experience Link to heading

A system this small should be easy to use without memorizing a manual. The client library is a slim wrapper over the wire format. Callers construct a context with a timeout and call Set, Get, Delete, or Exists. For Set, there are two flags that cover the only interesting write behaviors I need: Overwrite (allow an existing key to be replaced) and Old (return the previous value if I want it). Encryption, frame building, deadlines applied to read and write, is all handled internally. For local testing, there’s a CLI that mirrors the library and makes it trivial to poke the server from a shell.

More importantly, the library has sharp edges where they help. If we forget to set a deadline, the call fails immediately. If a packet can’t be decrypted or the frame isn’t exactly the expected size, it’s rejected.

What this wins us Link to heading

The obvious win is latency. In practice, fixed frames over UDP with a short data path give us consistently low numbers. Operationally, there’s less to configure and fewer background behaviors to learn. Auditing is easy because the code is small. Tracing a request from socket read to map write is trivial. When something fails, it fails in a small number of explicit places. Fixed size, explicit deadlines, and a tiny command set mean the system behaves the same way everywhere. We aren’t juggling persistence modes, eviction policies, or subtle differences in server versions. The store either answers inside the deadline or it doesn’t, and both outcomes are observable and expected. Security also follows the environment, not the public internet. The system relies on a shared secret and a trusted network boundary.

Where the tradeoffs land Link to heading

I was deliberate about what I didn’t build. There is no durability; if the process restarts, the data is gone. There’s no replication or clustering; one instance serves one purpose. UDP doesn’t guarantee delivery or ordering; I accept that and designed the callers accordingly. The command set is intentionally narrow, there are no data structures, transactions, or scripts. If any of those are requirements, this tool isn’t the right fit, and that’s fine. Mature systems exist to solve those problems, and they should be used in those circumstances.

Why build instead of buy Link to heading

The honest answer is that the delta between “what I need” and “what an off-the-shelf system provides” was larger than it looked. Even when the features are disabled, we still inherit their operational surface area. There is configuration to keep them off, documentation to read to be sure, and knowledge to maintain just in case. The domain didn’t justify that overhead. By writing something minimal, encrypted UDP, fixed frames, a guarded map, four commands, I ended up with software that is easier to run, easier to test, and easier to understand than any trimmed-down general solution I tried.

I didn’t build this to be clever. I built it because it was the most boring way to solve my specific problem. Boring is good. Boring is reliable.

Should you use this? Link to heading

Probably not. This is a small personal project that may not be maintained. However, if the use case looks like mine, small values, read heavy, owned endpoints, trusted network, tight latency budgets, then a tiny encrypted UDP key–value store can be the pragmatic choice. We get simplicity, predictability, and a short mean-time-to-understanding when something breaks. If we need durability, multi-tenant security, complex data structures, or internet-facing hardening, we shouldn’t roll our own. The battle-tested tools that exist for exactly those needs are the better choice. Boring is still good, but only if it’s boring in the right problem space.

Closing Link to heading

Building my own store wasn’t about outperforming Redis on benchmarks or reinventing the wheel. It was about picking, or in this case building, the right tool for the job. By sticking to a fixed frame, enforcing deadlines, and making encryption mandatory, I built a tool that does one job quickly and predictably, with almost nothing to babysit. That’s the kind of system we can keep in our heads and that’s exactly what I wanted. And if my needs grow, migrating to Redis or Valkey later is straightforward because the client API is deliberately minimal.