P2P end-to-end encrypted terminal chat with AI integration
Project description
kirbus
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
Quick start
pip install kirbus
kirbus --handle yourname
That'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 yourname
After 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).
LAN mode (no server needed)
# Terminal 1 — listen
kirbus --handle alice --listen 9000
# Terminal 2 — connect
kirbus --handle bob --connect localhost:9000
Registry
The 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.com
To skip the registry and connect directly to a known server:
kirbus --handle yourname --server http://SERVER_IP:8000
Running your own server
One person runs kirbus-server on a machine with a public IP. Everyone else connects through the registry or via --server.
kirbus-server --config server.toml
Open 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"
Server access modes
| 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"
Superuser (admin)
Connect from the same machine as the server with the --su flag:
kirbus --handle admin --su
Su users get admin commands: /kick, /ban, /unban, /who.
Running your own registry
kirbus-registry --config registry.toml
Example 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.
Encrypted history
Encrypt chat logs at rest with a passphrase:
kirbus --handle yourname --encrypt-history
First 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.log
To disable encryption and decrypt everything back to plaintext:
kirbus --handle yourname --no-encrypt-history
Multiple identities
Each --handle gets its own data directory (~/.kirbus-{handle}/) with its own keypair, peers, and history:
kirbus --handle work-me
kirbus --handle personal-me
Override with KIRBUS_HOME:
KIRBUS_HOME=~/.kirbus-custom kirbus --handle custom
Configuration
~/.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 = ""
Commands
| 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 |
AI integration
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}').
Verify message signatures
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 scratch
Test mode
Try the UI without any network setup:
kirbus --test
Simulated peers come online, join channels, and respond to messages.
Deploy to AWS
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-key
The stack creates an EC2 instance, Elastic IP, security group, nginx reverse proxy, and Let's Encrypt TLS. See deploy/kirbus_stack.py for details.
Security model
- 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.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
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 kirbus-0.4.1.tar.gz.
File metadata
- Download URL: kirbus-0.4.1.tar.gz
- Upload date:
- Size: 304.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f13361bdd240df1ae3c6c15dcea71700bc663bfa0fdabac8ae5e4952b7231caa
|
|
| MD5 |
0ba9925b07ba51e03d8e9a5dc43fcac4
|
|
| BLAKE2b-256 |
5eedb36b810836d6bc32ff08f88b3c393072bfc62ed48820fa81dc8d0fa45102
|
Provenance
The following attestation bundles were made for kirbus-0.4.1.tar.gz:
Publisher:
publish.yml on wehale/kirbus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kirbus-0.4.1.tar.gz -
Subject digest:
f13361bdd240df1ae3c6c15dcea71700bc663bfa0fdabac8ae5e4952b7231caa - Sigstore transparency entry: 1202566151
- Sigstore integration time:
-
Permalink:
wehale/kirbus@9fe141b58d282f3d36408277f34bd3e07b16373e -
Branch / Tag:
refs/tags/v0.4.1 - Owner: https://github.com/wehale
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9fe141b58d282f3d36408277f34bd3e07b16373e -
Trigger Event:
release
-
Statement type:
File details
Details for the file kirbus-0.4.1-py3-none-any.whl.
File metadata
- Download URL: kirbus-0.4.1-py3-none-any.whl
- Upload date:
- Size: 140.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e13c5845c1f3bb856ae4a3629cb424b06cbb3f53865b88ff5c911e565648c8dd
|
|
| MD5 |
fa9813d0ee077b9ede01ebc6d6e6f1ca
|
|
| BLAKE2b-256 |
2f33c130a72b20fe030b2b2dc139700e00ce82d33d15d0092a2634d3f695fc57
|
Provenance
The following attestation bundles were made for kirbus-0.4.1-py3-none-any.whl:
Publisher:
publish.yml on wehale/kirbus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kirbus-0.4.1-py3-none-any.whl -
Subject digest:
e13c5845c1f3bb856ae4a3629cb424b06cbb3f53865b88ff5c911e565648c8dd - Sigstore transparency entry: 1202566156
- Sigstore integration time:
-
Permalink:
wehale/kirbus@9fe141b58d282f3d36408277f34bd3e07b16373e -
Branch / Tag:
refs/tags/v0.4.1 - Owner: https://github.com/wehale
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9fe141b58d282f3d36408277f34bd3e07b16373e -
Trigger Event:
release
-
Statement type: