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.