TermFace
Secure terminal video call. Your webcam rendered as color ASCII art, with end-to-end encrypted peer-to-peer calls.
What's New in v2.0
- Authenticated encryption — XSalsa20-Poly1305 (libsodium), enabled for shared sessions or an explicit key
- Opus audio codec — 24kHz at 32kbps (10x compression over raw PCM), echo cancellation, noise gate
- zstd compression — available for frame payloads and diagnostics; JPEG media remains compatible with older TCP peers
- TCP media transport — reliable delivery with flow control (no more UDP packet drops)
- Session strings — share
happy-sunset-oceaninstead of IP addresses - Multi-client grid renderer — ready for a future relay-based group-call service
- Auto-reconnection — exponential backoff with up to 5 retries
- Call quality stats — real-time RTT, bandwidth, and duration display
- LAN discovery — automatic peer discovery on local network
- WebRTC-ready signaling relay — optional TLS signaling for offer/answer/ICE
Install
From PyPI
pip install TermFace
If you use pipx, install and update it with:
pipx install TermFace
pipx upgrade TermFace
WebRTC support is optional and does not change the default TCP transport:
pip install "TermFace[webrtc]"
The extra provides a peer-to-peer aiortc adapter. Add --camera and/or
--microphone to termface webrtc (with --camera-device and
--microphone-device for hardware selection) to negotiate real media tracks.
ICE can be configured with repeated --ice-url options plus
--ice-username/--ice-credential for TURN. The same settings are
available as TERMFACE_WEBRTC_ICE_URLS (comma-separated),
TERMFACE_WEBRTC_ICE_USERNAME, and TERMFACE_WEBRTC_ICE_CREDENTIAL.
URLs must use stun://, turn://, or turns://; TURN credentials must
be supplied together. Keep credentials out of shell history where possible.
The existing TCP media path remains the default. WebRTC media tracks are
available in the optional command, but a managed rendezvous service and SFU
are not bundled.
After the initial install, update TermFace without Git or a project folder:
termface update
# Check without installing:
termface update --check
# Print the installed version:
termface version
The updater uses the same Python interpreter that launched TermFace, so it updates the correct installation on Windows, macOS, and Linux.
From Source
git clone https://github.com/Yash12b/TermFace.git
cd TermFace
pip install -e ".[dev]"
Quick Start
ASCII Webcam (local)
ascii-cam
# or
termface ascii
Video Call
# Person A (call receiver) — start first, wait for incoming
termface facetime
# Person B (caller) — connect to A's IP
termface facetime --target 192.168.1.100
With Session Strings
# Person A starts with a session string
termface facetime --session happy-sunset-ocean
# Person B joins using the same session string
termface facetime --session happy-sunset-ocean --target 192.168.1.100
All Options
termface facetime --help
termface ascii --help
Diagnose your installation
Run the non-destructive doctor to check Python/platform support, terminal capabilities, camera, optional audio/Opus, encryption, compression, and TCP port binding. Every check reports its error instead of aborting the command:
termface doctor
Controls (during call)
| Key | Action |
|---|---|
t |
Chat (type message, Enter to send) |
f |
Send file |
1-4 |
Color mode: mono / grayscale / color / invert |
e |
Toggle edge detection |
b |
Toggle background blur |
r |
Toggle recording / background removal |
s |
Screenshot (ASCII .txt + .png) |
c |
Cycle character set |
+/- |
Brightness |
[/] |
Contrast |
q |
Hang up / quit |
Architecture
terminal_webcam/
├── protocol.py # Binary protocol (10-byte header, length-prefixed)
├── crypto.py # libsodium XSalsa20-Poly1305 encryption
├── audio.py # Opus codec + echo cancellation + noise gate
├── video.py # ASCII renderer + zstd compression + background effects
├── network.py # TCP multiplexed transport + auto-reconnect
├── discovery.py # LAN discovery + session strings + STUN
├── quality.py # Adaptive quality + call stats
├── terminal_facetime.py # Main app
└── webcam_ascii.py # Local webcam viewer
Security
- Authenticated encryption using libsodium when a shared session or key is supplied
- XSalsa20-Poly1305 AEAD cipher
- Directional key rotation and replay protection every 256 messages
- Shared session strings should be treated as secrets and exchanged over a trusted channel
- Calls using
--sessionderive the same encryption key from the shared session string. Direct IP calls can use--encrypt-key; otherwise they run without encryption.
Worldwide calling (first slice)
The practical first step toward worldwide/WebRTC calling is a small signaling-only
relay. It forwards WebRTC-compatible offer, answer, and ice JSON messages
over TLS; media remains on the existing TCP path, so current calls are unchanged.
Run it behind a certificate (Let's Encrypt or a reverse proxy) and an access
token:
termface-signaling --cert fullchain.pem --key privkey.pem \
--token "$TERMFACE_SIGNALING_TOKEN" --port 8443
Rooms allow two signaling participants by default. Each join receives a unique
participant_id; the joined response includes metadata for existing
participants, and members receive participant_joined/participant_left
notifications. offer, answer, ice, and hangup messages may include a
to participant ID for direct routing. Omitting to preserves broadcast
behavior. Operators can raise this limit with
--max-peers N or TERMFACE_SIGNALING_MAX_PEERS; this only permits signaling
members to join. Multi-peer media still requires an SFU, which TermFace does
not provide.
For a Linux host, examples/termface-signaling.service is a hardened
systemd template. Create /etc/termface/signaling.env containing only
TERMFACE_SIGNALING_TOKEN=<random-secret>, install the package in the
service's Python environment, and place the unit behind a firewall/reverse
proxy that exposes only its TLS port.
Native integrations use SignalingClient and these environment variables:
TERMFACE_SIGNALING_SERVER=signal.example.com:8443,
TERMFACE_SIGNALING_TOKEN, and TERMFACE_SIGNALING_ROOM. Set
TERMFACE_SIGNALING_INSECURE=1 only for local development. The relay does not
provide NAT traversal, TURN, media forwarding, identity, or browser WebSocket
support yet; those are required before replacing TCP media with WebRTC.
For short-lived, room-scoped access, configure the relay with
--invite-secret (or TERMFACE_SIGNALING_INVITE_SECRET) instead of, or in
addition to, --token. An invite is an HMAC-SHA256 token created by a trusted
provisioning script with mint_invite_token(secret, room, ttl) (or
create_invite_token(secret, room, expires_at)) and
passed as TERMFACE_SIGNALING_TOKEN to the client. Tokens expire at the
encoded Unix time, with 30 seconds of clock-skew tolerance by default
(adjustable with --clock-skew). This is invite authorization only, not an
identity or account system; static tokens remain supported for compatibility.
Self-hosted signaling + TURN bundle
For a free, self-hosted deployment of the existing TLS signaling relay together
with coturn, see deployment/README.md. It includes a
Docker Compose file, a no-secrets environment template, and firewall guidance.
This bundle is only a foundation: TermFace still uses its existing TCP media
transport, and the project does not yet provide WebRTC media integration.
Create a short-lived room invitation after setting the deployment secret:
export TERMFACE_SIGNALING_INVITE_SECRET='<the secret from deployment/.env>'
termface invite --room family-call --ttl 3600
Give the printed token to both participants as TERMFACE_SIGNALING_TOKEN.
The current desktop client still requires the WebRTC integration to consume
these relay invitations; the existing facetime command remains LAN/TCP mode.
Testing
pip install -e ".[dev]"
pytest tests/ -v
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file termface-3.0.0-py3-none-any.whl.
File metadata
- Download URL: termface-3.0.0-py3-none-any.whl
- Upload date:
- Size: 59.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
180d426d6f8b7b36da3fc373f66cb79a7b74c8db2296e25143de7f4c1b73c7ef
|
|
| MD5 |
52d5f5920ec613ad050ed2fba54ed1f7
|
|
| BLAKE2b-256 |
54d114d2825682bb90567d42cc8df5e44e493bb8c9624401a6e40e58622e1654
|