The shopping list is what finally did it.

We were at the store, and my wife had typed everything we needed into our chat the night before: onions, the good eggs, a birthday card for her sister. Except by the time I was in the aisle, that list had scrolled up into a river of other messages. A photo of the kids. A voice note. A “did you lock the door?” Three taps of scrolling and I still couldn’t find it. I bought the wrong eggs. (Of course I did.)

It’s such a small thing. But it sat with me. Our whole shared life, the groceries, the money conversations, the worries about our kids, the half-planned trips, was scattered across SMS, email, a couple of Google Docs nobody could ever find, and a messaging app owned by a company whose business model is, let’s be honest, us. Our most private conversations sat on someone else’s servers, in ways I’ll never fully see.

So I built our own.

Not a startup, not to disrupt anything, just a small, private app for the two of us (and the kids, eventually). I named it Enclave. It runs on a server I control, holds only our conversations, and does exactly the handful of things our family needs. Come along. The why is the whole point.

The itch: our private life shouldn’t live on someone else’s box

I rode a motorcycle from Chicago to Chennai over three years. A hundred thousand kilometers, thirty-three countries, one bike I trusted because I’d taken her apart and put her back together with my own hands. That trip rewired something in me about self-reliance: the things that matter most, you should be able to hold yourself.

For years that instinct didn’t extend to our data, because the alternative was too much work. WhatsApp is free and it just works, and in exchange, the most intimate record of a marriage (the inside jokes, the fights, the photos of a sleeping baby) lives on infrastructure we can’t control or inspect. I’m a software engineer now; I build full-stack apps for a living. At some point “it’s too much work” stopped being true. The work was just work I knew how to do.

The insight that made it sane: I could skip end-to-end encryption

Here’s where eyes glaze over, so let me explain it the way I explained it to my wife.

The gold standard for private messaging is end-to-end encryption (E2EE: your phone scrambles the message, and only the other person’s phone can unscramble it; the server in the middle just shuttles sealed envelopes it can’t read). It’s the right call for WhatsApp or Signal because they run the server, and you have to trust strangers not to peek. E2EE is what lets you not trust them.

But I control my server infrastructure. I own both phones. There is no stranger in the middle.

That changes everything. My threat model, the thing I’m actually defending against, isn’t “the company reading our chats.” There is no company. It’s “someone snooping on the wire between our phone and our server.” And that problem was solved decades ago by TLS (the same lock icon that protects your bank account, encryption in transit). Every message travels through an encrypted tunnel to a box that sits in my account, that I patched, that nobody else holds the keys to.

So I made a deliberate trade and skipped E2EE entirely. That deleted an enormous amount of complexity. Key exchange, device verification, the misery of “you can’t read your own history on a new phone.” And I’ll be honest about the cost, because the honesty is the point: I’m now trusting the box I run. If someone roots into that server, the messages are readable on it. With true E2EE, even a compromised server only sees sealed envelopes.

For a billion strangers, that residual risk matters. For my family’s grocery list, on a server only I touch? I’ll take that trade every day. The trick wasn’t building harder security, it was being honest about who I actually trust.

The real joy: building exactly the features we need

When you build for two people instead of two billion, you get to be a magpie. I went around the apps I use and took the good parts.

  • Threads, stolen from Slack. A reply hangs off the message that started it instead of derailing the whole channel. “5 replies, 2 minutes ago.” The shopping-list disaster, structurally solved.
  • Read receipts, stolen from WhatsApp. The colored double-check when she’s actually seen it. I know exactly how it works, because I wrote it.
  • And the feature that’s really ours: lists that live inside the chat. A channel in Enclave can hold documents; a checklist (shopping, packing, things to fix around the house) or a note (free-form, for the messier stuff). The grocery list isn’t a message that scrolls away anymore. It’s a living doc pinned to our “Shopping” channel. She checks off the olives from that aisle in Costco, and I see the box tick in real time from another aisle. The information efficiency is amazing. No more “Did you get the olives?”. We have a “Finances” channel, a “Kids” one, a “Travel” one, each with their own running lists and notes.

That last one is the whole dream. For years our shared brain was smeared across SMS and email and Google Drive. Now it’s in one place, our place, designed in every corner to fit the two specific humans using it.

What it was actually like to build (and run)

The honest version, because the never-struggled tone is a lie and you’d smell it: the client is React, built with Vite and styled with Tailwind, then wrapped with Capacitor so the same code runs as a native Android app on our phones, not a website pretending. The server is Node and Express, with Postgres holding the durable stuff (every message, every list item, who checked what) and Redis handling the ephemeral churn; who’s online, who’s typing.

The heartbeat of the thing is a plain secure WebSocket connection. Your phone opens a wss:// tunnel to the server, says hello with its token, and from then on it’s a live two-way line. A message lands, the other phone knows, instantly. Without any polling. Getting that connection to survive a phone falling asleep or a wifi-to-cellular handoff was the fiddly part. There’s a 30-second grace period before I mark someone “offline,” because phones lie about being gone all the time.

Then I got ambitious and added voice and video calls, which meant WebRTC and standing up a coturn server on its own VPS to relay the media when two phones can’t reach each other directly. The audio is encrypted peer-to-peer (DTLS-SRTP); the relay just forwards packets it can’t read. And because I refuse to let Google’s push service touch our notifications, the “ding” comes through a self-hosted ntfy server over the UnifiedPush standard. No Firebase.

Deploying it is a single multi-stage Docker build. One image bundling the web client and the server behind Traefik, which handles the TLS certificates automatically. One push and it’s live. The kind of boring, idempotent setup I wish I’d had on the road when sanDRina’s wiring decided to improvise in the rain.

It was weeks of evenings, and it’s not free the way WhatsApp is free. I pay in server bills and in maintenance I now own forever. I’m the on-call engineer for my family’s chat app, which my wife finds both endearing and slightly alarming.

Data sovereignty, but really it’s love

I keep seeing “data sovereignty” written up like a compliance checkbox. For me it turned out to be something much softer, the same impulse that put me on that bike. Not I want to control everything, more like I want to take care of the people I love with my own two hands, instead of outsourcing that care to a company that doesn’t know our names. The grocery list and the marriage and the motorcycle are, somehow, the same instinct: hold the things that matter close, learn how they work, and don’t hand them to a stranger just because it’s easy.

She bought the right eggs last week. Checked it off the list herself. I saw the little box tick from across town, her name beside it, and felt absurdly, disproportionately happy about it.

Build something for the people you love. Make it yours. It’s worth every late evening.

— Jay

(If you build your own and hit a wall on the WebSocket reconnection, drop me a line. That part nearly broke me, and I’d save you the evenings.)

Photo by Andrej Lišakov on Unsplash