Architecture

reins is three small pieces with one narrow contract between them. Everything runs on your machine, and everything binds 127.0.0.1.

Your agent

Claude Code, Cursor, Codex — anything with a shell

shells out

reins CLI

@karnstack/reins

HTTP /rpc · 127.0.0.1 · auto-spawned

reins daemon

one per machine, serves every browser

WebSocket · allowlisted chrome-extension:// origins

reins extension

MV3 — an offscreen document holds the socket

chrome.debugger · Chrome DevTools Protocol

Your tabs

Chrome, Brave, Edge, Arc, Dia

The CLI

The CLI is the entire interface: reins tabs, reins click, reins screenshot, and the rest of the command set. Agents use it because they already have a shell — no MCP server to register, no per-agent setup. A skill (npx skills add karnstack/reins) teaches agents the loop.

The daemon

The daemon is invisible plumbing. Any CLI command spawns it on demand; it exposes an HTTP /rpc endpoint for the CLI and holds the WebSocket that extensions dial into. One daemon serves any number of browsers. reins kill stops it, and logs live in ~/.reins/logs/.

The extension

A Manifest V3 extension. Its service worker executes commands against tabs through chrome.debugger — the Chrome DevTools Protocol — and an offscreen document holds the persistent WebSocket to the daemon, because MV3 service workers are suspended when idle and can't keep long-lived sockets.

The extension discovers the daemon by probing a small set of candidate localhost ports and authenticates itself by its chrome-extension://<id> origin — a header the browser stamps itself, which web pages and other extensions cannot forge.

Multiple browsers

Install the extension in several Chromium browsers — Chrome, Brave, Edge, Arc, Dia — and each connects to the same daemon. reins tabs lists every tab with a browser id; pass --browser <id> only when more than one browser is connected. reins never guesses which browser you meant.

Element refs

reins snapshot assigns stable refs (e5: button "Submit") to interactive elements. Commands act by ref, which survives page repaints better than hand-written selectors — and a CSS --selector fallback exists for everything else.