# BranchPy Server Architecture
Version: 1.1.1
Last Updated: January 23, 2026
Status: Production
Audience: Developers, Contributors, System Integrators
Executive Summary
BranchPy uses a unified daemon architecture with dual transports for real-time and request/response communication:
- WebSocket (port 8765) - Real-time events, subscriptions, watch notifications
- HTTP/REST (port 8766) - APIs for reports, health checks, graph generation
- Auto-start on demand - Zero manual server management required
- Auto-recovery - Graceful handling of crashes and failures
- Multi-project support - Concurrent analysis of multiple projects
- Protocol versioning - Backward compatibility and feature negotiation
Design Philosophy: Invisible infrastructure, seamless user experience, actionable errors.
Components
1. Unified Daemon (run.py)
Purpose: Single background process hosting both transports.
Location: BranchPyApp/branchpy/ws/run.py
Responsibilities:
- Start WebSocket server (port 8765) for real-time events
- Start HTTP server (port 8766) for request/response APIs
- Manage project state (active subscriptions, watch config)
- Write PID and metadata (
~/.branchpy/default/wsd.meta.json) - Graceful shutdown on signal (SIGTERM/SIGINT)
Key Features:
- Dual transport (optional HTTP; falls back to WS-only if FastAPI missing)
- Auto-restart logic for port conflicts (seeks 8765–8770 range)
- Structured logging (JSONL format, component=daemon)
- Lifecycle events:
daemon.start,daemon.http_ready,daemon.shutdown
Entry Points:
# Manual start (development)
python -m branchpy.ws.run --host 127.0.0.1 --port 8765 --runtime-dir ~/.branchpy --profile default
# Production (via extension)
Spawned by daemon.ts: ensureRunning() → start()
2. WebSocket Transport
Port: 8765 (WS) / 8766 (HTTP fallback detection)
Protocol: ws://127.0.0.1:8765/ws
Message Types:
- Client → Server:
{type: "sub", channel: "pilot:session_id"}(subscribe) - Server → Client:
{type: "watch.status", enabled: bool, activeReports: []} - Server → Client:
{type: "pilot.paths.update", paths: ["p001", "p002"]} - Error Envelope:
{type: "error", code: "BPY-1201", message: "...", severity: "warn"}
State Management:
DaemonState.clients: Set of active WebSocket connectionsDaemonState.subscribers: Map ofclient_id -> Set[report_types]- Persistent subscriptions (
.branchpy/daemon_subs.json)
Use Cases:
- File watch events (trigger auto-analysis)
- Live PILOT path updates (dropdown refresh without reload)
- Multi-client broadcast (all connected panels see same events)
3. HTTP Transport (FastAPI)
Port: 8766
Protocol: http://127.0.0.1:8766/
Routes:
| Endpoint | Method | Purpose | Response |
|---|---|---|---|
/api/handshake |
GET | Protocol version & capabilities | {protocol_version, server_version, capabilities} |
/pilot/health |
GET | Health check + stats2 presence | {ok, version, has_stats2, routes} |
/pilot/paths |
GET | List available path IDs | {paths: [...], schema_status, path_count} |
/pilot/graph |
GET | Mermaid graph for a path | HTML fragment |
/pilot/view |
GET | Interactive lane comparison UI | Full HTML page |
/api/v1/history/* |
* | History snapshots (if enabled) | JSON |
/api/governance/* |
* | Governance rules (if enabled) | JSON |
Features:
- CORS disabled (localhost-only)
- No authentication (future:
X-BranchPy-Tokenheader reserved) - Graceful error pages (no 500 for missing paths)
- Diagnostic payloads (
schema_status,suggestion)
4. Daemon Service (VS Code Extension)
Location: vscode-addon/src/daemon.ts
Purpose: Client-side lifecycle manager; bridge between UI and daemon.
Key Methods:
class DaemonService {
async ensureRunning(): Promise<boolean>
async start(): Promise<boolean>
async stop(): Promise<boolean>
async restart(): Promise<boolean>
async status(): Promise<DaemonStatus>
async ping(): Promise<number | null>
}
Metadata Format:
{
"pid": 14692,
"ws_port": 8765,
"http_port": 8766,
"protocol_version": "1.0.0",
"server_version": "0.9.0",
"started_at": "2025-11-07T19:15:32.456Z",
"capabilities": ["pilot", "history", "governance"]
}
Auto-Start Flow:
- User opens PILOT →
openPilot()called - Extension calls
daemonService.ensureRunning() - If not running → spawn Python process
- Wait for
/pilot/healthto return 200 - Read metadata to get actual ports
- Proceed with UI open
Status Bar Integration:
$(radio-tower) WS: 127.0.0.1:8765(green = running)$(warning) WS: 127.0.0.1:8765(yellow = degraded latency >50ms)$(debug-disconnect) WS: Stopped(red = not running)
Data Flow
Scenario 1: Open PILOT Visualization
[User clicks "PILOT" in Control Center]
↓
[Extension: openPilot()]
↓
[Check daemon status via daemonService.status()]
↓ (if not running)
[daemonService.ensureRunning() → spawns Python daemon]
↓
[Wait for /pilot/health → 200 OK]
↓
[Fetch /api/handshake → validate protocol_version]
↓
[Fetch /pilot/paths?project=X → get available paths]
↓ (if paths empty)
[Show notification: "No paths found (schema_status=...)" + "Run Stats2" button]
↓ (if paths exist)
[Open browser: http://127.0.0.1:8766/pilot/view?project=X&path=p001&session=abc]
↓
[Browser loads HTML → initializes WebSocket → subscribes to pilot:abc]
↓
[User changes lane dropdown → AJAX GET /pilot/graph?path=p002]
↓
[Mermaid diagram rendered in lane column]
Scenario 2: File Watch Auto-Analysis
[User saves game/script.rpy]
↓
[File watcher detects change (fswatch.ts or daemon internal)]
↓
[Daemon checks active subscriptions (DaemonState.subscribers)]
↓ (if "analyze" subscribed)
[Run: branchpy analyze]
↓
[Broadcast WS message: {type: "watch.event", report: "analyze", files: [...]}]
↓
[All connected WebSocket clients receive update]
↓
[Extension shows notification: "Analysis updated (3 files changed)"]
↓
[Report panel auto-refreshes if open]
Port Management
Strategy
Approach: Static base ports + conflict fallback
Default Ports:
- WebSocket: 8765
- HTTP: 8766
Conflict Resolution:
- Attempt base port (8765)
- If
EADDRINUSE→ try 8766, 8767, … up to 8770 - Write chosen port to
wsd.meta.json - Extension reads metadata; falls back to range scan if absent
Detection Algorithm (Extension):
async function detectDaemonPort(): Promise<number | null> {
const portsToTry = [8766, 8765, 8767, 8768, 8769, 8770];
for (const port of portsToTry) {
try {
const response = await fetch(`http://127.0.0.1:${port}/pilot/health`);
if (response.ok) return port;
} catch { continue; }
}
return null; // No daemon found
}
Future Enhancement: Port registry file for multi-daemon scenarios.
Protocol Contracts
Handshake Negotiation
Client (Extension) Requirements:
- Fetch
/api/handshakeon first connection - Validate
protocol_versioncompatibility (major version match) - Check
capabilitiesarray for required features (e.g.,pilot) - Store
ws_portandhttp_portfor session
Server Responsibilities:
- Return current protocol & server versions
- List enabled capabilities (pilot, history, governance)
- Include port info for multi-transport coordination
Compatibility Matrix:
| Client Protocol | Server Protocol | Compatible? | Action |
|---|---|---|---|
| 1.0.0 | 1.0.0 | ✅ Yes | Proceed |
| 1.0.0 | 1.1.0 | ✅ Yes | Minor version = backward compatible |
| 1.0.0 | 2.0.0 | ⌠No | Prompt user to upgrade daemon |
| 2.0.0 | 1.0.0 | ⌠No | Prompt user to upgrade extension |
Error Codes
Format: BPY-XXXX (BranchPy error taxonomy)
Categories:
| Range | Category | Examples |
|---|---|---|
| 1000–1099 | Validation | BPY-1001: Missing required parameter |
| 1100–1199 | Not Found | BPY-1101: Project not found |
| 1200–1299 | PILOT Errors | BPY-1201: Path not found |
| 1300–1399 | Schema Errors | BPY-1301: Invalid stats2 format |
| 2000–2999 | Internal Server | BPY-2001: Database connection failed |
Error Payload (JSON):
{
"error": true,
"code": "BPY-1201",
"message": "Path p999 not found in project myGame",
"severity": "warn",
"suggestion": "Available paths: p001, p002, p003",
"correlation_id": "req-abc123"
}
Error Payload (HTML for /pilot/view):
<div class='error-box'>
<div class='error-icon'>ðŸ”</div>
<div class='error-title'>Path Not Found</div>
<div class='error-detail'>Requested path p999 not found...</div>
<div class='error-suggestion'>Available paths: p001, p002</div>
<button onclick='window.history.back()'>Go Back</button>
</div>
See also: Technical/errors/README.md
Observability
Structured Logs
Format: JSONL (one JSON object per line)
Location:
- Daemon:
~/.branchpy/ws_daemon.log - Extension: VS Code Output panel (“BranchPy”)
Example Entry:
{
"ts": "2025-11-07T19:15:32.456Z",
"level": "info",
"component": "daemon",
"event": "pilot.paths.request",
"msg": "Fetching paths for project: myGame",
"project": "myGame",
"session_id": "abc-123",
"daemon_pid": 14692
}
Key Events:
daemon.start,daemon.http_ready,daemon.shutdownpilot.paths.request,pilot.paths.success,pilot.paths.emptypilot.view.path_not_found,pilot.graph.errorws.client.connect,ws.client.disconnectwatch.status,watch.event
See also: Technical/logging/README.md
Performance Targets
Latency Budget
| Operation | Target | Measured |
|---|---|---|
| Health check | <10ms | 5–8ms |
| Handshake | <20ms | 12–18ms |
| Fetch paths | <30ms | 15–25ms |
| Fetch graph | <50ms | 35–60ms (depends on path size) |
| WebSocket ping | <20ms | 8–15ms |
Optimization Strategies
- Cache parsed stats2.json (invalidate on file mtime change)
- Precompute path index
{path_id -> (labelSequence, stateChanges)} - Lazy-load large reports (pagination for >100 paths)
- Incremental JSON writing (stream large outputs)
- Connection pooling (reuse HTTP client in extension)
Security
Current (v1.1.1)
Authentication: None (localhost-only assumption)
Authorization: N/A
Transport: HTTP (no TLS for localhost)
Future-Proofing
Planned Features:
- Optional token-based auth (
X-BranchPy-Tokenheader) - Capability-based access control (read-only vs. admin)
- TLS support for remote scenarios (expose daemon over network)
Reserved Fields (Handshake):
{
"auth_required": false, // Future: true if token needed
"auth_methods": ["token", "oauth"], // Future options
"tls_enabled": false // Future: true if HTTPS
}
Related Documentation
- lifecycle.md - Auto-start, auto-recovery, health checks
- deployment.md - Production deployment
- Technical/backend/observability.md - Backend health endpoints
- Technical/api/api.md - API contracts
- Technical/events/README.md - Event bus
Source References
This document consolidates information from:
docs/v1.1.0/Server/ARCHITECTURE.mddocs/v1.1.0/Server/PROTOCOL.mddocs/v1.1.0/Server/PORT_MANAGEMENT.mddocs/v1.1.0/Server/MULTI_PROJECT.md