<?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: Philippe Quattrociocchi</title>
    <description>The latest articles on DEV Community by Philippe Quattrociocchi (@pquattro).</description>
    <link>https://dev.to/pquattro</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3897152%2F64c289e4-4bce-4045-beb4-0d8db2d1023c.jpg</url>
      <title>DEV Community: Philippe Quattrociocchi</title>
      <link>https://dev.to/pquattro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pquattro"/>
    <language>en</language>
    <item>
      <title># How MemoraEU Cannot Read Your Memories — Even If We Wanted To</title>
      <dc:creator>Philippe Quattrociocchi</dc:creator>
      <pubDate>Sun, 26 Apr 2026 07:05:09 +0000</pubDate>
      <link>https://dev.to/pquattro/-how-memoraeu-cannot-read-your-memories-even-if-we-wanted-to-5ceh</link>
      <guid>https://dev.to/pquattro/-how-memoraeu-cannot-read-your-memories-even-if-we-wanted-to-5ceh</guid>
      <description>&lt;h1&gt;
  
  
  How MemoraEU Cannot Read Your Memories — Even If We Wanted To
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Zero-knowledge architecture of a sovereign AI memory layer&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The question nobody asks enough
&lt;/h2&gt;

&lt;p&gt;When Claude, ChatGPT, or Gemini "remembers" something, where does it go? To Anthropic's, OpenAI's, or Google's servers. In plaintext. Potentially used to fine-tune future models. Subject to the Cloud Act if the company is American.&lt;/p&gt;

&lt;p&gt;That's the trade-off we implicitly accept in exchange for convenience.&lt;/p&gt;

&lt;p&gt;MemoraEU makes a different bet: &lt;strong&gt;the server must never be able to read your data&lt;/strong&gt;. Not as a policy. As an irreversible technical constraint. This post explains how we get there — and why it's harder than it sounds when you still want semantic search to work.&lt;/p&gt;




&lt;h2&gt;
  
  
  The architecture in one sentence
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Content is encrypted on your machine before leaving your machine. The key never leaves your machine. The server stores opaque blobs and floating-point vectors.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's it. Everything else is implementation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key derivation: PBKDF2-HMAC-SHA256
&lt;/h2&gt;

&lt;p&gt;You configure two environment variables in your MCP server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;MEMORAEU_SECRET&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your-long-unique-passphrase&lt;/span&gt;
&lt;span class="py"&gt;MEMORAEU_SALT&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;one-salt-per-installation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At startup, a single derivation operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cryptography.hazmat.primitives.kdf.pbkdf2&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PBKDF2HMAC&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cryptography.hazmat.primitives&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hashes&lt;/span&gt;

&lt;span class="n"&gt;kdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PBKDF2HMAC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;algorithm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;hashes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SHA256&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;# AES-256 → 32 bytes
&lt;/span&gt;    &lt;span class="n"&gt;salt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;salt_bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;iterations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# NIST SP 800-132 recommends ≥ 10,000
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;derive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;100,000 iterations of SHA-256: enough to make brute-forcing a long passphrase prohibitively expensive, without noticeably slowing down startup (&amp;lt; 200 ms on a modern laptop).&lt;/p&gt;

&lt;p&gt;The derived key is kept in RAM for the duration of the session. It is never written to disk, never transmitted, never logged.&lt;/p&gt;




&lt;h2&gt;
  
  
  Encryption: AES-256-GCM
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cryptography.hazmat.primitives.ciphers.aead&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AESGCM&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plaintext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;nonce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urandom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                              &lt;span class="c1"&gt;# 96-bit, random per message
&lt;/span&gt;    &lt;span class="n"&gt;ciphertext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AESGCM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plaintext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nonce&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ciphertext&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ascii&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The format of the blob stored server-side:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;base64( nonce[12 bytes] | ciphertext | auth_tag[16 bytes] )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three key properties of GCM:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Confidentiality&lt;/strong&gt; — without the key, the ciphertext is indistinguishable from random noise&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrity&lt;/strong&gt; — the 16-byte authentication tag detects any modification of the ciphertext (authenticated encryption)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unique nonce per message&lt;/strong&gt; — identical content produces different blobs on every encryption&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What the server sees: a base64 string. What it can infer: the approximate size of the original content (± a few bytes). Nothing else.&lt;/p&gt;




&lt;h2&gt;
  
  
  The real challenge: searching encrypted data
&lt;/h2&gt;

&lt;p&gt;Encrypting and storing is easy. But an AI memory without search is useless. And semantic search requires understanding the meaning of content — something a server cannot do on ciphertext.&lt;/p&gt;

&lt;p&gt;The naive solution would be to decrypt server-side to compute the embedding. Obviously we don't do that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our approach: embeddings are computed before encryption, on your machine.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plaintext
    │
    ├─► Mistral Embed (local) ──► float[1024] vector ──► Qdrant (server)
    │
    └─► AES-256-GCM ──► opaque blob ──► PostgreSQL (server)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you store "I use ESP32-S3 with UART on GPIO21":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The plaintext goes to the Mistral Embed API (from your machine, via your Mistral API key)&lt;/li&gt;
&lt;li&gt;Mistral returns a 1024-dimensional vector representing the semantics&lt;/li&gt;
&lt;li&gt;The text is encrypted locally&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Only the vector and the encrypted blob travel to our servers&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you search "UART wiring on my board":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The query is turned into a vector (same process, local)&lt;/li&gt;
&lt;li&gt;Qdrant performs a cosine similarity search in the vector space&lt;/li&gt;
&lt;li&gt;The matching blobs are returned&lt;/li&gt;
&lt;li&gt;They are decrypted locally before being shown to Claude&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What the server can do&lt;/strong&gt;: find the N nearest vectors to a query. It knows that two memories are "semantically close" without knowing what they say.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the server cannot do&lt;/strong&gt;: read the content, understand the topic, infer anything beyond the vector structure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Zero-knowledge deduplication
&lt;/h2&gt;

&lt;p&gt;Before storing a new memory, we check whether it already exists — without ever comparing plaintext:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;DEDUP_SKIP_THRESHOLD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.94&lt;/span&gt;  &lt;span class="c1"&gt;# exact duplicate → reject storage
&lt;/span&gt;&lt;span class="n"&gt;DEDUP_WARN_THRESHOLD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.85&lt;/span&gt;  &lt;span class="c1"&gt;# very similar → warn but store
&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;api_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/memories/search-by-vector&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vector&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# vector computed locally
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;limit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;threshold&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DEDUP_WARN_THRESHOLD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The comparison happens entirely in vector space. If the cosine similarity score exceeds 0.94, it's an exact duplicate: we reject the storage and return the existing ID. Between 0.85 and 0.94: we inform the user but store anyway.&lt;/p&gt;

&lt;p&gt;Result: zero plaintext transmitted for deduplication.&lt;/p&gt;




&lt;h2&gt;
  
  
  Smart compression (optional)
&lt;/h2&gt;

&lt;p&gt;If &lt;code&gt;MISTRAL_API_KEY&lt;/code&gt; is configured, the MCP server compresses long memories before encrypting them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Raw text (&amp;gt; 300 chars)
    │
    └─► Mistral (local): "summarize in 1-3 sentences"
            │
            └─► Compressed text ──► Embed ──► Encrypt ──► Store
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compression happens &lt;strong&gt;before&lt;/strong&gt; encryption, on plaintext, on your machine. What goes to the Mistral API is your raw text — but it's your Mistral key, on your infrastructure, and Mistral does not store prompts by default. What goes to our servers is always encrypted.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the server actually sees
&lt;/h2&gt;

&lt;p&gt;In the database, a memory looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mem_01HVKX9..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dGhpcyBpcyBub3QgcmVhZGFibGUgYXQgYWxs..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hardware"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"embedding"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.0234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-0.1823&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.0091&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-25T09:14:00Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;content&lt;/code&gt; is a base64 blob that cannot be decrypted without the key. &lt;code&gt;embedding&lt;/code&gt; is a vector that captures semantics but not literal content. &lt;code&gt;category&lt;/code&gt; is assigned locally by the LLM before encryption — it's the only readable metadata, and it's intentionally generic ("hardware", "personal", "project"…).&lt;/p&gt;




&lt;h2&gt;
  
  
  The threat model
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;This scenario is covered&lt;/strong&gt;: our servers are compromised. An attacker retrieves the entire database and the Qdrant vectors. They see base64 blobs and coordinates in a 1024-dimensional space. Without your passphrase, there's nothing they can do. Even we can't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This scenario is not covered&lt;/strong&gt;: your machine is compromised. If an attacker has access to your local environment, they can read &lt;code&gt;MEMORAEU_SECRET&lt;/code&gt; from your &lt;code&gt;.env&lt;/code&gt; or intercept content before encryption. No zero-knowledge architecture can protect against client-side compromise — this is a fundamental limitation, not specific to MemoraEU.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This scenario is partially covered&lt;/strong&gt;: passphrase reuse. If you use the same passphrase across multiple installations, compromising one machine affects all others. A different &lt;code&gt;MEMORAEU_SALT&lt;/code&gt; per installation mitigates this risk.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cryptographic roadmap
&lt;/h2&gt;

&lt;p&gt;The current v1 uses a fixed salt per installation (stored in &lt;code&gt;.env&lt;/code&gt;). This is pragmatic but imperfect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Phase 2&lt;/strong&gt;: unique salt per user, stored server-side (the server provides the salt, not the key — this doesn't break zero-knowledge)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 3&lt;/strong&gt;: per-memory-pair encryption to prevent temporal correlation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 4&lt;/strong&gt;: HSM support for enterprise deployments (key in hardware, never in RAM)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why this matters now
&lt;/h2&gt;

&lt;p&gt;LLMs are becoming permanent assistants. They will know more and more about you — your projects, your decisions, your family, your health, your finances. Where that memory is stored and who can access it is a question of personal sovereignty, not just product preference.&lt;/p&gt;

&lt;p&gt;Zero-knowledge is not a marketing argument. It's an architectural constraint we impose on ourselves so we are never in the position of having to choose between our commercial interests and your privacy.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;MemoraEU is open source. The encryption code is &lt;a href="https://github.com/pquattro/memoraeu-memoraeu-mcp" rel="noopener noreferrer"&gt;available on GitHub&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Technical questions: &lt;a href="mailto:contact@memoraeu.com"&gt;contact@memoraeu.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
