Service Discovery & Edge Routing — runtime endpoint registry in Koder DNS, consumed by Koder Jet; Koder Gate reserved for public product APIs

draft

Status

Draft — ratifies the /k-arch deliberation of 2026-06-08 (owner-approved via /k-go). This RFC fills a decision slot that until now existed only implicitly (id-RFC-008-api-gateway is the ID service's internal gateway, a different scope; there was no Stack-wide service-discovery decision on record).

Problem

The Koder Cloud has no runtime source of truth for where a service lives. Today a component finds another by one of three brittle means:

  1. Hardcoded URLs (*.koder.dev) baked into binaries and apps.
  2. Hardcoded container IPs / env vars (10.0.1.128:7800, KODER_NOTIFY_URL=…).
  3. A hand-edited static map: meta/docs/stack/registries/koder-service-ports.toml

    (the port registry) + infra/net/jet/sites.toml (205 vhosts, edited by hand, incus file push + reload).

When the topology moves — every migration does (s.r1 → s.forge → s.khost1) — the hand-maintained routing drifts, and consumers break with no warning.

Concrete incident (the motivation). voice.koder.dev (the Dek app's shipped transcription backend) accumulated three layers of drift across migrations: a dev auth-gate swept it behind a browser-only approval (→ every mobile transcription 403, DEK-AUT-003), and the jet upstream target drifted across 177.93.107.3:7779 / 10.0.1.126:7803 / 10.0.1.128:7803 while the app hardcoded voice.koder.dev. Each was a manual config edit with no registry to reconcile against. A runtime registry + dynamic, health-aware routing makes "move a service" a re-registration, not a hand-edit-and-pray.

This is the root cause (architecture-quality D12 — fix the cause, not the symptom): the absence of a runtime endpoint source of truth + hand-edited routing.

Decision

Adopt the separation of two concerns that the current stack conflates:

Concern Plane Owner (REUSE existing, do not add a server)
Service discovery — "where is service X right now?" control-plane (source of truth) Koder DNS (infra/net/dns) — already ships an SRV-based service registry (internal/discovery/registry.go)
Edge routing — TLS, proxy, auth, cache, rate-limit data-plane Koder Jet (infra/net/jet) — already the official, production edge (205 vhosts)
Registration + health contract control-plane contract Koder Mesh (infra/net/mesh, #002) — the registerheartbeathealth schema both sides honour

Koder Gate (infra/net/gate) is explicitly NOT the internal router. It is a complete, well-designed but experimental, never-deployed API gateway. Deploying it as the internal data-plane would stand up a second edge overlapping Jet (TLSproxycache/auth duplicated) — an anti-pattern under reuse-first (D10) and a boundary violation (D2). Gate is reserved for its real use case: a public, product-grade API gateway (third-party API keys, quotas, monetization, developer portal) — a future need, not internal discovery.

Why this over the alternatives (the /k-arch rubric)

  • vs. "deploy Koder Gate as central gateway" (rejected): duplicates Jet's edge

    (D2