# Telnyx Compute: Tunnel — Full Documentation > Complete page content for Tunnel (Compute section) of the Telnyx developer docs (https://developers.telnyx.com). > This file: https://developers.telnyx.com/docs/development/llms/compute-tunnel-llms-full-txt · Root index: https://developers.telnyx.com/llms.txt ## Get Started ### Overview > Source: https://developers.telnyx.com/docs/network/tunnel.md Tunnel is in beta. [Beta Limitations](/docs/network/tunnel/beta-limitations) lists what it does not do yet. A Tunnel connects a [*Network*](/docs/network/overview) to the user's site without any modification to its router or firewall. A tunnel process runs on a host within the user's network and opens an outbound connection to Telnyx. The diagram zooms into the Tunnel lane of the *Network* diagram. An application's binding places its traffic on the *Network*. Agents will place theirs there through an Agent Gateway, which is coming soon. A route sends `10.20.0.0/16` to the tunnel. A tunnel host within the user's network opens an outbound TLS connection to Telnyx and dials the hosts behind the tunnel, such as `10.20.3.7` and `10.20.8.9`. Flows start on the *Network*. Hosts behind the tunnel see each connection from the tunnel host, and nothing behind the tunnel starts a flow toward the *Network*. A tunnel carries traffic to the services behind it in three ways: | Feature | Client | Reaches | Status | | ----------------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------ | --------------------------------------------- | | [Route](/docs/network/tunnel/features/routes) | Functions on the *Network* | Every address in a CIDR behind the tunnel | Beta | | [Private Service](/docs/network/tunnel/features/private-services) | Clients on the *Network* | One service behind the tunnel, under a private hostname | Preview | | [Public Hostname](/docs/network/tunnel/features/public-hostnames) | Anyone on the internet | One HTTP or HTTPS service behind the tunnel, at a hostname the user owns | Preview | --- ### Quick Start > Source: https://developers.telnyx.com/docs/network/tunnel/quick-start.md This Quick Start connects a function on the [*Network*](/docs/network/overview) `acme-prod` to a web service at `10.20.3.7:8080`, behind the tunnel `chicago-dc`. Two hosts within the user's network run the tunnel process. The diagram shows the setup this Quick Start builds. An Edge Compute function with the network binding `PRIVATE_NET` reaches the *Network* `acme-prod` through its WireGuard Interface, `acme-prod-wg`, on `172.27.0.0/24`. A route on `acme-prod` sends `10.20.0.0/16` to the tunnel `chicago-dc`. Two tunnel hosts, `dc1-tunnel-a` and `dc1-tunnel-b`, run the tunnel's token and open outbound TLS connections to Telnyx, with no port forwarding. Either tunnel host dials the web service at `10.20.3.7:8080`, which sees the connection from that host. ## Prerequisites - A *Network* with a WireGuard Interface, and a function shipped with a [network binding](/docs/edge-compute/runtime/bindings/network) to that interface. Here the *Network* is `acme-prod` and the binding is `PRIVATE_NET`. - A host within the user's network that reaches the subnet. It needs an outbound connection to Telnyx. It needs no public IP address, no inbound port, and no root. - The `telnyx-edge` CLI, v0.5.14 or newer, on the workstation and on that host. ## Create the Tunnel ```bash Workstation telnyx-edge tunnel create chicago-dc ``` ```text Output Created tunnel chicago-dc (6afe50f3-bcff-48e9-93e8-4a906a91067e) Token (shown once, store it now): tnl_… ``` The token is the tunnel process's only credential. Telnyx shows it once. ## Run the Tunnel Process On the tunnel host, start the process with the token: ```bash Tunnel host telnyx-edge tunnel run --token tnl_… ``` ```text Output INF connected site=tunnel-relay.telnyx.com:8443 conn=0 tunnel=6afe50f3-bcff-48e9-93e8-4a906a91067e relay=tunnel-relay.telnyx.com:8443 ``` The process opens an outbound TLS connection to Telnyx and logs one line when it connects. It keeps running in the foreground, and it reconnects on its own when its connection drops. Without `--token`, it reads the token from `TELNYX_TUNNEL_TOKEN`. Run the same command on a second host within the user's network. Both processes serve the tunnel. Telnyx spreads requests across them, and either one carries requests when the other stops. ## Check the Tunnel ```bash Workstation telnyx-edge tunnel get chicago-dc ``` ```text Output NAME STATUS CONNECTIONS ---------- ------- ----------- chicago-dc healthy 2 ... ``` `healthy` means at least one process is connected. [Lifecycle](/docs/network/tunnel/concepts/lifecycle) lists the statuses. ## Route the Subnet Add a route on the *Network* that sends the subnet to the tunnel: ```bash Workstation telnyx-edge network route add acme-prod 10.20.0.0/16 --tunnel chicago-dc ``` ```text Output Added route 1a826484-8bfe-4346-b9fc-26aa70a22ccc acme-prod 10.20.0.0/16 -> tunnel chicago-dc (status: active) ``` The route list shows it beside the route that the WireGuard Interface installed: ```bash Workstation telnyx-edge network route list acme-prod ``` ```text Output ID CIDR NEXT HOP ORIGIN STATUS ------------------------------------ ------------- -------------------------------------------------------- ---------- ------ 1a826484-8bfe-4346-b9fc-26aa70a22ccc 10.20.0.0/16 tunnel chicago-dc static active 03aefa24-609b-4fce-b88a-0dc9afeed95d 172.27.0.0/24 wireguard_interface 03aefa24-609b-4fce-b88a-0dc9afeed95d attachment active ``` ## Reach the Subnet From the Function The function reaches the subnet through its network binding: ```ts Function import { env } from "@telnyx/edge-runtime"; const response = await env.PRIVATE_NET.fetch("http://10.20.3.7:8080/"); ``` The function needs no redeploy and no change to `func.toml`. It picks up the new route within about a minute. The web service in this Quick Start answers with the address it saw the connection from. That address is the tunnel host's, not the function's: ```json Output {"service": "site-dest", "address": "10.20.3.7:8080", "client": "10.20.0.10", "path": "/"} ``` ## Remove the Route ```bash Workstation telnyx-edge network route delete 1a826484-8bfe-4346-b9fc-26aa70a22ccc ``` ```text Output Deleted route 1a826484-8bfe-4346-b9fc-26aa70a22ccc ``` The function's requests to `10.20.0.0/16` then fail. The tunnel stays `healthy`. ## Stop the Tunnel Process Stop the process on each host with `Ctrl+C` or `SIGTERM`. The tunnel turns `inactive` once no process is connected. ## Delete the Tunnel ```bash Workstation telnyx-edge tunnel delete chicago-dc ``` ```text Output Deleted tunnel chicago-dc (6afe50f3-bcff-48e9-93e8-4a906a91067e) ``` A tunnel that a route names cannot be deleted. [Lifecycle](/docs/network/tunnel/concepts/lifecycle#deletion) shows what the CLI reports instead. --- ### Beta Limitations > Source: https://developers.telnyx.com/docs/network/tunnel/beta-limitations.md Tunnel is in beta, with these limits. ## Functions Only Functions with a [network binding](/docs/edge-compute/runtime/bindings/network) reach the routes that point to a tunnel, without a redeploy. WireGuard peers on the [*Network*](/docs/network/overview) do not reach them yet. ## One Tunnel per *Network* Every route on a *Network* goes to one tunnel. A route on the same *Network* to a second tunnel stays `pending`, and its traffic does not reach that tunnel. ## TCP Only The tunnel carries TCP. UDP and ICMP do not cross it, so a `ping` to an address behind the tunnel gets no reply. ## One Relay Region Every tunnel process connects to one relay in the United States, at `tunnel-relay.telnyx.com` on port 8443. Every flow through a tunnel passes through that relay, whatever the region of the *Network* and the site. --- ## Concepts ### What It Is Not > Source: https://developers.telnyx.com/docs/network/tunnel/concepts/what-it-is-not.md A tunnel carries flows from a [*Network*](/docs/network/overview) to hosts behind it. It never joins the two networks. A VPN joins two networks, so that hosts on each side can send packets to the other, subject to firewalls. A tunnel carries only the flows that Telnyx hands it, and only to the destinations declared for it: - From the *Network*, to the CIDRs that routes send to the tunnel, and to its [private services](/docs/network/tunnel/features/private-services) Preview. - From the internet, to its [public hostnames](/docs/network/tunnel/features/public-hostnames) Preview. The examples on this page run on the [Quick Start](/docs/network/tunnel/quick-start) setup. ## From the User's Network ### Not an Interface, a Link, or a Route Starting the tunnel process adds no interface and no route to the tunnel host. The host's interfaces and routing table read the same before and after `telnyx-edge tunnel run`: ```bash Tunnel host ip -brief address && ip route ``` ```text Output lo UNKNOWN 127.0.0.1/8 ::1/128 eth0@if55 UP 10.20.0.10/16 eth1@if57 UP 10.30.0.10/24 default via 10.20.0.1 dev eth0 10.20.0.0/16 dev eth0 proto kernel scope link src 10.20.0.10 10.30.0.0/24 dev eth1 proto kernel scope link src 10.30.0.10 ``` ### No Route Back to Telnyx Hosts behind the tunnel need no route to Telnyx either. Every connection reaches them from the tunnel host, so their replies stay within the user's network. The web service's routing table holds only its own subnet and the site's default route: ```bash 10.20.3.7 ip route ``` ```text Output default via 10.20.0.1 dev eth0 10.20.0.0/16 dev eth0 proto kernel scope link src 10.20.3.7 ``` A request from the function at the same moment returned `200`, through the tunnel host `10.20.0.11`. ### No Listening Port The tunnel opens no port within the user's network, so there is nothing to scan or attack, from the internet or from the site's other hosts. A scan of `dc1-tunnel-a` from `dc1-tunnel-b`, while both run the tunnel process, finds every port closed: ```bash Another host nmap -n -p- 10.20.0.10 ``` ```text Output ... Nmap scan report for 10.20.0.10 Host is up (0.0000010s latency). All 65535 scanned ports on 10.20.0.10 are in ignored states. Not shown: 65535 closed tcp ports (reset) ... ``` ### No Way Into Telnyx No host within the user's network, the tunnel host included, can open a connection through the tunnel toward the *Network*. A WireGuard peer is a host on the *Network*. It sends packets for the function's address, `172.27.0.2`, into its WireGuard interface: ```bash WireGuard peer ip route get 172.27.0.2 ``` ```text Output 172.27.0.2 dev wg0 src 172.27.0.4 uid 0 cache ``` A tunnel host is not on the *Network*. It sends packets for the same address to the site's default gateway, where they lead nowhere: ```bash Tunnel host ip route get 172.27.0.2 ``` ```text Output 172.27.0.2 via 10.20.0.1 dev eth0 src 10.20.0.10 uid 0 cache ``` Both answers were taken while the function reached `10.20.3.7` through the tunnel. ### No Address of Its Own The tunnel has no IP address. Only the tunnel host has one. On the *Network*, the route names the tunnel as its next hop, where an attachment's route names the attachment: ```bash Workstation telnyx-edge network route list acme-prod ``` ```text Output ID CIDR NEXT HOP ORIGIN STATUS ------------------------------------ ------------- -------------------------------------------------------- ---------- ------ 1a826484-8bfe-4346-b9fc-26aa70a22ccc 10.20.0.0/16 tunnel chicago-dc static active 03aefa24-609b-4fce-b88a-0dc9afeed95d 172.27.0.0/24 wireguard_interface 03aefa24-609b-4fce-b88a-0dc9afeed95d attachment active ``` ## From the *Network* ### No IP Packets The tunnel process re-originates each TCP connection. No packet from the *Network* crosses the tunnel, so routing protocols, other IP protocols, and multicast never reach the hosts behind it. A capture on the web service during two function requests shows two connections from the tunnel hosts, and no packet from the *Network*'s subnet: ```bash 10.20.3.7 tcpdump -n -i eth0 'tcp port 8080 or net 172.27.0.0/24' ``` ```text Output 14:27:07.448515 IP 10.20.0.10.35280 > 10.20.3.7.8080: Flags [S], seq 3418263517, win 64240, options [mss 1460,sackOK,TS val 1627743448 ecr 0,nop,wscale 7], length 0 ... 14:27:08.255506 IP 10.20.0.11.39900 > 10.20.3.7.8080: Flags [S], seq 838061873, win 64240, options [mss 1460,sackOK,TS val 3644785740 ecr 0,nop,wscale 7], length 0 ... 26 packets captured ``` Each `[S]` line opens a connection from a tunnel host. None of the 26 packets comes from `172.27.0.0/24`. ### Not the Client's Address The destination sees the tunnel host, never the client's address on the *Network*. The function is `172.27.0.2` on `acme-prod`, and the web service logs its request from the tunnel host `10.20.0.10`: ```text 10.20.3.7 access log 10.20.0.10 - - "GET / HTTP/1.1" 200 - ``` A firewall rule that admits the tunnel host therefore admits every flow through the tunnel. ### Nothing That Is Not Declared Telnyx asks the tunnel process to dial only the destinations declared for the tunnel. The tunnel host reaches `10.30.0.7:8080`: ```bash Tunnel host curl -s http://10.30.0.7:8080/ ``` ```json Output {"service": "site-other", "address": "10.30.0.7:8080", "client": "10.30.0.10", "path": "/"} ``` No route sends `10.30.0.0/24` to the tunnel, so the function cannot reach it: ```ts Function await env.PRIVATE_NET.fetch("http://10.30.0.7:8080/"); ``` The fetch rejects with `TypeError: fetch failed` once its connect timeout passes. Telnyx never sends it to the tunnel, so neither tunnel process receives it. --- ### What It Is > Source: https://developers.telnyx.com/docs/network/tunnel/concepts/what-it-is.md A tunnel has two sides. Within the user's network it is a process. Within Telnyx it is the next hop of a route on a [*Network*](/docs/network/overview), or the target of a [private service](/docs/network/tunnel/features/private-services) Preview or a [public hostname](/docs/network/tunnel/features/public-hostnames) Preview. The examples on this page run on the [Quick Start](/docs/network/tunnel/quick-start) setup. ## From the User's Network ### A Process on One Host The tunnel process holds a long-lived TLS connection that it opened to Telnyx. An inbound connection would need a public address, a port forward on the router, and a firewall rule. An outbound connection needs none of them, because networks already let hosts open outbound connections. The router and the firewall stay as they are. The tunnel host has only private addresses, and its one connection to Telnyx is one it opened: ```bash Tunnel host ip -brief address && ss -tn state established ``` ```text Output lo UNKNOWN 127.0.0.1/8 ::1/128 eth0@if55 UP 10.20.0.10/16 eth1@if57 UP 10.30.0.10/24 Recv-Q Send-Q Local Address:Port Peer Address:Port 0 0 10.20.0.10:46826 64.16.236.14:8443 ``` The one established connection is the tunnel's. The router forwards no port to the tunnel host. ### No Root The tunnel process runs as a normal user. It creates no interface and changes no route, so it needs no administrator rights on the host: ```bash Tunnel host id && telnyx-edge tunnel run --token tnl_… ``` ```text Output uid=1000(tunnel) gid=1000(tunnel) groups=1000(tunnel) INF connected site=tunnel-relay.telnyx.com:8443 conn=0 tunnel=6afe50f3-bcff-48e9-93e8-4a906a91067e relay=tunnel-relay.telnyx.com:8443 ``` ### Streams on That Connection The process sends its token as soon as it connects, and Telnyx confirms the token on the first stream of the connection. Telnyx then opens one more stream for each flow, and each stream opens with a connect request that names the destination. [Connections](/docs/network/tunnel/concepts/connections) follows one flow through every step. ### A Client Like Any Other The process dials each destination as any program on its host would, and copies bytes between the stream and that connection until the flow ends. On the web service, a request that `curl` makes on a tunnel host and requests from the function look the same: ```bash Tunnel host curl -s http://10.20.3.7:8080/from-curl ``` ```text 10.20.3.7 access log 10.20.0.10 - - "GET /from-curl HTTP/1.1" 200 - 10.20.0.10 - - "GET /from-function HTTP/1.1" 200 - 10.20.0.11 - - "GET /from-function HTTP/1.1" 200 - ``` The first line is the `curl` on `dc1-tunnel-a`. The other two are function requests, which arrived from `dc1-tunnel-a` and `dc1-tunnel-b`. ## From the *Network* ### The Next Hop of a Route A route that names a tunnel hands each flow in its CIDR to the tunnel process. The route list shows the tunnel as the next hop for `10.20.0.0/16`: ```bash Workstation telnyx-edge network route list acme-prod ``` ```text Output ID CIDR NEXT HOP ORIGIN STATUS ------------------------------------ ------------- -------------------------------------------------------- ---------- ------ 1a826484-8bfe-4346-b9fc-26aa70a22ccc 10.20.0.0/16 tunnel chicago-dc static active 03aefa24-609b-4fce-b88a-0dc9afeed95d 172.27.0.0/24 wireguard_interface 03aefa24-609b-4fce-b88a-0dc9afeed95d attachment active ``` ### Telnyx Terminates the Client's Connection Telnyx terminates the client's connection itself and opens a stream to the process for it. When the destination refuses, the process reports the refusal and Telnyx resets the client's connection. A request to a closed port shows it: ```ts Function await env.PRIVATE_NET.fetch("http://10.20.3.7:9999/"); ``` The fetch rejects with `TypeError: fetch failed`, and its cause is `ECONNRESET`. Run with `-v`, the tunnel process that received the connect request logs the refusal: ```text Tunnel host {"time":"2026-10-26T14:28:17.99189773Z","level":"INFO","msg":"tunnel: dial failed","dest":"10.20.3.7:9999","error":"dial tcp 10.20.3.7:9999: connect: connection refused"} ``` [Connections](/docs/network/tunnel/concepts/connections) follows one request through every step. --- ### Connections > Source: https://developers.telnyx.com/docs/network/tunnel/concepts/connections.md A function on the [*Network*](/docs/network/overview) `acme-prod` connects to `10.20.3.7:8080`, an address that a route sends to the tunnel `chicago-dc`. The diagram follows that one request: The diagram shows four parties side by side: the function, Telnyx, the tunnel process on `dc1-tunnel-a`, and the web service at `10.20.3.7:8080`. The six steps below run between them, and a bar at the bottom shows the three parts of the flow: connection A, the stream, and connection B. 1. The tunnel process opens an outbound TLS connection to Telnyx and sends its token. Telnyx checks the token and confirms on the first stream of the connection. The process then waits. It opens nothing else toward Telnyx. 2. The function opens TCP connection A to `10.20.3.7:8080`. The route on `acme-prod` brings A to Telnyx, which terminates it. 3. Telnyx opens a new stream on the connection of one tunnel process and sends a connect request. The request names the protocol, the destination address and port, the *Network*, and the client's address. It is a message on a stream, not a packet. Nothing is encapsulated, and no route is exchanged. 4. The process dials `10.20.3.7:8080` from its own host. Connection B is an ordinary connection from the tunnel host, so the destination needs no route to Telnyx and sees the tunnel host as the source. 5. The process answers `ok`, or refuses. A refusal resets A, and the function's request fails with a reset connection. 6. Bytes then flow across A, the stream, and B until either side closes. A and B are network connections. The stream between them relays bytes and is not a connection to anything. No packet from the function crosses the tunnel. For the same reason, hosts behind the tunnel cannot start connections through it, and a stream that the tunnel process opens toward Telnyx goes nowhere. The process stores no configuration. Routes, [private services](/docs/network/tunnel/features/private-services) Preview, and [public hostnames](/docs/network/tunnel/features/public-hostnames) Preview live in Telnyx, and Telnyx asks the process to dial only the destinations that they declare. ## One Connect Request per Flow Each flow reaches a tunnel process as its own connect request, which names one destination. The process dials that destination and nothing else. Each flow is placed on its own as well. With two tunnel processes connected, three requests from the function reach the web service as three connections, from the two tunnel hosts in turn: ```text 10.20.3.7 access log 10.20.0.11 - - "GET /n1 HTTP/1.1" 200 - 10.20.0.10 - - "GET /n2 HTTP/1.1" 200 - 10.20.0.11 - - "GET /n3 HTTP/1.1" 200 - ``` By default the tunnel process logs nothing for a flow. With `-v`, it logs each flow that it relays, and each destination that refuses a connection: ```bash Tunnel host telnyx-edge -v tunnel run --token tnl_… ``` ```text Output {"time":"2026-10-26T14:27:44.768359757Z","level":"DEBUG","msg":"tunnel: relaying","dest":"10.20.3.7:8080","caller":"172.27.0.2:53840","network":"d8a7e0b1-2d64-4d62-aeb2-b77465835059"} {"time":"2026-10-26T14:28:17.99189773Z","level":"INFO","msg":"tunnel: dial failed","dest":"10.20.3.7:9999","error":"dial tcp 10.20.3.7:9999: connect: connection refused"} ``` --- ### Lifecycle > Source: https://developers.telnyx.com/docs/network/tunnel/concepts/lifecycle.md | Status | Meaning | | -------------------------------------------------------- | ------------------------------------------------------------------ | | `inactive` | No tunnel process is connected. | | `healthy` | One or more tunnel processes are connected. | | `degraded` Preview | A tunnel process is connected but fails to reach its destinations. | A new tunnel is `inactive`. It turns `healthy` when its first process connects, and `inactive` again when its last process stops. `telnyx-edge tunnel get` shows the status and the number of connected processes. ## Replicas Any number of hosts can run the same token. Every process serves the same routes, [private services](/docs/network/tunnel/features/private-services) Preview, and [public hostnames](/docs/network/tunnel/features/public-hostnames) Preview. Telnyx spreads requests across the connected processes in turn. When one process stops, the next request goes via another, and no request fails. With `dc1-tunnel-a` stopped, the tunnel stays `healthy` on the one process left: ```bash Workstation telnyx-edge tunnel get chicago-dc ``` ```text Output NAME STATUS CONNECTIONS ---------- ------- ----------- chicago-dc healthy 1 ... ``` A process whose connection drops reconnects on its own and serves again within seconds. ## Routes | Status | Meaning | | ------------------------------------------------------ | ------------------------------------------------ | | `pending` | Telnyx is installing the route on the *Network*. | | `active` | The route carries traffic to its tunnel. | | `inactive` | The route's tunnel has no connected process. | | `failed` Preview | Telnyx could not install the route. | A function on the [*Network*](/docs/network/overview) picks up a route within about a minute of its creation. A route that is `inactive` turns `active` again when a process connects. ## Tunnel Token The token is the only credential the tunnel process holds. Telnyx returns it once, when the tunnel is created or its token is rotated, and stores only a hash of it. `telnyx-edge tunnel get` and `telnyx-edge tunnel list` never print it. The token lets a process serve the tunnel and nothing else. It is not an API key. The Telnyx API answers a request that carries it with `401 Unauthorized`, so a host that holds it cannot create, change, or delete resources. [Rotating the token](/api-reference/tunnels/rotate-a-tunnel-token) issues a new one and invalidates the old one: ```bash Workstation telnyx-edge tunnel rotate-token chicago-dc ``` ```text Output Rotated token for tunnel chicago-dc (6afe50f3-bcff-48e9-93e8-4a906a91067e) Token (shown once, store it now): tnl_… The previous token no longer works. ``` A process on the old token stays connected and keeps serving. When it reconnects, Telnyx refuses it, and the process exits: ```text Tunnel host Error: tunnel: token rejected (rotate-token issued a new one?) ``` ## Deletion A tunnel that a route names cannot be [deleted](/api-reference/tunnels/delete-a-tunnel). `telnyx-edge tunnel delete` lists the routes instead: ```text Output Error: delete tunnel: tunnel 6afe50f3-bcff-48e9-93e8-4a906a91067e is the next hop of 1 route; delete it first: 38aa69b4-9a4a-4181-bb92-e1571cc14533 ``` Once the tunnel is deleted, Telnyx refuses its token. --- ## Features ### Routes > Source: https://developers.telnyx.com/docs/network/tunnel/features/routes.md A tunnel belongs to no [*Network*](/docs/network/overview). A route attaches it, and so does a [private service](/docs/network/tunnel/features/private-services) Preview. A route names a *Network*, a CIDR, and the tunnel as its next hop, and traffic on that *Network* to an address in the CIDR goes via the tunnel. This route sends `10.20.0.0/16` on `acme-prod` to `chicago-dc`: ```bash Workstation telnyx-edge network route add acme-prod 10.20.0.0/16 --tunnel chicago-dc ``` The CIDR follows the [route rules](/docs/network/routes) of the *Network*. - One tunnel serves routes on several *Networks*. Each *Network* reaches only the CIDRs that its own routes send to the tunnel, and traffic never passes from one *Network* to another through it. - A function on the *Network* picks up a new route within about a minute, without a redeploy. ## Decided on the *Network* The route is the decision, and it is made on the *Network*, not at the edge of the user's network. Deleting the route cuts the CIDR off. The tunnel stays connected, and nothing changes within the user's network: ```bash Workstation telnyx-edge network route delete 1a826484-8bfe-4346-b9fc-26aa70a22ccc && telnyx-edge tunnel get chicago-dc ``` ```text Output Deleted route 1a826484-8bfe-4346-b9fc-26aa70a22ccc NAME STATUS CONNECTIONS ---------- ------- ----------- chicago-dc healthy 2 ... ``` Both tunnel processes stay connected, and the function's requests to `10.20.3.7:8080` then fail with `TypeError: fetch failed`. --- ### Private Services > Source: https://developers.telnyx.com/docs/network/tunnel/features/private-services.md **Preview.** Not available yet. A private service registers one destination behind a tunnel on a [*Network*](/docs/network/overview), under a hostname that resolves only on that *Network*. Clients on the *Network* reach that service and get no route to the subnet around it. ```bash Workstation telnyx-edge network service add acme-prod wiki --tunnel chicago-dc --service https://10.20.3.7:8443 --hostname wiki.corp.internal ``` The service names its tunnel and the address that the tunnel process reaches. The API takes the same fields: ```json POST /v2/network_services { "name": "wiki", "network_id": "d8a7e0b1-2d64-4d62-aeb2-b77465835059", "hostname": "wiki.corp.internal", "tunnel_id": "abb096ba-61ae-4f9c-9269-98d8ed94f6aa", "service": "https://10.20.3.7:8443" } ``` `wiki.corp.internal` resolves only for clients on `acme-prod`. Telnyx creates no public DNS record for it. ## Bind the Service A function binds the service by its ID: ```toml func.toml [network_service.WIKI] id = "b41c7e02-…" ``` The binding fixes the destination, so the URL of a request sets only its path: ```ts Function const page = await env.WIKI.fetch("/runbooks/deploys"); ``` The request arrives at `10.20.3.7:8443` through the tunnel. --- ### Public Hostnames > Source: https://developers.telnyx.com/docs/network/tunnel/features/public-hostnames.md **Preview.** Not available yet. A public hostname publishes one service behind a tunnel on the internet, at a hostname the user owns. The service keeps no public IP address and no inbound port. ```bash Workstation telnyx-edge tunnel hostname add chicago-dc api.example.com http://localhost:8080 ``` `http://localhost:8080` is the address that the tunnel process dials on its own host. The API takes the same fields and returns the CNAME target: ```json POST /v2/tunnel_hostnames { "tunnel_id": "abb096ba-61ae-4f9c-9269-98d8ed94f6aa", "hostname": "api.example.com", "service": "http://localhost:8080" } ``` ```json Output { "data": { "record_type": "tunnel_hostname", "id": "c970d16c-…", "dns_target": "c970d16c.telnyxcompute.com", "status": "pending_dns" } } ``` ## Point the Hostname at Telnyx At the DNS provider for `example.com`, the hostname gets a CNAME to its `dns_target`: ```text DNS api.example.com CNAME c970d16c.telnyxcompute.com ``` Telnyx activates the hostname once that CNAME resolves, and serves it on port 443. The CNAME only brings traffic to Telnyx. The `tunnel_hostname` record is what authorizes it, so a hostname that arrives without a record is rejected at the edge, whatever target its CNAME names. | Situation | Client Gets | | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | Record, CNAME, and a connected process | The service. | | CNAME, and no record for the hostname | Rejected at the edge. HTTPS fails in the TLS handshake with `unrecognized_name`, and HTTP gets `421 Misdirected Request`. | | Record, and no CNAME | Nothing reaches Telnyx. The record stays `pending_dns`. | | Record and CNAME, and no connected process | `503`. The record shows `inactive`. | --- ## Operations ### Run as a Service > Source: https://developers.telnyx.com/docs/network/tunnel/operations/run-as-a-service.md **Preview.** Not available yet. `telnyx-edge tunnel service install` installs the tunnel process as a system service, with systemd on Linux and launchd on macOS. The service starts at boot and restarts on failure: ```bash Tunnel host sudo telnyx-edge tunnel service install --token tnl_… ``` The token is written to a file that only root can read, and nothing else is stored on the host. Installing a system service needs administrator rights. Running the tunnel process [does not](/docs/network/tunnel/concepts/what-it-is#no-root). `status` reports the service, and `uninstall` removes it: ```bash Tunnel host sudo telnyx-edge tunnel service status sudo telnyx-edge tunnel service uninstall ``` After a [token rotation](/docs/network/tunnel/concepts/lifecycle#tunnel-token), `install` with the new token replaces the stored one. --- ### Test a Destination > Source: https://developers.telnyx.com/docs/network/tunnel/operations/test-a-destination.md **Preview.** Not available yet. `telnyx-edge tunnel test` dials a destination from the tunnel host, as the tunnel process does for each flow, and reports the result. Telnyx takes no part in the check: ```bash Tunnel host telnyx-edge tunnel test https://10.20.3.7:8443 ``` ```text Output ok — TLS handshake completed in 4 ms ``` The destination takes the same form as the `service` of a [private service](/docs/network/tunnel/features/private-services) Preview or a [public hostname](/docs/network/tunnel/features/public-hostnames) Preview, with its scheme and port. --- ## API Reference (Tunnel) ### Tunnels - [List all Tunnels](https://developers.telnyx.com/api-reference/tunnels/list-all-tunnels.md): Lists the tunnels on the account with their status and connected tunnel processes. Responses never include `tunnel_token`. - [Create a Tunnel](https://developers.telnyx.com/api-reference/tunnels/create-a-tunnel.md): Creates a tunnel and returns its `tunnel_token`. The token is the only credential the tunnel process needs, and this response and `POST /tunnels/{id}/actions/r… - [Retrieve a Tunnel](https://developers.telnyx.com/api-reference/tunnels/retrieve-a-tunnel.md): Retrieves a tunnel with its status and the tunnel processes connected to it. The response never includes `tunnel_token`. - [Delete a Tunnel](https://developers.telnyx.com/api-reference/tunnels/delete-a-tunnel.md): Deletes a tunnel. A tunnel that a route names as its next hop cannot be deleted; delete those routes first. Connected tunnel processes are disconnected and can… - [Rotate a Tunnel Token](https://developers.telnyx.com/api-reference/tunnels/rotate-a-tunnel-token.md): Issues a new `tunnel_token` and invalidates the previous one. Tunnel processes already connected stay connected until they reconnect, and are then refused unti…