---
title: "Tunnels: Hostname Routes to a Service on Your Machine"
description: "Give a service on your machine a public hostname with fixedmarkd: route kinds, TLS passthrough or HTTP termination, certificates, DNS failover, and quotas."
canonical: https://fixedmark.com/docs/tunnels
updated: 2026-11-07
source: Fixedmark (https://fixedmark.com), BitMask LLP
---

Inbound

# Tunnels: hostname routes to your machine

A tunnel gives a service on your own machine a public hostname. You run fixedmarkd next to the service, and partners reach it at that hostname on port 443, or at a TCP port, through two shared Fixedmark ingress IPs on two nodes. TLS passes through end to end. No Fixedmark node terminates it, and your machine opens no inbound port.

Docs preview: Fixedmark is not live yet. Endpoints are issued at launch, and these pages describe planned launch behavior. Anything marked planned may change. [Join early access](https://fixedmark.com/early-access) to get yours first.

## What a tunnel route is

- A route belongs to an app. Its IPs are placements, chosen by Fixedmark. A route on the shared pool is placed on two shared ingress IPs, one on each of two nodes in its region.
- fixedmarkd on your machine dials each of those IPs on 443 and holds one session to each. Each partner connection comes down one of those sessions to the local target you name, such as `127.0.0.1:8080`.
- A node reads only the server name in the partner's TLS ClientHello, or the port for a TCP route, and splices the bytes. It never holds a key for your traffic.
- Every control you know from inbound IPs works the same: partner allowlists, inbound metering, connection logs, webhooks, and the ingress block.
- A shared route takes a connector origin, since a shared IP never dials out to a host. For a host origin, or for traffic that must arrive only at your own address, use the [dedicated inbound IP add-on](https://fixedmark.com/docs/inbound).

## Route kinds

_Route kinds_

| Kind     | Partners reach                             | TLS                                                                          | Use it for                                                            |
| -------- | ------------------------------------------ | ---------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `http`   | `https://<hostname>` on 443                | fixedmarkd terminates it on your machine with a certificate it orders itself | A plain-HTTP service, such as a web app or an API on `127.0.0.1:8080` |
| `tls`    | `<hostname>` on 443                        | Passed through to your target, which terminates it                           | A service that already speaks TLS and holds its own certificate       |
| `tcp`    | `<hostname>` on a port from 20000 to 29999 | None added. The bytes pass unchanged                                         | Databases, SSH, MQTT, and other raw TCP                               |
| `access` | Signed-in people through `fm-access`       | Passed through                                                               | Staff reaching an internal service. It takes no partner list          |

The kinds are spelled `http`, `tls`, `tcp`, and `access` in the CLI, the API, and fixedmarkd's plan. The API refuses any other kind with `route.kind_invalid`.

## Before you start

- Tunnels on Pro and up, with a dedicated inbound IP as an add-on at $9 per IP-month. Your plan's routes, hostnames, connectors, and inbound bandwidth are in Limits. A plan without tunnels refuses a route with `route.tunnel_not_entitled` (402).
- An app. `fm apps list` prints its id and name. `--app` takes either.
- A Linux or macOS machine on amd64 or arm64 that can reach your service and make outbound connections to port 443. fixedmarkd does not run on Windows yet.
- The partner's source addresses, as IPv4 addresses or CIDRs. Every route except `access` admits only the addresses you list.

## Quick start: a first hostname route

Create a connector, install fixedmarkd as a service on the machine that holds your service, then create an `http` route that names the connector.

**Terminal**

```bash
fm connectors create --app shop --name laptop
sudo fixedmarkd install --token-file fm-connector-laptop.token
```

`fm connectors create` writes the token to a file readable by you only and prints the install line. Run the install line on the service's machine, from the directory that holds the file, then delete the file. [Connectors](https://fixedmark.com/docs/connectors#install) covers the service and its files.

**fm**

```bash
fm routes create --app shop --kind http --name web \
  --connector laptop --to 127.0.0.1:8080 \
  --partner 198.51.100.0/24
```

**API**

```bash
curl -X POST "https://api.fixedmark.com/v1/apps/APP_ID/routes" \
  -H "Authorization: Bearer $FIXEDMARK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"kind":"http","name":"web","origin":{"kind":"connector","connector_id":"CONNECTOR_ID","target":"127.0.0.1:8080"},"partner_cidrs":["198.51.100.0/24"]}'
```

The API answers `202` with the route and a `job_id`. `fm` prints the route:

```
route ROUTE_ID
name web
kind http
hostname web.acme.fmtunnel.test
mode shared
region REGION
state pending
dns unpublished
placements 2
  IP_ID  203.0.113.20:443
  IP_ID  203.0.113.21:443
job JOB_ID
watch it with: fm jobs watch JOB_ID
```

Within about a minute fixedmarkd reads the route from its plan, attaches a session to each IP, and orders the hostname's certificate. Then, from a partner address:

**Terminal**

```bash
curl -sv https://web.acme.fmtunnel.test/
```

The request reaches `127.0.0.1:8080` on your machine as plain HTTP. `fm routes show web` reads the route's state, health, and DNS record, and `fixedmarkd status` shows the sessions and the certificate.

`fmtunnel.test` is a placeholder. The tunnel domain is not chosen yet. It will be a domain of its own, never under `fixedmark.net`. Hostnames on this page show it in the zone's place.

## Hostnames

Every route except `access` gets the hostname `<name>.<org-slug>.<zone>`, such as `web.acme.fmtunnel.test`. Partners use that name for every kind. A `tcp` route adds its port, such as `db.acme.fmtunnel.test:20000`.

- `<name>` is the route's name, from `--name`. It is 1 to 40 lowercase letters, digits, and single hyphens, starts and ends with a letter or digit, and does not start with `xn`. It is unique among your org's live routes. Without `--name` the route is named its kind and the first 8 hex digits of its id, such as `tcp-5f0c2b1e`.
- Names such as `www`, `api`, `app`, `login`, and `status`, and any name starting `pg-` or `ip-`, are reserved and refused with `route.name_reserved`. So is any name containing `fixedmark`. The same rules hold for the org slug.
- `<org-slug>` is set by your org's first hostname route, from `--org-slug` or your org's name, and it is fixed after that. A later route always gets that slug. A first route that asks for a slug another org holds is refused with `route.slug_taken`.
- Your org's slug is its own label under the zone, but that label does not keep cookies apart between orgs yet. Browsers treat each org's hostnames as separate sites only once the tunnel domain is on the Public Suffix List, and that entry is pending. Until then, set cookies without a `Domain` attribute, so each one stays on the hostname that set it.
- Every org shares the weekly certificate budget, with a share for each org. See Certificates.

`fmtunnel.test` is a placeholder. The tunnel domain is not chosen yet. It will be a domain of its own, never under `fixedmark.net`. Hostnames on this page show it in the zone's place.

## Passthrough and HTTP termination

A `tls` or `tcp` route is passthrough. The partner's bytes reach your target unchanged, and your target holds the certificate, if any. fixedmarkd does not hand its own certificates to a local TLS server yet, so a `tls` target needs a certificate of its own that its partners accept.

An `http` route terminates TLS in fixedmarkd, on your machine. The node still passes the encrypted bytes through, and the key never leaves your machine. fixedmarkd then sends each request to your target as plain HTTP.

- Partners speak HTTP/2 or HTTP/1.1, picked by ALPN. Your target always gets HTTP/1.1.
- WebSockets work over HTTP/1.1 upgrades and over HTTP/2 extended CONNECT. Other upgrade tokens are not passed.
- The `Host` header is kept as the partner sent it. A request for any other host than the route's hostname gets `421`. A `Host` that is not all visible ASCII gets `400`.
- An upgrade request that declares a body, by a `Content-Length` other than 0 or by a `Transfer-Encoding`, gets `400`.
- A plain `CONNECT` gets `405`, so no partner can tunnel past your target.
- A target that refuses or fails the request gets the partner a bare `502`, and fixedmarkd logs `upstream failed`.
- Up to 256 requests and WebSockets go to your targets at once. A request past that cap gets `503`, and so does one that needs a new target connection while the connection cap is full.
- A connection with nothing in flight closes after 75 seconds. A request's headers must arrive within 30 seconds and fit in 64 KiB, else the partner gets `431` for size. fixedmarkd waits 10 seconds for your target to accept.

fixedmarkd drops these headers from the partner's request: every header whose name starts `X-Forwarded`, plus `Forwarded`, `X-Real-IP`, `Fixedmark-Tunnel-Request`, and the client-address headers `X-Client-IP`, `True-Client-IP`, `CF-Connecting-IP`, `X-Cluster-Client-IP`, `Fastly-Client-IP`, `X-Original-Forwarded-For`. A name matches in any case and with `_` in place of `-`, so `X_Forwarded_For` is dropped too. CGI, WSGI, Rack, and PHP read that name as `X-Forwarded-For`. Then fixedmarkd sets its own headers. It never appends to a partner's value.

_Forwarding headers fixedmarkd sets_

| Header                         | Value                                       |
| ------------------------------ | ------------------------------------------- |
| `X-Forwarded-For`, `X-Real-IP` | The partner's address                       |
| `X-Forwarded-Proto`            | `https`                                     |
| `X-Forwarded-Host`             | The route's hostname                        |
| `Forwarded`                    | `for=<partner>;proto=https;host=<hostname>` |
| `Fixedmark-Tunnel-Request`     | `?1`                                        |

fixedmarkd sets no other header. Any header it does not drop, such as `Authorization` or `Cookie`, reaches your target as the partner sent it.

Trust these headers only on connections from loopback, where fixedmarkd runs. Bind your target to `127.0.0.1`, or check the source address, so no one else on your network can send a request with a forged `X-Forwarded-For`.

## Certificates

fixedmarkd orders the certificate for each `http` route's hostname from Let's Encrypt, by the ACME DNS-01 challenge. It makes the key on your machine. Fixedmark only writes the challenge's TXT record in the tunnel zone for it, so no key and no certificate ever leaves your machine.

1. fixedmarkd makes a key, opens an order, and asks the control plane to publish the challenge value at `_acme-challenge.<hostname>`.
2. It waits until every Fixedmark nameserver answers the value, then asks the CA to validate.
3. It stores the certificate and serves it from the next handshake. The challenge record is removed.
4. It renews in the window the CA's renewal information names, else at two thirds of the lifetime, and checks once an hour. A renewal never counts against the budget below.

Until the first certificate is issued, a partner's connection to the route is refused with `cert_unavailable`, and `fixedmarkd status` shows the certificate as `queued` or `issuing`.

_Where fixedmarkd keeps its keys_

| How it runs                    | State directory                                                                                           |
| ------------------------------ | --------------------------------------------------------------------------------------------------------- |
| Linux service                  | `/var/lib/private/fixedmarkd`, systemd's `StateDirectory`, mode 0700, owned by the service's dynamic user |
| macOS service                  | `/Library/Application Support/fixedmarkd/state`, mode 0700, owned by `nobody`                             |
| `fixedmarkd run` in a terminal | `--state-dir`, else `$XDG_STATE_HOME/fixedmarkd`, else `~/.local/state/fixedmarkd`                        |

- The directory holds the ACME account and one key and certificate per hostname. Every file is mode 0600.
- fixedmarkd refuses a state directory that is a symbolic link or that another user owns, and exits 2 with `fixedmarkd.state_dir`.
- `sudo fixedmarkd uninstall` leaves the state directory in place, so the keys stay on the machine. Remove the directory yourself when you retire the machine.
- On macOS the directory is owned by `nobody`, which other services on the machine may also run as. Run fixedmarkd on a machine where that is acceptable.

Until the tunnel domain is on the Public Suffix List, Let's Encrypt allows the zone a small number of new certificates a week. Fixedmark shares it out: at most 45 first certificates across every org in 7 days, and at most 9 for one org. A first certificate past either share waits, and fixedmarkd retries after the time the control plane names. Renewals are exempt.

## Two sessions per route

- fixedmarkd holds one session to each IP that carries one of its routes. A shared route has two placements, so it has two sessions.
- A partner reaches either IP. Each node sends the connection down its own session, so both sessions serve traffic all the time.
- fixedmarkd reads its plan every 60 seconds. When a dial to an IP fails, it reads the plan again within about 8 seconds, then backs off to once a minute, so it follows a route that moved without a restart. It stops these extra reads 10 minutes after the first failure, or as soon as the plan changes.
- One fixedmarkd per token. A second one with the same token replaces the first one's sessions.

## Partners

Every route except `access` admits only the partner addresses you list. The node checks the list before anything reaches your machine. Setting partners replaces the whole list.

**Terminal**

```bash
fm routes partners set web --partner 198.51.100.0/24 --partner 192.0.2.44
```

A public website takes `--partner 0.0.0.0/0 --allow-any`. Without `--allow-any`, that entry, or any entry wider than a /8, is refused with `route.partner_open_to_all`, and with it the route carries the warning `open_to_all`.

## DNS records

Each hostname has one CNAME record in the tunnel zone. A shared route's record points at its placement group's name, `pg-<id>.<zone>`, which holds one A record for each of the group's two IPs. Every record has a TTL of 60 seconds.

```
web.acme.fmtunnel.test.            60  IN  CNAME  pg-0123456789ab.fmtunnel.test.
pg-0123456789ab.fmtunnel.test.     60  IN  A      203.0.113.20
pg-0123456789ab.fmtunnel.test.     60  IN  A      203.0.113.21
```

`fmtunnel.test` is a placeholder. The tunnel domain is not chosen yet. It will be a domain of its own, never under `fixedmark.net`. Hostnames on this page show it in the zone's place.

- The record is written about two seconds after the route is created. The route's `dns.state` reads `unpublished` right after the create, as `fm` prints it, then `pending`, then `synced` within seconds.
- When only one of the route's placements is healthy, the CNAME points at that IP's own name, `ip-<id>.<zone>`, and returns to the group when both are.
- Health never removes the record. It goes only while the route is disabled or has no placement, and when you delete the route.
- `dns.state` `refused` means the DNS provider refused the record. Fixedmark retries it when the route changes and at an hourly check. `dns.error` says why.

## Health and failover

Fixedmark judges each placement from its node's heartbeat, its listener, and your connector's session, and rolls those up into the route's health. Failover is by DNS, in three layers.

_Failover bounds_

| What fails             | Within                                                                                         | What happens                                                                                                                                                                                                                                                                                                                                                 |
| ---------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| One node               | About 47 seconds, plus up to 60 seconds of a resolver's cache                                  | Its IP leaves the group's A records. A client that cannot connect to one A record tries the other one, so most partners see no failed connection.                                                                                                                                                                                                            |
| Your session to one IP | Held 30 seconds, then the next DNS write                                                       | The route's CNAME points at the other IP's name. A session that comes back within the hold leaves the record alone, so a flapping session moves nothing.                                                                                                                                                                                                     |
| Both IPs of the group  | The route moves after 120 seconds down, and every route of a dead group within about 3 minutes | Fixedmark places the route on another healthy group in the same region and sends the `route.placement` [webhook](https://fixedmark.com/docs/webhooks) . The hostname follows. A `tcp` route keeps its port. If every other healthy group in the region already holds that port for another route, the route waits where it is and Fixedmark staff are paged. |

- These bounds are measured with 2 second heartbeats in Fixedmark's test lanes and scaled to the 10 second heartbeats production nodes send.
- A route never moves to another region by default. When every group of its region is down, it waits there, and Fixedmark is paged. Cross-region moves are off until Fixedmark turns them on.
- A failure on your side, such as `no_session` or `origin_down`, never moves a route, since a move would not fix it.
- A route on a [dedicated IP](https://fixedmark.com/docs/inbound) is never re-placed. It follows its IP.
- Partners that dial the hostname follow every move. A partner that dials an address it looked up once must look it up again.

## Status fields

**Terminal**

```bash
fm routes show web
fixedmarkd status
```

_Route status fields_

| Field                 | Values                                                                                                                                                                                                                                                                                                                                                                                     |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `state`               | `pending` until a node binds a listener, `bound`, then `active` once the node's firewall admits the partners. `disabled` when Fixedmark staff stopped the route, `paused` when its IP is suspended.                                                                                                                                                                                        |
| `state_reason`        | Why a route is still `pending`, such as `plan_lapsed`, `pool_draining`, `ip_retired`, or `placement_changed`.                                                                                                                                                                                                                                                                              |
| `health`              | The roll-up: `healthy`, `degraded` when one placement is healthy and another is not, `down` when none is, or `unknown` before the first check.                                                                                                                                                                                                                                             |
| `placements[].health` | `healthy`, `no_session` when fixedmarkd holds no session on that IP, `origin_down`, `listener_down` when Fixedmark cannot reach the port, `ip_down` when the node stopped answering, or `unknown`.                                                                                                                                                                                         |
| `dns.state`           | `unpublished`, `pending`, `synced`, or `refused`, with `dns.target` and `dns.error`.                                                                                                                                                                                                                                                                                                       |
| `cert`                | An `http` route's certificate as fixedmarkd last reported it, and `null` on every other kind. `cert.state` is `none` before the first report, then `queued`, `issuing`, `valid`, `renewing`, `renewal_failing` when a renewal failed while the current certificate still serves, or `failed` when there is none to serve. It comes with `serial`, `not_after`, `error`, and `reported_at`. |

`fixedmarkd status` prints one line per IP with its session state, `connecting`, `attached`, `detached`, or `stopped`, and a CERTS table with one row per hostname: `queued`, `issuing`, `valid`, `renewing`, `renewal_failing`, or `failed`, with the expiry and the last error. `--json` prints the same file, with each `http` route's request and error counters.

## Move a route to a dedicated IP

A route keeps its name and hostname when it moves between the shared pool and a dedicated IP of its app.

**Terminal**

```bash
fm routes placement web --dedicated-ip IP_ID
fm routes placement web --shared
```

- `PUT /v1/apps/APP_ID/routes/ROUTE_ID/placement` takes `{"mode":"shared"}` or `{"mode":"dedicated","ip_id":"IP_ID"}`. It answers `202` when the route moves.
- Asking for the placement the route already has changes nothing. The API answers `200` with `changed` false, and `fm` prints `unchanged: the route is already there`.
- A `tcp` route with partners gets a new address and maybe a new port, which its partners dial. It moves only with `--confirm`, or `confirm: true`, else `route.placement_unconfirmed` (409).
- A dedicated placement needs the add-on. A plan without it refuses with `route.dedicated_not_entitled` (402).

## fm routes reference

A route is named by its id or its name. Commands that take one route search every app your key reads unless you pass `--app`.

_fm routes commands_

| Command                                                                   | What it does                                                                                                                                                                                                                                                                                                 |
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `fm routes create --app <app> --kind http\|tls\|tcp\|access`              | Creates a route. Add `--name`, `--connector` and `--to`, and `--partner` for each source. `--org-slug` sets your org's slug on its first route. `--region` picks a shared route's region. `--dedicated-ip`, `--origin`, `--sni`, and `--port` are for a [dedicated IP](https://fixedmark.com/docs/inbound) . |
| `fm routes list --app <app>`                                              | Lists the app's routes with hostname, mode, port, state, and DNS.                                                                                                                                                                                                                                            |
| `fm routes show [--app <app>] <route>`                                    | Prints one route with each placement.                                                                                                                                                                                                                                                                        |
| `fm routes partners set [--app <app>] <route> --partner <cidr>`           | Replaces the partner list. Add `--allow-any` for `0.0.0.0/0`.                                                                                                                                                                                                                                                |
| `fm routes placement [--app <app>] <route> --shared\|--dedicated-ip <id>` | Moves the route. Add `--confirm` for a `tcp` route with partners.                                                                                                                                                                                                                                            |
| `fm routes delete [--app <app>] <route>`                                  | Deletes the route. Its hostname's record goes too.                                                                                                                                                                                                                                                           |

A `tcp` route takes the same flags with `--kind tcp`, such as `fm routes create --app shop --kind tcp --name db --connector laptop --to 127.0.0.1:5432 --partner 198.51.100.0/24`. Partners then reach `db.acme.fmtunnel.test` on the port the route prints.

## Limits and quotas

_Tunnels by plan_

| Plan     | Routes | Hostnames | Connectors | Inbound bandwidth |
| -------- | ------ | --------- | ---------- | ----------------- |
| Pro      | 10     | 10        | 5          | 50 GB             |
| Business | 50     | 50        | 20         | 250 GB            |
| Scale    | 200    | 200       | 50         | 1 TB              |

- Every route with a hostname counts against `hostnames`, and every route against `routes`. Past either, a create is refused with `route.hostname_quota` or `route.quota` (409), and the error's `limit` names the quota.
- Tunnels come with paid plans only. The trial has none.
- A `tcp` route takes one port from 20000 to 29999, the same port on both IPs of its group.
- A route admits 1 to 256 partner entries, IPv4 only.
- fixedmarkd holds up to 256 partner connections at once by default, set by `--max-streams`.
- Inbound bytes count toward your plan's inbound bandwidth, apart from outbound. Ingress packs add more for one period.

See [inbound limits](https://fixedmark.com/docs/limits#inbound) for the rest. Each count comes from the plan catalog; 3 plans on sale include tunnels.

## Troubleshooting

_fixedmarkd exit codes_

| Exit | Meaning                                                                                                                                                                                                                 | What to do                                                                                              |
| ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `0`  | Stopped by SIGINT or SIGTERM.                                                                                                                                                                                           | Nothing.                                                                                                |
| `1`  | An unexpected error, or a state directory fixedmarkd could not read or write (`fixedmarkd.state_dir`).                                                                                                                  | Read the last log line. The service restarts it.                                                        |
| `2`  | Bad usage, a token file group or others can read (`fixedmarkd.token_file_mode`), an install on an OS with no service support (`fixedmarkd.install_unsupported`), or a refused state directory (`fixedmarkd.state_dir`). | `chmod 600` the token file, or fix the directory's owner, then start it again.                          |
| `3`  | The token was refused (`connector.token_invalid`) or the connector disabled (`connector.disabled`).                                                                                                                     | Install a new token, or contact [support](https://fixedmark.com/docs/support) for a disabled connector. |
| `4`  | The control plane sent a plan version this fixedmarkd does not read (`fixedmarkd.plan_version`).                                                                                                                        | Upgrade fixedmarkd. The Linux service does not restart on exit 4.                                       |
| `5`  | The control plane sent an older plan version than this fixedmarkd reads (`fixedmarkd.plan_version_old`).                                                                                                                | Nothing on your side. The service restarts and tries again.                                             |

_Tunnel symptoms by status field_

| You see                                         | Meaning                                                                                        | What to do                                                                                                  |
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `placements[].health` `no_session`              | fixedmarkd holds no session on that IP.                                                        | Check `fixedmarkd status` and look for `connector dial failed`. The machine needs outbound 443 to both IPs. |
| Connection refused with `cert_unavailable`      | The `http` route has no valid certificate yet.                                                 | Read the CERTS table. `queued` or `issuing` clears in a few minutes. A budget wait names its retry time.    |
| CERTS last error `fixedmarkd.acme_io`           | The control plane or the CA did not answer in time while fixedmarkd ordered the certificate.   | Nothing. fixedmarkd retries with backoff, and a challenge record it left behind is removed within an hour.  |
| `dns.state` `refused`                           | The DNS provider refused the hostname's record.                                                | Read `dns.error`, and contact [support](https://fixedmark.com/docs/support) if it stays.                    |
| `state` `pending`, `state_reason` `plan_lapsed` | The plan no longer includes tunnels, and the route lost its placements after the grace period. | Renew or upgrade. The route is placed again within 15 minutes.                                              |
| `421` on every request                          | The partner sent another `Host` than the route's hostname.                                     | Call the route's hostname, not an IP or another name.                                                       |
| `502` with `upstream failed` in the log         | The target refused or dropped the request.                                                     | Check the service listens on the route's `--to` address on the connector's machine.                         |

_Route create refusals_

| Code                                                            | Meaning                                                                   |
| --------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `route.tunnel_not_entitled` (402)                               | Your plan does not include tunnels.                                       |
| `route.dedicated_not_entitled` (402)                            | A dedicated placement needs the dedicated inbound IP add-on.              |
| `route.kind_origin` (422)                                       | A shared route or an `http` route needs a connector origin.               |
| `route.name_invalid`, `route.name_reserved`, `route.name_taken` | The name breaks the rules in Hostnames, or another live route has it.     |
| `route.slug_taken`                                              | Another org holds the slug you sent with your org's first hostname route. |
| `route.quota`, `route.hostname_quota` (409)                     | Your plan's routes or hostnames are all in use.                           |
| `route.pool_full` (503)                                         | The region's shared pool has no room. Retry later.                        |
| `route.tunnel_disabled` (409)                                   | Hostname routes are not on yet on this control plane.                     |

`fm` exits 2 on a malformed request (`400` or `422`) and 1 on any other refusal, such as `402`, `409`, or `503`. Either way it prints the code. When you write to [support](https://fixedmark.com/docs/support), include the route id and the `connector_id`, never the token.

## Frequently asked questions

### Does Fixedmark see my traffic in clear text?

No. Nodes pass TLS through. For an http route, fixedmarkd terminates TLS on your own machine with a key that never leaves it.

### Do I need a public IP or an open port?

No. fixedmarkd dials out to Fixedmark on port 443 and partner connections come back down that session.

### Can I use my own domain?

Not yet. Every route gets a name under the tunnel zone, such as web.acme.\<zone> for the acme org. Custom domains are planned.

### When do I need the dedicated inbound IP add-on?

When partners must allowlist one address of your own, when traffic must arrive only at that address, or when the origin is a host Fixedmark dials rather than a connector.
