<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.4">Jekyll</generator><link href="https://confer.to/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://confer.to/blog/" rel="alternate" type="text/html" /><updated>2026-01-21T14:46:32-08:00</updated><id>https://confer.to/blog/feed.xml</id><title type="html">Confer Blog</title><subtitle>Updates and insights from the Confer team</subtitle><entry><title type="html">Sorting secrets: how to organize encrypted data</title><link href="https://confer.to/blog/2026/01/encrypted-folders/" rel="alternate" type="text/html" title="Sorting secrets: how to organize encrypted data" /><published>2026-01-21T00:00:00-08:00</published><updated>2026-01-21T00:00:00-08:00</updated><id>https://confer.to/blog/2026/01/encrypted-folders</id><content type="html" xml:base="https://confer.to/blog/2026/01/encrypted-folders/"><![CDATA[<p>Confer now supports encrypted folders. You can organize your conversations however you like, give each folder custom instructions, and drag them into whatever order makes sense to you.</p>

<p>Just like your Confer messages, your folders are stored as encrypted blobs using keys that only you have access to. That makes ordering the folders slightly more complicated. In a normal application, reordering items is trivial. You store a sort index, update it when things move, and let the database handle the rest. But since Confer folders are encrypted blobs, the database can’t “see” a sort index.</p>

<p>Instead, the sort information is stored inside the encrypted blob. That works for something like folders, because the client can download all the folders, decrypt them, and sort locally without too much overhead.</p>

<p>But this creates a new problem. If the sort order lives inside encrypted blobs, what happens when you reorder a folder? In a traditional system with integer sort indices, moving folder C between folders A and B might require updating the indices of every folder after the insertion point. With encryption, each update means re-encrypting and re-uploading. Reorder one folder, re-encrypt and re-upload twenty.</p>

<h2 id="fractional-indexing">Fractional indexing</h2>

<p>Instead, we can use fractional indexing—a technique that generates sort keys which can always be split.</p>

<p>Instead of assigning folders indices like 1, 2, 3, we assign them opaque strings that sort lexicographically. The first folder might get “a”, the second “b”, the third “c”. If you move the third folder between the first two, we don’t update three indices. We generate a new key <em>between</em> “a” and “b”—something like “aV”—and only update the moved folder.</p>

<p>The main insight is that strings, unlike integers, have infinite room between any two values. Between “a” and “b” there’s “aV”. Between “a” and “aV” there’s “aK”. You can always find a midpoint. No matter how many times you reorder, you only ever update one folder.</p>

<p>In code, it looks like this:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">midpoint</span><span class="p">(</span><span class="nx">a</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">b</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="kr">string</span> <span class="p">{</span>
  <span class="c1">// Find first character where strings differ</span>
  <span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="k">while</span> <span class="p">(</span><span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">a</span><span class="p">.</span><span class="nx">length</span> <span class="o">&amp;&amp;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">b</span><span class="p">.</span><span class="nx">length</span> <span class="o">&amp;&amp;</span> <span class="nx">a</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">===</span> <span class="nx">b</span><span class="p">[</span><span class="nx">i</span><span class="p">])</span> <span class="nx">i</span><span class="o">++</span><span class="p">;</span>

  <span class="k">if</span> <span class="p">(</span><span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">a</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// a has a character at position i</span>
    <span class="kd">const</span> <span class="nx">charA</span> <span class="o">=</span> <span class="nx">a</span><span class="p">.</span><span class="nx">charCodeAt</span><span class="p">(</span><span class="nx">i</span><span class="p">);</span>
    <span class="kd">const</span> <span class="nx">charB</span> <span class="o">=</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">b</span><span class="p">.</span><span class="nx">length</span> <span class="p">?</span> <span class="nx">b</span><span class="p">.</span><span class="nx">charCodeAt</span><span class="p">(</span><span class="nx">i</span><span class="p">)</span> <span class="p">:</span> <span class="mi">123</span><span class="p">;</span> <span class="c1">// 'z' + 1</span>

    <span class="k">if</span> <span class="p">(</span><span class="nx">charB</span> <span class="o">-</span> <span class="nx">charA</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
      <span class="c1">// Room between characters—use the midpoint</span>
      <span class="k">return</span> <span class="nx">a</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">+</span> <span class="nb">String</span><span class="p">.</span><span class="nx">fromCharCode</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">((</span><span class="nx">charA</span> <span class="o">+</span> <span class="nx">charB</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">));</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="c1">// No room—append a character after a's prefix</span>
      <span class="k">return</span> <span class="nx">a</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">n</span><span class="dl">'</span><span class="p">;</span> <span class="c1">// 'n' is roughly middle of alphabet</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="c1">// a is empty or a prefix of b—append to a</span>
  <span class="k">return</span> <span class="nx">a</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">n</span><span class="dl">'</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// Moving folder C between folders A (sortKey "a") and B (sortKey "b")</span>
<span class="kd">const</span> <span class="nx">newSortKey</span> <span class="o">=</span> <span class="nx">midpoint</span><span class="p">(</span><span class="dl">"</span><span class="s2">a</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">b</span><span class="dl">"</span><span class="p">);</span> <span class="c1">// Returns "an"</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">midpoint</code> function finds a string that sorts after the first argument and before the second. If there’s room between characters (like between ‘a’ and ‘c’), it picks the middle (‘b’). If not, it extends the string.</p>

<p>The beauty is that each reorder is O(1). One folder gets a new sort key, one encrypted blob gets updated, one request goes to the server. The server does not store any plaintext about what order your folders are in—that information only exists in decrypted form on your device.</p>

<h2 id="why-this-matters">Why this matters</h2>

<p>This is a trivial detail, and folder organization seems mundane, but it’s a good example of why end-to-end encryption is often more complicated than just “encrypt everything.” The server isn’t just storing bytes—it’s providing a service. When the service requires structure (sorting, searching, filtering), and the structure must remain hidden, you have to think carefully about what leaks.</p>

<p>The answer here – fractional indexing inside encrypted blobs, sorted client-side – is nice because it hides everything. The server learns nothing about your folder names, nothing about their order, nothing about how often you rearrange them. All it sees is a collection of opaque ciphertexts and timestamps.</p>

<p>Some things are worth the complexity.</p>

<p><a href="https://confer.to">Try it out!</a></p>]]></content><author><name>Moxie Marlinspike</name></author><summary type="html"><![CDATA[Confer now supports encrypted folders. You can organize your conversations however you like, give each folder custom instructions, and drag them into whatever order makes sense to you.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://confer.to/blog/assets/images/og-image.png" /><media:content medium="image" url="https://confer.to/blog/assets/images/og-image.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Private inference</title><link href="https://confer.to/blog/2026/01/private-inference/" rel="alternate" type="text/html" title="Private inference" /><published>2026-01-05T00:00:00-08:00</published><updated>2026-01-05T00:00:00-08:00</updated><id>https://confer.to/blog/2026/01/private-inference</id><content type="html" xml:base="https://confer.to/blog/2026/01/private-inference/"><![CDATA[<p>When you use an AI service, you’re handing over your thoughts in plaintext. The operator stores them, trains on them, and–inevitably–will monetize them. You get a response; they get everything.</p>

<p>Confer works differently. In the <a href="/blog/2025/12/passkey-encryption/">previous post</a>, we described how Confer encrypts your chat history with keys that never leave your devices. The remaining piece to consider is inference—the moment your prompt reaches an LLM and a response comes back.</p>

<p>Traditionally, end-to-end encryption works when the endpoints are devices under the control of a conversation’s participants. However, AI inference requires a server with GPUs to be an endpoint in the conversation. Someone has to run that server, but we want to prevent the people who are running it (us) from seeing prompts or the responses.</p>

<h2 id="confidential-computing">Confidential computing</h2>

<p>This is the domain of confidential computing. Confidential computing uses hardware-enforced isolation to run code in a Trusted Execution Environment (TEE). The host machine provides CPU, memory, and power, but cannot access the TEE’s memory or execution state.</p>

<p>LLMs are fundamentally stateless—input in, output out—which makes them ideal for this environment. For Confer, we run inference inside a confidential VM. Your prompts are encrypted from your device directly into the TEE using <a href="https://noiseprotocol.org/noise.html">Noise Pipes</a>, processed there, and responses are encrypted back. The host never sees plaintext.</p>

<p>But this raises an obvious concern: even if we have encrypted pipes in and out of an encrypted environment, it really matters <em>what</em> is running inside that environment. The client needs assurance that the code running is actually doing what it claims.</p>

<h2 id="attestation">Attestation</h2>

<p>Confidential computing solves this with <em>remote attestation</em>. When a confidential VM boots, the hardware generates a signed quote—a cryptographic statement containing hashes of the kernel, initrd, and command line. These hashes are called measurements, and they uniquely identify the code running inside the TEE.</p>

<p>For Confer, we extend the measurement to cover the entire root filesystem using <a href="https://docs.kernel.org/admin-guide/device-mapper/verity.html">dm-verity</a>. This builds a merkle tree over every byte of the filesystem and embeds the merkle root hash in the kernel command line. Since the command line is measured, any change to any file changes the attestation. The measurement now covers everything.</p>

<p>But a measurement is just a hash. To verify it, someone needs to be able to reproduce it.</p>

<h2 id="making-the-attestation-verifiable">Making the attestation verifiable</h2>

<p>The Confer <a href="https://github.com/conferlabs/confer-proxy">proxy</a> and <a href="https://github.com/conferlabs/confer-image">image</a> are built with nix and mkosi to produce bit-for-bit reproducible outputs. Anyone can clone the repository, run the build, and get the exact same measurements.</p>

<p>Each release is also signed and published to a transparency log that is <a href="https://search.sigstore.dev/?email=releases%40conferlabs.iam.gserviceaccount.com">easily searchable</a>. The log is append-only and publicly auditable, which means we can’t quietly publish a different build for different users.</p>

<p>Now we have an intuition for how these parts can come together.</p>

<h2 id="the-connection">The connection</h2>

<p>When you open Confer and start a conversation, your client initiates a Noise handshake with the inference endpoint. The TEE responds with its attestation quote embedded in the noise handshake.</p>

<p>Your client verifies the quote’s signature, confirming it came from genuine TEE hardware. It checks that the public key in the quote matches the one used in the handshake–this binds the encrypted channel to the TEE, preventing interception or replay by anything outside it. It extracts the measurements and confirms they match a release in the transparency log.</p>

<p>Once verification succeeds, the handshake completes. Your client now has cryptographic assurance that it’s talking directly to verified code running in hardware-enforced isolation.</p>

<p>All traffic to the inference endpoint is then encrypted with ephemeral session keys that provide forward secrecy: even if a long-term key were later compromised, past conversations remain protected because the ephemeral keys no longer exist.</p>

<h2 id="a-different-model">A different model</h2>

<p>Confer combines confidential computing with <a href="/blog/2025/12/passkey-encryption/">passkey-derived encryption</a> to ensure your data remains private.</p>

<p>This is different from traditional AI services, where your prompts are transmitted in plaintext to an operator who stores them in plaintext (where they are vulnerable to hackers, employees, subpoenas), mines them for behavioral data, and trains on them.</p>

<p>We think Confer is how AI should work.</p>]]></content><author><name>Moxie Marlinspike</name></author><summary type="html"><![CDATA[When you use an AI service, you’re handing over your thoughts in plaintext. The operator stores them, trains on them, and–inevitably–will monetize them. You get a response; they get everything.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://confer.to/blog/assets/images/og-image.png" /><media:content medium="image" url="https://confer.to/blog/assets/images/og-image.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Making end-to-end encrypted AI chat feel like logging in</title><link href="https://confer.to/blog/2025/12/passkey-encryption/" rel="alternate" type="text/html" title="Making end-to-end encrypted AI chat feel like logging in" /><published>2025-12-29T00:00:00-08:00</published><updated>2025-12-29T00:00:00-08:00</updated><id>https://confer.to/blog/2025/12/passkey-encryption</id><content type="html" xml:base="https://confer.to/blog/2025/12/passkey-encryption/"><![CDATA[<p>We want private AI chat to be simple. Yet today, many end-to-end encrypted experiences still have a level of friction that make them feel like they’re from another era: it usually either involves a long seed phrase users are asked to “store securely,” insecure password based encryption, or apps that aren’t cross-device and lose your data periodically (on reinstall, browser cache clear, etc).</p>

<p>At the heart of this friction is a simple truth: <em>cryptography turns data security problems into key management problems</em>. It’s now straightforward enough to encrypt something before backing it up to the cloud, but how do you “back up” the key used for the backup encryption?</p>

<p>This is one reason why, despite its power, end-to-end encryption has historically been impractical for the web. Web pages are designed as ephemeral views, not durable applications. So even once <a href="https://en.wikipedia.org/wiki/Web_Cryptography_API">we were able to generate strong encryption keys in the browser</a>, we’ve lacked a reliable way to <em>keep</em> them – securely and seamlessly – for ongoing use across devices and sessions. Without any other options, we’ve been stuck with marginal, often user-hostile solutions.</p>

<p>For Confer, we face the same core dilemma: we want users to seamlessly access their chats from any device, including a web browser, while ensuring those chats are end-to-end encrypted so no one else—<em>not even us</em>—can access them.</p>

<h2 id="passkeys-not-just-for-passing">Passkeys: not just for passing</h2>

<p>The WebAuthn standard allows websites to authenticate users by proving possession of a private key. A user visits a site, generates a keypair, registers the public key with the website, and subsequently authenticates by proving possession of the private key (signing a challenge). On modern platforms, this happens fairly seamlessly, leveraging built-in security features like Face ID or Touch ID for key storage and authentication.</p>

<p>Incidentally, this means modern browsers and devices provide a mechanism to:</p>

<ul>
  <li>Generate a per-service keypair.</li>
  <li>Store that key securely on the device.</li>
  <li>Authenticate access via biometrics.</li>
  <li>Synchronize the key across a user’s devices using secure, platform-specific mechanisms.</li>
  <li>Extend access to native applications via app-site association.</li>
</ul>

<p><br />
That sounds… useful. If we have a persistent, biometrically-protected, cross-device key, what if we could use it to do arbitrary cryptography rather than just authentication?</p>

<p>As it turns out, we can. The WebAuthn <a href="https://github.com/w3c/webauthn/wiki/Explainer:-PRF-extension">PRF extension</a> – while still relatively new – is now beginning to see fairly wide support:</p>

<section>
  <div style="overflow-x: auto;">
    <table>
      <thead>
        <tr>
          <th style="text-align: left; padding: 10px; border-bottom: 1px solid #ddd;">Platform</th>
          <th style="text-align: left; padding: 10px; border-bottom: 1px solid #ddd;">Chrome</th>
          <th style="text-align: left; padding: 10px; border-bottom: 1px solid #ddd;">Safari</th>
          <th style="text-align: left; padding: 10px; border-bottom: 1px solid #ddd;">Firefox</th>
        </tr>
      </thead>

      <tbody>
        <tr>
          <td style="vertical-align: top; padding: 10px; border-bottom: 1px solid #eee;">
            <strong>macOS 15+</strong>
          </td>
          <td style="vertical-align: top; padding: 10px; border-bottom: 1px solid #eee;">
            <div>✅</div>
          </td>
          <td style="vertical-align: top; padding: 10px; border-bottom: 1px solid #eee;">
            <div>✅ (Safari 18+)</div>
          </td>
          <td style="vertical-align: top; padding: 10px; border-bottom: 1px solid #eee;">
            <div>✅</div>
          </td>
        </tr>

        <tr>
          <td style="vertical-align: top; padding: 10px; border-bottom: 1px solid #eee;">
            <strong>iOS / iPadOS 18+</strong>
          </td>
          <td style="vertical-align: top; padding: 10px; border-bottom: 1px solid #eee;">—</td>
          <td style="vertical-align: top; padding: 10px; border-bottom: 1px solid #eee;">
            <div>✅ (Safari 18+)</div>
          </td>
          <td style="vertical-align: top; padding: 10px; border-bottom: 1px solid #eee;">—</td>
        </tr>

        <tr>
          <td style="vertical-align: top; padding: 10px; border-bottom: 1px solid #eee;">
            <strong>Android</strong>
          </td>
          <td style="vertical-align: top; padding: 10px; border-bottom: 1px solid #eee;">
            <div>✅</div>
          </td>
          <td style="vertical-align: top; padding: 10px; border-bottom: 1px solid #eee;">—</td>
          <td style="vertical-align: top; padding: 10px; border-bottom: 1px solid #eee;">
            <div>-</div>
          </td>
        </tr>


        <tr>
          <td style="vertical-align: top; padding: 10px; border-bottom: 1px solid #eee;">
            <strong>Windows 11</strong>
          </td>
          <td style="vertical-align: top; padding: 10px; border-bottom: 1px solid #eee;">
            <div>⚠️ (Requires Password Manager)</div>
          </td>
          <td style="vertical-align: top; padding: 10px; border-bottom: 1px solid #eee;">—</td>
          <td style="vertical-align: top; padding: 10px; border-bottom: 1px solid #eee;">
            <div>⚠️ (Requires Password Manager)</div>
          </td>
        </tr>

        <tr>
          <td style="vertical-align: top; padding: 10px;">
            <strong>Linux</strong>
          </td>
          <td style="vertical-align: top; padding: 10px;">
            <div>⚠️ (Requires Password Manager)</div>
          </td>
          <td style="vertical-align: top; padding: 10px;">—</td>
          <td style="vertical-align: top; padding: 10px;">
            <div>⚠️ (Requires Password Manager)</div>
          </td>
        </tr>
      </tbody>
    </table>
  </div>

</section>

<p><br /></p>

<p>It has browser support in the most recent versions of Chrome, Safari, and Firefox. And it has platform support in the most recent versions of macOS, iOS, and Android. It still isn’t built into Windows, so requires an “authenticator” (like a password manager) to be installed there. Even if it isn’t fully supported on every platform right now, it does look like where the puck is going.</p>

<p>This extension to passkeys allows a client to derive “PRF output”—a 32-byte secret—from the underlying private key. This output is deterministically derived from the long-term key associated with the website, which means it is durable.</p>

<p>This is how the UX looks in Confer:</p>

<div class="image-row">
  <img src="/blog/assets/images/posts/passkey-page.png" alt="Screenshot of the unlock key page" />
  <img src="/blog/assets/images/posts/passkey-prompt.png" alt="Screenshot of the unlock key page in process" />
</div>

<p>With a single tap and Face ID or Touch ID on any authorized device, the client has durable key material! That is a much better user experience than being shown 12 random words to write down and “store securely.”</p>

<p>In code, it looks like this:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="kd">const</span> <span class="nx">assertion</span> <span class="o">=</span> <span class="k">await</span> <span class="nb">navigator</span><span class="p">.</span><span class="nx">credentials</span><span class="p">.</span><span class="kd">get</span><span class="p">({</span>
    <span class="na">mediation</span><span class="p">:</span> <span class="dl">"</span><span class="s2">optional</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">publicKey</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">challenge</span><span class="p">:</span> <span class="nx">crypto</span><span class="p">.</span><span class="nx">getRandomValues</span><span class="p">(</span><span class="k">new</span> <span class="nb">Uint8Array</span><span class="p">(</span><span class="mi">32</span><span class="p">)),</span>
      <span class="na">allowCredentials</span><span class="p">:</span> <span class="p">[{</span> <span class="na">id</span><span class="p">:</span> <span class="nx">credId</span><span class="p">,</span> <span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">public-key</span><span class="dl">"</span> <span class="p">}],</span>
      <span class="na">userVerification</span><span class="p">:</span> <span class="dl">"</span><span class="s2">required</span><span class="dl">"</span><span class="p">,</span>
      <span class="na">extensions</span><span class="p">:</span> <span class="p">{</span> <span class="na">prf</span><span class="p">:</span> <span class="p">{</span> <span class="na">eval</span><span class="p">:</span> <span class="p">{</span> <span class="na">first</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Uint8Array</span><span class="p">(</span><span class="nx">salt</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span>
    <span class="p">}</span>
  <span class="p">})</span> <span class="k">as</span> <span class="nx">PublicKeyCredential</span><span class="p">;</span>

  <span class="kd">const</span> <span class="p">{</span> <span class="nx">prf</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">assertion</span><span class="p">.</span><span class="nx">getClientExtensionResults</span><span class="p">();</span>
  <span class="kd">const</span> <span class="nx">rawKey</span>  <span class="o">=</span> <span class="k">new</span> <span class="nb">Uint8Array</span><span class="p">(</span><span class="nx">prf</span><span class="p">.</span><span class="nx">results</span><span class="p">.</span><span class="nx">first</span><span class="p">);</span> 

</code></pre></div></div>

<p>From there, we have root key material that we can derive subkeys from and encrypt/decrypt entirely on the client. The root key material is derived from your passkey secret, which we (the provider) never have access to.</p>

<p>This means you can conveniently access your Confer chats and data from any of your devices in a way that feels as seamless as logging into a website, while we remain completely unable to access your data.</p>

<p><a href="https://confer.to">Try it out!</a></p>

<p>In our next post we’ll talk about how we do private inference with this private data.</p>]]></content><author><name>Moxie Marlinspike</name></author><summary type="html"><![CDATA[We want private AI chat to be simple. Yet today, many end-to-end encrypted experiences still have a level of friction that make them feel like they’re from another era: it usually either involves a long seed phrase users are asked to “store securely,” insecure password based encryption, or apps that aren’t cross-device and lose your data periodically (on reinstall, browser cache clear, etc).]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://confer.to/blog/assets/images/og-image.png" /><media:content medium="image" url="https://confer.to/blog/assets/images/og-image.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Confessions to a data lake</title><link href="https://confer.to/blog/2025/12/confessions-to-a-data-lake/" rel="alternate" type="text/html" title="Confessions to a data lake" /><published>2025-12-23T00:00:00-08:00</published><updated>2025-12-23T00:00:00-08:00</updated><id>https://confer.to/blog/2025/12/confessions-to-a-data-lake</id><content type="html" xml:base="https://confer.to/blog/2025/12/confessions-to-a-data-lake/"><![CDATA[<p>I’ve been building <a href="https://confer.to">Confer</a>: end-to-end encryption for AI chats. With Confer, your conversations are encrypted so that nobody else can see them. Confer can’t read them, train on them, or hand them over – because only you have access to them.</p>

<p>The core idea is that your conversations with an AI assistant should be as private as your conversations with a person. Not because you’re doing something wrong, but because privacy is what lets you think freely.</p>

<p>I founded Signal with a simple premise: when you send someone a message, only that person should be able to read it. Not the company transmitting it, not the government, not anyone else on the internet. It took years, but eventually this idea became mainstream enough that even Facebook adopted end-to-end encryption.</p>

<p>These days I spend a lot of time “talking to” LLMs. They are amazing. A big part of what makes them so powerful is the conversational interface – so once again I find myself sending messages on the internet; but these messages are very different than before.</p>

<h2 id="the-medium-is-the-message">The medium is the message</h2>

<p>Marshall McLuhan argued that the form of a medium matters more than its content. Television’s format - passive, visual, interrupt-driven - shaped society more than any particular broadcast. The printing press changed how we think, not just what we read.</p>

<p>You could say that LLMs are the first technology where the medium <em>actively invites confession</em>.</p>

<p>Search trained us to be transactional: keywords in, links out. When you type something into a search box, it has the “feeling” of broadcasting something to a company rather than communicating in an intimate space.</p>

<p>The conversational format is different. When you’re chatting with an “assistant,” your brain pattern-matches to millennia of treating dialogue as intimate. You elaborate. You think out loud. You share context. That’s a big part of what makes it so much more useful than search – you can iterate, elaborate, change your mind, ask follow-up questions.</p>

<p>One way to think about Signal’s initial premise is that the visual interfaces of our tools should faithfully represent the way the underlying technology works: if a chat interface shows a private conversation between two people, it should actually be a private conversation between two people, rather than a “group chat” with unknown parties underneath the interface.</p>

<p>Today, AI assistants are failing this test harder than anything ever before. We are using LLMs for the kind of unfiltered thinking that we might do in a private journal – except this journal is an API endpoint. An API endpoint to a data lake specifically designed for extracting meaning and context. We are shown a conversational interface with an assistant, but if it were an honest representation, it would be a group chat with all the OpenAI executives and employees, their business partners / service providers, the hackers who will compromise that plaintext data, the future advertisers who will almost certainly emerge, and the lawyers and governments who will subpoena access.</p>

<p>None of this is <em>entirely</em> new, exactly. We went through the same cycle with email. In the early days, people treated email like private correspondence. Then we learned that our emails live forever on corporate servers, that they’re subject to discovery in lawsuits, that they’re available to law enforcement, that they’re scanned for advertising. Slowly, culturally, we adjusted our expectations. We learned not to put certain things in email.</p>

<h2 id="advertising-is-coming">Advertising is coming</h2>

<p>What is new is that email and social media are interfaces where we mostly post completed thoughts. AI assistants are a medium that invites us to post our uncompleted thoughts.</p>

<p>When you work through a problem with an AI assistant, you’re not just revealing information - you’re revealing how you think. Your reasoning patterns. Your uncertainties. The things you’re curious about but don’t know. The gaps in your knowledge. The shape of your mental model.</p>

<p>When advertising comes to AI assistants, they will slowly become oriented around convincing us of something (to buy something, to join something, to identify with something), but they will be armed with total knowledge of your context, your concerns, your hesitations. It will be as if a third party pays your therapist to convince you of something.</p>

<h2 id="making-the-interface-match-the-way-the-technology-works">Making the interface match the way the technology works</h2>

<p>Confer is designed to be a service where you can explore ideas without your own thoughts potentially conspiring against you someday; a service that breaks the feedback loop of your thoughts becoming targeted ads becoming thoughts; a service where you can learn about the world – without data brokers and future training runs learning about you instead.</p>

<p>It’s still an early project, but I’ve been testing it with friends for a few weeks. Keep an eye on this blog for more technical posts to follow. <a href="https://confer.to">Try it out</a> and let me know what you think!</p>]]></content><author><name>Moxie Marlinspike</name></author><summary type="html"><![CDATA[I’ve been building Confer: end-to-end encryption for AI chats. With Confer, your conversations are encrypted so that nobody else can see them. Confer can’t read them, train on them, or hand them over – because only you have access to them.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://confer.to/blog/assets/images/posts/confessions.png" /><media:content medium="image" url="https://confer.to/blog/assets/images/posts/confessions.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>