Docs-Builder

# 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 connections
  • DaemonState.subscribers: Map of client_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-Token header 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:

  1. User opens PILOT → openPilot() called
  2. Extension calls daemonService.ensureRunning()
  3. If not running → spawn Python process
  4. Wait for /pilot/health to return 200
  5. Read metadata to get actual ports
  6. 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:

  1. Attempt base port (8765)
  2. If EADDRINUSE → try 8766, 8767, … up to 8770
  3. Write chosen port to wsd.meta.json
  4. 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/handshake on first connection
  • Validate protocol_version compatibility (major version match)
  • Check capabilities array for required features (e.g., pilot)
  • Store ws_port and http_port for 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.shutdown
  • pilot.paths.request, pilot.paths.success, pilot.paths.empty
  • pilot.view.path_not_found, pilot.graph.error
  • ws.client.connect, ws.client.disconnect
  • watch.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

  1. Cache parsed stats2.json (invalidate on file mtime change)
  2. Precompute path index {path_id -> (labelSequence, stateChanges)}
  3. Lazy-load large reports (pagination for >100 paths)
  4. Incremental JSON writing (stream large outputs)
  5. 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-Token header)
  • 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
}


Source References

This document consolidates information from:

  • docs/v1.1.0/Server/ARCHITECTURE.md
  • docs/v1.1.0/Server/PROTOCOL.md
  • docs/v1.1.0/Server/PORT_MANAGEMENT.md
  • docs/v1.1.0/Server/MULTI_PROJECT.md