Gateway Operations
Bootstrap from zero
Section titled “Bootstrap from zero”Local HTTP-only loop:
gateway.svc config generate quickstart > .gateway.yamlgateway.svc config generate spa-http -d config/mkdir -p public && echo '<h1>hello</h1>' > public/index.htmlgateway.svc -f .gateway.yaml -L debugcurl http://localhost:8443/Production: start from the bootstrap profile (port 443, zerotrust: true, cert flags), keep routing in config.router.dir or on config.svc.
Run flags: -f/--config, -p/--port, -l/--log, -L/--loglevel, --sid, --datacenter, --cluster, --zerotrust, --svccert/--svckey/--rootca, --jwt_public_key, --config_url/--config_apikey, --servicediscovery_url/--servicediscovery_apikey.
The platform supervisor manages it as service gateway with default state up.
Hot reload
Section titled “Hot reload”Routing config reloads without restart, from either source:
- Local dir: recursive filesystem watch; any write/create/remove/rename triggers a debounced reload.
- Remote: config.svc pushes over SSE.
Reload semantics:
- The update is overlaid onto the last-known-good config, so a routing-only push can never wipe listener/boot keys.
- On reload failure the previous config is re-applied; if that fails too, the process exits: the supervisor restarts it clean.
- In-flight requests finish on the old configuration.
- Static router config cannot change at runtime: entrypoints, ACME resolvers, and provider settings apply at boot only: changing them requires a restart. Everything in the routing plane (domainmaps, backends, endpoints, routes, staticpaths, LB profiles, passthrough dynamic config) is hot.
Backend server lists additionally update live via the service-discovery watcher (default 5s poll) without any config push.
Observability
Section titled “Observability”- Metrics listener (
kisaimetrics, default127.0.0.1:8088): Prometheus/metricsand Go pprof/debug/pprof/*. There is no health endpoint on the data plane: probe a route or the metrics listener. - Logs:
log: stdoutor fluentdhost:port.loglevelcontrols the gateway;router.loglevelindependently dials the embedded router’s verbosity. - Access logs / tracking: an anonymous-tracker cookie and access-log events publish asynchronously (fire-and-forget) to configured endpoints; disabled on internal traffic; non-fatal when unset.
- Dashboard: expose the router dashboard only through a normal,
kisai-auth-gated route (dashboardexample), the insecure API mode is a protected key and rejected at boot.
Knowing what you are running
Section titled “Knowing what you are running”gateway.svc versiongateway 2.0.0 …traefik (vendored fork): v3.7.13 (fc92cc118a05)The second line is the upstream release the embedded router was built from, with its pinned commit. Check it when triaging anything that looks like proxy behaviour rather than routing behaviour, and when an upstream advisory names a version range.
Routing warnings at boot and reload
Section titled “Routing warnings at boot and reload”The expensive failure in a gateway is the quiet one: the config parses, the process starts, and a route is simply not there. An advisory pass at boot and on every reload names the shapes that load without routing anything.
| Warning | What it means |
|---|---|
| A domainmap, endpoint or staticpath is bound by no route | It was declared and nothing references it |
| One local staticpath is bound by two routes | The file server starts once per route, so the two contend for the same port |
| Two routes claim the same entrypoint, host and path | The router picks by priority, and the loser never sees a request |
These are warnings rather than errors. Each shape is legal and some are deliberate, so the reload carries on; read them as a list of things to confirm you meant.
Observability does not gate the data plane
Section titled “Observability does not gate the data plane”A metrics listener that fails to start, or that is slow to stop during a reload, is logged and the
gateway keeps routing. A taken metrics port or a bad kisaimetrics: block does not stop the boot,
and a slow shutdown of that listener does not fail a reload.
Failure modes
Section titled “Failure modes”| Symptom | Cause / action |
|---|---|
| Every request on a domain → 400 | Domain missing from all domainmaps: or domains: written as a scalar instead of a list (caught with a targeted error at load) |
| Nothing matches behind another proxy | The fronting proxy sends its own Host (e.g. localhost); add its IP to the entrypoint’s trustedips so X-Forwarded-Host is honored |
| Boot fails parsing routing files | They’re in config.path instead of config.router.dir |
| Reload rejected | Duplicate names / unresolved references / protected passthrough paths / unknown certresolver. The error names the offender; last-known-good stays live |
| Browser shows a self-signed cert | ACME issuance failed: check the certresolver is referenced by the route, ACME logs, :80 reachability for HTTP-01, and that acme.json is writable |
| Boot error “internal.port required” | Legacy listener mode needs internal.port, or switch to an explicit entrypoints: list |
| TCP/UDP routes skip tenant headers | L4 routes bypass the HTTP middleware chain by design. No CEPT attribution |
Every request on a route answers 503 with rate_limit_unavailable | The rate-limit store is unreachable and that profile is set to on_store_failure: deny. See Rate limiting |