<h4class="stegoExampleH4">Step D2: Bob runs the model again</h4>
<p>
Context: <code>"I like to eat apple"</code>. Same distribution as Alice's
Step 3. Bob sees Alice chose <strong>tart</strong>, interval [0.70, 0.86).
"Which half?" Second half [0.50, 1.00) → <strong>bit <code>1</code></strong> ✓
</p>
<h4class="stegoExampleH4">Step D3: Bob runs the model again</h4>
<p>
Context: <code>"I like to eat apple tart"</code>. Same distribution as
Alice's Step 4. Bob sees Alice chose <strong>with</strong>, interval
[0.60, 0.78). "Which half?" Second half [0.50, 1.00) →
<strong>bit <code>1</code></strong> ✓
</p>
<h3class="stegoExampleH3">Result</h3>
<p>
Bob has recovered: <code>0 1 1</code> — exactly the message Alice sent.
</p>
<h3class="stegoExampleH3">Key Points</h3>
<ol>
<li><strong>The LLM never saw the secret message.</strong> The secret bits controlled which word was picked from the distribution. The LLM just produced distributions.</li>
<li><strong>The output looks natural.</strong> "I like to eat apple tart with" is a perfectly normal sentence. A censor seeing this has no reason to be suspicious.</li>
<li><strong>Both sides run the same model with the same context.</strong> This is why they get the same distributions. If Bob had a different model or different context, decoding would fail.</li>
<li><strong>The shared key provides the random stream.</strong> Without it, a censor could run the same model and try to decode. With the key, the censor can't reproduce the random choices.</li>
<li><strong>Each token encodes roughly 1 bit</strong> in this toy example (2-way split). With arithmetic coding over a 50,000-word vocabulary, you can encode ~2–4 bits per token.</li>
<li><strong>The "half" splitting is the simplified version.</strong> The real scheme (from the <ahref="https://eprint.iacr.org/2021/686"target="_blank"rel="noopener">Meteor paper</a>) uses full arithmetic coding, which is more efficient. But the principle is the same: secret bits steer selection within the model's probability distribution.</li>
</ol>
</div>
</div>
@@ -554,35 +744,38 @@
</button>
<divclass="stegoCollapsibleContent stegoHidden">
<p>
This project is inspired by research into <strong>linguistic
steganography</strong> — the art of hiding information within natural
language. Traditional steganography hides data in images, audio, or
file metadata, but text-based steganography is harder because text is
compact and every character is visible.
The direct inspiration for this project is a
<ahref="https://primal.net/e/nevent1qqstxmgsd0egtq57gj8mghckthsv79dhegh7xh97hwtwve6l56g4s9sqtjd7k"target="_blank"rel="noopener">post by waxwing on Nostr</a>
that walks through the core idea of using a language model's
next-token probability distribution as a steganographic carrier
channel. The post explains how secret bits can steer token selection
while preserving the model's output distribution, making the
resulting cover text statistically indistinguishable from normal
model output.
</p>
<p>
The breakthrough idea is to use a <strong>language model's own
probability distribution</strong> as the carrier channel. Instead of
tweaking pixels or LSBs, you steer which token the model emits next,
encoding one secret bit per token. Key inspirations include:
That post references the academic paper that formalized this
— early experiments that used statistical text generation to hide
messages, limited by the poor quality of pre-LLM language models.</li>
<li><strong>Modern LLM steganography research</strong> — papers that
use GPT-style models to achieve near-perfect cover text quality
while encoding multiple bits per token via arithmetic or
half-splitting codes.</li>
<li>
<strong><ahref="https://eprint.iacr.org/2021/686"target="_blank"rel="noopener">Meteor: A Simple and Practical Steganographic Protocol for LLMs</a></strong>
— this paper introduces the entropy-coding mechanism that makes
the output distribution-preserving. Instead of a naive 2-way
split, the full scheme uses arithmetic coding over the model's
probability distribution, achieving roughly 2–4 bits per token
while remaining information-theoretically undetectable to a censor
who only observes the channel.
</li>
</ul>
<p>
The half-splitting scheme used here is one of the simplest robust
approaches: it guarantees exactly one bit per token, is symmetric
(encoder and decoder run the same logic), and produces text that's
indistinguishable from normal model output to a casual reader.
The half-splitting scheme implemented in this demo is a simplified
version of Meteor's approach: it guarantees exactly one bit per
token, is symmetric (encoder and decoder run the same logic), and
produces text that's indistinguishable from normal model output to
a casual reader. The full arithmetic-coding version from the paper
is more efficient but the core principle is identical — secret bits
steer selection within the model's probability distribution.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.