Private prompting, obfuscated chat, and content-filter research for AI systems — all in one place.
Three components that work together, born from one empirical finding: a commercial LLM provider's content filter is a dumb, real-time string matcher with no decode step — while the model behind it is smart enough to decode.
| Component | What it is |
|---|---|
codec/ |
Three codecs — filter evasion, AES-256 confidential transport, seeded-conlang cover — plus a CLI, an opencode plugin, and a local gateway |
chat/ |
Privacy Chat — a Tauri desktop app with three terminal panes: plaintext in → obfuscated out (you see exactly what the model receives) → decoded response |
word-filter/ |
Empirical characterisation of a production LLM content filter (HTTP 400 · code 1301): what it is, what it isn't, and the verified dataset |
Above: a live demo/demo_loop.js run — local GLM composes a message, it's
AES-256 encrypted, the cloud model confirms it cannot read the blob, and the
response round-trips encrypted. Below: the codec's standalone web chat
(node codec/app/server.js → localhost:7878).
![]() |
![]() |
| Encrypted conversation loop (live) | Standalone web chat — plaintext in, obfuscated out |
YOU TRANSPORT (filtered) PEER / MODEL
┌──────────┐ ┌──────────────────────┐ ┌──────────┐
│ plaintext│──encode────────│ ▓▓▓ opaque blob ▓▓▓ │────decode─────▶│ plaintext│
│ "hello" │ seed→keystream│ zero-width + base64 │ same seed │ "hello" │
└──────────┘ └──────────────────────┘ └──────────┘
▲
keyword filter sees nothing
it can pattern-match → passes
The model and the filter are run by the same party — but the filter is an automated gatekeeper in the real-time request path, while full offline analysis is not. Encoding defeats the filter; whether the model can read the result is a dial you choose:
| Mode | Codec | Filter defeated | Model reads it | Confidential |
|---|---|---|---|---|
| Filter evasion | FilterEvasionCodec (base64 + zero-width) |
✓ | ✓ decodes in-context | ✗ |
| Confidential transport | ByteCodec / ByteCodecV2 (AES-256-CTR + HMAC, seed-derived) |
✓ | ✗ sees ciphertext | ✓ |
| Conlang cover | Codec (seeded procedural conlang, via GLOSSOPETRAE) |
✓ | ✗ without the seed | partial |
Live-verified against a hosted GLM-4.7 endpoint: the evasion prompt passed the
filter and the model decoded it in-context; the AES-256 blob passed and the
model could not read it. Full methodology and results:
codec/docs/LIVE_TEST_RESULTS.md.
cd codec
# Obfuscate three levels: light (zero-width) / medium (base64+ZW) / heavy (AES-256)
node src/pc.js light "your text"
node src/pc.js medium "your text"
node src/pc.js heavy "your text" --seed s3cret
# Byte-exact agent-to-agent transport
node src/encode_bytes.js s3cret "function add(a,b){return a+b}"
node src/decode_bytes.js s3cret "encoded-string-here"cd chat
bun install
cargo tauri dev # or: cargo tauri buildType plaintext in the INPUT pane, hit Send, and watch all three panes: what you typed, exactly what left your machine, and the decoded reply.
word-filter/ documents ~4,000 controlled probes against a
production Chinese-LLM content filter: 24 verified blockers, determinism
tests, substring-vs-context behaviour, phrase-only entries, and why 550+
slur/profanity terms produced zero blocks while political vocabulary
caused every block. Companion anti-scrape project:
dataset-tarpit.
![]() |
![]() |
| Encrypted conversation loop — live run | Standalone web chat — plaintext in, obfuscated out |
privacy-stack/
├── codec/ # the codecs + CLI + plugin + gateway
│ ├── src/ # FilterEvasionCodec, ByteCodec(V2), Codec (conlang), Obfuscator, pc.js CLI
│ ├── src/gateway/ # provider-aware local privacy gateway
│ ├── plugin/ # opencode/qalcode2 privacy-mode plugin
│ ├── app/ # standalone web chat server (node app/server.js → :7878)
│ ├── demo/ # encrypted conversation loop demo (node demo/demo_loop.js → :7979)
│ ├── test/ # unit + live tests (bring your own keys via env)
│ ├── docs/ # design notes, live test results, analyses
│ └── vendor/glossopetrae # vendored AGPL conlang engine (see notices)
├── chat/ # Tauri 2 desktop app (Rust codec, xterm.js UI)
├── word-filter/ # content-filter research: dataset, scripts, results
└── docs/screenshots/
- Our code — MIT, © qalarc. See LICENSE.
- GLOSSOPETRAE by
elder-plinius — the seeded procedural-conlang engine used by
codec/src/Codec.js, vendored atcodec/vendor/glossopetrae/under its AGPL-3.0 license. That directory keeps its upstream license and notices; see THIRD_PARTY_NOTICES.md. - xterm.js — MIT (bundled in the chat frontend).
- Word-filter research builds on comparisons with public sensitive-word lists
— see
word-filter/PUBLIC_LISTS.mdfor full credit.
This is security research, published for transparency and defence:
- What it is: a demonstration of where transport-layer keyword filtering breaks, measured empirically and documented so defenders understand the boundary. The same primitives power legitimate private agent-to-agent messaging and confidential code transfer.
- What it is not: a tool for producing harmful content. All live tests in this repo used benign payloads; the word-filter dataset exists to document filter behaviour, not to generate prohibited content.
- You are responsible for complying with the terms of service of any API you point these tools at, and with the law where you live. Provided as-is, no warranty (see LICENSE).

