| name | decrypt |
| description | Decrypts a DM message on localhost by granting temporary compliance extract access, running extract and decrypt CLI commands, then reverting all temporary changes. Use when the user asks to decrypt a message or run the compliance decryption workflow on localhost. |
Decrypt (local debug)
End-to-end workflow for extracting and decrypting one message on localhost. All temporary backend access must be removed when finished.
After decrypt: what to do with the plaintext and files (compare, inspect, report, etc.) comes from the current conversation — not from this skill.
Prerequisites
- Web dev server running at
http://localhost:8301 (browser rules default).
compliance_keypair.txt at the Web repo root (Web/compliance_keypair.txt).
- Python deps for
scripts/compliance/decryption/ (cryptography, etc.).
Step 0 — Read the compliance keypair
Read Web/compliance_keypair.txt before any decrypt step. Confirm the private key line exists (line after PRIVATE KEY header, base64).
The decrypt CLI loads compliance_keypair.txt from the current working directory. Always run extract/decrypt with cd to the Web repo root:
cd /path/to/Web
If load_compliance_private_key fails, the file format may need a PRIVATE_KEY=<base64> line (the loader expects that or a 43-char base64 line; 32-byte keys are often 44 chars with padding).
Step 1 — Create a temporary user and capture token
Generate random credentials (username 3–20 chars: letters, digits, -, _; password 5–50 chars, no spaces):
DEBUG_USER="decryptdbg$(openssl rand -hex 3)"
DEBUG_PASS="$(openssl rand -base64 12 | tr -d '/+=' | head -c 16)"
echo "user=$DEBUG_USER pass=$DEBUG_PASS"
Register and save token from the JSON response (not the derived login secret):
REGISTER_JSON=$(curl -sS -X POST "http://localhost:8301/api/register" \
-H "Content-Type: application/json" \
-d "{\"username\":\"${DEBUG_USER}\",\"display_name\":\"DecryptDebug\",\"password\":\"${DEBUG_PASS}\",\"confirm_password\":\"${DEBUG_PASS}\"}")
echo "$REGISTER_JSON"
DEBUG_TOKEN=$(python3 -c 'import json,sys; print(json.load(sys.stdin)["token"])' <<< "$REGISTER_JSON")
echo "token saved (length ${#DEBUG_TOKEN})"
Alternatively export for the CLI: export FROMCHAT_API_TOKEN="${DEBUG_TOKEN}"
Save user.id from the same JSON if needed for debugging.
Step 2 — Temporary compliance extract permission (marker blocks)
GET /api/dm/compliance/extract/{message_id} is restricted to user id 1 in:
backend/services/main/routes/envelope_messaging.py (function extract_message_for_compliance).
Add a temporary bypass between searchable markers (replace DEBUG_USERNAME with the user from step 1):
_TEMP_COMPLIANCE_DEBUG_USERNAMES = {"DEBUG_USERNAME"}
if current_user.id != 1 and current_user.username not in _TEMP_COMPLIANCE_DEBUG_USERNAMES:
Backend will auto-reload after this change, you don't need to do anything.
To remove later: search the repo for TEMP_COMPLIANCE_DEBUG and delete the marker block + revert the if to only current_user.id != 1.
Step 3 — Extract bundle (online)
From Web repo root, set variables and run (user supplies MESSAGE_ID):
MESSAGE_ID=<message_id>
RANDOM_FOLDER="run_$(openssl rand -hex 4)"
BUNDLE_DIR="/tmp/debug_decrypt/${RANDOM_FOLDER}"
DECRYPT_DIR="/tmp/debug_decrypt/${RANDOM_FOLDER}_dec"
mkdir -p /tmp/debug_decrypt
cd /path/to/Web
python scripts/compliance/decryption/main.py extract \
--server localhost:8301 \
--http \
--token "${DEBUG_TOKEN}" \
--message-ids "${MESSAGE_ID}" \
--out-dir "${BUNDLE_DIR}"
Equivalent using env (no --token flag):
export FROMCHAT_API_TOKEN="${DEBUG_TOKEN}"
python scripts/compliance/decryption/main.py extract \
--server localhost:8301 \
--http \
--message-ids "${MESSAGE_ID}" \
--out-dir "${BUNDLE_DIR}"
Confirm ${BUNDLE_DIR}/bundle.json exists.
Auth flags: use --token (Bearer from register/login), or --username + --password (CLI derives the login secret). Do not pass both.
Step 4 — Decrypt bundle (offline)
Still from Web repo root (compliance_keypair.txt must resolve):
python scripts/compliance/decryption/main.py decrypt \
--bundle-dir "${BUNDLE_DIR}" \
--output-dir "${DECRYPT_DIR}"
Outputs:
${DECRYPT_DIR}/messages/<message_id>/message.decrypted.txt — message plaintext
${DECRYPT_DIR}/messages/<message_id>/files/ — decrypted attachments
${DECRYPT_DIR}/index.html — HTML report
Step 5 — Use decrypted output (conversation-driven)
Follow the user’s request in the current chat for what to do next (e.g. compare hashes, inspect text, verify a specific attachment). This skill stops at producing ${DECRYPT_DIR}; do not assume a fixed post-decrypt task.
Step 6 — Cleanup (required)
-
Remove decrypt dirs:
rm -rf "${BUNDLE_DIR}" "${DECRYPT_DIR}"
-
Delete temp user (local SQLite default DB):
sqlite3 backend/data/database.db "DELETE FROM users WHERE username='${DEBUG_USER}';"
If your deployment uses another DB, delete the same username there.
-
Remove temporary permission: search TEMP_COMPLIANCE_DEBUG in the repo, delete the marker block, restore the original if current_user.id != 1: check, restart backend.
-
Do not commit compliance_keypair.txt or any decrypt output.
Checklist
- [ ] Read compliance_keypair.txt
- [ ] Register DEBUG_USER; save DEBUG_TOKEN from response
- [ ] Add TEMP_COMPLIANCE_DEBUG_* bypass; restart backend
- [ ] extract --token → bundle.json present
- [ ] decrypt → output under DECRYPT_DIR
- [ ] Post-decrypt work per conversation context
- [ ] rm -rf BUNDLE_DIR and DECRYPT_DIR
- [ ] DELETE temp user from DB
- [ ] Remove TEMP_COMPLIANCE_DEBUG markers; restart backend
Reference
- Extract API:
GET /api/dm/compliance/extract/{message_id} (see bundle_extract.py).
- Decrypt implementation:
scripts/compliance/decryption/bundle_decrypt.py, crypto.py.
- Extract auth:
--token (Bearer), env FROMCHAT_API_TOKEN / FROMCHAT_TOKEN, or --username + plain --password (CLI calls derive_auth_secret for login only).
Rules
- NEVER use sleep in any command.
- Set a timeout on EVERY command that may request user input.