DEV Community

Cover image for Building a Zero-Knowledge Dating Platform for HIV-Positive Communities
Venkat
Venkat

Posted on

Building a Zero-Knowledge Dating Platform for HIV-Positive Communities

Erlang, Vue, and Client‑Side Encryption by Design

A close friend of mine works as an AIDS counsellor.

One day she told me something that stayed with me:

“My clients want companionship. They want marriage. They want normalcy.

But they’re terrified of being exposed.”

She had seen it repeatedly — people who were HIV‑positive, stable on treatment, undetectable… but isolated. Dating apps didn’t feel safe. Disclosure was complicated. Screenshots could ruin lives. A database leak would be catastrophic.

Then she asked me:

“Can you build something for them?

But it has to be completely private.”

Not “secure enough.”

Not “we encrypt passwords.”

Completely private.

That’s where this journey started.


Why Traditional Dating Apps Fail This Community

Most dating platforms assume the server is a trusted party:

  • The server stores your profile
  • The server reads your profile
  • The server decides who sees your profile
  • The server can be breached, subpoenaed, or misused

For HIV‑positive communities, this model is dangerous. Disclosure can affect employment, housing, family relationships, and mental health. Privacy isn’t a feature — it’s psychological safety.

So I flipped the premise:

What if the server never sees plaintext user data at all?

Not at rest. Not in logs. Not in admin dashboards. Not even temporarily.

That led to a zero‑knowledge architecture.


The Core Principle

The Server Must Not Be Able to Read Profiles

Instead of being a traditional CRUD backend, the server becomes:

  • A cryptographic relay
  • A coordination layer
  • A match facilitator
  • Not a data owner

Everything meaningful happens on the client.


The Stack

The platform combines:

  • Erlang + Yaws for backend reliability
  • Vue 3 + Naive UI for the frontend
  • Google OAuth for authentication
  • TweetNaCl for client‑side cryptography
  • Deterministic key derivation using handle + PIN
  • Hybrid symmetric + asymmetric encryption for profiles and matching

This stack supports a fully zero‑knowledge flow without sacrificing usability.


Step 1: OAuth for Authentication, Everything Else Encrypted

Google OAuth is used purely for login and identity verification.

Once authenticated:

  • The email is encrypted client‑side
  • A hash of the encrypted email is stored for lookup
  • Only minimal metadata is visible to the server:
    • OAuth provider
    • User role
    • Membership tier

All profile data is encrypted before it ever leaves the browser.

If the database leaks, it’s unreadable.


Step 2: Client‑Side Encryption of Profiles

When a user creates a profile:

  1. A symmetric AES key is generated in the browser
  2. The entire profile is encrypted client‑side
  3. Only encrypted blobs and wrapped keys are sent to the server

The database stores:

  • Encrypted profile vault
  • Encrypted “card preview”
  • Wrapped encryption keys
  • Public keys
  • Search‑safe hashed fields

The server never sees readable health, lifestyle, or personal information.


Step 3: Deterministic Key Derivation Using Handle + PIN

To reduce friction, we avoided traditional passwords.

Instead:

  • Users choose a handle and a PIN
  • These values become salt inputs for key derivation
  • Keys are derived entirely in the browser
  • The PIN is never stored

This means the server cannot brute‑force vaults, even if it wanted to.

What this achieves:

A login flow that feels simple but produces strong cryptographic guarantees.


Step 4: Public / Private Keys for Matching

Each user generates a public/private key pair in the browser.

  • The public key is sent to the server
  • The private key is encrypted client‑side using the PIN

Matching uses a controlled key‑wrapping flow:

  1. User vault keys are wrapped with the user’s public key
  2. The server temporarily unwraps with its private key
  3. The server re‑wraps with the requesting user’s public key
  4. The frontend unwraps locally

A simple metaphor:

It’s like passing a sealed envelope through a courier who can’t read it — only re‑seal it for the next recipient.

At no point does the server retain readable profile data.


Step 5: Erlang + Yaws for Reliability

Why Erlang?

  • Massive concurrency
  • Fault tolerance
  • Lightweight processes
  • Predictable performance under load

Yaws serves dynamic HTML with embedded markers (data-app-id, session IDs, etc.) which Vue hydrates on the frontend.

This gives us a modern UI with a backend that is resilient, minimal, and secure.


Step 6: AI Matching Without Leaking Profiles

Compatibility is computed using client‑generated embeddings:

  • Each browser computes embeddings locally
  • Embeddings are normalized and binarized
  • Only non‑reversible binary hashes are sent to the server

The server compares hashes — it never sees raw interests, lifestyle traits, or personal details.

What this achieves:

AI‑powered matching without exposing sensitive information.


Threat Model: What Happens If Everything Goes Wrong?

This architecture protects users even under worst‑case scenarios:

  • Database leak: attackers get only encrypted blobs
  • Rogue admin: cannot read profiles
  • Server compromise: no plaintext data to steal
  • Network interception: TLS + client‑side encryption
  • Legal pressure: server literally cannot decrypt anything

The system is designed so that no one — including me — can read user profiles.


Lessons Learned

Most apps are built around:

“How do we store and use data?”

This app is built around:

“How do we avoid ever possessing data?”

That shift changes everything:

  • UX
  • Cryptography
  • Backend architecture
  • Matching logic
  • Threat modeling
  • Error handling
  • Recovery flows

It forces you to design with humility — assuming the server is a liability, not a guardian.


Next Steps

Future posts will dive into:

  • Key‑wrapping flows in practice
  • Account recovery without exposing secrets
  • Erlang concurrency for cryptographic workloads
  • Zero‑knowledge search and abuse prevention
  • UX patterns for client‑side encryption

This project started with a counselor who wanted her clients to feel safe finding love.

It evolved into one of the most technically challenging — and meaningful — systems I’ve ever built.

For sensitive communities, privacy‑first design isn’t optional.

It’s essential.

Top comments (0)