feat: harden remote serve and reuse connections
Testing / remote-protocol-compat (0.9.5) (push) Successful in 56s
Testing / remote-protocol-compat (0.9.3) (push) Successful in 59s
Testing / test (push) Successful in 1m1s
Build & Publish Package / publish (push) Successful in 33s
Package Extension / package-extension (push) Successful in 36s
Testing / remote-protocol-compat (0.9.5) (push) Successful in 56s
Testing / remote-protocol-compat (0.9.3) (push) Successful in 59s
Testing / test (push) Successful in 1m1s
Build & Publish Package / publish (push) Successful in 33s
Package Extension / package-extension (push) Successful in 36s
- Gate TCP serve commands with safe-by-default policies, per-key allow tokens, per-key rate limiting, and audit labels. - Reuse authenticated encrypted remote sessions and parallelize/caches multi-browser fanout to reduce repeated handshake roundtrips. - Increase paged native-host batch size with extension-side byte budgeting to speed large tab listings safely. - Point install output at public Chrome Web Store / Firefox AMO listings by default, with --dev preserving unpacked workflows. - Share search-engine metadata between CLI and SDK and bump the package/extension version to 0.16.0. - Cover the new security, pooling, paging, install, and fanout behavior with expanded Python and extension tests.
This commit is contained in:
@@ -53,8 +53,10 @@ Every response:
|
||||
|
||||
**Requirements:** Python 3.10+, [uv](https://github.com/astral-sh/uv), Chrome, Chromium, Brave, Edge, Vivaldi, or Firefox
|
||||
|
||||
browser-cli has two parts: the **CLI / native host** (a Python package) and the **browser extension** (published on the public stores).
|
||||
|
||||
### Install with uv
|
||||
Install the CLI from PyPI as a uv tool:
|
||||
Install the CLI from PyPI as a uv tool, then register the native host:
|
||||
|
||||
```sh
|
||||
uv tool install real-browser-cli
|
||||
@@ -76,21 +78,31 @@ To upgrade later:
|
||||
uv tool upgrade real-browser-cli
|
||||
```
|
||||
|
||||
### Add the browser extension
|
||||
Install the extension from its public store listing (the `install` command prints the right link for you):
|
||||
|
||||
- Chrome / Chromium / Brave / Edge / Vivaldi — [Chrome Web Store](https://chromewebstore.google.com/detail/browser-cli/hekaebjhbhhdbmakimmaklbblbmccahp)
|
||||
- Firefox — [Firefox Add-ons](https://addons.mozilla.org/firefox/addon/browser-cli/)
|
||||
|
||||
The native host manifest trusts both the published store ID and the unpacked development ID, so the store extension works out of the box. If you are hacking on the extension yourself, run `browser-cli install <browser> --dev` for the unpacked / temporary-add-on load steps instead.
|
||||
|
||||
### Install from source
|
||||
```sh
|
||||
git clone <repo>
|
||||
cd browser-cli
|
||||
uv sync
|
||||
uv run browser-cli install brave # or: chrome, chromium, edge, vivaldi, firefox
|
||||
npm ci && npm run build:extension # build the unpacked extension bundles
|
||||
uv run browser-cli install brave --dev # --dev prints unpacked-load steps; or: chrome, chromium, edge, vivaldi, firefox
|
||||
```
|
||||
|
||||
The `install` command will:
|
||||
1. Ask you to load the browser-specific extension package
|
||||
2. Show the stable extension ID used by that browser family
|
||||
3. Write the native messaging manifest to your OS so the browser can find the host
|
||||
4. Copy the native host into an internal `libexec` directory and create a small wrapper outside your `PATH`
|
||||
Omit `--dev` to be pointed at the public store listing instead of loading the unpacked build.
|
||||
|
||||
After install, **fully restart your browser** (Quit and reopen — not just close the window). The extension will connect to the native host automatically on startup.
|
||||
The `install` command will:
|
||||
1. Write the native messaging manifest to your OS so the browser can find the host
|
||||
2. Copy the native host into an internal `libexec` directory and create a small wrapper outside your `PATH`
|
||||
3. Print the public store link for installing the extension (or, with `--dev`, the unpacked / temporary-add-on load steps)
|
||||
|
||||
After install, add the extension from the store link above and **fully restart your browser** (Quit and reopen — not just close the window). The extension will connect to the native host automatically on startup.
|
||||
|
||||
Only the `browser-cli` command needs to be on your `PATH`. The browser launches the native host wrapper directly from its absolute path in the native messaging manifest, and that wrapper imports the installed `browser_cli.native.host` entry point. On Windows the install command also registers the host in the current user's Registry for the selected browser.
|
||||
|
||||
@@ -305,6 +317,13 @@ PUBKEY=$(browser-cli auth show --key ~/.config/browser-cli/client.key | tail -n1
|
||||
browser-cli auth trust "$PUBKEY"
|
||||
browser-cli serve --host 0.0.0.0 --port 8765 --authorized-keys ~/.config/browser-cli/authorized_keys
|
||||
|
||||
# Allow remote browser control (navigation, clicks); safe-only otherwise
|
||||
browser-cli serve --authorized-keys ~/.config/browser-cli/authorized_keys --allow-control
|
||||
|
||||
# Per-key authorization (inline in authorized_keys) + a tighter rate limit
|
||||
browser-cli auth trust "$PUBKEY" --name ci-bot --allow-read-page --allow-control
|
||||
browser-cli serve --authorized-keys ~/.config/browser-cli/authorized_keys --rate-limit 20
|
||||
|
||||
# From another machine
|
||||
browser-cli --remote browser-host.example:8765 --key ~/.config/browser-cli/client.key tabs list
|
||||
browser-cli remote trust browser-host.example:8765 ~/.config/browser-cli/client.key
|
||||
@@ -317,6 +336,16 @@ curl -H "Authorization: Bearer <token>" http://127.0.0.1:8766/tabs
|
||||
|
||||
Remote auth uses Ed25519 challenge/response. `--remote` domains default to port 443; explicit `host:port` endpoints are also supported. Saved remote endpoints participate in aggregate list/count commands, where output is grouped by endpoint.
|
||||
|
||||
#### Security model
|
||||
|
||||
- **`serve` (TCP)** authenticates every connection with an Ed25519 signature over a fresh server nonce and, for modern clients, wraps the transport in an ML-KEM-768 (post-quantum) AEAD channel. Commands are gated by a **safe-only policy by default** — even a trusted key can only run read-only status/listing commands until you open more with `--allow-read-page`, `--allow-control`, `--allow-dangerous`, or `--allow-all` (full control, including `dom.eval`/`storage.*`). `--no-auth` is rejected on non-loopback hosts.
|
||||
- **Per-key authorization:** a key in `authorized_keys` can carry an optional `allow:` token (`<pubkey> <name> allow:read-page,control`) listing its categories (`all`, `safe`, `read-page`, `control`, `dangerous`). That key uses its own policy, overriding the server-wide `--allow-*` default; keys without a token fall back to the default. Set it with `auth trust <pubkey> --allow-control …` (works locally and over `--remote`); `auth keys` shows each key's policy.
|
||||
- **Rate limiting:** `--rate-limit N` caps commands/second per client key (token bucket, default `100`, `0` disables) so a compromised key can't hammer the browser.
|
||||
- **Audit logging:** request logs include the acting key (its name from `authorized_keys` plus a short pubkey), not just the client address.
|
||||
- **`serve-http`** is a convenience gateway with the inverse trade-off: commands are gated by the same `--allow-*` policy (safe-only by default), but the bearer token travels in **clear text over plain HTTP**. It binds to loopback by default; `--no-auth` is only permitted there. If you must expose it beyond loopback, put it behind a TLS-terminating reverse proxy — never send the token over an untrusted network unencrypted.
|
||||
|
||||
For low latency, an authenticated encrypted remote connection is kept open and reused for further commands in the same process — so SDK scripts and multi-browser fan-out avoid repeating the TCP/TLS/challenge handshake on every command. Aggregate commands also fan out to remote targets concurrently. Both degrade gracefully against older servers that handle one command per connection.
|
||||
|
||||
---
|
||||
|
||||
## Python SDK
|
||||
@@ -513,7 +542,7 @@ nix-shell # automatically runs npm ci when node_modules is missing/outdated
|
||||
npm run check:extension
|
||||
```
|
||||
|
||||
The extension source lives in `extension/src/`. `extension/background.js` and `extension/content-dispatch.js` are generated and ignored by git. Run `npm run build:extension` before using `Load unpacked` with `extension/`. On NixOS, use `nix-shell` first if npm is not installed globally.
|
||||
The extension source lives in `extension/src/`. `extension/background.js` and `extension/content-dispatch.js` are generated and ignored by git. Run `npm run build:extension` before loading the unpacked `extension/` directory; `browser-cli install <browser> --dev` prints the per-browser load steps. On NixOS, use `nix-shell` first if npm is not installed globally.
|
||||
|
||||
Packaging:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user