n8n-nodes-browser-cli
v0.3.1
Published
n8n community node that controls a remote browser by talking directly to a browser-cli serve endpoint (Ed25519 + post-quantum encrypted)
Maintainers
Readme
n8n-nodes-browser-cli
An n8n community node, published on npm as n8n-nodes-browser-cli, that controls a real, visible browser
from your workflows via browser-cli.
browser-cli drives a running browser through a native-messaging host and a
browser extension — it cannot be installed inside the n8n container. So this
node speaks the browser-cli serve protocol directly: a length-framed TCP
connection authenticated with an Ed25519 key, with request/response bodies
encrypted end-to-end via an ML-KEM-768 (post-quantum) key exchange — the same
wire protocol the browser-cli --remote client uses.
n8n workflow ──TCP (Ed25519 + ML-KEM-768)──▶ browser-cli serve (remote host) ──▶ browserBecause the payloads are end-to-end encrypted, the endpoint is safe to expose on an untrusted network without a TLS proxy in front of it.
Remote setup (on the browser machine)
Install browser-cli, register the extension, trust your n8n key, then start
serve opening exactly the command tiers you need (it is safe-only by default):
uv tool install real-browser-cli
browser-cli install brave # one-time: register the extension/native host
# On the n8n side, generate a client key and print its public key:
browser-cli auth keygen -o n8n_key.pem
# On the browser machine, trust that public key (optionally scope its policy):
browser-cli auth trust <pubkey-hex> --allow-control
# Expose the browser. Open only what your workflow needs:
browser-cli serve --host 0.0.0.0 --port 8765 \
--authorized-keys ~/.browser_cli/authorized_keys --allow-read-page --allow-controlPaste the contents of n8n_key.pem into the n8n credential.
n8n credential — "Browser CLI API"
| Field | Description |
|-------|-------------|
| Host | host of the serve endpoint, e.g. browser-host.example |
| Port | serve TCP port (default 8765) |
| Ed25519 Private Key | PKCS8 PEM from browser-cli auth keygen (empty only for --no-auth loopback) |
| Browser Alias | optional _route target — required if the endpoint serves multiple browsers |
| Server Public Key/Fingerprint | pinned browser-cli serve identity (SHA256:... fingerprint or 64-char server public key hex) |
| Allow Unknown Server Identity | disables SSH-style server pinning; use only for loopback/dev |
| Use TLS | wrap the connection in TLS (only for a TLS-terminating proxy; the protocol is already encrypted) |
| Ignore SSL Issues | when TLS is on, accept a self-signed proxy cert |
Server identity pinning
Recent browser-cli serve versions advertise a persistent Ed25519 server
identity in the challenge frame. The n8n node verifies the challenge signature
and compares the key against the credential's Server Public Key/Fingerprint
field, similar to SSH known_hosts.
On a trusted machine, pin the server once with the Python CLI and copy the fingerprint into the n8n credential:
browser-cli remote trust-host browser-host.example:8765
browser-cli remote known-hostsIf the server key changes, the node fails with REMOTE SERVER IDENTITY CHANGED.
Only enable Allow Unknown Server Identity for local/dev endpoints where you
explicitly do not want pinning.
Operations
Every operation maps to one raw browser-cli command, each subject to the server policy tier noted below.
| Resource | Operation | Command | Server flag needed |
|----------|-----------|---------|--------------------|
| Tab | List / Query / Get / Count / Filter / Active in Window | tabs.list / tabs.query / tabs.status / tabs.count / tabs.filter / tabs.active_in_window | safe (default) |
| Tab | Get HTML | tabs.html | --allow-read-page |
| Tab | Open / Close / Activate / Move / Navigate To / Reload / Hard Reload / Back / Forward | navigate.open / tabs.close / tabs.active / tabs.move / navigate.to / navigate.reload / navigate.hard_reload / navigate.back / navigate.forward | --allow-control |
| Tab | Mute / Unmute / Pin / Unpin / Dedupe / Sort / Merge Windows | tabs.mute / tabs.unmute / tabs.pin / tabs.unpin / tabs.dedupe / tabs.sort / tabs.merge_windows | --allow-control |
| Tab | Screenshot | tabs.screenshot | --allow-dangerous |
| Page | Get Info | page.info | safe (default) |
| Page | Extract Text / Links / Images / HTML / Markdown / JSON | extract.* | --allow-read-page |
| DOM | Query / Text / Attribute / Exists | dom.query / dom.text / dom.attr / dom.exists | --allow-read-page |
| DOM | Click / Type / Select / Hover / Focus / Check / Uncheck / Clear / Submit / Scroll / Key | dom.* | --allow-control |
| DOM | Eval | dom.eval | --allow-dangerous |
| Group | List / Query / Tabs | group.list / group.query / group.tabs | safe (default) |
| Group | Count / Create / Add Tab / Move / Close | group.count / group.open / group.add_tab / group.move / group.close | --allow-control |
| Window | List | windows.list | safe (default) |
| Window | Open / Close / Rename | windows.open / windows.close / windows.rename | --allow-control |
| Session | List / Save / Load / Remove / Export / Diff / Auto Save | session.* | --allow-control |
| Storage | Get / Set | storage.get / storage.set | --allow-dangerous |
| Performance | Status | perf.status | safe (default) |
| Extension | Info / Capabilities | extension.info / extension.capabilities | safe (default) |
| Extension | Reload | extension.reload | --allow-control |
| Client | List | clients.list | safe (default) |
| Command | Execute | any command name + JSON args | per command |
Command → Execute is the escape hatch: any command string the server policy
allows (tabs.query, session.save, windows.list, …) with a JSON args object.
Use it for anything the typed operations don't cover.
Note:
servereturns the raw command result (no SDK post-processing).extract.markdowntherefore returns the page payload as the extension hands it back, not the CLI's rendered Markdown. For clean text use Extract Text.
Tab → Filter / Count URL Pattern: matched against the full tab URL. A plain string is a case-sensitive substring (
twitch.tv); a pattern containing*or?is a glob (twitch.tv/*,*.twitch.tv). Glob needs the serve-side extension at 0.16.4+; older extensions treat the whole pattern as a literal substring, sotwitch.tv/*matches nothing there — usetwitch.tvinstead.
Develop / build
cd n8n-nodes-browser-cli
npm install # add --ignore-scripts if a transitive native dep
# (isolated-vm) fails to compile on your Node version
npm test # pure unit tests: command mapping + crypto known-answer vectors
npm run build # tsc -> dist/, copies the iconInstall the published package in n8n as a community node using the package name n8n-nodes-browser-cli, or symlink dist/ into ~/.n8n/custom for local testing.
License
PolyForm Noncommercial License 1.0.0 — same as browser-cli.
