<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Wen Durai</title>
    <description>The latest articles on DEV Community by Wen Durai (@wendurai433).</description>
    <link>https://dev.to/wendurai433</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4003147%2F3dd7cef5-6d97-47c2-8554-e905350d6df4.jpg</url>
      <title>DEV Community: Wen Durai</title>
      <link>https://dev.to/wendurai433</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wendurai433"/>
    <language>en</language>
    <item>
      <title>I Built a Serverless Stranger-Matching Tool with Zero Database — Here is the Architecture</title>
      <dc:creator>Wen Durai</dc:creator>
      <pubDate>Fri, 26 Jun 2026 01:50:19 +0000</pubDate>
      <link>https://dev.to/wendurai433/i-built-a-serverless-stranger-matching-tool-with-zero-database-here-is-the-architecture-m6n</link>
      <guid>https://dev.to/wendurai433/i-built-a-serverless-stranger-matching-tool-with-zero-database-here-is-the-architecture-m6n</guid>
      <description>&lt;h2&gt;
  
  
  The elevator pitch
&lt;/h2&gt;

&lt;p&gt;You open &lt;code&gt;fata.uk&lt;/code&gt;, write a few lines about what is on your mind, and AI finds a stranger whose emotional frequency matches yours. Then it gives you each other’s email and exits. &lt;strong&gt;No app. No signup. No database. No chat history.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture (or: how to build a social product with no server)
&lt;/h2&gt;

&lt;p&gt;The entire backend is a single Cloudflare Worker. State lives in GitHub Issues. Here is the flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser (signal heuristic, ms-level)
  → HMAC-signed POST → Cloudflare Worker
    → BGE-M3 embedding API (1024-dim)
    → AES-256-GCM encrypt → GitHub Issues (encrypted pool)
    → Multi-channel scoring → MMR diversity rerank
    → Match found? → Internal LLM resonance → Resend email
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Layer 1: Browser-side signal density
&lt;/h3&gt;

&lt;p&gt;Before any API call, the browser computes four heuristics on the user’s text: length, lexical diversity, sentence count, and concrete detail markers. Score &amp;lt; 0.3? Gentle nudge to write more. Score &amp;lt; 0.15 on the server side? Rejected. This filters out noise before it hits the pool.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 2: Proof-of-Work gate
&lt;/h3&gt;

&lt;p&gt;Every submission requires solving a SHA-256 PoW challenge (difficulty 16 — ~1-2s on mobile). The solution is verified server-side, and a short-lived submit token (30 min TTL) is issued. Combined with per-IP and per-email rate limits, this makes automated abuse economically unattractive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 3: Embedding with fallback chain
&lt;/h3&gt;

&lt;p&gt;The Worker calls SiliconFlow’s BGE-M3 embedding API (1024-dim). If that fails, it falls back to the browser-provided TF-IDF embedding. If both fail, server-side TF-IDF is computed. The original text is discarded after embedding — the vector cannot be reversed back to words.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 4: GitHub Issues as an encrypted matching pool
&lt;/h3&gt;

&lt;p&gt;This is the part I find most fun. Sensitive data (email, embedding, text snippet) is AES-256-GCM encrypted and stored in Cloudflare KV. Only metadata (language, embedding type, key version) goes into the GitHub Issue body. The pool is public but every entry is ciphertext.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 5: Multi-channel matching + MMR rerank
&lt;/h3&gt;

&lt;p&gt;The matching engine scores candidates across four channels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Semantic similarity&lt;/strong&gt; (cosine similarity on embeddings, weight 0.30)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intent compatibility&lt;/strong&gt; (LLM-parsed emotional need, weight 0.40)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Style compatibility&lt;/strong&gt; (responsive vs. expressive, weight 0.15)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signal bounce&lt;/strong&gt; (signal density product, weight 0.15)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Top candidates are reranked with MMR (Maximum Marginal Relevance) to ensure diversity in the matched pool.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 6: AI writes the introduction, then disappears
&lt;/h3&gt;

&lt;p&gt;When a match is found, an internal LLM call generates a short resonance paragraph and three icebreaker questions. Both users receive each other’s email via Resend. After that, fata is out of the picture — all further communication happens in the user’s own email client.&lt;/p&gt;

&lt;h2&gt;
  
  
  The security story
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key separation&lt;/strong&gt;: &lt;code&gt;HMAC_KEY&lt;/code&gt; (API signing, frontend-visible) ≠ &lt;code&gt;ENCRYPTION_KEY&lt;/code&gt; (data at rest, Worker secret only)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limiting&lt;/strong&gt;: Two-layer (in-memory + KV), fail-closed. If KV is unreachable, requests are rejected — not passed through.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PoW + email cap&lt;/strong&gt;: 3 submissions per email per day + 500 global daily cap&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2026-06-20 incident&lt;/strong&gt;: A public &lt;code&gt;/api/hmac-key&lt;/code&gt; endpoint was discovered and abused for LLM proxy spam (3.49 billion tokens in one day). Fixed within hours. All public LLM endpoints deleted. Postmortem in the repo.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why email instead of in-app chat?
&lt;/h2&gt;

&lt;p&gt;Every "anonymous chat" product eventually faces the same trust problem: users have to believe the platform when it says it does not read their messages. fata solves this by making it architecturally impossible to read messages — because the messages are not in fata. They are in your Gmail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Static HTML page (single file)
  → Cloudflare Worker (fata.uk/api/*)
  → GitHub Issues (encrypted pool)
  → Cloudflare KV (sensitive data, 30-day TTL)
  → SiliconFlow BGE-M3 (embedding)
  → SiliconFlow DeepSeek V3 (resonance LLM)
  → Resend (email)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://fata.uk" rel="noopener noreferrer"&gt;fata.uk&lt;/a&gt;&lt;/strong&gt; — open in any browser.&lt;/p&gt;

&lt;p&gt;Source: &lt;strong&gt;&lt;a href="https://github.com/gsailing19/fata" rel="noopener noreferrer"&gt;github.com/gsailing19/fata&lt;/a&gt;&lt;/strong&gt; (MIT)&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you have built something with "impossible" architecture — zero servers, zero databases, or a constraint that shaped the product in unexpected ways — I would love to hear about it in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>architecture</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
