You type a command. A button gets clicked somewhere inside a headless Chrome window you can't see. Then, roughly two hundred milliseconds later, your terminal prints a result and hands control back to you.

That gap is where six separate things happen, in order, every single time. I know because I watched them happen — using ps -eww process inspection, daemon status checks, and a loop of performance.now() calls. Not from reading documentation. From tracing an actual CLI session, process by process, on a live machine.

01 — The Command You Type Is Not the Process That Does the Work

The first surprise: vibium click "button" spins up a brand-new OS process every time you run it. It is not a long-lived client sitting around between calls — it launches, does one thing, prints a result, and exits. Every invocation, from scratch.

That sounds wasteful, and in a narrow sense it is: process spawn is real overhead, paid on every command. But it buys something in exchange — no client-side state to corrupt, no zombie connections, no wondering whether last session's context leaked into this one. Each call starts clean.

02 — One Daemon, Reached Over a Unix Socket

The ephemeral CLI process's first job is to find the thing that actually holds state: a background daemon, listening on a Unix domain socket at ~/Library/Caches/vibium/vibium.sock. This isn't a metaphor — vibium daemon status shows a single PID, and both the CLI and an MCP server connect to that exact same process.

The daemon is a dispatcher, not a shared browser. It routes; it doesn't merge sessions.

03 — Nothing Running? It Launches Itself

Stop the daemon. Stop the browser. Run a command cold. I tested this directly: kill everything, then issue vibium go <url> from a clean slate. The daemon spins up. Chrome spins up. No explicit start command required — the whole stack is lazy by design. You ask for a browser action, and the infrastructure required to perform it materializes on demand.

04 — Every Session Gets Its Own Chrome, Its Own Profile

This is the one I didn't expect going in. Each session — whether launched from the CLI or from an MCP browser_start call — spins up a distinct Chrome-for-Testing process, each with its own freshly-generated temporary --user-data-dir.

I didn't take this on faith. I set a cookie in one session and tried to read it from a second, concurrent one. Empty. No leak, no shared state — verified, not assumed. It's a deliberate isolation boundary: real test isolation for free, and a real cost — a full browser process — for every distinct session you open.

Verified, not assumed

Cross-session cookie leakage was tested directly on this machine, not inferred from behavior. A cookie set in session A read back empty from concurrent session B. See the related session-isolation investigation for the full methodology.

05 — The Protocol Underneath: BiDi, Not Raw CDP

Once the daemon has a live browser, it doesn't poke at Chrome's DevTools Protocol directly. It speaks WebDriver BiDi, a bidirectional WebSocket protocol built for exactly this. The daemon translates the parsed command into a BiDi message, ships it over the socket, and Chrome executes it against the live page: the click dispatch, the JS evaluation, the navigation.

06 — And Back Again — the Full Round Trip

The result retraces the whole path in reverse. Here's the full round trip, laid flat:

One call, six hops
bashspawns a fresh CLI process
CLIconnects to the daemon over the Unix socket
daemonforwards the command to Chrome over BiDi
Chromedispatches the action against the live page
pagereturns DOM state or a JS value
CLIprints the result, exits — daemon and browser stay warm

07 — What This Actually Costs

Measured, not estimated, from a loop of vibium eval 'performance.now()' against an already-warm daemon and browser:

170–250ms
round-trip for one CLI call, warm daemon and browser.
~3,000ms
fixed setup cost — navigate, dismiss, fill, arm — before a multi-step flow reaches its first timed action.
Why it matters

If you're measuring something with a tight timing window — the exact millisecond gap during which a page silently swallows a click before its JS finishes wiring up — and your own instrumentation contributes two or three unaccounted-for round trips, your measurement is lying to you before you've started. Splitting “read the clock” and “fire the click” across two separate round trips once ate a full trip's worth of latency inside a window that was only ~500ms wide to begin with.

The Takeaway

None of this is in a README. It's what you get from watching the machinery — process tables, socket paths, timing loops — instead of trusting the mental model you'd otherwise assume. The CLI looks like one command hitting one browser. It's actually six hops through a dispatcher, a lazily-launched daemon, and an isolated Chrome process, every time.

If you're building anything that automates a browser and cares about when, not just whether, something happened — that gap between “looks instant” and “is actually six round trips” is exactly where your flaky tests and bad timing data will come from.

Further reading

This trace has a companion diagram with the full sequence rendered visually: What happens when a vibium CLI call is sent ↗ — and its counterpart for the MCP-driven path, which spends its extra cost on reasoning turns instead of process spawns: the 8-step test case, command by command ↗, walking one real flow through both interfaces side by side.

I welcome comments and contributions on this topic. Find me here: