ACDS enables clients to discover servers using memorable session strings, provides WebRTC signaling for P2P connections, and handles NAT traversal.
Overview
Welcome to ACDSâthe magic that makes ascii-chat "just work" without dealing with networking headaches!
The Problem: Running a video chat server should be as easy as starting a Zoom call. But networking is complicated:
- Port forwarding: Most home routers block incoming connections, requiring manual router configuration
- IP addresses: You need to know your public IP and communicate it to friendsâtedious and error-prone
- NAT traversal: Symmetric NAT makes peer-to-peer connections nearly impossible without help
- Firewalls: Corporate and university networks often block non-standard ports
The ACDS Solution: Instead of dealing with IP addresses and ports, you get:
Three-word session strings like purple-mountain-lake that uniquely identify your session. Share those three words and people can joinâno IP addresses, no port forwarding, no network configuration.
ACDS transforms ascii-chat from a LAN-only toy into a production-ready video conferencing system that works anywhere, for anyone.
Implementation: src/acds/*.c, lib/acds/*.c
What makes ACDS powerful?
- Human-friendly: Share "happy-sunset-ocean" instead of "73.251.42.118:27224"
- Automatic NAT traversal: Works behind firewalls without manual configuration
- Fallback strategy: Tries best connection first (direct TCP), falls back gracefully (WebRTC/TURN)
- mDNS for local: Zero-config on LANsâno internet/server required
- Privacy-first: ACDS never sees your mediaâonly exchanges connection metadata
- Open protocol: Run your own ACDS server for complete privacy
Architecture
Core Components
ACDS consists of several tightly integrated components:
Discovery Server
File: src/acds/main.c, src/acds/server.c
The main ACDS server that:
- Listens for TCP connections on port 27225 (default)
- Handles session creation, lookup, and join requests
- Relays WebRTC signaling (SDP offers/answers, ICE candidates)
- Manages rate limiting to prevent abuse
- Runs cleanup threads for expired sessions
Threading Model:
- Main Thread: Accept loop using select() for responsive shutdown
- Per-Client Handler Threads: One thread per connected client
- Cleanup Thread: Background thread for rate limit and session expiration cleanup
- Worker Pool: Thread pool for background tasks
Key Functions:
void acds_server_shutdown(acds_server_t *server)
Shutdown discovery server.
asciichat_error_t acds_server_init(acds_server_t *server, const acds_config_t *config)
Initialize discovery server.
asciichat_error_t acds_server_run(acds_server_t *server)
Run discovery server main loop.
Discovery server configuration.
Session Database
File: lib/acds/database.c, lib/acds/database.h
SQLite-based session storage:
- Sessions table: session_id, session_string, host_pubkey, password_hash, participants, capabilities
- Participants table: participant_id, session_id, identity_pubkey, joined_at
- Rate events table: IP address, event type, timestamp for rate limiting
- WAL mode: Write-Ahead Logging for concurrent read performance
Database Schema:
CREATE TABLE sessions (
session_id BLOB PRIMARY KEY,
session_string TEXT UNIQUE NOT NULL,
host_pubkey BLOB NOT NULL,
password_hash TEXT,
max_participants INTEGER DEFAULT 4,
current_participants INTEGER DEFAULT 0,
capabilities INTEGER DEFAULT 3,
has_password INTEGER DEFAULT 0,
expose_ip_publicly INTEGER DEFAULT 0,
session_type INTEGER DEFAULT 0,
server_address TEXT,
server_port INTEGER DEFAULT 0,
created_at INTEGER NOT NULL,
expires_at INTEGER NOT NULL
);
CREATE TABLE participants (
participant_id BLOB PRIMARY KEY,
session_id BLOB NOT NULL,
identity_pubkey BLOB NOT NULL,
joined_at INTEGER NOT NULL,
FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
);
Key Functions:
asciichat_error_t
database_init(
const char *db_path, sqlite3 **db);
asciichat_error_t database_session_lookup(sqlite3 *db, const char *session_string, const acds_config_t *config, acip_session_info_t *resp)
asciichat_error_t database_session_join(sqlite3 *db, const acip_session_join_t *req, const acds_config_t *config, acip_session_joined_t *resp)
asciichat_error_t database_session_leave(sqlite3 *db, const uint8_t session_id[16], const uint8_t participant_id[16])
void database_close(sqlite3 *db)
asciichat_error_t database_session_create(sqlite3 *db, const acip_session_create_t *req, const acds_config_t *config, acip_session_created_t *resp)
asciichat_error_t database_init(const char *db_path, sqlite3 **db)
void database_session_cleanup_expired(sqlite3 *db)
uint8_t participant_id[16]
WebRTC Signaling
File: src/acds/signaling.c, src/acds/signaling.h
Pure relay server for WebRTC signalingâno media processing:
- Relays SDP offers/answers between participants
- Relays ICE candidates for P2P connection establishment
- Supports unicast (to specific participant) and broadcast (to all in session)
- Uses participant_id â socket mapping from TCP server registry
Key Functions:
const acip_webrtc_sdp_t *sdp, size_t total_packet_len);
const acip_webrtc_ice_t *ice, size_t total_packet_len);
const uint8_t
session_id[16], packet_type_t packet_type,
const void *packet, size_t packet_len);
asciichat_error_t signaling_broadcast(sqlite3 *db, tcp_server_t *tcp_server, const uint8_t session_id[16], packet_type_t packet_type, const void *packet, size_t packet_len, const uint8_t *exclude_participant_id)
Broadcast packet to all session participants.
asciichat_error_t signaling_relay_ice(sqlite3 *db, tcp_server_t *tcp_server, const acip_webrtc_ice_t *ice, size_t total_packet_len)
Relay ICE candidate to recipient.
asciichat_error_t signaling_relay_sdp(sqlite3 *db, tcp_server_t *tcp_server, const acip_webrtc_sdp_t *sdp, size_t total_packet_len)
Relay SDP offer/answer to recipient.
Identity Management
File: lib/acds/identity.c, lib/acds/identity.h
Ed25519 identity key management for ACDS server:
- Auto-generates identity key on first run
- Loads existing key from
~/.config/ascii-chat/acds_identity
- Computes SHA256 fingerprint for display
- Cross-platform key storage (Unix and Windows paths)
Key Functions:
asciichat_error_t
acds_identity_load(
const char *path, uint8_t public_key[32], uint8_t secret_key[64]);
const uint8_t secret_key[64]);
asciichat_error_t acds_identity_load(const char *path, uint8_t public_key[32], uint8_t secret_key[64])
asciichat_error_t acds_identity_default_path(char *path_out, size_t path_size)
asciichat_error_t acds_identity_save(const char *path, const uint8_t public_key[32], const uint8_t secret_key[64])
void acds_identity_fingerprint(const uint8_t public_key[32], char fingerprint[65])
asciichat_error_t acds_identity_generate(uint8_t public_key[32], uint8_t secret_key[64])
Session Strings
File: lib/acds/strings.c, lib/acds/strings.h
Human-friendly session identifier generation:
- Format:
adjective-noun-noun (e.g., bright-forest-river)
- ~100 adjectives, ~100 nouns = 1,000,000+ unique combinations
- Cryptographically random selection via libsodium
- Easy to speak, remember, and type without errors
Key Functions:
asciichat_error_t acds_string_init(void)
bool is_session_string(const char *str)
asciichat_error_t acds_string_generate(char *output, size_t output_size)
Validation Rules (enforced by is_session_string()):
- Only lowercase ASCII letters and hyphens allowed
- Exactly 2 hyphens separating 3 words (adjective-noun-noun format)
- First word must be a valid adjective from wordlist (2500 adjectives)
- Second and third words must be valid nouns from wordlist (5000 nouns)
- Each word is 2-12 characters
- No consecutive hyphens
- Cannot start or end with hyphen
- Length 5-47 characters
Protocol
Packet Flow
ACDS uses the ACIP (ascii-chat Internet Protocol) packet format over TCP.
Session Creation Flow:
Server ACDS
| |
|--- ACIP_SESSION_CREATE --------------->| (identity, capabilities, max_participants)
| | [Rate limit check]
| | [Signature verification (optional)]
| | [IP reachability check (Direct TCP)]
| | [Generate session_string]
|<-- ACIP_SESSION_CREATED ---------------| (session_string, STUN/TURN servers)
| |
Session Join Flow:
Client ACDS
| |
|--- ACIP_SESSION_LOOKUP --------------->| (session_string)
| | [Rate limit check]
|<-- ACIP_SESSION_INFO ------------------| (found, capabilities, has_password)
| |
|--- ACIP_SESSION_JOIN ----------------->| (session_string, identity, password)
| | [Rate limit check]
| | [Signature verification (optional)]
| | [Password verification (if required)]
|<-- ACIP_SESSION_JOINED ----------------| (participant_id, server_address, TURN creds)
| |
WebRTC Signaling Flow:
Client A ACDS Client B
| | |
|--- WEBRTC_SDP ------>| | (SDP offer)
| |--- WEBRTC_SDP ---->| (relay to recipient)
| | |
| |<-- WEBRTC_SDP ----| (SDP answer)
|<-- WEBRTC_SDP -------| | (relay to sender)
| | |
|--- WEBRTC_ICE ------>| | (ICE candidates)
| |--- WEBRTC_ICE --->| (relay)
| | |
Packet Types
ACDS handles these ACIP packet types:
| Packet Type | Direction | Description |
| ACIP_SESSION_CREATE | ClientâACDS | Create new session |
| ACIP_SESSION_CREATED | ACDSâClient | Session created response |
| ACIP_SESSION_LOOKUP | ClientâACDS | Query session info |
| ACIP_SESSION_INFO | ACDSâClient | Session info response |
| ACIP_SESSION_JOIN | ClientâACDS | Join existing session |
| ACIP_SESSION_JOINED | ACDSâClient | Join response with connection info |
| ACIP_SESSION_LEAVE | ClientâACDS | Leave session |
| ACIP_WEBRTC_SDP | ClientâACDS | SDP offer/answer relay |
| ACIP_WEBRTC_ICE | ClientâACDS | ICE candidate relay |
| ACIP_ERROR | ACDSâClient | Error response |
NAT Traversal Technologies
ACDS leverages multiple NAT traversal techniques to maximize connectivity:
UPnP (Universal Plug and Play)
- Works on ~70% of home routers
- Automatic port forwarding via miniupnpc library
- Enables direct TCP connections (lowest latency)
- Server registers with
--upnp flag
NAT-PMP (NAT Port Mapping Protocol)
- Apple's alternative to UPnP
- Common on Apple routers and some enterprise equipment
- Also enables direct TCP connections
- Supported via libnatpmp integration
WebRTC (ICE/STUN/TURN)
- STUN: Discovers public IP/port mapping through NAT
- ICE: Negotiates P2P connection through NAT using candidate gathering
- TURN: Provides relay fallback when P2P fails (always succeeds, higher latency)
- WebRTC DataChannels provide reliable data transfer
Connection Priority:
- Direct TCP (if UPnP/NAT-PMP succeeded) â Best latency
- WebRTC P2P (via ICE/STUN) â Good latency, works behind most NATs
- TURN relay (fallback) â Higher latency, always works
mDNS Support (Local Network Discovery)
ACDS also supports mDNS (multicast DNS) for local network discovery:
# Server announces on local network
ascii-chat server --mdns
# Client discovers servers automatically
ascii-chat client --mdns
How it works:
- ACDS server advertises as
_ascii-chat-discovery-service._tcp.local
- Clients can discover ACDS servers on the LAN without knowing their IP
- Zero-configuration for local sessions
- No internet connectivity required
Security
Identity Verification
ACDS supports optional Ed25519 identity verification:
Server Identity (--require-server-identity):
- Servers must sign their SESSION_CREATE request with Ed25519
- Prevents impersonation attacks
- Signature covers: timestamp, capabilities, max_participants
Client Identity (--require-client-identity):
- Clients must sign their SESSION_JOIN request with Ed25519
- Enables client whitelisting
- Signature covers: timestamp, session_string
Timestamp Validation:
- All signed requests include Unix timestamp
- 5-minute window for replay attack protection
- Prevents captured requests from being replayed later
Privacy Protection
IP Address Protection:
- By default, server IP is NOT revealed to clients
- IP revealed only when:
- Password authentication succeeds, OR
- Server explicitly opts in with
expose_ip_publicly flag
- Prevents IP harvesting attacks
ACDS Never Sees Media:
- Only exchanges connection metadata (IPs, ports, session strings)
- All media flows directly between peers using ACIP encryption
- ACDS doesn't decrypt, proxy, or store media content
Rate Limiting
ACDS implements rate limiting to prevent abuse:
- SESSION_CREATE: Max 10 per hour per IP
- SESSION_LOOKUP: Max 60 per minute per IP
- SESSION_JOIN: Max 30 per minute per IP
- Events stored in SQLite with automatic cleanup (1 hour retention)
- Rate limit exceeded returns
ACIP_ERROR packet
Configuration
Command-Line Options
Run ascii-chat discovery-server --help for complete options:
Network Options:
-p --port PORT: TCP listen port (default: 27225)
[address] [address]: IPv4 and/or IPv6 bind addresses (default: 127.0.0.1 ::1)
Database & Identity:
-d --database PATH: SQLite database file path (default: ~/.config/ascii-chat/acds.db)
-k --key PATH: Ed25519 identity key file path (default: ~/.config/ascii-chat/acds_identity)
Security Policies:
-S --require-server-identity: Require servers to provide signed identity
-C --require-client-identity: Require clients to provide signed identity
-V --require-server-verify: Require servers to verify client identity during handshake
-c --require-client-verify: Require clients to verify server identity during handshake
WebRTC Configuration:
--stun-servers LIST: Comma-separated STUN server URLs
--turn-servers LIST: Comma-separated TURN server URLs
--turn-username USER: Username for TURN server authentication
--turn-credential PASS: Credential/password for TURN server authentication
--turn-secret SECRET: Shared secret for dynamic TURN credential generation (HMAC-SHA1)
--upnp: Enable UPnP/NAT-PMP for automatic router port mapping
Usage Examples
# Run discovery server on all interfaces
ascii-chat discovery-server 0.0.0.0 :: --port 27225
# Run with custom database and identity key
ascii-chat discovery-server --database /var/lib/acds/sessions.db --key /etc/acds/identity.key
# Run with strict identity requirements
ascii-chat discovery-server --require-server-identity --require-client-identity
# Run with debug logging
ascii-chat -V --log-level debug discovery-server --port 27225
# Run with custom STUN/TURN servers
ascii-chat discovery-server \
--stun-servers "stun:stun.example.com:3478,stun:stun.l.google.com:19302" \
--turn-servers "turn:turn.example.com:3478" \
--turn-username myuser --turn-credential mypassword
Integration with Client and Server
Server Integration
When starting an ascii-chat server with ACDS registration:
# Server registers with ACDS and gets a session string
ascii-chat server --acds --acds-server acds.example.com --acds-port 27225
# Server displays session string for users to share
# "Session: happy-sunset-ocean"
Server Registration Process:
- Server sends ACIP_SESSION_CREATE to ACDS
- ACDS generates unique session_string and stores session info
- ACDS returns session_string and STUN/TURN servers
- Server displays session_string to user
- Server maintains keepalive with ACDS
Client Integration
When connecting to an ascii-chat server via ACDS:
# Client looks up session and connects
ascii-chat client happy-sunset-ocean
# Or with explicit ACDS server
ascii-chat client happy-sunset-ocean --acds-server acds.example.com
Client Connection Process:
- Client sends ACIP_SESSION_LOOKUP to ACDS
- If session found, client sends ACIP_SESSION_JOIN
- ACDS returns server address/port (if allowed) or TURN credentials
- Client attempts connection:
- Direct TCP to server_address:server_port (if available)
- WebRTC P2P via ICE/STUN (if direct fails)
- TURN relay (fallback)
Error Handling
ACDS returns standardized error codes via ACIP_ERROR packets:
| Error Code | Meaning |
| ERROR_SESSION_NOT_FOUND | Invalid session string |
| ERROR_SESSION_FULL | Session at max capacity |
| ERROR_INVALID_PASSWORD | Password verification failed |
| ERROR_CRYPTO_VERIFICATION | Signature verification failed |
| ERROR_RATE_LIMITED | Too many requests from IP |
| ERROR_INVALID_PARAM | Malformed request |
ACDS Deployment & Operation
Official ACDS Infrastructure
The official ACDS infrastructure consists of two components:
- discovery.ascii-chat.com - Public key distribution website (HTTPS)
- discovery-service.ascii-chat.com:27225 - ACDS server (TCP)
Public Key Distribution via HTTPS (discovery.ascii-chat.com):
- SSH Ed25519 public key:
https://discovery.ascii-chat.com/key.pub (OpenSSH format)
- GPG public key:
https://discovery.ascii-chat.com/key.gpg (GPG export format)
- Both key types are provided as a convenience - clients can use either
- Keys served over HTTPS for integrity and authenticity
- HTML landing page displays key fingerprints and usage instructions
- Plaintext rendering allows users to verify keys manually before downloading
ACDS Server (discovery-service.ascii-chat.com:27225):
- Default ACDS server address built into ascii-chat client
- Handles session creation, lookup, join, and WebRTC signaling
- Operates transparently - no manual configuration needed
- Uses the Ed25519 keys published at discovery.ascii-chat.com
Client Trust Model:
- ascii-chat client automatically trusts keys downloaded from
discovery.ascii-chat.com over HTTPS
- Connects to
discovery-service.ascii-chat.com:27225 by default for ACDS operations
- Keys are cached locally and trusted for subsequent connections
- User verification required if ACDS keys ever change (prevents MITM attacks)
- Alternative: Users can manually specify ACDS server and keys via command-line flags
Default Behavior (no flags needed):
# Client automatically uses discovery-service.ascii-chat.com:27225
# and trusts keys from discovery.ascii-chat.com
ascii-chat client happy-sunset-ocean
# Or explicitly specify ACDS server and key
ascii-chat client happy-sunset-ocean \
--acds-server discovery-service.ascii-chat.com \
--acds-key https://discovery.ascii-chat.com/key.pub
Manual Key Specification (optional):
# Download and verify SSH key
curl -O https://discovery.ascii-chat.com/key.pub
ssh-keygen -lf key.pub # Verify fingerprint
# Use local key file
ascii-chat client happy-sunset-ocean \
--acds-key ~/.config/ascii-chat/acds_known_keys/discovery.ascii-chat.com.pub
Running Your Own ACDS Server
If you want to run your own ACDS instance for privacy or custom deployments, you should follow the same pattern as the official server:
Public Key Hosting via HTTPS (Required for public ACDS servers):
To make your ACDS server usable by clients, you must host at least one public key over HTTPS at a URL of your choice:
- Host your ACDS key (SSH Ed25519 or GPG) at any HTTPS URL
- Examples:
https://your-domain.com/acds-key.pub (SSH) or https://your-domain.com/acds.gpg (GPG)
- You can provide both SSH and GPG keys (like the official server) or just one type
- HTTPS is required - HTTP is not acceptable for key distribution
- Serve keys as plaintext so users can verify them in their browser
- Provide an HTML landing page with key fingerprints and usage instructions
Why HTTPS Key Hosting is Critical:
For third-party ACDS servers, ascii-chat clients will not work unless they can:
- Download your public key from your HTTPS URL, OR
- Be manually configured with your public key file via
--acds-key flag
Note: The official discovery.ascii-chat.com server is special-cased in the client - it automatically trusts keys downloaded from that domain over HTTPS. Your ACDS server will require explicit client configuration.
Client Configuration for Custom ACDS:
Clients connecting to your ACDS server must explicitly trust your key:
# Method 1: Download SSH key from your HTTPS endpoint (recommended)
ascii-chat client session-name \
--acds-server acds.example.com \
--acds-key https://acds.example.com/acds-key.pub
# Or download GPG key from your HTTPS endpoint
ascii-chat client session-name \
--acds-server acds.example.com \
--acds-key https://acds.example.com/acds.gpg
# Method 2: Distribute key file to users manually
ascii-chat client session-name \
--acds-server acds.example.com \
--acds-key /path/to/acds-example-com.pub
IMPORTANT: Without the --acds-key flag, ascii-chat will not connect to third-party ACDS servers. Only the official discovery.ascii-chat.com server has automatic HTTPS key trust built into the client.
Best Practices
For Public ACDS Operators:
- Host at least one public key (SSH or GPG) over HTTPS - this is mandatory for clients to use your server
- You can provide both SSH and GPG keys (like discovery.ascii-chat.com) or just one type
- Serve keys as plaintext for easy browser verification
- Display key fingerprints on your HTML landing page
- Document the
--acds-key https://your-domain.com/path/to/key flag usage for your server
- Clients must explicitly configure your key - only discovery.ascii-chat.com has automatic trust
- Enable identity requirements for production (
--require-server-identity, --require-client-identity)
- Monitor rate limit events for abuse detection
- Configure appropriate STUN/TURN servers for your region
- Regular database backups (SQLite file)
- Consider key rotation policies (with advance user notification)
For Private ACDS Operators (Corporate/Organizational):
- Distribute ACDS public key via internal documentation or HTTPS
- Require client/server identity verification (
--require-*-identity flags)
- Run ACDS on internal network (no public internet exposure)
- Use mDNS for local network discovery where appropriate
- Implement monitoring and alerting for session activity
For Server Hosts:
- Enable
--upnp for automatic port forwarding
- Use identity keys for server authentication
- Consider password-protecting sensitive sessions
- Don't expose IP publicly unless necessary
For Clients:
- Verify server identity when possible (
--server-key)
- Use identity keys for authenticated sessions
- Prefer local mDNS discovery on trusted networks
Implementation Files
Source Files (src/acds/):
main.c - ACDS entry point and initialization
server.c - TCP server and packet dispatch
signaling.c - WebRTC SDP/ICE relay
Library Files (lib/acds/):
database.c - SQLite session storage
session.c - Session data structures
identity.c - Ed25519 key management
strings.c - Session string generation
Network Protocol (lib/network/acip/):
- See also
- Network Protocol
-
Cryptographic Handshake
-
Server Architecture
-
Client Architecture