DM channels (createChannel), channel authz, security/docs, future improvements
- Add createChannel RPC (node.capnp @18): create 1:1 channel, returns 16-byte channelId - Store: create_channel(member_a, member_b), get_channel_members(channel_id) - FileBackedStore: channels.bin; SqlStore: migration 003_channels, schema v4 - channel_ops: handle_create_channel (auth + identity, peerKey 32 bytes) - Delivery authz: when channel_id.len() == 16, require caller and recipient are channel members (E022/E023) - Error codes E022 CHANNEL_ACCESS_DENIED, E023 CHANNEL_NOT_FOUND - SUMMARY: link Certificate lifecycle; security audit, future improvements, multi-agent plan docs - Certificate lifecycle doc, SECURITY-AUDIT, FUTURE-IMPROVEMENTS, MULTI-AGENT-WORK-PLAN - Client/core/tls/auth/server main: assorted fixes and updates from review and audit Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
152
scripts/chat-test.sh
Executable file
152
scripts/chat-test.sh
Executable file
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env bash
|
||||
# ── Interactive Chat Test ─────────────────────────────────────────────────────
|
||||
#
|
||||
# Spins up a server + two client containers (Alice & Bob), sets up an MLS
|
||||
# group, and opens a tmux session with side-by-side interactive chat panes.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/chat-test.sh
|
||||
#
|
||||
# Requirements: docker, docker compose, tmux
|
||||
# Exit: Ctrl+D in both panes, or: tmux kill-session -t qpc-chat
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
COMPOSE_FILE="$PROJECT_ROOT/docker/docker-compose.chat-test.yml"
|
||||
COMPOSE="docker compose -f $COMPOSE_FILE -p qpc-chat-test"
|
||||
|
||||
# ── Colors ────────────────────────────────────────────────────────────────────
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
CYAN='\033[0;36m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
step() { echo -e "${GREEN}==> $1${NC}"; }
|
||||
info() { echo -e " ${CYAN}$1${NC}"; }
|
||||
error() { echo -e "${RED}ERROR: $1${NC}" >&2; }
|
||||
|
||||
# ── Cleanup on exit ──────────────────────────────────────────────────────────
|
||||
|
||||
cleanup() {
|
||||
echo ""
|
||||
step "Tearing down containers..."
|
||||
$COMPOSE down -v --remove-orphans 2>/dev/null || true
|
||||
}
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
# ── Preflight checks ─────────────────────────────────────────────────────────
|
||||
|
||||
if ! command -v docker &>/dev/null; then
|
||||
error "docker is required but not installed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! docker compose version &>/dev/null; then
|
||||
error "docker compose (v2 plugin) is required."
|
||||
echo " See: https://docs.docker.com/compose/install/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v tmux &>/dev/null; then
|
||||
error "tmux is required but not installed."
|
||||
echo " macOS: brew install tmux"
|
||||
echo " Linux: sudo apt-get install tmux"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Step 1: Build ─────────────────────────────────────────────────────────────
|
||||
|
||||
step "Building Docker image (server + client)..."
|
||||
$COMPOSE build
|
||||
|
||||
# ── Step 2: Start services ────────────────────────────────────────────────────
|
||||
|
||||
step "Starting server, alice, bob..."
|
||||
$COMPOSE up -d --wait
|
||||
|
||||
# ── Step 3: Verify server reachable via QUIC ──────────────────────────────────
|
||||
|
||||
step "Verifying QUIC connectivity from alice..."
|
||||
$COMPOSE exec -T alice quicnprotochat health
|
||||
|
||||
# ── Step 4: Alice — register identity + upload KeyPackage ─────────────────────
|
||||
|
||||
step "Alice: register-state..."
|
||||
$COMPOSE exec -T alice quicnprotochat register-state --state /chat/alice.bin
|
||||
|
||||
ALICE_KEY=$($COMPOSE exec -T alice quicnprotochat whoami --state /chat/alice.bin \
|
||||
| grep 'identity_key' | awk '{print $3}' | tr -d '[:space:]')
|
||||
info "Alice identity: ${ALICE_KEY}"
|
||||
|
||||
if [ -z "$ALICE_KEY" ]; then
|
||||
error "Failed to extract Alice's identity key."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Step 5: Bob — register identity + upload KeyPackage ───────────────────────
|
||||
|
||||
step "Bob: register-state..."
|
||||
$COMPOSE exec -T bob quicnprotochat register-state --state /chat/bob.bin
|
||||
|
||||
BOB_KEY=$($COMPOSE exec -T bob quicnprotochat whoami --state /chat/bob.bin \
|
||||
| grep 'identity_key' | awk '{print $3}' | tr -d '[:space:]')
|
||||
info "Bob identity: ${BOB_KEY}"
|
||||
|
||||
if [ -z "$BOB_KEY" ]; then
|
||||
error "Failed to extract Bob's identity key."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Step 6: Alice creates group ───────────────────────────────────────────────
|
||||
|
||||
step "Alice: create-group 'docker-chat'..."
|
||||
$COMPOSE exec -T alice quicnprotochat create-group \
|
||||
--state /chat/alice.bin \
|
||||
--group-id docker-chat
|
||||
|
||||
# ── Step 7: Alice invites Bob ─────────────────────────────────────────────────
|
||||
|
||||
step "Alice: invite Bob..."
|
||||
$COMPOSE exec -T alice quicnprotochat invite \
|
||||
--state /chat/alice.bin \
|
||||
--peer-key "$BOB_KEY"
|
||||
|
||||
# ── Step 8: Bob joins ─────────────────────────────────────────────────────────
|
||||
|
||||
step "Bob: join group..."
|
||||
$COMPOSE exec -T bob quicnprotochat join --state /chat/bob.bin
|
||||
|
||||
# ── Step 9: Launch tmux ──────────────────────────────────────────────────────
|
||||
|
||||
step "Launching interactive chat (tmux side-by-side)..."
|
||||
echo ""
|
||||
echo " Left pane: Alice"
|
||||
echo " Right pane: Bob"
|
||||
echo ""
|
||||
echo " Type a message + Enter to send."
|
||||
echo " Ctrl+D exits a pane."
|
||||
echo " tmux kill-session -t qpc-chat to stop."
|
||||
echo ""
|
||||
|
||||
ALICE_CMD="$COMPOSE exec alice quicnprotochat chat --state /chat/alice.bin"
|
||||
BOB_CMD="$COMPOSE exec bob quicnprotochat chat --state /chat/bob.bin"
|
||||
|
||||
# Kill any stale tmux session with the same name.
|
||||
tmux kill-session -t qpc-chat 2>/dev/null || true
|
||||
|
||||
# Create tmux session with Alice in the first pane, Bob in a horizontal split.
|
||||
tmux new-session -d -s qpc-chat -x 200 -y 50 "$ALICE_CMD"
|
||||
tmux split-window -h -t qpc-chat "$BOB_CMD"
|
||||
|
||||
# Label panes (tmux >= 2.6).
|
||||
tmux select-pane -t qpc-chat:0.0 -T "Alice"
|
||||
tmux select-pane -t qpc-chat:0.1 -T "Bob"
|
||||
tmux set-option -t qpc-chat pane-border-status top 2>/dev/null || true
|
||||
tmux set-option -t qpc-chat pane-border-format " #{pane_title} " 2>/dev/null || true
|
||||
|
||||
# Attach — blocks until the user exits tmux.
|
||||
tmux attach-session -t qpc-chat
|
||||
|
||||
step "Chat session ended."
|
||||
Reference in New Issue
Block a user