Kachd, and the plumbing of privacy.
Most "privacy-first" claims are policy pages. This one is about code.
When I started Kachd, I decided up front that the privacy story had to be a property of how the thing was built, not a paragraph in the footer. That has consequences: some convenient third-party tools don't get used, and some "obvious" analytics don't exist. I'll walk through what the trade actually looks like.
Third parties are data surfaces.
Every vendor you call is a copy of your users' data you don't control. Kachd runs on a deliberately short list:
- Cloudflare for infrastructure (Workers, R2, D1, Queues, Email Routing)
- Stripe for creator payouts
Authentication is self-hosted — no third-party identity vendor in the request path, no SSO provider holding session state, no shared tenant pool. Voice and video route through a WebRTC SFU that never sees plaintext — payloads are end-to-end encrypted between participants and the SFU only ships opaque bytes.
No product analytics vendor. No session replay. No third-party chat widget. No growth tool that sits between the visitor and the app and takes a copy of the events on the way.
The list isn't short because I'm lazy. It's short because each item on it is an organization whose servers briefly hold something user-adjacent. Every addition widens the surface.
Files: SHA-256 dedup before R2, via a Worker.
When a user uploads a file to Kachd, it goes through kachd-scan-worker, a Cloudflare Worker whose
one job is to hash the upload with SHA-256, check whether R2 already has an object with that
addressable name, and — if not — write it. If the file is already there (because another user
uploaded the same thing), the new user just gets a reference.
The Worker itself never keeps the file body outside the R2 path it writes to. The index key is the hash, not the content. Deletion is by reference count; when the last reference drops, the object goes.
Metadata is pooled; content is not.
On the mail side (same philosophy), raw MIME lands in R2 under a per-domain prefix, and D1 stores only the thread pointers. A breach of D1 gives you subjects and timestamps, not bodies. To reconstruct anything meaningful you'd need both stores, and they live behind different bindings.
Kachd's document channels follow the same rule: the Drizzle schema for messages stores references to R2 content and the minimum org-scoping metadata the API needs. It does not store the rendered content of uploads.
Voice, video, and DMs: end-to-end encrypted.
All voice, video, and direct messages on Kachd are end-to-end encrypted. Session keys are negotiated between participants and never leave the client. The WebRTC SFU that routes traffic sees opaque payloads — it can't decode them, transcribe them, or mine them for signal. Tokens are short-lived, and rooms don't persist audio.
Creator payouts don't require content review, which means the system doesn't need to be able to read the content in the first place.
Multi-tenant isolation, enforced at middleware.
Every query on the Kachd backend is scoped to the active organization at the middleware layer before the query builder ever runs. Row-level filters are a last defense, not the first. You can't accidentally join across orgs — the scoped query builder doesn't know about rows outside the current tenant until you explicitly break out, and breaking out requires privileged code paths.
For an engineer, this is the most boring part of the system and also the part with the highest cost of failure. It's worth being boring.
What this costs us.
No behavioral analytics means we guess at funnels instead of measuring them. No third-party chat means I answer tickets myself. No session replay means some UI bugs take longer to reproduce. When something goes wrong, we have less data to look at than a typical platform would.
It's worth it.
What this buys users.
A creator on Kachd knows the short list of places their data has been: two companies, neither of them in the business of selling it, plus a WebRTC router that can't read what it's routing. The architecture makes certain failure modes impossible instead of asking users to trust that they won't happen.
Privacy by default is plumbing. You either design it in or you don't.