-
Notifications
You must be signed in to change notification settings - Fork 8
Architecture
Understanding how bdg works under the hood.
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.) │
└─────────────────────────────────────────────────────────┘
- 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
- Fast commands: CLI connects to daemon instantly via Unix socket
- Persistent state: Maintains Chrome connection and telemetry across commands
- Continuous monitoring: Captures network, console, and other events in background
- Session isolation: Each session is independent with its own daemon
bdg https://example.comWhat happens:
- CLI checks if daemon is already running (
~/.bdg/daemon.pid) - If not, spawns daemon process
- Daemon launches Chrome with CDP enabled
- Daemon connects to Chrome via WebSocket
- Daemon navigates to URL and waits for page load
- 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
bdg dom query "button"What happens:
- CLI connects to daemon via Unix socket (
~/.bdg/daemon.sock) - CLI sends command request as JSON
- Daemon executes CDP command(s)
- Daemon sends response back to CLI
- CLI formats and displays result
- CLI disconnects
Performance:
- Connection time: <10ms (Unix socket)
- No Chrome launch overhead
- Shared state (DOM cache, etc.)
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 afterbdg stop
bdg stopWhat happens:
- CLI sends stop command to daemon
- Daemon writes final telemetry to
~/.bdg/session.json - Daemon closes Chrome connection
- Daemon exits gracefully
- Chrome window closes (unless
--kill-chromenot used)
Files written:
-
~/.bdg/session.json- Complete session data
Files cleaned:
~/.bdg/daemon.pid~/.bdg/daemon.sock~/.bdg/session.meta.json
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)
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
}Daemon connects to Chrome via WebSocket:
- Chrome launches with
--remote-debugging-port=9222 - Chrome exposes WebSocket at
ws://localhost:9222/devtools/page/<id> - Daemon connects and maintains persistent WebSocket
- All CDP commands flow through this connection
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.
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.loadEventFiredEach 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
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-profileBy default, bdg waits for pages to be fully ready before returning:
-
window.onload fires
- HTML parsed, resources loaded
- Standard browser readiness
-
Network idle (200ms without new requests)
- All XHR/Fetch complete
- Important for SPAs
-
DOM stable (300ms without mutations)
- React hydration complete
- Dynamic content rendered
Use --no-wait to skip:
bdg https://example.com --no-waitUse when:
- Connecting to already-loaded page
- Testing static HTML
- You need immediate control
-
Starts: On first
bdg <url>command -
Runs: In background until
bdg stop - Survives: Terminal closes, parent process exits
-
Stops: Explicitly via
bdg stoporbdg cleanup
Normal cleanup:
bdg stop # Graceful shutdownStale daemon:
bdg cleanup --force # Remove stale files, kill daemonAggressive cleanup:
bdg cleanup --aggressive # Kill all Chrome processes tooNot supported yet - only one session at a time:
- Attempting
bdg <url>while session active → exit code 86 - Solution:
bdg stoporbdg cleanup --forcefirst
Future: Multi-session support is planned.
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.
CDP Error → Daemon → IPC → CLI → Exit Code
Example:
- Chrome returns "Node not found"
- Daemon maps to
RESOURCE_NOT_FOUND - IPC sends error with
exitCode: 83 - CLI exits with code 83
- Unix socket connection: <10ms
- CDP command: 10-100ms (depends on command)
- Page load: 500ms-5s (depends on page)
- CLI process: ~50MB (Node.js overhead)
- Daemon process: ~100MB (Node.js + CDP state)
- Chrome process: 100MB-1GB+ (depends on page)
- IPC: Zero (Unix socket)
- CDP WebSocket: ~1KB per command
- Telemetry: Grows with session activity
- Unix socket: Filesystem permissions only
- Chrome CDP: Bound to localhost:9222
- No network exposure by default
~/.bdg/ directory:
- Owned by user
- Mode 755 (rwxr-xr-x)
- Socket not world-readable
- Separate profile: No access to main browser data
- No persistence: Cleaned on
bdg cleanup - Sandboxed: Chrome's standard sandbox applies
- Getting Started - Basic usage
- Commands - Command reference
- Quick Reference - Fast lookup
- Troubleshooting - Common issues