Sign inSign up

marcusholtz/reticulum-party-line

By marcusholtz

Updated 13 days ago

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

Image
Networking
Security
0

64

marcusholtz/reticulum-party-line repository overview

Reticulum Party Line


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

marcusholtz/reticulum-party-line

Encrypted push-to-talk voice and group party line over Reticulum. 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/reticulum-party-line:latest
    container_name: reticulum-party-line
    stdin_open: true
    tty: true
    shm_size: "512m"
    security_opt:
      - label:disable
    tmpfs:
      - /dev/shm:size=512m,mode=1777
    volumes:
      - ./data:/app/data
      - ./secrets:/run/secrets:ro
      - ${XDG_RUNTIME_DIR:-/run/user/1000}/pulse:/run/user/${UID:-1000}/pulse
    environment:
      - DATA_DIR=/app/data
      - RUNTIME_DIR=/dev/shm/partyline
      - SHARED_SECRET_FILE=/run/secrets/shared_secret.txt
      - REFLECTOR_PORT=4242
      - TERM=xterm
      - PULSE_SERVER=unix:/run/user/${UID:-1000}/pulse/native
      - XDG_RUNTIME_DIR=/run/user/${UID:-1000}
    entrypoint: ["docker-entrypoint.sh", "bash", "/app/src/partyline.sh"]
docker cli
docker run -it --rm \
  --name reticulum-party-line \
  --security-opt label:disable \
  --shm-size 512m \
  --tmpfs /dev/shm:size=512m,mode=1777 \
  -v ./data:/app/data \
  -v ./secrets:/run/secrets:ro \
  -v ${XDG_RUNTIME_DIR}/pulse:/run/user/$(id -u)/pulse \
  -e DATA_DIR=/app/data \
  -e RUNTIME_DIR=/dev/shm/partyline \
  -e SHARED_SECRET_FILE=/run/secrets/shared_secret.txt \
  -e REFLECTOR_PORT=4242 \
  -e TERM=xterm \
  -e PULSE_SERVER=unix:/run/user/$(id -u)/pulse/native \
  -e XDG_RUNTIME_DIR=/run/user/$(id -u) \
  marcusholtz/reticulum-party-line:latest \
  docker-entrypoint.sh bash /app/src/partyline.sh
First run
  1. RNS initializes and discovers network peers
  2. Your Reticulum destination hash appears
  3. Press 1 to set a shared secret (both sides need the same one)
  4. Share your destination hash + secret, one side runs relay, the other calls

Your RNS identity persists in ./data/ across restarts.

Quick Start: Headless Reflector

Run a persistent reflector daemon. Callers dial your destination hash and are bridged together.

docker-compose

Using the same docker-compose.yml above, add a reflector profile service, or use the one from the source repo:

# Start reflector in background
docker compose --profile reflector up -d reflector

# Watch live activity
docker compose logs -f reflector

# Stop
docker compose --profile reflector down
docker cli
docker run -d \
  --name reticulum-reflector \
  --restart unless-stopped \
  --security-opt label:disable \
  --shm-size 512m \
  --tmpfs /dev/shm:size=512m,mode=1777 \
  -v ./data:/app/data \
  -v ./secrets:/run/secrets:ro \
  -e DATA_DIR=/app/data \
  -e RUNTIME_DIR=/dev/shm/partyline \
  -e SHARED_SECRET_FILE=/run/secrets/shared_secret.txt \
  -e REFLECTOR_PORT=4242 \
  marcusholtz/reticulum-party-line:latest \
  docker-entrypoint.sh bash /app/src/partyline.sh relay

No audio mounts needed for reflector 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 DATA_DIR=/app/dataPersistent data (identity, config, secret)
-e RUNTIME_DIR=/dev/shm/partylineEphemeral runtime (audio, pids, RNS storage)
-e REFLECTOR_PORT=4242Reflector listen port
-e RNS_LISTEN_HOST=0.0.0.0Reflector bind address
-e OPUS_BITRATE=16Opus encoding bitrate in kbps
-e CIPHER=aes-256-cbcEncryption cipher (21 options)
-e HMAC_AUTH=1HMAC-sign protocol messages (0/1)
-e PULSE_SERVERPulseAudio/PipeWire socket path
--shm-size 512mRequired: audio queue and runtime dir live in /dev/shm
-v /app/dataIdentity, secret, config (persistent)
-v /run/secretsShared secret file (read-only mount)
-v /run/user/$UID/pulseHost audio socket

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 <dest> --secret 'my-secret'

A reflector does not need a secret.

Audio

The entrypoint starts a headless PulseAudio server inside the container. The host's PulseAudio/PipeWire socket mount is the route to real speakers.

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 reflector: forwards blobs, never has the secret
AuthenticationDestination hash + pre-shared secret
Forward secrecyNone; rotate secrets between conversations
SourceSingle bash script + Python RNS bridge, 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:7bd6fe907

Size

301.6 MB

Last updated

13 days ago

docker pull marcusholtz/reticulum-party-line