Agent Tunnels
When a tunnel is started inside a space, the knot agent can own it for you so it keeps running even after the command that launched it exits. This is distinct from desktop tunnels, which run as a foreground process on your local machine for the life of that command.
Agent-owned tunnels live for the lifetime of the knot agent — they are not persisted, so they stop if the space (and therefore the agent) is stopped or restarted.
You can manage them either inside the space (knot tunnel ...) or
remotely from the desktop (knot space tunnel ...). Both operate on the same
agent-owned registry, so a tunnel started one way is visible to the other.
Prerequisites
- The knot server must be configured for tunnels.
- Inside a space: run
knot tunnel ...in a terminal in the space. The--daemon,stop, andlistsubcommands require the knot agent. - From the desktop: run
knot space tunnel ...(the space must be running). This drives the space’s agent remotely via the knot server.
Starting a Tunnel
Foreground (default)
knot tunnel http 8080 test1This opens an HTTP tunnel exposing local port 8080 as
<user>--test1.<tunnel_domain>. The tunnel stays active until you press
Ctrl-C (or the process is killed), exactly as before.
By default the tunnel is created on the server that owns the space. A tunnel can instead be created on any other knot server — see Targeting Another Knot Server.
Use https instead of http for an HTTPS tunnel:
knot tunnel https 8443 secure1Daemon mode
Add --daemon to hand the tunnel to the knot agent and return immediately:
knot tunnel http 8080 test1 --daemon- The command prints the tunnel URL and exits.
- The tunnel is owned by the agent and runs until the agent exits, or until you stop it explicitly.
- The agent uses its own server credentials, so no
--server/--tokenflags are needed for a tunnel on the space’s own server — a different server can be targeted too, see Targeting Another Knot Server.
Targeting Another Knot Server
A tunnel — foreground or --daemon — can be created on any knot server
this space can reach, not just the one that owns the space. Pass the target
server and an API token valid on it:
knot tunnel http 8080 test1 --server https://other.knot.internal --token <api-token>A Tunnels-only scoped token is enough — and the right key to hand a machine that should do nothing but expose a port; see API Tokens.
With --daemon the agent owns the tunnel like any other: it survives the
launching command exiting, appears in knot tunnel list, and is stopped with
knot tunnel stop.
Aliases work the same way. Configure them in the space’s config file
(knot.toml in the current directory, ~/knot.toml, or
~/.config/knot/knot.toml) using the same layout knot connect writes on the
desktop:
[client.connection.staging]
server = "https://staging.knot.internal"
token = "<api-token>"
[client.connection.prod]
server = "https://prod.knot.internal"
token = "<api-token>"knot tunnel http 8080 web1 -a staging --daemon
knot tunnel http 8081 web2 -a prod --daemonThe --tunnel-server / --tunnel-token / --tunnel-alias spellings are
accepted as synonyms of --server / --token / --alias, so the same flags
work here and on knot space tunnel from the desktop.
Each tunnel is independent, so several can run at once against different servers and ports, mixing foreground and daemon freely.
The same works remotely from the desktop with knot space tunnel, using the
--tunnel-* flags (named differently from -s / -t / -a because on
knot space commands those select the server the CLI itself talks to):
knot space tunnel http myspace 8080 web1 --tunnel-server https://other.knot.internal --tunnel-token <api-token>
knot space tunnel http myspace 8081 web2 --tunnel-alias prodNotes:
- Daemon tunnels to other servers are not persisted, like all daemon tunnels: they stop when the space (and therefore the agent) stops or restarts.
- The tunnel address is built from your username on the target server, and the tunnel counts against that server’s tunnel quota and permissions.
- Without
--server/--token/--alias(or the--tunnel-*equivalents) the tunnel is created on the server that owns the space, as before. - The space must be able to reach the target server over the network.
knot tunnel listinside the space andknot space tunnel listfrom the desktop share one registry, so both list and stop tunnels started against any server.- Scripts get the same via
knot.space.tunnel_start(space, protocol, port, name, server, token)from theknot.spacelibrary.
Stopping a Tunnel
Stop a daemon-owned tunnel by its name:
knot tunnel stop test1This only affects tunnels owned by the agent. A foreground tunnel started
without --daemon is stopped by exiting that process (e.g. Ctrl-C).
Listing Tunnels
List all tunnels currently owned by the agent:
knot tunnel listExample output:
Active tunnels:
test1 8080 http https://alice--test1.tunnels.knot.internalA tunnel’s URL shows the domain of the server it runs on, so tunnels on other servers are distinguishable at a glance.
Remote Management
You can also manage a space’s agent-owned tunnels from the desktop with
knot space tunnel, without opening a terminal in the space. These commands
drive the same agent-owned registry through the knot server, so a tunnel started
from the desktop appears in knot tunnel list inside the space, and vice versa.
The space must be running.
Starting a tunnel
knot space tunnel http myspace 8080 test1Daemon mode is implied — the command prints the tunnel URL and exits, and the
tunnel is owned by the space’s agent. https is also supported.
A different knot server can be targeted with --tunnel-server /
--tunnel-token or --tunnel-alias — see
Targeting Another Knot Server.
Listing tunnels
knot space tunnel list myspaceStopping a tunnel
knot space tunnel stop myspace test1Scripting
Agent-owned tunnels can be managed from scripts via knot.space, which is
available in all scriptling environments (startup scripts, MCP tools, and
standalone scripts). The space must be running.
import knot.space as space
# Start a tunnel — returns the public URL
url = space.tunnel_start("myspace", "http", 8080, "myapp")
print(url)
# Start a tunnel on another knot server (token must be valid there)
url = space.tunnel_start("myspace", "http", 8081, "web1",
server="https://other.knot.internal", token="<api-token>")
# List active tunnels — each URL's domain shows which server it runs on
for t in space.tunnel_list("myspace"):
print(t["name"], t["url"])
# Stop a tunnel by name
space.tunnel_stop("myspace", "myapp")These call the same server API as the knot space tunnel CLI commands, so a
tunnel started from a script is visible to knot tunnel list inside the space.
Behaviour Notes
- Not persistent: agent-owned tunnels are not stored anywhere. If the space is stopped or restarted, all daemon tunnels are removed and must be started again.
- One tunnel per name: starting a daemon tunnel with a name that already
has one is rejected. Use
knot tunnel stop <name>first. - Identity: the tunnel name is combined with the space owner’s username to
form
<user>--<name>.<domain>, just like desktop tunnels.