4.3 KiB
terminalphone
encrypted, anonymous walkie-talkie over tor. single bash script (~3k lines), no servers, no accounts. your .onion address is your identity.
what it does
two parties share a secret, connect via tor hidden services, and talk using push-to-talk voice (opus-encoded, aes-encrypted). also supports encrypted text chat mid-call.
audio pipeline
mic → raw pcm (8khz mono) → opus encode (16kbps) → aes encrypt → base64 → socat → tor
tor → socat → base64 decode → aes decrypt → opus decode → speaker
record-then-send model (not streaming) - irregular transmission patterns make traffic analysis harder.
wire protocol
text-based, line-delimited over socat/tor:
ID:<onion>- caller id exchangeCIPHER:<name>- cipher negotiationPTT_START/PTT_STOP- recording stateAUDIO:<base64>- encrypted audio payloadMSG:<base64>- encrypted text messageHANGUP/PING- control signals
optional HMAC-SHA256 signing with nonce replay protection (both sides must match).
project structure
terminalphone.sh the whole thing. single script
README.md usage docs
CHANGELOG version history
LICENSE MIT
CLI.png screenshot
runtime data lives in .terminalphone/ (auto-created):
tor_data/- tor hidden service keysaudio/- temp audio files (cleaned on exit)pids/- process id trackingrun/- named pipes, flags, cipher stateshared_secret- encrypted pre-shared keytorrc- generated tor configconfig- user settings
dependencies
core: tor, opus-tools, sox, socat, openssl, arecord/aplay
optional: snowflake-client, qrencode, jq (termux vol-ptt)
platforms: linux (deb/fed/arch), android (termux + termux:api from f-droid)
running it
bash terminalphone.sh # interactive menu
bash terminalphone.sh listen # listen mode
bash terminalphone.sh call ADDR # call a .onion address
bash terminalphone.sh install # install deps
bash terminalphone.sh test # audio loopback test
bash terminalphone.sh status # show status
in-call: SPACE=ptt, T=text msg, S=settings, Q=hangup
key code areas
the script is organized roughly as:
- config (~line 1-150): load_config/save_config, defaults
- deps/install (~150-400): install_deps, check_dep, platform detection
- tor management (~400-700): setup_tor, start/stop/restart, get_onion, rotate_onion
- encryption (~700-900): encrypt_file/decrypt_file, PBKDF2 key derivation, fd passing
- protocol (~900-1050): proto_send/proto_verify, HMAC auth, nonce tracking
- audio (~1050-1400): audio_record, start_recording, stop_and_send, audio_play, voice effects
- call flow (~1400-1900): listen_for_call, call_remote, in_call_session, cleanup_call
- settings menus (~1900-2600): cipher (21 options), opus quality, snowflake, voice effects, tor, security
- auto-listen (~2600-2750): background listener management
- main menu (~2750-3079): interactive menu loop, cli arg parsing
config options
stored in .terminalphone/config:
LISTEN_PORT(7777),TOR_SOCKS_PORT(9050)OPUS_BITRATE(16kbps),CIPHER(aes-256-cbc)SNOWFLAKE_ENABLED,AUTO_LISTEN,PTT_KEYSHOW_CIRCUIT,EXCLUDE_NODES,HMAC_AUTHVOICE_EFFECT(none/deep/high/robot/echo/whisper/custom)
security model
- e2e encryption with 21 cipher options (aes, chacha20, camellia, aria)
- secrets passed via file descriptors (not visible in process table)
- tor hidden service per instance (no ip exposure)
- passphrase-protected secrets at rest (aes-256-cbc + 100k pbkdf2)
- opaque temp filenames (random hex, generic .tmp extension)
- country exclusion for tor circuits (five/nine/fourteen eyes presets)
hacking notes
- it's all bash - no build step, no transpilation
- named pipes (fifos) are the ipc mechanism between socat and the call session
uid()generates random hex from /dev/urandom- platform branching happens via
is_termux()checks throughout - cleanup_call() is critical - handles pipe/process/file cleanup on disconnect
- the settings system uses a simple key=value flat file
- no test framework exists yet - testing is manual via loopback (option 5)
style
- bash with
#!/usr/bin/env bash - functions use snake_case
- log_info/log_warn/log_error/log_debug for output
- match existing formatting and comment style