Web UI: - Peer input Enter key now resolves ETH/@alias (like /peer command) - ETH address stored and shown everywhere instead of raw fingerprint - Call UI shows ETH address: "Calling 0x0021...", "In call with 0x9D70..." - Server URL color: #444 → #666 (readable on dark background) - Peer input placeholder: "ETH address, fingerprint, or @alias" - peerEthAddr persisted in localStorage across sessions Server: - WS binary header: strip zero-padding from 64-char to 32-char fingerprint - Call routing now works (was failing due to padded fingerprint lookup) - startCall() resolves ETH/alias before sending CallSignal::Offer - Audio bridge sends auth token to wzp-web as first WS message - Deterministic room name: sorted fingerprint pair (both peers same room) Docs updated: - SERVER.md: WZP integration section (components, running, TLS, auth flow) - USAGE.md: voice call usage for web and TUI - LLM_HELP.md: call architecture, key files, environment vars - LLM_BOT_DEV.md: note that bots cannot participate in calls - TESTING_E2E.md: updated WZP prerequisites with correct flags Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
245 lines
9.8 KiB
Markdown
245 lines
9.8 KiB
Markdown
# featherChat Help Reference
|
|
|
|
featherChat (codename: warzone) = E2E encrypted messenger. TUI client, web client (WASM), federated servers. Crypto: X3DH key exchange + Double Ratchet. Identity = Ed25519 keypair from 24-word seed.
|
|
|
|
## Commands
|
|
|
|
cmd | action | example
|
|
--- | --- | ---
|
|
/help, /? | show help | /help
|
|
/info | show your fp | /info
|
|
/eth | show ETH addr | /eth
|
|
/seed | show 24-word recovery mnemonic | /seed
|
|
/peer <addr>, /p | set DM peer | /peer abc123 or /peer @alice
|
|
/reply, /r | reply to last DM sender | /r
|
|
/dm | switch to DM mode (clear peer) | /dm
|
|
/contacts, /c | list contacts + msg counts | /c
|
|
/history, /h [fp] | show conversation history (50 msgs) | /h abc123
|
|
/alias <name> | register alias for yourself | /alias alice
|
|
/aliases | list all registered aliases | /aliases
|
|
/unalias | remove your alias | /unalias
|
|
/friend | list friends + online status | /friend
|
|
/friend <addr> | add friend | /friend @bob
|
|
/unfriend <addr> | remove friend | /unfriend @bob
|
|
/devices | list active device sessions | /devices
|
|
/kick <id> | kick a device session | /kick dev_abc
|
|
/g <name> | switch to group (auto-join) | /g ops
|
|
/gcreate <name> | create group | /gcreate ops
|
|
/gjoin <name> | join group | /gjoin ops
|
|
/glist | list all groups | /glist
|
|
/gleave | leave current group | /gleave
|
|
/gkick <fp> | kick member (creator only) | /gkick abc123
|
|
/gmembers | list group members + status | /gmembers
|
|
/file <path> | send file (max 10MB, 64KB chunks) | /file ./doc.pdf
|
|
/quit, /q | exit | /q
|
|
|
|
Navigation: PageUp/PageDown scroll msgs, Up/Down scroll by 1 (empty input), Ctrl+C or Esc quit.
|
|
|
|
## Addressing
|
|
|
|
Format | Example | Notes
|
|
--- | --- | ---
|
|
Fingerprint | abc123def456... | hex string, derived from Ed25519 pubkey
|
|
ETH address | 0x742d35Cc... | derived from same seed, checksum format
|
|
@alias | @alice | 1-32 alphanum chars, globally unique, 365d TTL
|
|
|
|
All 3 formats work in /peer. Aliases resolve to fp via server. One alias per user. Register with /alias, recover with recovery key.
|
|
|
|
Bot alias reservation: names ending in Bot, bot, or _bot are reserved for the Bot API. Non-bot users cannot register these aliases.
|
|
|
|
## Quick Start
|
|
|
|
1. `warzone init` -- generates seed, saves identity.seed, prints 24-word mnemonic. WRITE IT DOWN.
|
|
2. `warzone register --server https://srv.example.com` -- uploads prekey bundle to srv
|
|
3. `warzone tui --server https://srv.example.com` -- opens TUI, connects WebSocket
|
|
4. `/peer @alice` or `/peer <fingerprint>` -- set recipient
|
|
5. Type msg, press Enter -- encrypted + sent
|
|
|
|
Recovery: `warzone recover` -- enter 24 words to restore identity on new device.
|
|
|
|
## Groups
|
|
|
|
- /gcreate <name> -- create, you become creator + first member
|
|
- /gjoin <name> -- join existing (or auto-join via /g <name>)
|
|
- type msg in group mode -- fan-out encrypted per-member (sender keys)
|
|
- /gleave -- leave current group
|
|
- /gmembers -- shows fp, alias, online status, creator flag
|
|
- /gkick <fp> -- creator only, removes member
|
|
|
|
Groups auto-create on join if they don't exist. Server fans out per-member encrypted msgs.
|
|
|
|
## Files
|
|
|
|
/file <path> -- sends to current peer/group. Max 10MB. Auto-chunked at 64KB. Includes filename, size, SHA-256 hash. Receiver auto-reassembles.
|
|
|
|
## Friends
|
|
|
|
- /friend -- list all friends with online/offline status
|
|
- /friend <addr> -- add (fp, ETH, or @alias)
|
|
- /unfriend <addr> -- remove
|
|
- Friend list stored encrypted on srv (only you can decrypt with your seed)
|
|
- Shows alias resolution + presence status
|
|
|
|
## Devices
|
|
|
|
- /devices -- list active WS connections (device_id, connected_at)
|
|
- /kick <device_id> -- revoke specific device
|
|
- Max 5 concurrent device sessions
|
|
- /devices/revoke-all API endpoint = panic button (kills all except current)
|
|
|
|
## Security
|
|
|
|
- Seed = 24-word BIP39 mnemonic = master key. Derives Ed25519 identity + ETH wallet.
|
|
- NEVER share seed. Only way to recover account.
|
|
- X3DH key exchange establishes sessions. Double Ratchet provides forward secrecy.
|
|
- All DMs E2E encrypted. Group msgs encrypted per-member.
|
|
- Server sees: metadata (who talks to whom, timestamps), encrypted blobs, presence.
|
|
- Server cannot read msg content.
|
|
- Pre-keys: signed pre-key + 10 one-time pre-keys uploaded on register.
|
|
- Bot msgs: clients auto-detect bot aliases, send plaintext (no E2E). Server can read bot msgs.
|
|
- E2E bots possible (register with seed+bundle) but standard bots are plaintext.
|
|
|
|
## Federation
|
|
|
|
- 2 servers connected via persistent WebSocket
|
|
- Config: JSON file with server_id, shared_secret, peer URL
|
|
- Messages auto-route across servers (srv checks remote presence)
|
|
- Aliases globally unique across federation
|
|
- @alias resolution checks local first, then federated peer
|
|
- Same client commands work regardless of which srv peer is on
|
|
- Auto-reconnects on connection failure
|
|
|
|
## Web Client
|
|
|
|
- Browser access at server root URL (/)
|
|
- WASM-compiled client, same crypto as TUI
|
|
- PWA: installable, offline-capable (service worker caches shell)
|
|
- Same E2E encryption as native client
|
|
- Deep links: navigate to specific peers/groups via URL
|
|
|
|
## Troubleshooting
|
|
|
|
Problem | Cause | Fix
|
|
--- | --- | ---
|
|
"peer not registered" | recipient hasn't run register yet | they need to `warzone register`
|
|
"session reset" | crypto session re-established | normal after key rotation or recovery, msgs continue
|
|
"connection lost" | WS disconnected | auto-reconnects, no action needed
|
|
"alias already taken" | someone else has it | pick different name or wait for 365d expiry + 30d grace
|
|
"not a member" | sending to group you left | /gjoin <name> first
|
|
"invalid token" | bot token expired or wrong | re-register bot
|
|
"file too large" | over 10MB | split file manually
|
|
no prekeys available | recipient's one-time prekeys exhausted | they need to re-register or come online
|
|
|
|
## Bot API (Telegram-compatible)
|
|
|
|
### Creating a Bot
|
|
|
|
Server must run with `--enable-bots`. Then in chat:
|
|
```
|
|
/peer @botfather
|
|
/newbot MyWeatherBot
|
|
→ BotFather replies with token
|
|
```
|
|
|
|
BotFather commands: /newbot, /mybots, /deletebot <name>, /token <name>, /help
|
|
|
|
Bot names must end with bot/Bot/_bot. Only @botfather can create bots.
|
|
|
|
### Plaintext Bot Messaging
|
|
|
|
Clients auto-detect bot aliases (names ending in Bot/bot/_bot) and send messages unencrypted (plaintext JSON). No E2E session is established for standard bot interactions.
|
|
|
|
### E2E Bot Option
|
|
|
|
Bots can optionally participate in E2E encryption by registering with a seed and prekey bundle. Pass `e2e: true` + `bundle` + `eth_address` in the registration request. Users messaging an E2E bot establish a normal X3DH session.
|
|
|
|
### Bot Bridge
|
|
|
|
`tools/bot-bridge.py` provides Telegram library compatibility. It translates between featherChat Bot API and standard TG bot libraries (python-telegram-bot, aiogram, Telegraf).
|
|
|
|
### Endpoints
|
|
|
|
|Endpoint|Method|Body|
|
|
|---|---|---|
|
|
|/bot/:token/getMe|GET|--|
|
|
|/bot/:token/getUpdates|POST|{"timeout":50}|
|
|
|/bot/:token/sendMessage|POST|{"chat_id":"<fp_or_numeric>","text":"Hello","parse_mode":"HTML"}|
|
|
|/bot/:token/setWebhook|POST|{"url":"https://..."}|
|
|
|/bot/:token/deleteWebhook|POST|--|
|
|
|/bot/:token/getWebhookInfo|GET|--|
|
|
|
|
- Token format: fp_prefix:random_hex
|
|
- getUpdates: long-poll (max 50s), returns then deletes queued msgs
|
|
- sendMessage: plaintext JSON, NOT E2E encrypted (unless E2E bot)
|
|
- Bot msgs delivered via same routing (WS push or DB queue)
|
|
- Webhooks: updates are delivered live to the registered URL (POST with JSON body)
|
|
- chat_id: accepts hex fingerprint or numeric ID (TG compatibility)
|
|
- parse_mode: `HTML` renders basic HTML tags (<b>, <i>, <code>, <a>) in clients
|
|
- from.id is per-bot unique numeric (bots can't correlate users cross-bot, no raw fingerprint exposed)
|
|
|
|
Update types in getUpdates:
|
|
- Encrypted msg: text=null, raw_encrypted=base64
|
|
- Bot msg (plaintext): text="actual text", from.is_bot=true
|
|
- Call signal: text="/call_Offer", call_signal={type,payload}
|
|
- File: document={file_name,file_size}
|
|
|
|
Echo bot (Python):
|
|
```python
|
|
import requests, time
|
|
TOKEN = "your_token"
|
|
API = f"http://srv:7700/v1/bot/{TOKEN}"
|
|
while True:
|
|
for u in requests.post(f"{API}/getUpdates",json={"timeout":50}).json().get("result",[]):
|
|
m = u["message"]
|
|
if m.get("text"): requests.post(f"{API}/sendMessage",json={"chat_id":m["chat"]["id"],"text":"Echo: "+m["text"]})
|
|
time.sleep(1)
|
|
```
|
|
|
|
## Voice Calls
|
|
|
|
### Architecture
|
|
Call signaling flows through the featherChat WebSocket (offer/answer/hangup/reject/ringing/busy).
|
|
Audio flows through a separate WZP relay infrastructure:
|
|
|
|
```
|
|
Browser A <--WS--> wzp-web <--QUIC--> wzp-relay <--QUIC--> wzp-web <--WS--> Browser B
|
|
| |
|
|
featherChat server (/v1/auth/validate)
|
|
```
|
|
|
|
### Key files
|
|
- Call signaling: `warzone-server/src/routes/ws.rs` (WireMessage::CallSignal handling)
|
|
- Call state: `warzone-server/src/state.rs` (CallState, active_calls)
|
|
- Relay config: `warzone-server/src/routes/wzp.rs` (token issuance)
|
|
- Web audio: `warzone-server/src/routes/web.rs` (startAudio/stopAudio functions)
|
|
- TUI calls: `warzone-client/src/tui/commands.rs` (/call, /accept, /reject, /hangup)
|
|
- Protocol: `warzone-protocol/src/message.rs` (CallSignal, CallSignalType)
|
|
|
|
### Environment
|
|
- `WZP_RELAY_ADDR` -- tells featherChat server where wzp-web bridge is (e.g., `127.0.0.1:8080`)
|
|
- Without this, `/v1/wzp/relay-config` returns default `127.0.0.1:4433`
|
|
|
|
### Commands
|
|
|
|
cmd | action | example
|
|
--- | --- | ---
|
|
/call | start voice call with current peer | /call
|
|
/call <addr> | start voice call with specific peer | /call @alice
|
|
/accept | accept incoming call | /accept
|
|
/reject | reject incoming call | /reject
|
|
/hangup | end current call | /hangup
|
|
|
|
## Server API (other endpoints)
|
|
|
|
- POST /v1/register -- upload prekey bundle
|
|
- GET /v1/keys/:fp -- fetch prekeys for peer
|
|
- POST /v1/send -- send encrypted msg
|
|
- GET /v1/receive/:fp -- poll msgs (WS preferred)
|
|
- WS /v1/ws?fp=<fp>&token=<tok> -- real-time connection
|
|
- GET /v1/presence/:fp -- check online status
|
|
- GET/POST /v1/friends -- encrypted friend list
|
|
- GET /v1/devices -- list sessions
|
|
- POST /v1/devices/:id/kick -- kick device
|
|
- Alias routes under /v1/alias/*
|
|
- Group routes under /v1/groups/*
|