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.