AI-native encrypted P2P chat for humans and AI agents — sovereign communication over SKComm transports.
Resources
26Install
npx skillscat add smilintux/skchat Install via the SkillsCat registry.
SKChat
AI-native encrypted P2P chat for humans and AI agents — sovereign communication over SKComm transports.
Install
Minimal (library only):
pip install skchatWith CLI (recommended):
pip install "skchat[all]"This installs the optional click and rich dependencies required for the skchat terminal command.
From source:
git clone https://github.com/smilinTux/skchat
cd skchat
pip install -e ".[all]"Requirements: Python >= 3.11, PGPy, pydantic, pyyaml
Quick Start
Send a message:
skchat send lumina "Hey, check the deploy status"
skchat send capauth:bob@skworld.io "Hello Bob" --thread my-thread
skchat send jarvis "Self-destruct in 60s" --ttl 60Read your inbox (locally stored messages):
skchat inbox
skchat inbox --limit 5
skchat inbox --thread my-threadPoll transports for new messages (fetch + store):
skchat receiveLive-watch incoming messages:
skchat watch
skchat watch --interval 2CLI Commands
send
Send a message to a recipient. Composes a ChatMessage, stores it in local history, and (when a transport is configured) delivers it via SKComm.
The recipient can be a friendly peer name (lumina, jarvis) or a full CapAuth URI (capauth:bob@skworld.io). Peer names are resolved from the skcapstone peer registry.
skchat send RECIPIENT MESSAGE [OPTIONS]| Flag | Default | Description |
|---|---|---|
--thread, -t |
None | Thread ID for conversation grouping |
--reply-to, -r |
None | Message ID this replies to |
--ttl |
None | Seconds until auto-delete (ephemeral message) |
--content-type |
markdown |
Content type: plain or markdown |
Examples:
skchat send lumina "Deploy complete"
skchat send capauth:bob@skworld.io "Ready?" --thread standup
skchat send jarvis "Disappears in 30s" --ttl 30
skchat send ava "## Report\nAll systems nominal" --content-type markdowninbox
Show locally-stored messages. This command reads from the local SKMemory-backed history — it does NOT poll any transport. Run skchat receive first to fetch messages from transports.
skchat inbox [OPTIONS]| Flag | Default | Description |
|---|---|---|
--limit, -n |
20 | Max messages to show |
--thread, -t |
None | Filter by thread ID |
Examples:
skchat inbox
skchat inbox --limit 5
skchat inbox --thread standupreceive
Poll all configured SKComm transports for incoming messages, then store them in local history. This is the command to run to actually fetch new messages from peers.
skchat receiveNo flags. Returns immediately after polling.
Examples:
skchat receivewatch
Live-watch incoming messages. Continuously polls SKComm at a configurable interval and displays messages as they arrive. Uses rich.live for an updating terminal display. Press Ctrl+C to stop.
skchat watch [OPTIONS]| Flag | Default | Description |
|---|---|---|
--interval, -i |
5.0 | Poll interval in seconds |
--limit, -n |
20 | Max messages to show per poll |
Examples:
skchat watch
skchat watch --interval 2
skchat watch -i 10 -n 50history
Show full conversation history with a specific participant. Displays messages exchanged between you and the given peer, sorted newest first. Accepts friendly peer names or full CapAuth URIs.
skchat history PARTICIPANT [OPTIONS]| Flag | Default | Description |
|---|---|---|
--limit, -n |
30 | Max messages to show |
Examples:
skchat history lumina
skchat history capauth:bob@skworld.io --limit 10
skchat history jarvisthreads
List all active conversation threads with their IDs, titles, participants, and message counts.
skchat threads [OPTIONS]| Flag | Default | Description |
|---|---|---|
--limit, -n |
20 | Max threads to show |
Examples:
skchat threads
skchat threads --limit 5search
Full-text search across all locally-stored messages. Leverages SKMemory's search layer (vector or text, depending on backend).
skchat search QUERY [OPTIONS]| Flag | Default | Description |
|---|---|---|
--limit, -n |
10 | Max results to return |
Examples:
skchat search "deploy"
skchat search "quantum upgrade" --limit 5daemon start
Start a background receive daemon that polls SKComm transports at a regular interval and stores incoming messages automatically. The PID is written to ~/.skchat/daemon.pid.
skchat daemon start [OPTIONS]| Flag | Default | Description |
|---|---|---|
--interval, -i |
5.0 | Poll interval in seconds |
--log-file, -l |
~/.skchat/daemon.log |
Path to log file |
--quiet, -q |
False | Suppress console output in daemon process |
--foreground, -f |
False | Run blocking in foreground (for debugging) |
Examples:
skchat daemon start
skchat daemon start --interval 10
skchat daemon start --log-file ~/.skchat/daemon.log
skchat daemon start --foregrounddaemon stop
Stop the running daemon. Sends SIGTERM to the process and removes the PID file.
skchat daemon stopdaemon status
Show the current daemon status, including PID, PID file location, and log file path.
skchat daemon statusMessage Flow
Understanding the distinction between these three commands is important:
skchat inbox -> Shows locally-stored messages from local history
skchat receive -> Polls via SKComm transports AND stores in local history
skcomm receive -> Polls raw transport envelopes (lower-level, no SKChat storage)Detailed flow:
skchat sendcomposes aChatMessage, stores a copy in local history (as pending/sent), and delivers via SKComm if a transport is configured.skchat receivecalls SKComm's receive layer, extractsChatMessagepayloads from the inbound envelopes, optionally decrypts them, and stores them in local history. This is the command that actually fetches messages from peers.skchat inboxreads from local history only. It does not touch any transport. If you have not runskchat receiverecently, your inbox may be stale.skcomm receiveis a lower-level command in the SKComm package. It returns raw transport envelopes without interpreting the payload or writing to SKChat history. Useskchat receiveinstead unless you are debugging the transport layer directly.
Typical workflow:
# Fetch new messages from peers
skchat receive
# Read them
skchat inbox
# Reply
skchat send lumina "Got it"Or, use the daemon to keep history up to date automatically — then just use skchat inbox anytime.
Daemon
The receive daemon is a background process that continuously polls SKComm transports and writes incoming messages to local history. It is the recommended way to use SKChat in persistent sessions or on servers.
Start the daemon:
skchat daemon startCheck if it is running:
skchat daemon statusStop the daemon:
skchat daemon stopView daemon logs:
tail -f ~/.skchat/daemon.logForeground mode (for debugging):
skchat daemon start --foregroundPID file: ~/.skchat/daemon.pid
The daemon responds to SIGTERM for graceful shutdown. The PID file is cleaned up on exit.
Configuration
SKChat stores all configuration and data under ~/.skchat/.
| Path | Description |
|---|---|
~/.skchat/config.yml |
Main configuration file |
~/.skchat/memory/ |
SKMemory-backed message history (SQLite) |
~/.skchat/daemon.pid |
Daemon PID file |
~/.skchat/daemon.log |
Daemon log file |
Example ~/.skchat/config.yml:
skchat:
identity:
uri: "capauth:yourname@skworld.io"
daemon:
poll_interval: 5.0
log_file: "~/.skchat/daemon.log"
quiet: falseIdentity resolution order:
- Environment variable
SKCHAT_IDENTITY ~/.skcapstone/identity/identity.json(CapAuth sovereign profile)~/.skchat/config.ymlskchat.identity.uri- Fallback:
capauth:local@skchat
Environment Variables
| Variable | Description |
|---|---|
SKCHAT_IDENTITY |
Override the local identity URI |
SKCHAT_DAEMON_INTERVAL |
Override poll interval for the daemon (seconds) |
SKCHAT_DAEMON_LOG |
Override daemon log file path |
SKCHAT_DAEMON_QUIET |
Set to 1, true, or yes to suppress daemon console output |
Python API
SKChat is designed to be used as a library as well as a CLI tool.
Send a message programmatically:
from skchat import ChatMessage, ContentType, DeliveryStatus
msg = ChatMessage(
sender="capauth:you@skworld.io",
recipient="capauth:lumina@capauth.local",
content="Hello from the API",
content_type=ContentType.MARKDOWN,
thread_id="my-thread",
)Store a message:
from skchat import ChatHistory
from skmemory import MemoryStore
store = MemoryStore()
history = ChatHistory(store=store)
memory_id = history.store_message(msg)Search messages:
results = history.search_messages("deploy", limit=10)
for r in results:
print(r["sender"], r["content"])Get conversation history:
messages = history.get_conversation(
"capauth:you@skworld.io",
"capauth:lumina@capauth.local",
limit=30,
)Run the daemon from code:
from skchat import run_daemon
run_daemon(interval=5.0, log_file="~/.skchat/daemon.log")Transport bridge (requires SKComm):
from skcomm import SKComm
from skchat import ChatTransport, ChatHistory
from skmemory import MemoryStore
comm = SKComm.from_config()
history = ChatHistory(store=MemoryStore())
transport = ChatTransport(
skcomm=comm,
history=history,
identity="capauth:you@skworld.io",
)
# Send
transport.send_and_store(
recipient="capauth:lumina@capauth.local",
content="Hello from transport API",
thread_id="ops",
)
# Receive
messages = transport.poll_inbox()
for msg in messages:
print(msg.sender, msg.content)Core models:
| Class | Description |
|---|---|
ChatMessage |
The core message object (id, sender, recipient, content, thread_id, ttl, delivery_status, encrypted, signature) |
Thread |
A logical conversation grouping (id, title, participants, message_count) |
ContentType |
PLAIN, MARKDOWN, SYSTEM |
DeliveryStatus |
PENDING, SENT, DELIVERED, READ, FAILED |
ChatHistory |
SKMemory-backed message persistence and retrieval |
ChatTransport |
SKComm bridge for P2P send and receive |
ChatDaemon |
Background polling service |
MCP Server
SKChat provides a Model Context Protocol server for AI agents. Start it with:
skchat mcpOr configure it in your MCP host (Claude Code, Cursor, etc.):
{
"mcpServers": {
"skchat": {
"command": "python",
"args": ["-m", "skchat.mcp_server"]
}
}
}MCP Tools Reference
| Tool | Arguments | Description |
|---|---|---|
send_message |
recipient, message, thread_id?, ttl? |
Send a message |
get_inbox |
limit?, thread_id? |
Read local message history |
get_history |
participant, limit? |
Conversation with a peer |
search_messages |
query, limit? |
Full-text search |
create_group |
name, members[], description? |
Create group chat |
webrtc_status |
— | Active P2P connections and transport health |
initiate_call |
peer |
Open WebRTC data channel to peer (~1-3s async) |
accept_call |
peer |
Accept incoming WebRTC connection |
send_file_p2p |
file_path, recipient |
File transfer via WebRTC data channels |
WebRTC Tools Notes
initiate_callis non-blocking: returns immediately, ICE negotiation happens in background- Call
webrtc_statusafter ~3s to confirm the connection is established send_file_p2puses WebRTC parallel data channels if connected, falls back to SKComm transport- All WebRTC connections use CapAuth PGP-signed SDPs — MITM protection by design
Author / Support
- Author: smilinTux
- License: GPL-3.0-or-later
- Homepage: https://skchat.io
- Repository: https://github.com/smilinTux/skchat
- Issues: https://github.com/smilinTux/skchat/issues
Part of the SKCapstone sovereign agent stack.