Utility and WASM UI for carrying hidden Unicode messages inside ordinary visible text. It can hide a message in visible text, optionally protect that hidden message with a password, and extract the hidden message again later. Without a password, the message is hidden but not password-protected.
raphecrypt is useful when you want text to carry a second layer of information while still looking like ordinary text.
- Add private notes to visible messages, documents, or snippets without changing what readers see.
- Send a password-protected note through a plain-text channel while keeping the visible text harmless and readable.
- Attach metadata, tracking IDs, or workflow hints to text that still needs to look clean in editors, terminals, or logs.
- Create puzzle, game, or ARG-style messages where hidden text can be discovered later.
- Mark generated text or drafts with invisible context so you can identify, extract, or verify it later.
- Store a short recovery hint or operator note inside a copied text block without needing a separate sidecar file.
This is not a replacement for secure messaging. Some apps and websites may remove invisible Unicode characters, so test the full copy/paste or delivery path before relying on it.
Hide a message:
printf 'Visible text\n' | raphecrypt --hide 'hidden café 東京 🔐' > output.txtExtract a message that was hidden without a password:
raphecrypt --input output.txt --extractHide a message with password protection:
printf 'Visible text\n' | raphecrypt --hide 'hidden café 東京 🔐' --password 'mysecret' > output.txtExtract and decrypt a password-protected message:
raphecrypt --input output.txt --extract 'mysecret'Scan text for invisible or non-visible Unicode characters:
raphecrypt --input output.txt --scanraphecrypt [OPTIONS]Options:
-i, --input <FILE> Read visible text from this file
-o, --output <FILE> Write output text to this file
-e, --hide <TEXT> Hide this Unicode text inside the visible input
-d, --extract [PASSWORD] Extract hidden text; optional value is the decryption password
--scan Scan input for non-visible Unicode characters
-p, --password <TEXT> Password used to protect or decrypt hidden text
--password-file <FILE> Read the password from a file
--password-stdin Read the password from standard input
-h, --help Print help
-V, --version Print version
--encrypt remains available as an alias for --hide, and --decrypt remains available as an alias for --extract.
Read from a file and write to a file:
raphecrypt --input input.txt --output output.txtRead from standard input and write to standard output:
printf 'café 東京 🔐\n' | raphecryptHide Unicode text while leaving the visible text readable:
printf 'Visible text\n' | raphecrypt --hide 'hidden café 東京 🔐'Read visible text from a file, hide Unicode text, and write the result to a file:
raphecrypt --input input.txt --output output.txt --hide 'hidden café 東京 🔐'Protect the hidden text with a password before hiding it:
printf 'Visible text\n' | raphecrypt --hide 'hidden café 東京 🔐' --password 'mysecret'Extract hidden text that was hidden without a password:
raphecrypt --input output.txt --extractDecrypt hidden text that was protected with a password:
raphecrypt --input output.txt --extract 'mysecret'Scan a file for invisible or non-visible Unicode characters:
raphecrypt --input output.txt --scanRead the password from a file:
raphecrypt --input output.txt --extract --password-file password.txtRead the password from standard input:
printf 'mysecret\n' | raphecrypt --input output.txt --extract --password-stdinWhen running through Cargo during development, put -- before the program options:
cargo run --bin raphecrypt -- --input input.txt --output output.txtPasswords are optional. If you hide text without a password, the message is only hidden in the visible text. Anyone who knows how to extract the payload can read it.
If the hidden message is sensitive, use a long, unique passphrase. Avoid passing sensitive passwords directly on the command line when possible, because command arguments can be saved in shell history or visible to other local tools. Prefer --password-file or --password-stdin for sensitive use.
There is no password recovery. If the password is lost or typed differently later, the hidden message cannot be decrypted.
When you use a password, raphecrypt protects the hidden message with XChaCha20-Poly1305, a modern authenticated encryption algorithm. For end users, this means the hidden message is designed to remain private unless the correct password is known, and accidental or intentional changes to the protected message should be detected during extraction.
The encryption is strong, but password strength still matters. A short, reused, or easy-to-guess password can make the protected message much easier to attack.
Hidden Unicode text can be fragile. Some websites, chat systems, editors, formatters, or copy/paste paths may remove or normalize invisible Unicode characters. If that happens, the hidden message may no longer be extractable.
Related reference: ChaCha20-Poly1305
This software is provided without any warranty or guarantee of any kind.
By downloading, copying, building, running, or otherwise using this software, you acknowledge and agree that you are solely responsible for using it in a lawful manner and in compliance with all applicable local, regional, national, and international laws and regulations.
This software may only be used for lawful purposes. You may not use this software to create, hide, transmit, store, protect, encrypt, obfuscate, or otherwise handle illegal content, unlawful communications, unauthorized data, or any material whose creation, possession, transmission, concealment, or use is illegal under applicable law.
The authors, contributors, and copyright holders do not endorse, encourage, or authorize any illegal use of this software. You are solely responsible for any content you process with this software and for any consequences resulting from your use or misuse of it.
If you do not agree to these terms, do not download, copy, build, run, or use this software.
See DISCLAIMER.md.
When --hide is provided, the program inserts an invisible Unicode payload into the output. The visible part of the output remains the original input text.
The hidden payload uses Unicode format characters from the \p{Cf} category. This hides the text in the output stream, but hiding without a password is steganographic hiding, not cryptographic encryption.
When --password, --password-file, or --password-stdin is provided, the hidden text is encrypted before it is embedded. When --extract is used without a password, the program extracts a plaintext hidden payload. When a password is provided during extraction, the password is used to decrypt an encrypted hidden payload and the recovered hidden text is written as the output.
The visible input bytes stay readable in the output. The hidden payload is added as extra invisible Unicode bytes before the final newline.
Create a small input file:
printf 'Visible text\n' > input.txt
xxd -g 1 -c 16 input.txtHEX output:
00000000: 56 69 73 69 62 6c 65 20 74 65 78 74 0a Visible text.
Interpretation:
56 69 73 69 62 6c 65 20 74 65 78 74 = "Visible text"
0a = newline
Hide hi without a password:
raphecrypt --input input.txt --hide 'hi' --output output.txt
xxd -g 1 -c 16 output.txtAbbreviated HEX output:
00000000: 56 69 73 69 62 6c 65 20 74 65 78 74 f3 a0 80 81 Visible text....
00000010: f3 a0 80 b0 f3 a0 80 b1 f3 a0 80 b0 f3 a0 80 b1 ................
...
000000d0: f3 a0 81 bf 0a .....
Interpretation:
56 69 73 69 62 6c 65 20 74 65 78 74 = unchanged visible text
f3 a0 80 81 = hidden payload start marker
f3 a0 80 b0 / f3 a0 80 b1 = hidden payload bit characters
f3 a0 81 bf = hidden payload end marker
0a = final newline
Extracting this file returns the hidden text:
raphecrypt --input output.txt --extracthi
Hide hi with password protection:
raphecrypt --input input.txt --hide 'hi' --password 'mysecret' --output output.txt
xxd -g 1 -c 16 output.txtAbbreviated HEX output:
00000000: 56 69 73 69 62 6c 65 20 74 65 78 74 f3 a0 80 81 Visible text....
00000010: f3 a0 80 b1 f3 a0 80 b0 f3 a0 80 b1 f3 a0 80 b0 ................
...
The visible prefix is still Visible text, but the hidden section is different from the no-password example. It is also different on each run because password-protected output uses fresh random values for encryption.
Decrypting this file with the same password returns the hidden text:
raphecrypt --input output.txt --extract 'mysecret'hi
cargo buildFor an optimized release build:
cargo build --releaseThe release binary will be written to:
target/release/raphecryptBuild the static single-page app:
sh scripts/build-web.shThis creates:
dist/index.html
dist/styles.css
dist/app.js
dist/raphecrypt.wasm
Serve the dist directory with the native Rust web server:
cargo run --bin webserverThen open:
http://127.0.0.1:8000
The web app provides:
- Encode: visible text + hidden text + optional password -> encoded text
- Decode: encoded text + optional password -> hidden text
Build the deployment image:
docker build -t raphecrypt .Run it:
docker run --rm -p 8000:8000 raphecryptThen open:
http://127.0.0.1:8000
This project is licensed under the MIT License with the Commons Clause.
Copyright (c) 2026 raphecrypt contributors.
You may use, copy, modify, and distribute the software, but the license does not grant the right to sell the software or a product/service whose value derives entirely or substantially from the functionality of this software. See LICENSE for the full terms.


