Privacy-preserving mutual match: detect when two parties select each other without revealing unilateral selections to anyone.
  • TypeScript 67.8%
  • Rust 31.7%
  • JavaScript 0.5%
Find a file
sibyl a725c371e2
Some checks failed
CI / rust (push) Failing after 1s
CI / node (push) Successful in 3m43s
Update README.md
2026-05-06 01:09:30 +00:00
.forgejo/workflows Add Forgejo CI workflows 2026-04-24 14:47:23 -04:00
crates/matchlock-core feat: add matchlock-core Rust crate with full cryptographic primitive port 2026-03-30 18:58:33 -04:00
dist feat: barrel exports and build 2026-03-23 23:55:52 -04:00
examples feat: end-to-end example 2026-03-23 23:56:39 -04:00
src feat: typed errors, validation utilities, and complete validator guards 2026-03-30 13:05:49 -04:00
test feat: typed errors, validation utilities, and complete validator guards 2026-03-30 13:05:49 -04:00
.gitignore chore: init matchlock repo 2026-03-23 23:24:35 -04:00
Cargo.lock feat: add matchlock-core Rust crate with full cryptographic primitive port 2026-03-30 18:58:33 -04:00
Cargo.toml feat: add matchlock-core Rust crate with full cryptographic primitive port 2026-03-30 18:58:33 -04:00
jest.config.js chore: init matchlock repo 2026-03-23 23:24:35 -04:00
package-lock.json feat: end-to-end example 2026-03-23 23:56:39 -04:00
package.json feat: end-to-end example 2026-03-23 23:56:39 -04:00
PROTOCOL.md refactor: protocol hardening, API ergonomics, test coverage, and docs 2026-03-30 09:13:33 -04:00
README.md Update README.md 2026-05-06 01:09:30 +00:00
tsconfig.json chore: init matchlock repo 2026-03-23 23:24:35 -04:00

🗝️ Matchlock

Privacy-preserving mutual match: detect when two parties select each other without revealing unilateral selections to anyone.

The problem

Every existing matching platform operates as a trusted intermediary that sees all preferences. They know who you selected and who selected you, including rejections.

How Matchlock works

Matchlock composes two primitives:

1. DH token derivation

Two parties independently derive identical match tokens when they mutually select each other, using X25519 Diffie-Hellman:

Alice selects Bob:
  token = SHA256(DH(alice_priv, bob_pub) || poolId || "rendezvous-match-v1")

Bob selects Alice:
  token = SHA256(DH(bob_priv, alice_pub) || poolId || "rendezvous-match-v1")

// DH commutativity: same shared secret → same token

Tokens are derived locally. No server interaction required. The server never sees your selections — only the derived tokens you choose to submit.

2. Private Set Intersection (PSI)

PSI allows the server to detect overlapping tokens without learning which tokens each participant submitted. Built on OpenMined's PSI.js with an owner-held key architecture: the PSI server key is encrypted to the pool owner's public key, so the server cannot process queries without owner participation.

Together:

Party Learns Does NOT learn
Server Set sizes, timing Your selections or matches
You Your matches only Who rejected you, or who others selected
Pool owner Match token hashes Whose key belongs to whom

Installation

npm install matchlock

Usage

import { generateKeypair, deriveMatchToken, PsiService } from 'matchlock';

const alice = generateKeypair();
const bob = generateKeypair();

// Both derive the same token (DH commutativity)
const tokenA = deriveMatchToken(alice.privateKey, bob.publicKey, 'pool-1');
const tokenB = deriveMatchToken(bob.privateKey, alice.publicKey, 'pool-1');
console.log(tokenA === tokenB); // true — mutual match

See examples/mutual-match.ts for a complete end-to-end walkthrough.

Security properties

  • Zero server knowledge: The server sees only opaque hash values.
  • Unilateral privacy: If Alice selects Bob but Bob doesn't select Alice, Bob learns nothing about Alice's selection.
  • Pool isolation: Tokens are scoped to a pool ID. Cross-pool linkability requires breaking SHA-256.
  • Replay protection: Nullifiers prevent re-submission within a pool while remaining unlinkable across pools.
  • Owner-held PSI keys: The PSI server key is ECIES-encrypted to the pool owner's X25519 public key. The infrastructure operator cannot process PSI queries independently.

License

Apache-2.0