Wingsmith
Configuration

Gateway Configuration

All HANGAR_GATEWAY_* variables, listeners, limits, timeouts, shutdown, tunnel config.

The gateway is configured entirely through environment variables and a few command-line flags. Only the plane URL and service token are required.

Required

VariableDescription
-plane-url / HANGAR_PLANE_URLThe plane’s base URL
HANGAR_GATEWAY_SERVICE_TOKENThe gateway’s service credential for the plane. Accepted on GET /v1/endpoints/resolved, GET /v1/gateway/tenants, and POST /v1/clusters/tunnel-auth only.

Listeners

VariableDefaultDescription
-listen:8080Internal listener address (no auth; trust is the network boundary)
HANGAR_GATEWAY_PUBLIC_LISTENunset (off)Address for the public, tenant-identified listener
HANGAR_GATEWAY_TUNNEL_LISTEN:8443Where cluster agents dial; off to disable
HANGAR_GATEWAY_METRICS_LISTEN:9090Where /metrics is served; off to disable

Internal vs public listeners

  • Internal listener (-listen): authenticates nobody. Reaching it is the proof. A tenant behind it may declare auth: none, and a caller there can reach every enabled tenant’s endpoints. Needs a NetworkPolicy to restrict access.
  • Public listener (HANGAR_GATEWAY_PUBLIC_LISTEN): sits behind a *.hangar.example.com wildcard. A request is identified by its Host header, checked against a key when the tenant declares auth: api_key, and scoped to that tenant’s own catalog and routing.

A disabled tenant is refused on both listeners. The gateway checks the tenant table itself instead of trusting the routing table.

Resync and coordination

VariableDefaultDescription
-resync30sHow often to re-read the routing table and tenant table
-gateways / HANGAR_GATEWAY_REPLICAS1 (warns)How many gateway replicas are running; must equal the StatefulSet’s replicas
-gateway-id / HANGAR_GATEWAY_IDhostnameThis replica’s identity, reported to agents

HANGAR_GATEWAY_REPLICAS must match the StatefulSet’s replicas. Set too low, some replicas hold no tunnels and answer 503 no_capacity. Set too high, agents keep redialling with no harm but wasted dials.

Limits

VariableDefaultDescription
HANGAR_GATEWAY_TENANT_MAX_IN_FLIGHT1024Requests one tenant may have in flight on one replica; 0 for no cap
HANGAR_GATEWAY_TENANT_RPSunsetSustained requests per second one tenant may send to one replica; unset means no rate limit
HANGAR_GATEWAY_TENANT_BURSTunsetBurst above the rate limit
HANGAR_GATEWAY_MAX_BODY_BYTES100 MiBLargest request body accepted; larger ones are 413
HANGAR_GATEWAY_MAX_BUFFERED_BYTES1000 MiBRequest-body memory held at once across the replica; 0 for no bound. A request that would pass it is 503 no_capacity.

Limits are per tenant, not per key. Every key belongs to one tenant, and a tenant’s keys share its allowance. Limits are per replica, so the fleet-wide figure is the per-replica value times the replica count.

On the internal listener, an operator’s edge is usually one tenant carrying every customer, so size the in-flight cap to the edge’s own concurrency, or set it to 0.

Timeouts

VariableDefaultDescription
HANGAR_GATEWAY_READ_HEADER_TIMEOUT10sHow long a caller may take to send its headers
HANGAR_GATEWAY_BODY_READ_TIMEOUT2mHow long a caller may take to send its body once the headers are in; a stalled body is 408
HANGAR_GATEWAY_WRITE_IDLE_TIMEOUT2mHow long one write to the caller may block. Per write, so a long stream that keeps arriving is never cut; a caller that stops reading is.

Shutdown

VariableDefaultDescription
HANGAR_GATEWAY_SHUTDOWN_GRACE30sThe pod’s terminationGracePeriodSeconds; the drain phases are cut from it
HANGAR_GATEWAY_DRAIN_DELAY5sHow long to wait while endpoint removal reaches load balancers
HANGAR_GATEWAY_DRAIN_TIMEOUT16sHow long to give internal and public listeners to finish serving
HANGAR_GATEWAY_TUNNEL_DRAIN_TIMEOUT5sHow long to give the tunnel listener to drain after the request listeners

The phases are cut from HANGAR_GATEWAY_SHUTDOWN_GRACE, which must equal the pod’s terminationGracePeriodSeconds. At the default 30 seconds: 5s delay + 16s request drain + 5s tunnel drain + 1s metrics + 3s unspent before kubelet’s SIGKILL.

That budget bounds how long a generation can survive a rollout. To carry longer generations through one, raise terminationGracePeriodSeconds and HANGAR_GATEWAY_SHUTDOWN_GRACE together; the extra time goes to the request listeners.

Tunnel TLS

VariableDescription
-tunnel-cert / HANGAR_GATEWAY_TUNNEL_TLS_CERTPEM certificate for the tunnel listener
-tunnel-key / HANGAR_GATEWAY_TUNNEL_TLS_KEYPEM key for the tunnel listener

Neither set means plaintext, which requires TLS termination in front of the listener.

Tunnel listener limits

Every value has a default in libs/packages/tunnel. Set one only to change it.

VariableDefaultDescription
HANGAR_GATEWAY_TUNNEL_MAX_PER_CLUSTER16Tunnels one cluster may hold on a replica
HANGAR_GATEWAY_TUNNEL_MAX1024Tunnels a replica may hold across every cluster
HANGAR_GATEWAY_TUNNEL_MAX_HANDSHAKES128Dials mid-handshake at once
HANGAR_GATEWAY_TUNNEL_MAX_HANDSHAKES_PER_SOURCE32Of those, from one address (IPv6 by /64); off disables
HANGAR_GATEWAY_TUNNEL_SOURCE_FAILURES10Failed dials one address may make in a minute before refused as throttled; off disables
HANGAR_GATEWAY_TUNNEL_CACHE_TTL1mHow long a verified token is reused without asking the plane
HANGAR_GATEWAY_TUNNEL_CACHE_GRACE15mHow long it survives a plane that cannot be reached
HANGAR_GATEWAY_TUNNEL_REVERIFY_INTERVAL1mHow often an open tunnel’s token is checked again (cannot be turned off)
HANGAR_GATEWAY_TUNNEL_BODY_IDLE_TIMEOUT2mA tunnelled response body nobody reads for this long is closed; off disables

The per-source limits key on the dial’s address. Behind a Service or load balancer that rewrites it (externalTrafficPolicy: Cluster), every agent shares a few node addresses. Keep the client address (externalTrafficPolicy: Local) or set both per-source limits to off.

Remote media

VariableDefaultDescription
HANGAR_GATEWAY_MEDIA_URL_HOSTSunsetComma-separated hosts (media.example.com, *.cdn.example.net) a request’s remote media may come from. Unset allows public hosts and refuses internal ones.

A multimodal request can name its image, video, or audio by URL, and the engine fetches it from inside the serving cluster. The gateway checks every remote media URL before the request leaves this hop.

  • By default (unset): a URL must be http or https and must not name a private or cluster-internal host (loopback, private, link-local, CGNAT, unspecified addresses, numeric host forms, single-label names, .local, .localhost, .internal, .svc, .cluster.local, .lan, .home.arpa). Every public host is allowed.
  • With HANGAR_GATEWAY_MEDIA_URL_HOSTS: a URL must name a host on that list, and nothing else passes.

The check is by name, so a public name that resolves to a private address gets through. It does not replace the engine’s own control or an egress NetworkPolicy. Use all three where tenants can send media URLs.

Credentials and authentication

VariableDescription
-credentials / HANGAR_GATEWAY_CREDENTIALSJSON file of {"<credential id>": {"<header>": "<value>"}}
-probe-token / HANGAR_GATEWAY_PROBE_TOKENWhat the conformance runner presents to mark its traffic as a probe; a comma-separated pair while rotating
HANGAR_GATEWAY_PUBLIC_ALLOW_NO_AUTHtrue serves a public tenant declaring auth: none; off by default, so such a tenant gets 401

The credential file is how a reference on a cluster’s serving address becomes headers on the wire. The plane stores the reference and never the secret.

Logging and debugging

VariableDefaultDescription
HANGAR_GATEWAY_REDACT_NAMESunsetComma-separated names your own infrastructure goes by, redacted from quoted engine messages on top of built-in stack names
-log-upstream-bodies / HANGAR_GATEWAY_LOG_UPSTREAM_BODIESoffWrite a discarded upstream error body to the log whole. Off in production and must stay off: an engine’s error quotes the request it refused.

-log-upstream-bodies is a debugging switch for a cluster carrying no customer traffic. It warns on every start while on.