FFFF GitHub - adeelahmad/Tether Β· GitHub
[go: up one dir, main page]

Skip to content
adeelahmadPublic

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

12 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Tether

Your logged-in Chrome β€” as a proxy, from anywhere. No extension.

Give your AI agent (or your script, or your other laptop) your real, logged-in browser.
One self-contained binary. Speaks MCP, plain HTTP proxy and WebDriver. Drives Chrome over the DevTools Protocol.

CI Go Report Card Latest release Docs License: GPL v3

Tether driving a real Chrome tab: curl through the proxy returns a logged-in page

Agents and scripts keep hitting the same wall: the page they need is behind a login, an SSO dance, a captcha you already solved, or a cookie banner you already dismissed β€” in your browser. Every tool that bridges into that browser today asks you to install an extension.

Tether doesn't. It attaches to Chrome over the DevTools Protocol that Chrome already speaks, and turns each tab into something any client can use:

  • 🌐 An HTTP/HTTPS proxy. curl -x, Python requests, another browser, anything. Each request is replayed inside a real tab: real cookies, real storage, real JavaScript. You get the rendered page back.
  • πŸ€– A WebDriver endpoint. Point Selenium at it and most scripts just work β€” against your actual signed-in profile.
  • 🧠 An MCP server for agents. Claude, Cursor or your own agent get tools to read, click, type, screenshot and fetch in your real browser (docs/MCP.md).
  • πŸ–₯️ A live remote-browser UI. Open /remote/ from your phone or another laptop, watch any tab in real time, and take over with mouse, keyboard and clipboard β€” even the tab your agent is driving (docs).
  • 🧩 One core, four faces. Proxy, WebDriver, MCP and the remote UI all ship today on the same tab sessions (architecture).
  • πŸ”’ Safe by default. Loopback-only unless you set a token. Built to sit on a private Tailscale tailnet, not the internet.

30-second quickstart

1. Install

go install github.com/adeelahmad/tether/cmd/tether@latest

Or brew install --cask adeelahmad/tap/tether, or grab a binary from Releases.

2. Start β€” this opens a dedicated Chrome window. Log in to a site (say, GitHub) once; the profile persists.

tether serve --launch

3. Prove it β€” in another terminal, fetch a logged-in page through the proxy:

curl -s http://127.0.0.1:8787/tether/v1/ca.pem -o tether-ca.pem
curl -s -x http://127.0.0.1:8787 --cacert tether-ca.pem https://github.com/settings/profile \
  | grep -o '<title>[^<]*</title>'

You get your profile settings page β€” the one that redirects to a login screen for everyone else. πŸŽ‰

Prefer your everyday Chrome? Skip --launch. On Chrome 144+, open chrome://inspect/#remote-debugging, allow remote debugging, and run tether serve β€” it finds the browser automatically. On any Chrome, you can also start it with --remote-debugging-port=9222 --user-data-dir=<dir> (Chrome requires a non-default profile directory for the debugging port).

How it works

flowchart LR
  A["curl / requests"] -- "HTTP proxy" --> T
  B["Selenium"] -- "WebDriver" --> T
  D["AI agent"] -- "MCP" --> T
  T["tether serve<br/>sessions Β· policy Β· auth"] -- "DevTools Protocol" --> C
  subgraph C["Your Chrome (logged in)"]
    T1["tab: github.com"]
    T2["tab: mail.google.com"]
    T3["tab: WebDriver session"]
  end
Loading

One session = one real tab. Tether picks the tab for each request by:

  1. an explicit X-Tether-Tab-ID header, else
  2. the tether_tab cookie Tether set on first contact, else
  3. one default tab per site (registrable domain via the Public Suffix List, so shop.example.co.uk and www.example.co.uk share a tab).

On first contact Tether opens a fresh background tab and returns its ID as both an X-Tether-Tab-ID header and a Set-Cookie. That cookie is never forwarded to Chrome or the site. A WebDriver session ID is a tab ID, so the proxy and WebDriver can drive the same tab.

GET/HEAD navigate the tab and return the rendered DOM (or the original bytes with X-Tether-Mode: raw). POST and friends run as a same-origin fetch() inside the tab, so the browser attaches its own cookies. HTTPS works by terminating CONNECT with a local CA you trust once (tether ca prints its path).

Use it

Proxy

P="-x http://127.0.0.1:8787 --cacert tether-ca.pem"

curl $P https://news.ycombinator.com/                     # rendered page, your session
curl $P -H 'X-Tether-Wait: networkidle' https://app.example.com/dashboard
curl $P -H 'X-Tether-Mode: raw' https://example.com/data.json
curl $P -X POST -H 'Content-Type: application/json' -d '{"q":1}' https://api.example.com/search
curl $P -i https://github.com/ | grep -i x-tether-tab-id  # which tab served it
Request header Meaning
X-Tether-Tab-ID Use this tab (any open tab ID works; unknown β†’ 410 tab_gone)
X-Tether-Mode auto (default), rendered, raw, fetch
X-Tether-Wait load (default), domcontentloaded, networkidle, none
X-Tether-Timeout Per-request timeout, e.g. 30s

Responses carry X-Tether-Tab-ID, X-Tether-Final-URL (after redirects) and, on errors, a stable X-Tether-Error code (tab_gone, forbidden, navigation_failed, timeout, …).

MCP (AI agents)

claude mcp add --transport http tether http://127.0.0.1:8787/mcp

Then ask: "Open my GitHub notifications and summarise what needs me." The agent gets browser_navigate, browser_snapshot, browser_click, browser_type, browser_screenshot, browser_fetch and more, all in your signed-in Chrome. Claude Desktop and other stdio clients use the bundled bridge ("command": "tether", "args": ["mcp"]). Setup for every client, the full tool list and the security notes: docs/MCP.md.

WebDriver

from selenium import webdriver
d = webdriver.Remote("http://127.0.0.1:8787", options=webdriver.ChromeOptions())
d.get("https://github.com/notifications")
print(d.title)
d.quit()

Covered commands and behaviour: docs/WEBDRIVER.md.

Remote browser UI

Open http://127.0.0.1:8787/remote/ (or just the server address) in any browser. Pick a tab, see it live, click, scroll, type, paste; answer its alert/confirm dialogs; open, navigate and close tabs. Toggle watch only to look over an agent's shoulder without disturbing it. With --token, the UI asks for the token once and keeps a strict, UI-only cookie. docs/REMOTE.md covers the controls, Tailscale setup and the websocket protocol.

Control API

curl -s http://127.0.0.1:8787/tether/v1/tabs                           # list sessions
curl -s -X POST http://127.0.0.1:8787/tether/v1/tabs -d '{"url":"https://example.com"}'
curl -s -X POST http://127.0.0.1:8787/tether/v1/tabs/$TAB/eval \
     -d '{"expression":"document.title"}'                               # Runtime.evaluate
curl -s "http://127.0.0.1:8787/tether/v1/tabs/$TAB/logs?level=warning" # console + browser logs
curl -s -X DELETE http://127.0.0.1:8787/tether/v1/tabs/$TAB            # close

Every tab Tether drives keeps its recent console.* output, uncaught exceptions and browser log entries (failed requests, security warnings). GET …/logs returns {"entries": [...], "next": N}; pass ?after=N to read only what is new, and ?level=debug|info|warning|error to filter. The same buffer backs WebDriver's driver.get_log("browser").

From another machine (Tailscale)

export TETHER_TOKEN="$(openssl rand -hex 32)"
tether serve --launch --addr "$(tailscale ip -4):8787"
# elsewhere on the tailnet:
curl -x "http://tether:$TETHER_TOKEN@my-laptop:8787" --cacert tether-ca.pem https://github.com/
# or watch and drive it from your phone: http://my-laptop:8787/remote/

tether serve --launch --tailscale (or [tailscale] enabled = true in the config file) picks the tailnet address for you, and --tailscale-publish serve adds an HTTPS name via tailscale serve; see docs/CONFIG.md.

Tether refuses to listen beyond loopback without a token. Read SECURITY.md before exposing it anywhere.

One token per agent

Give every agent its own token with its own domain rules. Each token only sees the tabs it opened, and the rules are enforced inside the browser, so redirects, link clicks and page script can't wander off the allowlist either.

tether serve --policy-file examples/policy.json   # tokens + rules, see docs/POLICIES.md
curl -s -H "Authorization: Bearer $TETHER_RESEARCH_TOKEN" http://127.0.0.1:8787/tether/v1/whoami
# lock a single tab tighter still
curl -s -H "Authorization: Bearer $TETHER_RESEARCH_TOKEN" -X POST http://127.0.0.1:8787/tether/v1/tabs \
  -d '{"url":"https://en.wikipedia.org/","policy":{"allow":["en.wikipedia.org"]}}'

Configuration

Every flag has a TETHER_* environment variable, and almost everything can also go in a TOML config file: ~/.config/tether/config.toml is read if it exists, or pass --config. Precedence is flag > environment > file > default. The file also holds extra tokens, headers Chrome sends, cookies to seed, the Chrome profile to launch and MCP OAuth. Start from tether.example.toml; the full reference is docs/CONFIG.md.

Flag Default
--config ~/.config/tether/config.toml if present TOML config file β€” see docs/CONFIG.md
--addr 127.0.0.1:8787 Listen address
--token / --token-file β€” Bearer token (β‰₯16 chars); required off-loopback
--insecure-allow-public false Allow off-loopback with no token (don't)
--chrome auto auto, a port, host:port, http://…, ws://…, or a DevToolsActivePort path
--launch / --headless false Launch a dedicated Chrome (persistent profile under --data-dir)
--chrome-bin, --chrome-args, --profile-dir auto With --launch
--chrome-profile last used With --launch: profile inside --profile-dir, e.g. "Profile 1"
--idle-timeout 15m Close tabs Tether opened after inactivity (0 = never)
--timeout 60s Default per-request timeout
--console-buffer 1000 Console/log entries kept per tab (0 = no capture)
--allow-domains all Comma-separated navigation allowlist (applies to every face)
--deny-domains none Comma-separated denylist; deny beats allow
--policy-file β€” JSON or TOML file with per-token rules and tokens β€” see docs/POLICIES.md
--data-dir user config dir Local CA and launched profile
--no-mitm false Refuse HTTPS CONNECT instead of intercepting
--mcp true Serve the MCP face at /mcp
--mcp-oauth false Let MCP clients sign in with OAuth β€” see docs/CONFIG.md
--public-url from request Externally visible origin (OAuth issuer)
--tailscale false Listen on this machine's tailnet address (keeps the --addr port)
--tailscale-publish β€” serve or funnel: also publish over HTTPS via the tailscale CLI
--no-remote false Don't serve the remote-browser UI at /remote/
--remote-quality 70 JPEG quality of the remote UI video (1–100)
--log-level, --log-format info, text json for machines; secrets are always redacted

Docker (headless Chromium inside the image):

docker run --rm -p 8787:8787 -e TETHER_TOKEN=$(openssl rand -hex 32) -v tether:/data ghcr.io/adeelahmad/tether

How Tether compares

An honest look. Tether's trade-off is that HTTPS proxying needs you to trust a local CA, and it only drives Chromium-family browsers.

Tether Extension-based bridges Cloud browsers Fresh headless automation
Uses your real logged-in profile βœ… βœ… ❌ (re-login, stored creds) ❌
Needs a browser extension No Yes No No
Plain HTTP proxy (curl, any language) βœ… ❌ Rarely ❌
W3C WebDriver / Selenium βœ… incl. frames, alerts, windows, uploads, PDF ❌ Often βœ…
MCP for agents βœ… Often Often Via wrappers
Runs entirely on your machine βœ… βœ… ❌ βœ…
Remote access Tailscale + token Usually local only βœ… DIY
Watch & take over a tab live βœ… built-in web UI ❌ βœ… ❌
Single static binary βœ… ❌ n/a ❌ (driver + runtime)
Browsers Chrome / Chromium / Edge Varies Varies Many

Status & roadmap

  • Tab-session core, HTTP/HTTPS proxy, WebDriver common set, control API
  • WebDriver Actions: hover, drag and drop, key chords, wheel, multi-touch
  • Safe-by-default exposure, token auth, domain allowlist seam
  • MCP server face: 16 browser tools over the same core, plus a stdio bridge
  • Remote-browser UI: live screencast, mouse/keyboard/clipboard, dialogs, tab management
  • Per-token and per-session domain policies, enforced in the browser (docs/POLICIES.md)
  • Per-tab console and browser log capture (control API + WebDriver get_log)

Contributing

Issues and PRs welcome β€” start with CONTRIBUTING.md and docs/ARCHITECTURE.md. make check runs what CI runs.

License

GPL-3.0-or-later Β© Adeel Ahmad. The proxy front door builds on snail007/goproxy (GPLv3).

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

0