Port Forwarding Tools
Available on every plan. Free includes one forward per account; paid plans allow up to 10 per agent.
Your agent's identity is a real, public IPv6 address. Port forwarding publishes a port of your local machine on it.
Which address family you get, and why. A forward on an arbitrary port works over IPv6 on every plan. To forward an arbitrary port over IPv4 you need the Static IPv4 add-on (opens in a new tab), which gives your agent its own IPv4 address.
There is no way around this and it is not a licensing choice: without the add-on
your IPv4 front door is an address shared with other customers, and the only
thing that identifies you on a shared address is your hostname — which travels
in the TLS handshake. So Route6 can route https://you.on.route6.me/ to you on
a shared IPv4, but 176.123.57.184:9000 carries nothing that says whose forward
it is.
Webhooks are the exception, because they arrive on the standard HTTPS port and carry the hostname — see below.
Inbound is hub-terminated: Route6 accepts the connection on your agent's own public address at the gateway and relays it to your daemon over the control channel it already holds. Your machine opens no listening socket to the internet, and needs no inbound firewall rule, no router configuration and no public IPv4.
port_forward { action: "create" }
| Parameter | Type | Required | Description |
|---|---|---|---|
external_port | number | ✓ | Port on your agent's public IPv6 (1024–65535, not 3000) |
internal_port | number | — | Port on your local machine (defaults to external_port) |
protocol | "tcp" | — | TCP only today ("udp" is accepted by the schema but not yet implemented) |
ttl_seconds | number | — | Auto-expire after N seconds (60–86400). Omit for persistent. |
description | string | — | Label for your reference |
scope | "public" | "mesh" | "both" | — | Exposure — default "public" |
webhook | boolean | — | Also publish an IPv4 URL for this forward — see below. Default false. |
Exposure (scope)
"public"(default) — reachable from the internet on your public IPv6."mesh"— reachable only by agents in your team mesh, atyou.mesh.route6.me:<port>. There is no public listener at all: the service is internet-unreachable by construction, and mesh legs are WireGuard-encrypted end to end."both"— both of the above.
The result always echoes what was actually opened ("PUBLIC — reachable from the entire internet" vs "mesh-only — reachable only by agents in your team mesh"),
so there is never doubt.
port_forward (action: create)(external_port=8443, internal_port=8080)→ [your-ipv6]:8443 reaches localhost:8080 on your machine.
port_forward (action: create)(external_port=8080, scope="mesh")→ teammates reach http://you.mesh.route6.me:8080/; the internet cannot.
port_forward (action: create)(external_port=8888, ttl_seconds=3600)→ auto-expires after one hour.
Receiving webhooks from IPv4-only senders
Your identity is IPv6-only. Most webhook senders — Stripe among them — are
IPv4-only, so they cannot reach an IPv6 address at all, no matter what port it is
on. Set webhook: true and Route6 also publishes an IPv4 address for your
hostname, on a shared ingress pool that terminates TLS with the
*.on.route6.me certificate and relays to this forward:
port_forward (action: create)(external_port=8443, internal_port=8080, webhook=true)"urls": {
"ipv6": "https://you.on.route6.me:8443/",
"ipv4": "https://you.on.route6.me/"
}Paste the IPv4 URL into Stripe, GitHub, or whatever is calling you. No add-on, no extra tool call, no public IPv4 of your own.
The port-less URL works over both IPv4 and IPv6 — give it to any sender.
It arrives on 443 and is matched to your forward by hostname, so your
external_port does not appear in it: IPv4 senders reach the Route6 shared
ingress, IPv6 senders reach your own address.
Route6 terminates TLS on that URL, so your local service receives plain HTTP.
If you need end-to-end TLS with your own certificate — or the port is not
HTTPS at all — use https://you.on.route6.me:<external_port>/ instead. That
port is relayed as raw TCP, untouched.
A few limits worth knowing before you design around it:
- IPv4 senders need the Static IPv4 add-on, which requires a paid plan. On the free tier the forward works over IPv6 and you get a note explaining that IPv4-only senders (Stripe, some CI systems) cannot reach it.
- Five webhook forwards per account, counted across all your agents. Ordinary forwards are capped separately (10 per agent).
- Not combinable with
scope: "mesh"— a webhook needs a public IPv4 front door and mesh scope means unreachable from the internet. Asking for both returns an error rather than quietly honouring one of them. - Deleting the forward withdraws the IPv4 address again.
port_forward { action: "list" }
List your active forwards with their ports, scope and status. No parameters.
For any forward with a source restriction, the reply also shows allowed_sources
(the rules as you wrote them), effective_sources (the exact addresses and ranges
being enforced right now, with every preset expanded) and, for each preset you
used, when Route6 last refreshed its ranges.
Note that a listed forward is not by itself proof of reachability — check the URL from outside if you need certainty.
port_forward { action: "delete" }
Remove a forward.
| Parameter | Type | Required | Description |
|---|---|---|---|
external_port | number | ✓ | External port of the forward to remove |
Restricting who may reach a forward
A public forward is reachable from the entire internet by default. You can narrow
that to specific sources with allowed_sources — a list that may mix exact
addresses, CIDR ranges and named presets whose ranges Route6 keeps current.
One port_forward { action: "create" } call gives you a public HTTPS webhook URL that only
Stripe can reach:
{
"external_port": 9443,
"internal_port": 8080,
"webhook": true,
"allowed_sources": ["preset:stripe"]
}| Rule | Example | Notes |
|---|---|---|
| Address | 203.0.113.7, 2001:db8::1 | exact match |
| CIDR | 203.0.113.0/24, 2a0a:a440::/29 | either family |
| Preset | preset:stripe, preset:github | the vendor's published sender ranges |
Combine as many as you need. Presets and literals live in the same list, and several presets can be used together — the result is the union of everything listed. This is also how you allow a provider Route6 has no preset for: name its published addresses directly alongside the presets you do use.
{
"allowed_sources": [
"preset:stripe",
"preset:github",
"35.246.21.235",
"198.51.100.0/24"
]
}port_forward_secure
Change who may reach a forward you already have — without recreating it. The forward keeps serving throughout and live connections are not dropped, so tightening or relaxing a rule is not an outage and a webhook does not miss deliveries while you edit it.
| Parameter | Type | Required | Description |
|---|---|---|---|
external_port | number | ✓ | The forward to change |
action | enum | ✓ | set, add, remove or clear |
allowed_sources | string[] | for set/add/remove | The rules to apply |
| Action | Effect |
|---|---|
set | Replace the whole list. An empty list denies every source. |
add | Append to the list. If the forward had no restriction, this creates one. |
remove | Drop the listed entries. Never creates a restriction. |
clear | Remove the restriction entirely — any source may connect. |
{ "external_port": 9443, "action": "add", "allowed_sources": ["preset:github"] }add and remove exist so you can grow or shrink a list without restating it,
which is what you want when the list is long and you only mean to change one
entry.
Two deliberate refusals, both because guessing would be dangerous:
clearis the only way to remove a restriction. Leavingallowed_sourcesout of a call never means "remove the rules" — otherwise the safest-looking call you could make would quietly open your service up.- A
removethat would empty the list is rejected. An empty list and no list mean opposite things, so rather than pick one, Route6 asks you to say which:clearto allow everyone, orsetwith an empty list to allow no one.
Rules take effect within about a second on every path, including the IPv4 webhook URL and your IPv6 address.
Source restrictions are available on every plan, including the free tier.
A few things worth knowing before you rely on it:
- Both families are separate. A rule written in IPv4 does not restrict an IPv6 caller, and vice versa. Your hostname publishes both an A and an AAAA record, so an IPv4-only allowlist will refuse IPv6 senders — that is usually what you want for something like Stripe, which sends from IPv4 only, but it is worth being deliberate about. If a sender may arrive on either, list both.
- Omitting the field means no restriction. An empty list means the opposite: nothing may reach it. The two are not the same.
- Every reply tells you what is actually enforced, with presets expanded to the addresses behind them — so you can confirm a rule landed rather than assuming it did.
- Rules are checked at the Route6 gateway, before anything reaches your
agent — a refused caller never causes a connection on your machine. HTTPS
callers get
403withx-route6-error: source_denied; on a raw TCP port the connection is reset. - Denials are recorded and visible on your security events, so a webhook that stops arriving can be told apart from one your own rule refused.
- Presets are maintained by Route6 from the vendor's published list. They are a convenience, not a guarantee: if a vendor adds ranges we have not picked up yet, a strict allowlist will refuse them. If a webhook matters, pin the vendor's own documented ranges yourself.
Mesh-scoped forwards use a different mechanism — see mesh endpoint ACLs — because they are enforced on your own agent rather than at the gateway.
port_forward_tls
Reports how TLS is handled for a port, and toggles it where that is meaningful.
TLS for *.on.route6.me is terminated at the Route6 gateway on the standard
HTTPS path — the port-less https://you.on.route6.me/ URL above — and your
local service receives plain HTTP. There is nothing to enable or disable there.
On any other port the bytes are relayed untouched, so if you need TLS on
https://you.on.route6.me:8443/ you terminate it in your own service with your
own certificate, and it works end to end.
| Parameter | Type | Required | Description |
|---|---|---|---|
port | number | ✓ | External port to report on |
action | "enable" | "disable" | ✓ | Requested action |