Sign inSign up

marcusholtz/tor-party-line

By marcusholtz

Updated 13 days ago

Encrypted push-to-talk voice and group party line over Tor.

Image
Networking
Security
0

69

marcusholtz/tor-party-line repository overview

Tor Party Line


Built for Tor License: MIT Source: GitLab Source: GitHub

marcusholtz/tor-party-line

Encrypted push-to-talk voice and group party line over Tor hidden services. No accounts, no phone numbers, no third-party servers.

Supported Architectures

ArchitectureTag
x86-64amd64

Quick Start: Interactive Calling

Pull and run the interactive menu. Make calls, listen for calls, set secrets, test audio.

Save as docker-compose.yml, then run docker compose run --rm partyline:

services:
  partyline:
    image: marcusholtz/tor-party-line:latest
    container_name: tor-party-line
    restart: unless-stopped
    stdin_open: true
    security_opt:
      - label:disable
    devices:
      - /dev/snd:/dev/snd
    volumes:
      - ./data/docker/tor:/var/lib/tor
      - ./data/docker/partyline:/data/.partyline
      - ./secrets:/run/secrets:ro
      - ${XDG_RUNTIME_DIR:-/run/user/1000}/pulse:/run/user/${UID:-1000}/pulse
    environment:
      - TERM=${TERM:-xterm-256color}
      - SHARED_SECRET_FILE=/run/secrets/shared_secret.txt
      - PULSE_SERVER=unix:/run/user/${UID:-1000}/pulse/native
      - XDG_RUNTIME_DIR=/run/user/${UID:-1000}
docker cli
docker run -it --rm \
  --name tor-party-line \
  --security-opt label:disable \
  --device /dev/snd:/dev/snd \
  -v ./data/docker/tor:/var/lib/tor \
  -v ./data/docker/partyline:/data/.partyline \
  -v ./secrets:/run/secrets:ro \
  -v ${XDG_RUNTIME_DIR}/pulse:/run/user/$(id -u)/pulse \
  -e TERM=${TERM} \
  -e SHARED_SECRET_FILE=/run/secrets/shared_secret.txt \
  -e PULSE_SERVER=unix:/run/user/$(id -u)/pulse/native \
  -e XDG_RUNTIME_DIR=/run/user/$(id -u) \
  marcusholtz/tor-party-line:latest
First run
  1. Tor bootstraps (1-3 min first time, progress shown)
  2. Your permanent .onion address appears
  3. Press 1 to set a shared secret (both sides need the same one)
  4. Share your .onion + secret, one side listens (4), the other calls (5)

Your .onion keys persist in ./data/docker/tor/ across restarts.

Quick Start: Always-On Relay

Run a persistent group bridge. Callers dial your .onion and are bridged together.

docker-compose

Using the same docker-compose.yml above:

# Start relay in background (Tor bootstraps, group bridge opens)
docker compose up -d

# Watch live activity
docker compose logs -f

# Stop
docker compose down
docker cli
docker run -d \
  --name tor-party-line-relay \
  --restart unless-stopped \
  --security-opt label:disable \
  -v ./data/docker/tor:/var/lib/tor \
  -v ./data/docker/partyline:/data/.partyline \
  -v ./secrets:/run/secrets:ro \
  -e SHARED_SECRET_FILE=/run/secrets/shared_secret.txt \
  marcusholtz/tor-party-line:latest \
  relay

No audio mounts needed for relay mode (it forwards encrypted blobs, never decodes audio).

Parameters

ParameterFunction
-e SHARED_SECRET_FILEPath to secret file inside container. Default: /run/secrets/shared_secret.txt
-e OPUS_BITRATE=16Opus encoding bitrate in kbps
-e LISTEN_PORT=7777TCP listen port
-e TOR_SOCKS_PORT=9050Tor SOCKS proxy port
-e CIPHER=aes-256-cbcEncryption cipher (21 options)
-e HMAC_AUTH=1HMAC-sign protocol messages (0/1)
-e SINGLE_HOP=0Faster, less anonymous (0/1)
-e SNOWFLAKE_ENABLED=0Censorship-circumvention bridge (0/1)
-e AUTO_LISTEN=0Auto-listen when Tor boots (0/1)
-e SHOW_CIRCUIT=0Show circuit countries in call header (0/1)
-e EXCLUDE_NODES=Tor ExcludeNodes, e.g. {US},{GB}
-e ALSA_DEVICE=Force ALSA capture device, e.g. plughw:2,0
-e ALSA_PLAY_DEVICE=Force ALSA playback device, e.g. plughw:0,0
-e PULSE_SERVERPulseAudio/PipeWire socket path
-v /var/lib/tor.onion keys + Tor state (persistent)
-v /data/.partylineApp config + encrypted secret (persistent)
-v /run/secretsShared secret file (read-only mount)
-v /run/user/$UID/pulseHost audio socket
--device /dev/sndALSA fallback for bare-ALSA hosts

Shared Secret

The secret is not an environment variable. It is a bind-mounted file:

mkdir -p secrets
echo -n 'your-shared-secret' > secrets/shared_secret.txt
chmod 600 secrets/shared_secret.txt

Per-run override: docker compose run --rm partyline call <addr> --secret 'my-secret'

A relay does not need a secret.

Audio

Audio routes through the host's PulseAudio/PipeWire socket. ALSA direct (/dev/snd) is the fallback.

arecord -l    # find capture devices on the host
aplay -l      # find playback devices on the host

Security

PropertyDetail
EncryptionAES-256-CBC + PBKDF2 + HMAC-SHA256
RelayZero-knowledge: forwards blobs, never has the secret
Authentication.onion address + pre-shared secret
Forward secrecyNone; rotate secrets between conversations
SourceSingle bash script, no binaries, no telemetry

The Party Line Trifecta

Three networks, same app, same encryption:

ProjectTransportImage
Tor Party LineTor hidden servicesmarcusholtz/tor-party-line
I2P Party LineI2P garlic routingmarcusholtz/i2p-party-line
Reticulum Party LineReticulum meshmarcusholtz/reticulum-party-line

License

MIT

Tag summary

Content type

Image

Digest

sha256:d4e267f9f

Size

117.5 MB

Last updated

13 days ago

docker pull marcusholtz/tor-party-line