Conversation Steganography: Hiding Secrets Inside Ordinary AI Chat

A Go-based proof of concept that uses arithmetic coding, local language models, and conversation state to make encrypted messages look like harmless text.

8 min read • View on GitHub • More from nethical6

A person sends a message in a plain chat window while a hidden lattice of token paths sits underneath the bubbles. The visible conversation looks dull and ordinary, but the structure beneath it suggests a secret being steered through language generation. It explains how camouflage, not ciphertext, is the core trick.
The payload is not the text itself. It is the path the model took to generate text that nobody would bother to inspect.
Key Takeaways

Why This Looks Like Normal Chat

Most encryption advertises itself. It becomes a blob of base64, a block of noise, or a file that looks immediately suspicious. Conversation Steganography flips that instinct: the message is supposed to look boring, like the kind of AI chat nobody pauses to read twice.

That is the editorial punchline. The project is not trying to make the payload unreadable. It is trying to make the payload unremarkable, which is a different and more unsettling privacy problem.

The goal is to produce text resembling ordinary model-generated prose, with a tradeoff between payload capacity and text quality. This proof of concept has not been shown to be statistically undetectable, and its output must be copied exactly.

That limitation matters. The repo is not selling magic. It is showing a channel that works when the environment is controlled, the model is deterministic enough, and the text survives untouched.

The Trick: Bits Steer Token Choice

The core move is arithmetic coding. An encrypted bitstream does not get pasted into the output after generation. Instead, it nudges the model’s next-token choice so that each token corresponds to a slice of probability mass.

Arithmetic coding makes the hidden message ride inside the token selection process. Sender and receiver share the same state, so the cover text can be decoded back into bits when nothing in the chain has been altered.

A close-up of a token-selection machine where a stream of bits enters from one side, a probability interval narrows through several slices in the center, and a natural sentence emerges on the other side. The image explains that visible text is chosen through constrained probability regions rather than ordinary sampling.
The model does not just speak. It speaks along a path that the payload helps choose.

That is why the repo leans on local models and deterministic settings. If the sender and receiver do not see the same probability landscape, the path collapses. The system is only as stable as the model behavior it can reproduce.

Why Go Talks to Python

The codebase splits the job cleanly. Go owns the CLI, orchestration, state, and protocol flow. Python handles model inference through Hugging Face or MLX, which keeps the project close to the LLM ecosystem without dragging the whole tool into Python.

Go CLI and state
  -> spawn Python model process
  -> request next-token probabilities
  -> select token with arithmetic coder
  -> append to conversation chain
  -> persist local state for decoding later

That tradeoff is smart for a proof of concept. The project stays portable, can support different runtimes, and avoids hardwiring itself to one inference stack. The cost is obvious: subprocess boundaries and model determinism become part of the product surface.

The Message Is Chained, Not Just Hidden

The repository does more than hide a single blob. It keeps a conversation chain, versions the envelope, and ties each message to prior state. That makes reordering, dropping, or replaying messages harder, at least in the clean environment the tool assumes.

LayerWhat it doesWhy it matters
EnvelopeWraps the payload with versioning, nonce, and index fields.Keeps the message format explicit and auditable.
Chain stateLinks each message to the previous one.Makes the conversation behave like a sequence, not isolated packets.
Shared model pathUses the same token probability behavior on both sides.Lets the receiver recover the hidden bits if nothing changed.
Local persistenceStores conversation state on disk.Supports resume and decode without a network dependency.

That design makes the tool feel like a protocol, not a one-off stunt. The visible chat is only one layer. Under it sits state, ordering, and a key schedule that tries to keep sender and receiver aligned.

Where It Breaks

This is where the project earns credibility. If the text is edited, paraphrased, autocorrected, reformatted, or silently transformed by a platform, the hidden channel can fail. That fragility is not a bug in the story. It is the story.

The goal is to produce text resembling ordinary model-generated prose, with a tradeoff between payload capacity and text quality. This proof of concept has not been shown to be statistically undetectable, and its output must be copied exactly.

The repo’s own language around naturalness slack and semantic filtering points in the same direction. The system is balancing two incompatible goals: make the output look ordinary, and make it decodable with enough precision to recover the secret.

How It Compares to Older Steganography

The clearest comparison is not with a general encryption tool. It is with older steganography systems and academic LLM research that chase the same goal from different angles.

ProjectCore methodStealthRobustnessBest for
Conversation SteganographyLLM token selection with arithmetic codingHigh, because the output looks like normal chatFragile, especially if text changesLocal-first hidden conversations
StegCloakInvisible Unicode charactersMedium, but detectable by character analysisModerate, unless characters are strippedQuick hiding in existing text
Meteor and OD-StegaProbability distribution optimizationVery high in research settingsVaries by method and setupAcademic exploration of linguistic steganography
Ordinary encryptionCiphertextLow, because the output looks obviously encodedHigh for the payload itselfConfidentiality without camouflage

That is the project’s niche. It sits between academic rigor and usable tooling. It is not the strongest formal system on paper, but it is close enough to a real workflow to make the idea feel immediate.

What the Project Really Shows

The broader shift is bigger than this repo. Privacy tools are moving from hiding data to hiding the existence of data. In that world, the most effective secret is not a locked container. It is a sentence nobody thinks to question.

Conversation Steganography makes that future legible in one sharp prototype. It shows that an LLM can be more than a generator. It can also be a camouflage engine, one that turns everyday language into a hiding place.