Skip to content

Architecture

Kumak edited this page Nov 22, 2025 · 1 revision

Architecture

Understanding how bdg works under the hood.

Overview

bdg uses a daemon-based architecture with these key components:

┌─────────────────────────────────────────────────────────┐
│  CLI Process (bdg command)                              │
│  - Parses arguments                                     │
│  - Connects to daemon via Unix socket                   │
│  - Formats and displays output                          │
└─────────────────────────────────────────────────────────┘
                        │
                        │ IPC (Unix Domain Socket)
                        ▼
┌─────────────────────────────────────────────────────────┐
│  Daemon Process (background)                            │
│  - Manages Chrome instance                              │
│  - Maintains WebSocket to CDP                           │
│  - Collects telemetry (network, console, etc.)          │
│  - Handles commands from CLI                            │
└─────────────────────────────────────────────────────────┘
                        │
                        │ WebSocket (CDP)
                        ▼
┌─────────────────────────────────────────────────────────┐
│  Chrome Browser                                         │
│  - Launched with --remote-debugging-port=9222           │
│  - Exposes Chrome DevTools Protocol                     │
│  - Executes commands (DOM, Network, Page, etc.)         │
└─────────────────────────────────────────────────────────┘

Why a Daemon?

Problems Without a Daemon

  • Connection overhead: Every command would need to connect to Chrome
  • Lost context: No persistent state between commands
  • No background collection: Can't capture network/console events passively

Benefits of Daemon Architecture

  1. Fast commands: CLI connects to daemon instantly via Unix socket
  2. Persistent state: Maintains Chrome connection and telemetry across commands
  3. Continuous monitoring: Captures network, console, and other events in background
  4. Session isolation: Each session is independent with its own daemon

Session Lifecycle

1. Session Start (bdg <url>)

bdg https://example.com

What happens:

  1. CLI checks if daemon is already running (~/.bdg/daemon.pid)
  2. If not, spawns daemon process
  3. Daemon launches Chrome with CDP enabled
  4. Daemon connects to Chrome via WebSocket
  5. Daemon navigates to URL and waits for page load
  6. CLI disconnects, daemon continues running in background

Files created:

  • ~/.bdg/daemon.pid - Daemon process ID
  • ~/.bdg/daemon.sock - Unix socket for IPC
  • ~/.bdg/session.meta.json - Session metadata
  • ~/.bdg/chrome-profile/ - Chrome user data directory

2. Running Commands (bdg dom query "button")

bdg dom query "button"

What happens:

  1. CLI connects to daemon via Unix socket (~/.bdg/daemon.sock)
  2. CLI sends command request as JSON
  3. Daemon executes CDP command(s)
  4. Daemon sends response back to CLI
  5. CLI formats and displays result
  6. CLI disconnects

Performance:

  • Connection time: <10ms (Unix socket)
  • No Chrome launch overhead
  • Shared state (DOM cache, etc.)

3. Background Collection

While daemon runs, it continuously collects:

  • Network requests: All HTTP/XHR/Fetch activity
  • Console messages: Logs, warnings, errors
  • Performance metrics: Load times, resource sizes
  • CDP events: Page navigation, DOM mutations, etc.

This data is available via:

  • bdg peek - Snapshot of collected data
  • bdg tail - Live streaming updates
  • bdg session.json - Final output after bdg stop

4. Session Stop (bdg stop)

bdg stop

What happens:

  1. CLI sends stop command to daemon
  2. Daemon writes final telemetry to ~/.bdg/session.json
  3. Daemon closes Chrome connection
  4. Daemon exits gracefully
  5. Chrome window closes (unless --kill-chrome not used)

Files written:

  • ~/.bdg/session.json - Complete session data

Files cleaned:

  • ~/.bdg/daemon.pid
  • ~/.bdg/daemon.sock
  • ~/.bdg/session.meta.json

Inter-Process Communication (IPC)

Unix Domain Sockets

bdg uses Unix domain sockets for IPC instead of TCP:

Why Unix sockets?

  • Fast: No TCP overhead, ~10x faster than localhost TCP
  • Secure: Filesystem permissions, no network exposure
  • Reliable: No port conflicts, EADDRINUSE errors

Location: ~/.bdg/daemon.sock

Platform support:

  • ✅ macOS, Linux: Native support
  • ✅ Windows WSL: Works inside WSL
  • ❌ Native Windows: Not supported (no Unix sockets)

IPC Protocol

CLI ↔ Daemon communication uses JSON messages:

Request:

{
  "command": "dom.query",
  "params": {
    "selector": "button"
  }
}

Response:

{
  "success": true,
  "data": {
    "elements": [...],
    "count": 5
  }
}

Error:

{
  "success": false,
  "error": "Element not found",
  "exitCode": 83
}

Chrome DevTools Protocol (CDP)

WebSocket Connection

Daemon connects to Chrome via WebSocket:

  1. Chrome launches with --remote-debugging-port=9222
  2. Chrome exposes WebSocket at ws://localhost:9222/devtools/page/<id>
  3. Daemon connects and maintains persistent WebSocket
  4. All CDP commands flow through this connection

CDP Domains

CDP organizes functionality into 53 domains:

  • Page: Navigation, screenshots, lifecycle
  • DOM: Query, modify, events
  • Network: Requests, responses, cookies
  • Runtime: JavaScript execution
  • Performance: Metrics, tracing
  • etc.

bdg provides full CDP access - all 644 methods across all domains.

Event Handling

Daemon subscribes to CDP events for background collection:

// Network events
Network.requestWillBeSent
Network.responseReceived
Network.loadingFinished

// Console events
Runtime.consoleAPICalled
Log.entryAdded

// Page events
Page.frameNavigated
Page.loadEventFired

Chrome Profile Isolation

Default Profile

Each session uses an isolated Chrome profile in ~/.bdg/chrome-profile/:

Benefits:

  • No interference with your main Chrome browser
  • Clean state for each session (unless reused)
  • Session cookies, localStorage, etc. are isolated

Contents:

  • Default/ - Chrome user data
  • Cookies - Session cookies
  • Local Storage/ - localStorage data
  • IndexedDB/ - IndexedDB data

Custom Profiles

Use --user-data-dir for custom profiles:

# Persistent profile (cookies preserved across sessions)
bdg https://app.example.com --user-data-dir ~/.bdg/myapp-profile

# Temporary profile
bdg https://example.com --user-data-dir /tmp/test-profile

Page Readiness

By default, bdg waits for pages to be fully ready before returning:

Three Signals

  1. window.onload fires

    • HTML parsed, resources loaded
    • Standard browser readiness
  2. Network idle (200ms without new requests)

    • All XHR/Fetch complete
    • Important for SPAs
  3. DOM stable (300ms without mutations)

    • React hydration complete
    • Dynamic content rendered

Skipping Wait

Use --no-wait to skip:

bdg https://example.com --no-wait

Use when:

  • Connecting to already-loaded page
  • Testing static HTML
  • You need immediate control

Daemon Management

Daemon Lifecycle

  • Starts: On first bdg <url> command
  • Runs: In background until bdg stop
  • Survives: Terminal closes, parent process exits
  • Stops: Explicitly via bdg stop or bdg cleanup

Cleanup Scenarios

Normal cleanup:

bdg stop  # Graceful shutdown

Stale daemon:

bdg cleanup --force  # Remove stale files, kill daemon

Aggressive cleanup:

bdg cleanup --aggressive  # Kill all Chrome processes too

Multiple Sessions

Not supported yet - only one session at a time:

  • Attempting bdg <url> while session active → exit code 86
  • Solution: bdg stop or bdg cleanup --force first

Future: Multi-session support is planned.

Error Handling

Semantic Exit Codes

Exit codes follow semantic ranges for automation:

  • 0: Success
  • 80-89: User errors (invalid input)
  • 100-119: Software errors (Chrome, CDP, internal)

See Quick Reference for complete list.

Error Propagation

CDP Error → Daemon → IPC → CLI → Exit Code

Example:

  1. Chrome returns "Node not found"
  2. Daemon maps to RESOURCE_NOT_FOUND
  3. IPC sends error with exitCode: 83
  4. CLI exits with code 83

Performance Characteristics

Command Latency

  • Unix socket connection: <10ms
  • CDP command: 10-100ms (depends on command)
  • Page load: 500ms-5s (depends on page)

Memory Usage

  • CLI process: ~50MB (Node.js overhead)
  • Daemon process: ~100MB (Node.js + CDP state)
  • Chrome process: 100MB-1GB+ (depends on page)

Network Overhead

  • IPC: Zero (Unix socket)
  • CDP WebSocket: ~1KB per command
  • Telemetry: Grows with session activity

Security Considerations

Local-Only

  • Unix socket: Filesystem permissions only
  • Chrome CDP: Bound to localhost:9222
  • No network exposure by default

Filesystem Permissions

~/.bdg/ directory:

  • Owned by user
  • Mode 755 (rwxr-xr-x)
  • Socket not world-readable

Chrome Security

  • Separate profile: No access to main browser data
  • No persistence: Cleaned on bdg cleanup
  • Sandboxed: Chrome's standard sandbox applies

Related Pages