A peer-to-peer, end-to-end encrypted terminal chat with built-in AI integration.
- Private by design — messages are encrypted between peers; no server ever sees content
- No accounts — identity is an Ed25519 keypair generated on first run
- Zero-config start — connect to the public registry and pick a server, no URLs needed
- Works anywhere — direct LAN connections or through a relay server for internet use
- Local AI —
/aicommand talks to a local Ollama instance; AI context is shared between peers - Retro terminal UI — five built-in themes, channel support, scratch pad
pip install kirbus
kirbus --handle yournameThat's it. The client connects to the default registry at kirbus.ai, shows you available servers, and you pick one with Tab + Enter.
Requires Python 3.11+. Or run from source with uv:
git clone https://github.com/wehale/kirbus.git
cd kirbus
uv run kirbus --handle yournameAfter install, kirbus, kirbus-server, and kirbus-registry are available as commands. If running from the repo with uv instead, prefix with uv run (e.g. uv run kirbus).
# Terminal 1 — listen
kirbus --handle alice --listen 9000
# Terminal 2 — connect
kirbus --handle bob --connect localhost:9000The registry is a public directory of kirbus servers. The default registry is https://ezchat.kirbus.ai. When you run kirbus --handle yourname, it fetches the server list and lets you pick one.
To use a different registry:
kirbus --handle yourname --registry https://custom.example.comTo skip the registry and connect directly to a known server:
kirbus --handle yourname --server http://SERVER_IP:8000One person runs kirbus-server on a machine with a public IP. Everyone else connects through the registry or via --server.
kirbus-server --config server.tomlOpen ports 8000 (rendezvous API) and 9001 (TCP relay) in your firewall.
Example server.toml:
[server]
host = "0.0.0.0"
api_port = 8000
relay_port = 9001
ttl = 60
log_level = "info"
[registry]
url = "https://ezchat.kirbus.ai"
name = "my-server"
description = "A public kirbus server"
secret = "CHANGE_ME"
access = "open"
public_url = "http://YOUR_PUBLIC_IP:8000"
[auth]
mode = "open"| Mode | Description |
|---|---|
open |
Anyone can join |
password |
Password required on first connect; pubkey saved for future access |
allowlist |
Only pre-approved pubkeys can connect |
For password-protected servers, add to server.toml:
[auth]
mode = "password"
password = "your-server-password"Connect from the same machine as the server with the --su flag:
kirbus --handle admin --suSu users get admin commands: /kick, /ban, /unban, /who.
kirbus-registry --config registry.tomlExample registry.toml:
[registry]
host = "0.0.0.0"
port = 8080
heartbeat_ttl = 180
log_level = "info"Servers register themselves via heartbeat. The registry is stateless — listings live in memory and rebuild from heartbeats.
Encrypt chat logs at rest with a passphrase:
kirbus --handle yourname --encrypt-historyFirst run prompts you to set a passphrase. Subsequent runs prompt for the passphrase to unlock history. The passphrase is never stored — without it, the history files are unreadable.
To decrypt history for export:
kirbus --decrypt-history @alice > alice.log
kirbus --decrypt-history '#general' > general.logTo disable encryption and decrypt everything back to plaintext:
kirbus --handle yourname --no-encrypt-historyEach --handle gets its own data directory (~/.kirbus-{handle}/) with its own keypair, peers, and history:
kirbus --handle work-me
kirbus --handle personal-meOverride with KIRBUS_HOME:
KIRBUS_HOME=~/.kirbus-custom kirbus --handle custom~/.kirbus-{handle}/config.toml — created automatically on first run.
[ui]
theme = "ansi_bbs" # phosphor_green | amber_terminal | c64 | ansi_bbs | paper_white
handle = "you"
encrypt_history = false
[ai]
provider = "openai-compat"
model = "gemma3:4b"
base_url = "http://localhost:11434/v1"
api_key = ""| Command | Description |
|---|---|
/help |
Show all commands |
/ai <prompt> |
Ask the AI; response shown in conversation |
/ai-peer |
Forward the last peer message to your local AI |
/theme <name> |
Switch theme |
/themes |
List available themes |
/accept [peer] |
Accept a new or key-changed peer |
/block [peer] |
Mark a peer as blocked |
/unblock [peer] |
Remove blocked mark |
/servers |
Refresh server list from registry |
/connect <name> |
Connect to a server by name |
/disconnect |
Leave current server, return to server list |
/channel create <name> |
Create a channel |
/channel join <name> |
Join a channel |
/channel invite <peer> [channel] |
Invite a peer to a channel |
/channel leave <name> |
Leave a channel |
/clear |
Clear chat history |
/quit |
Exit |
Su commands (admin only):
| Command | Description |
|---|---|
/kick <handle> |
Disconnect a peer |
/ban <handle> |
Kick and revoke access |
/unban <handle> |
Restore access |
/who |
List connected peers with details |
Keyboard shortcuts:
| Key | Action |
|---|---|
Tab |
Focus peer list |
↑ / ↓ |
Navigate peers or command history |
Enter |
Select peer / send message |
Esc |
Return focus to input |
PgUp / PgDn |
Scroll chat |
| Mouse wheel | Scroll chat |
kirbus uses Ollama for local AI.
# Install Ollama, then pull a model
ollama pull gemma3:4b
# Ask the AI in any conversation
/ai what's the capital of France?AI context is shared between peers — if alice asks a question and bob follows up, the AI sees the full conversation history.
/ai-peer grabs the last message from your peer and forwards it to your AI automatically.
Each person's /ai runs against their own local model. For cloud AI, point base_url at any OpenAI-compatible endpoint and set api_key.
WSL2 note: if Ollama is running on Windows, set OLLAMA_HOST=0.0.0.0 before starting it and point base_url at the Windows host IP (find it with ip route | grep default | awk '{print $3}').
Every message is signed with the sender's Ed25519 key and stored in ~/.kirbus-{handle}/history/.
kirbus --verify-log @alice
kirbus --verify-log '#general'
kirbus --verify-log scratchTry the UI without any network setup:
kirbus --testSimulated peers come online, join channels, and respond to messages.
A CDK stack is included in deploy/ to provision an EC2 instance with the registry and a lobby server:
cd deploy
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cdk deploy -c key_name=your-ssh-keyThe stack creates an EC2 instance, Elastic IP, security group, nginx reverse proxy, and Let's Encrypt TLS. See deploy/kirbus_stack.py for details.
- Identity — Ed25519 keypair, generated locally, never leaves your machine
- Key exchange — X25519 ECDH ephemeral keys per session
- Encryption — AES-256-GCM with HKDF-derived keys
- Message signing — every message signed by sender's Ed25519 key
- History encryption — optional AES-256-GCM at rest with scrypt-derived key
- Server auth — password gate + pubkey allowlist for access control
- Relay — the relay server pipes opaque ciphertext; it cannot read messages
- Rendezvous — registrations are signed; the server stores handle → IP:port for 60 seconds then discards
- Registry — stateless directory; never touches chat traffic
Self-host the server and registry for full sovereignty.
