Peer-to-peer group chat with gossip-based sync, optional relay for browser clients, and a terminal UI + web client.
- Python 3.9+ (3.11+ recommended)
- Two terminals/devices on the same LAN or reachable network
- Open/forwarded port (default: 9999) if connecting across networks
git clone <repo>
cd felund-core
bash setup.shThis creates a .venv/ at the project root and installs all dependencies for
both the chat client and the optional API service.
State is stored at ~/.felundchat/state.json.
.venv/bin/python chat/felundchat.pyLaunches the panel-based terminal UI. On first run a short wizard guides you through creating or joining a circle.
┌─ felundchat ──────────────────────────────────────────────────────┐
│ node: a3f8b2 | #general | 2 peers ctrl+q=quit │
├──────────────────┬────────────────────────────────────────────────┤
│ Circles │ [10:32] alice: hey everyone │
│ ──────────────── │ [10:33] bob: yo │
│ ● mygroup │ [10:35] you: what's up │
│ #general ← │ │
│ #random │ │
├──────────────────┴────────────────────────────────────────────────┤
│ > _ │
└───────────────────────────────────────────────────────────────────┘
| Key | Action |
|---|---|
F3 |
Settings (display name, relay URL) |
F2 |
Show invite code for active circle |
F1 |
Help |
ctrl+q |
Quit |
Escape |
Re-focus input bar |
| Command | Action |
|---|---|
/help [command] |
List all commands or detail one |
/invite |
Show invite code for the active circle |
/join <code> |
Join a new circle via felund code |
/circles |
List joined circles |
/channels |
List channels in the active circle |
/channel create <name> [public|key|invite] |
Create a channel |
/channel join <name> [key] |
Join a channel |
/channel switch <name> |
Switch active channel |
/channel leave <name> |
Leave a channel |
/who [channel] |
Show members |
/name [new_name] |
Show or update display name |
/settings |
Open settings modal |
/debug |
Toggle gossip debug log |
/quit |
Exit |
A full chat client that runs in the browser. Uses the same invite codes and
message format as the Python TUI — users on both clients can share circles.
See chat-webclient/README.md for setup.
cd chat-webclient
cp .env.example .env # set VITE_FELUND_API_BASE
npm install && npm run dev| Key | Action |
|---|---|
F1 |
Help |
F2 |
Show invite code for active circle |
F3 |
Settings |
F4 |
Call panel |
Escape |
Close modals · focus input |
| Command | Action |
|---|---|
/help |
Show help modal |
/invite |
Show invite code for the active circle |
/join <code> |
Join a circle via invite code |
/circles |
List joined circles |
/circle create <name> |
Create a new circle |
/circle name <new-name> |
Rename the current circle (owner only) |
/circle leave |
Leave the active circle |
/circle join <code> |
Alias for /join |
/channels |
List channels in the active circle |
/channel create <name> |
Create a new channel |
/channel switch <name> |
Switch to another channel |
/call start |
Start a call in the current channel |
/call join |
Join an active call |
/call leave |
Leave the current call |
/call end |
End the call (host only) |
/name <new_name> |
Change your display name |
/settings |
Open settings modal |
An API server handles two concerns:
- Rendezvous — peers register their endpoints; others look them up to attempt direct connections.
- Relay — a simple store-and-forward message bus used by browser clients (which cannot open raw TCP connections) and useful as a fallback when direct peer connections are not possible.
Requires PHP 8.1+ with pdo_sqlite. No other dependencies.
# Development (built-in server)
php -S 0.0.0.0:8000 api/php/rendezvous.php
# Production — copy api/php/ into your document root.
# Apache .htaccess and an nginx config are included.Requires the api/ virtualenv.
.venv/bin/uvicorn api.rendezvous:app --reloadNote: the FastAPI server currently implements presence/rendezvous only
(/v1/register, /v1/peers, /v1/health). For full relay support
(/v1/messages) use the PHP server.
# Python TUI — set env var or configure via F3 in the TUI
export FELUND_API_BASE=http://your-server/api
.venv/bin/python chat/felundchat.py
# Web client — set in chat-webclient/.env before npm run dev / build
VITE_FELUND_API_BASE=http://your-server/api# Initialize local node settings
.venv/bin/python chat/felundchat.py init --bind 192.168.1.10 --port 9999 --name Alice
# Create a circle + print invite code
.venv/bin/python chat/felundchat.py invite
# Join via single invite code
.venv/bin/python chat/felundchat.py join --code <felund_code>
# Start gossip service (headless)
.venv/bin/python chat/felundchat.py run
# Send a message from the command line
.venv/bin/python chat/felundchat.py send --circle-id <id> "hello world"
# Show inbox
.venv/bin/python chat/felundchat.py inbox --circle-id <id> --limit 50
# List circles or peers in a circle
.venv/bin/python chat/felundchat.py peers [--circle-id <id>]Both nodes on the same local network. No relay needed, no port forwarding needed. Direct TCP gossip.
Alice (192.168.1.10:9999) ◄──────────► Bob (192.168.1.11:9999)
One node has a reachable public port. The other initiates the connection. The host shares their public IP:port in the invite code.
Alice (1.2.3.4:9999, port-forwarded) ◄──────────► Bob (behind NAT)
Neither peer can reach the other directly, or one is a browser client. Both push and pull messages through the relay API.
Alice (Python TUI) ──► relay server ◄── Bob (browser)
│
(HMAC-authenticated,
server cannot read
or forge messages)
The relay is store-and-forward: messages are retained for up to 30 days. Peers pull at 5-second intervals.
- Authentication — every message carries an HMAC-SHA256 MAC derived from the circle secret. Any tampered or forged message is rejected by recipients.
- Confidentiality — messages are not encrypted in the current version. The relay server and any network observer can read message content. Treat felund as "tamper-evident chat," not "end-to-end encrypted chat."
- Circle membership — anyone who obtains the invite code (which embeds the circle secret) can join the circle and read all messages. Protect invite codes accordingly.
- Relay trust — the relay server is untrusted. It stores opaque payloads and cannot forge valid MACs without the circle secret.
felund-core/
├── setup.sh # One-shot venv + dependency install
├── test_relay.py # Integration test for the relay API
├── .env.example # Environment variable reference
├── api/
│ ├── php/
│ │ ├── rendezvous.php # PHP relay + rendezvous server (recommended)
│ │ ├── .htaccess # Apache rewrite rules
│ │ └── nginx.conf # nginx config example
│ ├── rendezvous.py # FastAPI rendezvous server (presence only)
│ └── requirements.txt
├── chat/
│ ├── felundchat.py # Entry-point shim
│ ├── requirements.txt
│ └── felundchat/
│ ├── config.py # Constants (state file path, limits)
│ ├── models.py # Dataclasses: State, Circle, Peer, Channel, ChatMessage
│ ├── crypto.py # HMAC MAC generation and SHA-256 helpers
│ ├── invite.py # felund code encode/decode
│ ├── transport.py # TCP framing, IP detection
│ ├── persistence.py # load_state / save_state (JSON)
│ ├── gossip.py # GossipNode — TCP server + gossip loop
│ ├── channel_sync.py # Channel event messages and apply logic
│ ├── rendezvous_client.py # Relay + rendezvous API client
│ ├── chat.py # Circle/channel management helpers
│ ├── cli.py # argparse subcommands
│ └── tui/ # Textual panel TUI
└── chat-webclient/ # React + TypeScript browser client
- The app auto-detects your local IP for peer sharing.
- Keep at least one node running so gossip can propagate messages.
- For cross-network direct connections, ensure the chosen port is reachable from the internet.
- Sync debug logs are local-only and off by default.