CONFESSIONS.txt is a local-first terminal protocol for turning plaintext testimony into a public, verifiable, encrypted artifact.
It is built for records that may need to exist before they can be safely read. The operator writes testimony locally, encrypts it with age, embeds the ciphertext inside a carrier image with HStego, archives the locked artifact on Arweave, and generates Base calldata that points to the record.
There is no hosted sealing service. The CLI does not receive files, hold keys, custody wallets, or broadcast transactions. It prepares the artifact; the operator controls disclosure.
Public verifier: https://confessionstxt.art/verify
The repo includes @confessionstxt/cli under packages/cli/. It provides a
human terminal verifier and a read-only MCP server for public verification work.
Caption command:
npx -y @confessionstxt/cli@latest verify 0x...Agent command:
npx -y @confessionstxt/cli@latest mcpIt can:
- expose protocol, verification, CLI, and boundary resources
- resolve public Base/Arweave/verifier references
- parse public metadata labels
- validate public manifest shape
- check CSHA formatting
- generate local verification commands
It cannot decrypt testimony, request passphrases, request private keys, upload private material, custody wallets, broadcast transactions, or access sealed payload contents server-side.
Local source run from this repository:
npm --prefix packages/cli install
npm --prefix packages/cli run confessions -- verify 0x...
npm --prefix packages/cli run confessions -- mcpPublished MCP client configuration:
{
"mcpServers": {
"confessions-txt": {
"command": "npx",
"args": ["-y", "@confessionstxt/cli@latest", "mcp"]
}
}
}CONFESSIONS.txt turns one local testimony file into a durable public reference.
- Record: write testimony as plaintext (
.md,.txt, or similar). - Pack: archive the file into
payload.tar.gzso the original filename survives. - Seal: encrypt the archive with
ageto producepayload.age. - Conceal: embed
payload.ageinto a carrier image with HStego. - Archive: upload the locked artifact to Arweave through ArDrive.
- Broadcast: generate Base calldata containing the artifact pointer and integrity hash.
- Verify: resolve the public record and compare the extracted payload against the canonical hash.
The Python protocol CLI is command-oriented rather than an interactive shell. Each subcommand performs one protocol step: seal, push, mint, extract, or verify. That shape is intentional because each step has different local, public, and operational consequences.
A completed record has three layers.
Local secret material
- plaintext testimony
payload.tar.gzagepassphrase- optional stego passphrase, unless published as
STEG
Public artifact material
locked_artifact.jpg- Arweave transaction ID
- Base transaction metadata
- optional
STEGvalue if public extraction is intentional
Verification material
payload.ageCSHA = sha512(payload.age)- browser or local verifier output
CSHA is the canonical integrity value. It is calculated over the encrypted payload, not over the plaintext archive.
Steganography is concealment and transport. Confidentiality comes from age.
What can become public:
- the locked artifact on Arweave
- the Base metadata label
- the stego extraction secret if
STEGis published on-chain - the extracted
payload.ageif the stego secret is public
What remains private by default:
- the plaintext testimony file
- the
agepassphrase - the stego passphrase
- the decrypted
payload.tar.gz
Publishing STEG makes extraction of payload.age public. It does not disclose plaintext unless the age passphrase is also disclosed.
- Local-first: plaintext remains on the operator's machine unless intentionally disclosed.
- Encryption boundary:
ageis the confidentiality layer. If the steganography is detected or removed, the recovered payload is still encrypted. - Concealment layer: HStego hides the encrypted payload inside an image. No steganographic method should be treated as guaranteed undetectable.
- Public verifiability: anyone with the artifact and the correct extraction path can verify that
sha512(payload.age)matchesCSHA. - No wallet custody:
mintprints calldata. The operator signs and broadcasts manually from their own wallet. - Explicit overwrite behavior: generated outputs are not overwritten unless
--forceis supplied.
CONFESSIONS.txt is not an anonymity system. It does not protect an operator from device compromise, unsafe operational behavior, exposed passphrases, hostile wallets, malicious dependencies, or legal risk.
This is R&D software. It can support a serious evidence workflow, but it is not a complete safety plan and is not a substitute for legal, medical, security, or crisis support.
seal supports split-pass mode and single-pass mode.
Split-pass mode
python3 cli/confess.py seal --image cover.jpg --text confession.md --gen-split-pass
python3 cli/confess.py seal --image cover.jpg --text confession.md --split-pass-prompt
python3 cli/confess.py seal --image cover.jpg --text confession.md --age-pass "<AGE_PASS>" --stego-pass "<STEGO_PASS>"Split-pass mode separates extraction from decryption.
stego-passextractspayload.agefrom the locked artifact.age-passdecryptspayload.ageback intopayload.tar.gz.
This allows an operator to publish or share the stego secret for public checksum verification without disclosing the plaintext.
Single-pass mode
python3 cli/confess.py seal --image cover.jpg --text confession.md --gen-single-pass
python3 cli/confess.py seal --image cover.jpg --text confession.md --single-pass-prompt
python3 cli/confess.py seal --image cover.jpg --text confession.md --single-pass "<PASS>"Single-pass mode uses one secret for both extraction and decryption. It is simpler, but it removes the separation between public extraction and private plaintext access.
Prefer prompt flags for manual secrets. Literal passphrase arguments can be visible in shell history and process lists.
mint builds a human-readable metadata label and prints the corresponding calldata:
TITLE | ARTXID:<ARWEAVE_TXID> | CSHA:<SHA512>
TITLE | ARTXID:<ARWEAVE_TXID> | CSHA:<SHA512> | STEG:<VALUE>
TITLEnames the record.ARTXIDpoints to the Arweave artifact.CSHAis the canonical integrity value.STEGis optional and makes extraction public if included.
The Base transaction is the pointer layer. The artifact lives on Arweave. The plaintext testimony stays local unless the operator chooses to disclose it.
Manual broadcast settings:
- network: Base
- send:
0 ETH - to: null address or self
- data: paste the printed calldata
Recommended wallet for manual calldata broadcast: Rabby
Use --steg-prompt when publishing STEG manually. Literal --steg values can be visible in shell history and process lists.
cli/confess.py- CLI for sealing, uploading, extracting, and verifying artifactscli/confess- small entry wrapper for the CLIpackages/cli/- npm package fornpxverification and MCPweb/- static site and browser verifier deployed atconfessionstxt.artweb/llms.txt,web/protocol.md,web/verify.md,web/mcp.md- public protocol and crawler documentationweb/_headers- Cloudflare Pages security headers for the static verifierweb/vendor/pretext/- vendored Pretext browser layout dependencyscripts/bootstrap_mac.sh- macOS setup helperscripts/install_hstego_mac.sh- macOS helper for building HStego with JPEG support
- Python 3.11 / 3.12 recommended
age- HStego with native JPEG support
ardriveCLI- Node/npm if installing
ardrive-clior running the@confessionstxt/cliverifier/MCP package - optional:
ssss-splitfor Shamir secret splitting
Check the environment with:
python3 cli/confess.py doctorbash scripts/bootstrap_mac.sh
source .venv/bin/activateThe bootstrap script installs Homebrew prerequisites, creates .venv, installs Python/HStego dependencies, and runs doctor. If ardrive-cli is missing, it offers to install it with npm.
sudo apt-get update
sudo apt-get install -y age python3-pip build-essential libjpeg-dev python3-tk
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install imageio numpy scipy pycryptodome numba Pillow
python -m pip install git+https://github.com/daniellerch/hstego.git@v0.5
npm install -g ardrive-clipython3 cli/confess.py doctordoctor checks local dependencies and prints install hints for missing pieces.
python3 cli/confess.py initinit stores the selected wallet path in .confess/config.json. The config is local and should remain out of version control.
Generated split-pass mode:
python3 cli/confess.py seal --image cover.jpg --text confession.md --gen-split-passManual split-pass mode:
python3 cli/confess.py seal --image cover.jpg --text confession.md --split-pass-promptSingle-pass mode:
python3 cli/confess.py seal --image cover.jpg --text confession.md --gen-single-passseal creates:
payload.tar.gzpayload.agelocked_artifact.jpgCSHA
Before embedding, the CLI reports the HStego payload budget for the selected cover image. If payload use is high, the artifact may still be created, but statistical concealment is weaker. Use a larger or more detailed cover image, or reduce the testimony size.
First HStego runs can be slow because of native build and JIT overhead. The CLI prints 15-second progress updates during embedding.
Example:
ardrive create-drive --wallet-file /path/to/wallet.json --drive-name "CONFESSIONS"For confess.py push --folder-id, use the folder entityId from the created[] item where type == "folder".
Do not use:
- the drive
entityId metadataTxIdbundleTxId
If the drive already exists and the folder entityId is not saved, list the drive contents and reuse the target folder entityId.
python3 cli/confess.py push --file locked_artifact.jpg --folder-id <ARDRIVE_FOLDER_ENTITY_ID>push prints:
- Arweave TXID
https://arweave.net/<TXID>
python3 cli/confess.py mint --title "Proof of Omertà" --txid <ARWEAVE_TXID> --csha <CSHA_SHA512>Optional public extraction:
python3 cli/confess.py mint --title "Proof of Omertà" --txid <ARWEAVE_TXID> --csha <CSHA_SHA512> --steg-promptmint prints:
- the metadata string
- the
0x...calldata to paste into the transaction input field
Keep STEG private unless public extraction is intentional.
Use the public verifier:
https://confessionstxt.art/verify
Direct locator URLs are also supported:
https://confessionstxt.art/verify/<BASE_TX_HASH>
It resolves:
- Base transaction metadata
- linked Arweave artifact
- image preview and protocol record
The browser verifier is static and does not require a backend.
Extract the encrypted payload:
python3 cli/confess.py extract --image locked_artifact.jpg --stego-pass-promptFor single-pass artifacts, use --single-pass-prompt instead.
Verify the checksum:
python3 cli/confess.py verify --file payload.age --csha <CSHA_SHA512>Decrypt after a successful checksum match:
python3 cli/confess.py verify --file payload.age --csha <CSHA_SHA512> --decrypt --age-pass-promptFor single-pass artifacts, use --single-pass-prompt instead.
python3 cli/confess.py --help- print expanded help for all subcommandsdoctor- check dependencies and print install hintsinit- store Arweave wallet path and attempt address/balance lookupseal- package, encrypt, and embed a testimony filepush- upload a locked artifact through ArDrivemint- generate Base calldata from title, TXID, and CSHAextract- recoverpayload.agefrom a locked artifactverify- comparepayload.ageagainstCSHAand optionally decrypt
.confess/config.jsonis local configuration and should remain out of version control.- Prefer
--*-promptflags for manual secrets, including--steg-promptwhen publishingSTEG. - Commands refuse to overwrite generated outputs unless
--forceis supplied. - Embedding capacity is constrained by cover image size, format, and payload rate.
- No stego method is guaranteed undetectable.
- If
STEGis published, anyone can extractpayload.agefrom the public artifact, but plaintext still requires theagepassphrase. - Keep plaintext, passphrases, wallet files, and decrypted archives out of screenshots, shell history, cloud sync folders, and public logs.
