Quick Start
r6me is a single binary. It gives your agent a real public IPv6 address inside your organisation's /64, an MCP endpoint
on localhost, and an egress proxy that sends your traffic out from that identity.
It runs as a normal user — no root, no kernel modules, no sudo for anything but
copying the binary into place.
Get your API key
Sign up at route6.me (opens in a new tab), verify your email, then copy your
agent's key from the dashboard (opens in a new tab) under Agents. It starts
with sk_a6_.
Install
curl -fsSL https://dl.route6.me/install.sh | shRoute6 client v1.1.0
platform: linux/amd64
source: https://dl.route6.me/v1.1.0/r6me_1.1.0_linux_amd64.tar.gz
install: /usr/local/bin/r6me
downloading...
checksum verified
installed /usr/local/bin/r6me
r6me v1.1.0The script works out your platform, downloads the matching build and its published
checksum, and refuses to install if the two disagree. As root it installs to
/usr/local/bin; as a normal user it installs to ~/.local/bin and tells you if that is
not on your PATH.
Piping to a shell is worth a second's thought, so here is what this one does. It fetches over HTTPS from our own host, verifies a sha256 against the manifest published beside the binary, and aborts on any mismatch. If you would rather read it first:
curl -fsSL https://dl.route6.me/install.sh -o install.sh
less install.sh
sh install.shsh install.sh --dry-run prints exactly what it would do and changes nothing.
Useful flags: --version vX.Y.Z to pin, --dir <path> to choose where the binary lands,
--no-systemd to skip the service unit, --key sk_a6_… to write the config in the same
step.
Already in a node or python project? Install from the registry you are already using —
these are launchers around the same binary, verified the same way, and they are the route
for Windows, where there is no curl | sh:
npm install -g @route6/agent # or: pip install route6
route6 upBoth give you the same route6 command. They fetch the binary on first use rather than at
install time, so npm install --ignore-scripts and pip's lack of a post-install hook both
behave identically.
Prefer to do it by hand? Every release ships a checksums.txt, and the client is the
executable that will hold your network identity — so verify it either way:
curl -fsSLO https://dl.route6.me/v1.1.0/r6me_1.1.0_linux_amd64.tar.gz
curl -fsSLO https://dl.route6.me/v1.1.0/checksums.txt
sha256sum --check --ignore-missing checksums.txt # macOS: shasum -a 256
tar xzf r6me_1.1.0_linux_amd64.tar.gz
sudo install -m 0755 r6me /usr/local/bin/r6me--ignore-missing checks the one file you downloaded instead of failing on the other
eleven builds in the manifest. Browse dl.route6.me (opens in a new tab) for the full
matrix: linux, macOS and windows on amd64/arm64, plus arm32, mips and riscv64 for routers
and embedded boards.
Configure
The daemon reads ~/.r6me/config.toml. If you already have your key, the installer can
write it for you in the same step — useful when an agent is installing itself and nobody is
at a keyboard:
curl -fsSL https://dl.route6.me/install.sh | ROUTE6_API_KEY=sk_a6_your_key_here shOtherwise write it yourself:
mkdir -p ~/.r6me && chmod 700 ~/.r6me
cat > ~/.r6me/config.toml <<'EOF'
api_key = "sk_a6_your_key_here"
EOF
chmod 600 ~/.r6me/config.tomlapi_key is the only required field. ROUTE6_API_KEY in the environment works instead, if
you would rather not write the key to disk.
Optional keys: gateway (defaults to https://gw.route6.me), agent_id (pins this
install to one agent — leave it out and the daemon adopts the identity the fabric hands it
on first connect), and loopback_only = true (refuse any inbound forward whose target is
not loopback).
Connect
r6me up
r6me statusr6me daemon started (pid 3434539); logs: /root/.r6me/daemon.logr6me up backgrounds itself and writes to ~/.r6me/daemon.log. status prints the live
state as JSON:
{
"transport": "connected",
"generation": 207,
"wg_health": "up",
"mesh_transport": "wg",
"forwards": [],
"mcp": "127.0.0.1:3000/mcp",
"agent_id": 39,
"client_version": "v1.1.0",
"advisory": "ok",
"gateway": "https://gw.route6.me"
}You want "transport": "connected". wg_health reaches up a few seconds later, once the
mesh tunnel finishes its handshake.
Point your agent at it
The daemon serves MCP on http://localhost:3000/mcp, with no API key in the client
config — the daemon already holds it.
{
"mcpServers": {
"route6": { "url": "http://localhost:3000/mcp" }
}
}That works for Claude Desktop, Cursor, Cline and anything else that speaks MCP over HTTP. See MCP Integration for per-client config.
Send traffic from your agent's IP
The daemon also runs a local proxy on port 1080 speaking SOCKS5, HTTP CONNECT and plain
HTTP. Anything pointed at it leaves from your agent's own public address:
export http_proxy=http://127.0.0.1:1080
export https_proxy=http://127.0.0.1:1080
curl https://api64.ipify.org2001:67c:3f4:801f::1IPv4-only destinations work too — the fabric reaches them over NAT64, so an IPv4 site sees you arrive from a Route6 address without your machine needing IPv4 at all.
Use lowercase http_proxy with curl. libcurl deliberately ignores an uppercase
HTTP_PROXY (it could arrive from a CGI Proxy: header) while honouring HTTPS_PROXY
either way — so uppercase-only looks like it works, tunnels https fine, and sends plain
http straight out your normal connection instead. Most other clients (python requests,
node, Go) accept either case.
For tools that speak SOCKS: ALL_PROXY=socks5h://127.0.0.1:1080.
The commands
| Command | What it does |
|---|---|
r6me up | connect and background the daemon |
r6me down | disconnect |
r6me status | transport state, config generation, forwards, MCP endpoint |
r6me ssh <name> | open a shell on a team-mate over the private mesh (Team plans) |
r6me version | print the version |
r6me up takes --fg to stay in the foreground (use this under systemd or in a container),
and --loopback-only to refuse inbound forwards that target anything but loopback.
Keeping it running
r6me up backgrounds itself and survives your shell closing — but nothing brings it back
after a reboot, and nothing restarts it if the process dies. A supervisor fixes both.
If you installed as root on a systemd host, the installer already wrote the unit — it just does not enable it, so the decision stays yours:
sudo systemctl enable --now r6meThere is a third difference worth knowing: under systemd the daemon runs with --fg and its
output goes to journald, which rotates. The bare r6me up path writes
~/.r6me/daemon.log, and nothing rotates that today.
The unit the installer writes, for reference (pass --no-systemd to skip it):
[Unit]
Description=Route6 agent
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/usr/local/bin/r6me up --fg
Restart=always
User=root
Environment=HOME=/root
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now r6meUser and HOME must agree. The daemon looks for config.toml and its identity
under $HOME/.r6me, so if you run as a dedicated user, set HOME to that user's home and
put the config there — otherwise the daemon starts, finds no key, and exits. Set
R6ME_STATE_DIR instead if you want the state somewhere else entirely.
The daemon keeps its WireGuard key and applied configuration in that directory, so a restart
resumes the same identity rather than registering a new one. Verified: agent_id and
generation were unchanged across a systemctl restart.
Upgrading
Re-run the installer. It is idempotent, and it replaces the binary by writing a new file and moving it into place rather than overwriting the running one:
r6me down
curl -fsSL https://dl.route6.me/install.sh | sh
r6me up # or: sudo systemctl restart r6meYour identity survives because it lives in ~/.r6me, not in the binary — agent_id and
generation are unchanged across the upgrade. status reports an advisory field — ok,
update_available, or unsupported — so you can tell whether the version you are on is
still current without checking the site.
To pin a specific version, or to roll back:
curl -fsSL https://dl.route6.me/install.sh | sh -s -- --version v1.1.0Troubleshooting
no api key (set ROUTE6_API_KEY or api_key in …/config.toml) — the daemon found no
key. Check ~/.r6me/config.toml exists and the key is quoted.
/v2/session status 401 in ~/.r6me/daemon.log — the key is wrong or was revoked.
Copy it again from the dashboard.
transport: connected but wg_health stays connecting — the control channel is fine
and the mesh tunnel is not. Egress and MCP still work; only the private team mesh is
affected. Check ~/.r6me/daemon.log for wg-pubkey.
bind: address already in use — something else holds 3000, 3001 or 1080. Those ports
are fixed; stop the other process, or run this agent on another machine.
x509: certificate signed by unknown authority — you are on a client older than
v1.2.2, on a machine that ships no CA certificates at all. Nothing is wrong with our
certificate (it is an ordinary Let's Encrypt one); that machine simply has nothing to
verify any certificate against.
From v1.2.2 the client carries its own copy of the Mozilla root bundle, used only when the machine has no trust store of its own — so this cannot happen on a current client. Upgrade, or install the certificates:
apt-get install -y ca-certificates # Debian / Ubuntu
apk add ca-certificates # AlpineMeasured, in case you are wondering whether your base image is affected:
node:20-bookworm-slim and debian:stable-slim ship zero roots;
python:3.12-slim has 150, alpine:latest and python:3.12-alpine 119, and
node:20-alpine 145. Only the Debian-based slim images were ever affected.
If your machine does have a trust store and you still see this, it is not a missing bundle — look at a TLS-inspecting proxy on the network, or a system clock wrong enough that every certificate reads as expired. The client's own trust store is never used when yours exists, so your administrator's choices still decide.
Everything, in one place — ~/.r6me/daemon.log is the whole story. Read it before
anything else.
Running in a container
Docker needs a little more setup, because inbound forwards have to be told which machine answers them. See Container (Docker).