Jiddra is a language-mimicking involutory cipher. Its output looks and reads like the language it encrypts; about half the words come out as real words. And it is an involution: encrypt something twice and you get the original back, so encrypting and decrypting work the same way. It supports multiple languages and uses two types of context.
As Jiddra encrypts each letter, it tracks the last three characters in both the original and encrypted text. At every step, it ranks all possible letters by their likelihood in both contexts, first using groups of three letters, then two, then one. It then applies the Hungarian assignment to find the best one-to-one match, making sure no letter is mapped to itself.
The likelihood scores are smoothed: when the exact context is rare or missing, the cipher blends in statistics from shorter contexts and letter positions instead of falling back to plain letter frequency. This keeps rare contexts from producing letter combinations that never occur in the language.
Before ranking, a pair-frequency penalty weighs each candidate by how naturally it follows the previous letter. The smoothed scores already carry that information mid-word, so for most languages the penalty now applies only at the last letter, where it keeps word endings realistic. Word length also conditions the first and last letters, and at the second-to-last position letters that commonly sit just before a word ending get a push. All of this is tuned per language.
For words up to six letters, the scores also lean on the wordlist itself: letters that continue what has been written so far into a real word of the right length get a strong push, with each side following its own text. And letters that would leave the next position with only one natural follower are demoted (after a q, almost nothing but u can come), since whatever gets assigned there would usually read as a typo.
All context adjustments (pair-frequency, length weighting, wordlist blending, continuation demotion, ending preparation, vowel boost, and streak-break) are applied the same way to both the original and encrypted text rankings. This, along with the symmetric cost matrix, makes the cipher a perfect involution.
As a result, the encrypted text statistically resembles the original language. Letter frequencies, letter pairs, and word endings stay close to natural patterns, and roughly half of the words in typical text come out as real words of the language.
| Word length | Method |
|---|---|
| 1 letter | Kept as-is. Real one-letter words are so few (I and a in English) that any substitute would stick out as a letter that is not a word, and a fixed one-letter mapping hides nothing anyway |
| 2+ letters | Letter-by-letter cipher using three letters of context from both texts, with adjustments applied the same way to both rankings |
You can try it out at https://jiddra.xyz. Simply type or paste your text, and it will be encrypted instantly. Click the arrow to switch the output back to the input and see your original text again.
# Cipher (same operation encrypts and decrypts)
uv run python cipher.py --lang en "it was a dark and stormy night"
# Cipher files
uv run python cipher.py --lang en -i input.txt -o ciphered.txt
uv run python cipher.py --lang en -i ciphered.txt -o original.txt
# Pipe from stdin
echo "hello there" | uv run python cipher.py --lang enMake sure you have Python 3.10 or later and uv. You can find all the required packages in uv.lock.
Run uv sync to create a .venv/ folder and install numpy, scipy, and flask.
uv run python build_wordlist.py --lang en # text collection -> filtered word list
uv run python build_markov.py --lang en # word list -> pairs and triplets of letter transitionsThe pipeline loads the text collection from languages/{lang}/corpus/ and creates the data files the cipher needs in data/{lang}/. The text collections are not part of the repository. Download them using the links under Supported languages. To see the expected filename and format for each, check languages/{lang}/parse_corpus.py.
Flask runs under systemd and uses Python from the virtual environment. You only need uv during deployment to set up the virtual environment from uv.lock.
The generated files in data/ are not part of the repository, but the cipher needs them at runtime. Copy data/ from a machine that has run the build pipeline, or download the text collections and run the pipeline on the server.
# One-time
curl -LsSf https://astral.sh/uv/install.sh | sh
cd ~/git/jiddra && uv sync
sudo cp web/jiddra.service /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now jiddra
# After git pull
uv sync && sudo systemctl restart jiddraCaddy manages secure connections at / and forwards requests from /api/* to Flask, which runs on 127.0.0.1:5000.
Languages are listed in order of how common they are on the web, following Languages used on the Internet.
| Code | Language | Corpus | Download | On disk | License |
|---|---|---|---|---|---|
| en | English | Google Books n-grams (gwordlist) | 18 MB | 18 MB | CC-BY 3.0 |
| es | Spanish | OpenSubtitles 2018 (FrequencyWords) | 15 MB | 15 MB | CC-BY-SA 4.0 |
| de | German | OpenSubtitles 2018 (FrequencyWords) | 17 MB | 17 MB | CC-BY-SA 4.0 |
| fr | French | OpenSubtitles 2018 (FrequencyWords) | 10 MB | 10 MB | CC-BY-SA 4.0 |
| sv | Swedish | PAROLE (Språkbanken) | 8 MB | 40 MB | CC-BY 4.0 |
| no | Norwegian (Bokmål) | NB Digital n-grams 2022 | ~1 GB | ~5 GB | CC0 |
| la | Latin | LatinISE v6 (LINDAT) | 216 MB | 216 MB | CC BY-NC-SA 4.0 |
The scores use the complete wordlists for each language, and the results take word frequency into account.
| Metric | English | Spanish | German | French | Swedish | Norwegian | Latin |
|---|---|---|---|---|---|---|---|
| Letter frequency MSE | 0.91 | 0.60 | 0.21 | 0.15 | 0.32 | 0.27 | 0.49 |
| Real words in output | 64% | 67% | 71% | 68% | 67% | 72% | 48% |
| Top-50 bigram overlap | 40/50 | 36/50 | 37/50 | 34/50 | 40/50 | 39/50 | 38/50 |
| Roundtrip correctness | 100% | 100% | 100% | 100% | 100% | 100% | 100% |
| Trigram context hit rate | 57.8% | 54.8% | 55.8% | 52.3% | 58.0% | 53.9% | 61.7% |
| Letter pairs unseen in corpus | 0.04% | 0.02% | 0.06% | 0.12% | 0.01% | 0.01% | 0.30% |
| Zero-corpus word endings | 0.00% | 0.01% | 0.00% | 0.03% | 0.00% | 0.00% | 0.01% |
Released under the MIT License © 2026 Kilo Scheffer.
The corpora listed under Supported languages are distributed by third parties under their own licenses (see that table); this project ships only code and configuration, not the corpus data.