v3.3.2 9697f9d

traffic.md RawHistoryFolder

Traffic Plane Configuration

Gordon models network listeners as a traffic graph: entrypoints receive packets or connections, routers select a service, and services resolve to backends.

Smart TCP Edge Entrypoint

Public application traffic is normally exposed with a smart_tcp entrypoint. The conventional route-capable entrypoint name is edge, but Gordon does not require that name or assign a built-in public port. When exactly one route-capable smart_tcp or tls_mux entrypoint exists, app HTTP hosts use it even if it has a custom name. Choose the address that matches your deployment, firewall, and container mapping:

[entrypoints.edge]
address = ":443"
protocol = "smart_tcp"
trusted_cidrs = []

Do not treat entrypoints.edge.address as an HTTP port or an HTTPS port. It is one TCP socket that sniffs each new connection and dispatches the original byte stream.

Supported entrypoint protocols are smart_tcp, tls_mux, tcp, and udp. smart_tcp is the primary public edge model; tls_mux can also serve app HTTP hosts through TLS fallback, tcp and udp are for explicit L4 services, and UDP remains separate from the TCP entrypoint.

Smart TCP Dispatch Order

For each accepted connection on a smart_tcp entrypoint Gordon:

  1. Applies entrypoint-wide trusted_cidrs to the peer socket IP.
  2. Rejects PROXY protocol v1 and v2 prefixes. PROXY headers are not trusted or parsed.
  3. Peeks a minimal prefix and replays the bytes to the selected handler/backend.
  4. Dispatches cleartext HTTP/1.x and h2c prior-knowledge to Gordon's HTTP handler.
  5. Dispatches TLS ClientHello traffic by SNI:
    • matching TLS passthrough router -> raw TCP backend without TLS termination;
    • no passthrough match -> normal Gordon HTTPS fallback.
  6. Dispatches unknown non-HTTP/non-TLS bytes only to an explicit raw fallback router, after raw-fallback source policy allows it.
  7. Refuses the connection when no deterministic handler or allowed fallback exists.

Malformed HTTP-looking or TLS-looking traffic is rejected instead of bypassing to raw fallback.

Standalone and Network Services

Standalone services are Gordon-managed containers that L4 routers can target with service:<service>:<port-name>. Define them under [[services]] when Gordon should own the container lifecycle.

[[services]]
name = "rust"
image = "registry.example.com:5000/rust:latest"
enabled = true

[[services.ports]]
name = "game"
container = 28015
protocol = "udp"
publish = "127.0.0.1:38015"

[entrypoints.rust]
address = "0.0.0.0:28015"
protocol = "udp"

[[traffic.udp.routers]]
name = "rust-game"
entrypoint = "rust"
service = "service:rust:game"

Network services describe manually managed non-HTTP backends that L4 routers can target.

[[network_services]]
name = "postgres"

[[network_services.ports]]
name = "db"
container = 5432
protocol = "tcp"

Service references use:

  • route:<domain> for app HTTP hosts from ACTIVE state (merged with installation external routes)
  • external_route:<domain> for configured external HTTP routes
  • network_service:<service>:<port-name> for manually managed TCP, UDP, and TLS passthrough backends
  • service:<service>:<port-name> for Gordon-managed standalone service backends

static:<name> is reserved and currently unsupported.

TLS Passthrough on Smart TCP

TLS passthrough routers run on smart_tcp or tls_mux entrypoints and route by ClientHello SNI without terminating TLS.

[entrypoints.edge]
address = ":443"
protocol = "smart_tcp"

[[traffic.tls.routers]]
name = "raw-tls"
entrypoint = "edge"
sni = "raw.example.com"
service = "network_service:raw:tls"

Exact SNI matches win over wildcard matches. Ambiguous wildcard overlaps and HTTP-host/TLS-passthrough conflicts on the same smart TCP entrypoint are rejected at validation time. App HTTPS hosts that do not match a passthrough SNI use Gordon's normal HTTPS fallback and certificate selection.

Raw TCP Fallback

Raw fallback is disabled by default. Unknown non-HTTP/non-TLS bytes reach a TCP backend only when the smart TCP entrypoint explicitly names one TCP router as raw_fallback and the source policy allows the peer socket IP.

[entrypoints.edge]
address = ":443"
protocol = "smart_tcp"
raw_fallback = "ssh-fallback"
raw_fallback_trusted_cidrs = ["100.64.0.0/10"]

[[traffic.tcp.routers]]
name = "ssh-fallback"
entrypoint = "edge"
service = "network_service:ssh:ssh"

Use raw_fallback_trusted_cidrs for private raw fallback exposure. To expose raw fallback publicly, acknowledge it explicitly:

[entrypoints.edge]
address = ":443"
protocol = "smart_tcp"
raw_fallback = "public-raw"
allow_public_raw_fallback = true

trusted_cidrs and raw_fallback_trusted_cidrs both use the peer socket IP. They do not use X-Forwarded-For.

HTTP Timeouts

The HTTP servers behind smart_tcp and tls_mux entrypoints serve apps and the admin API. They do not use a fixed read or write timeout, so a download or upload that keeps moving data is never cut for taking too long. A connection is cut only after transfer_idle_timeout passes with no data in the direction being waited on.

[traffic.http]
read_header_timeout = "5s"
idle_timeout = "30s"
transfer_idle_timeout = "2m"
Option Env Default Description
traffic.http.read_header_timeout GORDON_TRAFFIC_HTTP_READ_HEADER_TIMEOUT "5s" Maximum time to read request headers
traffic.http.idle_timeout GORDON_TRAFFIC_HTTP_IDLE_TIMEOUT "30s" Maximum keep-alive idle time between requests
traffic.http.transfer_idle_timeout GORDON_TRAFFIC_HTTP_TRANSFER_IDLE_TIMEOUT "2m" Inactivity timeout for request bodies and responses

Values are Go duration strings; malformed, zero, or negative values are rejected when the configuration loads. Servers pick up changes when the traffic graph is next applied (reload); connections already in flight keep their previous timeout.

Inactivity timeout

Each request has an inactivity timer, armed when the request starts. Progress resets it: a response chunk accepted by the client connection (8 KiB granularity), a flush, or request body bytes read. If transfer_idle_timeout passes without progress, Gordon cancels the request context, which aborts the upstream request of a reverse-proxied app, and closes the client connection. When the handler returns, the final response flush and the drain of an unread request body are bounded by the same duration. Consequences:

  • a response that streams data at least once per transfer_idle_timeout can last arbitrarily long, and a client that keeps reading is never cut (it must accept 8 KiB per transfer_idle_timeout, about 68 B/s at the 2 minute default);
  • an upload whose body arrives at least once per transfer_idle_timeout can last arbitrarily long;
  • a backend that stays silent longer than transfer_idle_timeout before or between response chunks has its connection cut, so set it above the slowest expected backend response time;
  • the timer also fires when a handler or backend is silent and no write is pending;
  • WebSocket and other upgraded connections are not subject to it, and HTTP/2 streams are handled the same way as HTTP/1.

transfer_idle_timeout does not apply to [traffic.tcp] or [traffic.udp] routers; those use their own idle_timeout (see below).

TCP Routers

Plain TCP entrypoints support one TCP router per entrypoint. TCP routers on a smart_tcp entrypoint are valid only when referenced by that entrypoint's raw_fallback.

[entrypoints.postgres]
address = "0.0.0.0:5432"
protocol = "tcp"
trusted_cidrs = ["100.64.0.0/10"]

[traffic.tcp]
dial_timeout = "10s"
idle_timeout = "5m"
drain_timeout = "30s"
max_connections = 1024

[[traffic.tcp.routers]]
name = "postgres"
entrypoint = "postgres"
service = "network_service:postgres:db"

If max_connections is omitted or set to 0, Gordon applies the safe runtime default of 1024 active connections per entrypoint.

UDP Routers

UDP is not sniffed or unified into smart TCP. Use a separate UDP entrypoint:

[entrypoints.game]
address = "0.0.0.0:7777"
protocol = "udp"

[traffic.udp]
idle_timeout = "30s"
drain_timeout = "30s"
max_sessions = 4096

[[traffic.udp.routers]]
name = "game"
entrypoint = "game"
service = "network_service:game:game"

UDP sessions are keyed by client address and expire after idle_timeout. If max_sessions is omitted or set to 0, Gordon applies the safe runtime default of 4096 active sessions per entrypoint.

source
1# Traffic Plane Configuration
2
3Gordon models network listeners as a traffic graph: entrypoints receive packets or connections, routers select a service, and services resolve to backends.
4
5## Smart TCP Edge Entrypoint
6
7Public application traffic is normally exposed with a `smart_tcp` entrypoint. The conventional route-capable entrypoint name is `edge`, but Gordon does not require that name or assign a built-in public port. When exactly one route-capable `smart_tcp` or `tls_mux` entrypoint exists, app HTTP hosts use it even if it has a custom name. Choose the address that matches your deployment, firewall, and container mapping:
8
9```toml
10[entrypoints.edge]
11address = ":443"
12protocol = "smart_tcp"
13trusted_cidrs = []
14```
15
16Do not treat `entrypoints.edge.address` as an HTTP port or an HTTPS port. It is one TCP socket that sniffs each new connection and dispatches the original byte stream.
17
18Supported entrypoint protocols are `smart_tcp`, `tls_mux`, `tcp`, and `udp`. `smart_tcp` is the primary public edge model; `tls_mux` can also serve app HTTP hosts through TLS fallback, `tcp` and `udp` are for explicit L4 services, and UDP remains separate from the TCP entrypoint.
19
20## Smart TCP Dispatch Order
21
22For each accepted connection on a `smart_tcp` entrypoint Gordon:
23
241. Applies entrypoint-wide `trusted_cidrs` to the peer socket IP.
252. Rejects PROXY protocol v1 and v2 prefixes. PROXY headers are not trusted or parsed.
263. Peeks a minimal prefix and replays the bytes to the selected handler/backend.
274. Dispatches cleartext HTTP/1.x and h2c prior-knowledge to Gordon's HTTP handler.
285. Dispatches TLS ClientHello traffic by SNI:
29   - matching TLS passthrough router -> raw TCP backend without TLS termination;
30   - no passthrough match -> normal Gordon HTTPS fallback.
316. Dispatches unknown non-HTTP/non-TLS bytes only to an explicit raw fallback router, after raw-fallback source policy allows it.
327. Refuses the connection when no deterministic handler or allowed fallback exists.
33
34Malformed HTTP-looking or TLS-looking traffic is rejected instead of bypassing to raw fallback.
35
36## Standalone and Network Services
37
38Standalone services are Gordon-managed containers that L4 routers can target with `service:<service>:<port-name>`. Define them under `[[services]]` when Gordon should own the container lifecycle.
39
40```toml
41[[services]]
42name = "rust"
43image = "registry.example.com:5000/rust:latest"
44enabled = true
45
46[[services.ports]]
47name = "game"
48container = 28015
49protocol = "udp"
50publish = "127.0.0.1:38015"
51
52[entrypoints.rust]
53address = "0.0.0.0:28015"
54protocol = "udp"
55
56[[traffic.udp.routers]]
57name = "rust-game"
58entrypoint = "rust"
59service = "service:rust:game"
60```
61
62Network services describe manually managed non-HTTP backends that L4 routers can target.
63
64```toml
65[[network_services]]
66name = "postgres"
67
68[[network_services.ports]]
69name = "db"
70container = 5432
71protocol = "tcp"
72```
73
74Service references use:
75
76- `route:<domain>` for app HTTP hosts from ACTIVE state (merged with installation external routes)
77- `external_route:<domain>` for configured external HTTP routes
78- `network_service:<service>:<port-name>` for manually managed TCP, UDP, and TLS passthrough backends
79- `service:<service>:<port-name>` for Gordon-managed standalone service backends
80
81`static:<name>` is reserved and currently unsupported.
82
83## TLS Passthrough on Smart TCP
84
85TLS passthrough routers run on `smart_tcp` or `tls_mux` entrypoints and route by ClientHello SNI without terminating TLS.
86
87```toml
88[entrypoints.edge]
89address = ":443"
90protocol = "smart_tcp"
91
92[[traffic.tls.routers]]
93name = "raw-tls"
94entrypoint = "edge"
95sni = "raw.example.com"
96service = "network_service:raw:tls"
97```
98
99Exact SNI matches win over wildcard matches. Ambiguous wildcard overlaps and HTTP-host/TLS-passthrough conflicts on the same smart TCP entrypoint are rejected at validation time. App HTTPS hosts that do not match a passthrough SNI use Gordon's normal HTTPS fallback and certificate selection.
100
101## Raw TCP Fallback
102
103Raw fallback is disabled by default. Unknown non-HTTP/non-TLS bytes reach a TCP backend only when the smart TCP entrypoint explicitly names one TCP router as `raw_fallback` and the source policy allows the peer socket IP.
104
105```toml
106[entrypoints.edge]
107address = ":443"
108protocol = "smart_tcp"
109raw_fallback = "ssh-fallback"
110raw_fallback_trusted_cidrs = ["100.64.0.0/10"]
111
112[[traffic.tcp.routers]]
113name = "ssh-fallback"
114entrypoint = "edge"
115service = "network_service:ssh:ssh"
116```
117
118Use `raw_fallback_trusted_cidrs` for private raw fallback exposure. To expose raw fallback publicly, acknowledge it explicitly:
119
120```toml
121[entrypoints.edge]
122address = ":443"
123protocol = "smart_tcp"
124raw_fallback = "public-raw"
125allow_public_raw_fallback = true
126```
127
128`trusted_cidrs` and `raw_fallback_trusted_cidrs` both use the peer socket IP. They do not use `X-Forwarded-For`.
129
130## HTTP Timeouts
131
132The HTTP servers behind `smart_tcp` and `tls_mux` entrypoints serve apps and the admin API. They do not use a fixed read or write timeout, so a download or upload that keeps moving data is never cut for taking too long. A connection is cut only after `transfer_idle_timeout` passes with no data in the direction being waited on.
133
134```toml
135[traffic.http]
136read_header_timeout = "5s"
137idle_timeout = "30s"
138transfer_idle_timeout = "2m"
139```
140
141| Option | Env | Default | Description |
142|--------|-----|---------|-------------|
143| `traffic.http.read_header_timeout` | `GORDON_TRAFFIC_HTTP_READ_HEADER_TIMEOUT` | `"5s"` | Maximum time to read request headers |
144| `traffic.http.idle_timeout` | `GORDON_TRAFFIC_HTTP_IDLE_TIMEOUT` | `"30s"` | Maximum keep-alive idle time between requests |
145| `traffic.http.transfer_idle_timeout` | `GORDON_TRAFFIC_HTTP_TRANSFER_IDLE_TIMEOUT` | `"2m"` | Inactivity timeout for request bodies and responses |
146
147Values are Go duration strings; malformed, zero, or negative values are rejected when the configuration loads. Servers pick up changes when the traffic graph is next applied (reload); connections already in flight keep their previous timeout.
148
149### Inactivity timeout
150
151Each request has an inactivity timer, armed when the request starts. Progress resets it: a response chunk accepted by the client connection (8 KiB granularity), a flush, or request body bytes read. If `transfer_idle_timeout` passes without progress, Gordon cancels the request context, which aborts the upstream request of a reverse-proxied app, and closes the client connection. When the handler returns, the final response flush and the drain of an unread request body are bounded by the same duration. Consequences:
152
153- a response that streams data at least once per `transfer_idle_timeout` can last arbitrarily long, and a client that keeps reading is never cut (it must accept 8 KiB per `transfer_idle_timeout`, about 68 B/s at the 2 minute default);
154- an upload whose body arrives at least once per `transfer_idle_timeout` can last arbitrarily long;
155- a backend that stays silent longer than `transfer_idle_timeout` before or between response chunks has its connection cut, so set it above the slowest expected backend response time;
156- the timer also fires when a handler or backend is silent and no write is pending;
157- WebSocket and other upgraded connections are not subject to it, and HTTP/2 streams are handled the same way as HTTP/1.
158
159`transfer_idle_timeout` does not apply to `[traffic.tcp]` or `[traffic.udp]` routers; those use their own `idle_timeout` (see below).
160
161## TCP Routers
162
163Plain TCP entrypoints support one TCP router per entrypoint. TCP routers on a `smart_tcp` entrypoint are valid only when referenced by that entrypoint's `raw_fallback`.
164
165```toml
166[entrypoints.postgres]
167address = "0.0.0.0:5432"
168protocol = "tcp"
169trusted_cidrs = ["100.64.0.0/10"]
170
171[traffic.tcp]
172dial_timeout = "10s"
173idle_timeout = "5m"
174drain_timeout = "30s"
175max_connections = 1024
176
177[[traffic.tcp.routers]]
178name = "postgres"
179entrypoint = "postgres"
180service = "network_service:postgres:db"
181```
182
183If `max_connections` is omitted or set to `0`, Gordon applies the safe runtime default of `1024` active connections per entrypoint.
184
185## UDP Routers
186
187UDP is not sniffed or unified into smart TCP. Use a separate UDP entrypoint:
188
189```toml
190[entrypoints.game]
191address = "0.0.0.0:7777"
192protocol = "udp"
193
194[traffic.udp]
195idle_timeout = "30s"
196drain_timeout = "30s"
197max_sessions = 4096
198
199[[traffic.udp.routers]]
200name = "game"
201entrypoint = "game"
202service = "network_service:game:game"
203```
204
205UDP sessions are keyed by client address and expire after `idle_timeout`. If `max_sessions` is omitted or set to `0`, Gordon applies the safe runtime default of `4096` active sessions per entrypoint.
206
207## Related
208
209- [Server Settings](./server.md)
210- [App Manifest](./apps.md)
211- [Standalone Services](./services.md)
212- [CLI traffic status](../cli/daemon.md#gordon-daemon-traffic)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close