# Nyro Gateway (Go) Go implementation of the Nyro AI protocol gateway. It is being developed alongside the Rust implementation, which remains available until the Go cutover is complete. > **Decision record — why not Ollama as a base?** Ollama was evaluated and > **rejected**. It is a *local inference engine* (≈90% of its code is inference > — scheduler, llama.cpp subprocess, GGUF/model management), structurally the > inverse of a *gateway*. It lacks Gemini, multi-upstream routing, per-request > key selection, the admin/control plane, the trusted request pipeline > framework, OAuth-for-upstream-credentials, and multi-backend storage. We > therefore **port Nyro's own Rust architecture** and use Ollama's > `openai/` + `anthropic/` converters only as a *wire-format reference* > (streaming SSE state machines, tool-call normalization, thinking blocks). > Gemini has no Ollama equivalent and is ported from Nyro's Rust codec. ## Layout | Path | Responsibility | |---|---| | `internal/kernel/` | workload-neutral lifecycle graph, atomic typed generations, request leases, readiness, and status | | `internal/llm/` | Canonical LLM IR for chat and embedding workloads | | `internal/llm/ingress/http/` | LLM HTTP routing, ingress decoding, response encoding, and streaming sink | | `internal/llm/pipeline/` | fixed trusted phase order, terminal delivery hook, and reverse finalizers | | `internal/llm/protocol/` | endpoint identity, ingress/egress codec contracts, and immutable protocol catalog | | `internal/llm/provider/` | immutable provider catalog, transport-neutral drivers, definitions, and generic fallback | | `internal/llm/provider/httptransport/` | outbound HTTP transport, proxy handling, and connection policy | | `internal/llm/routing/` | weighted, priority, cooldown, and latency target selection plus health state | | `internal/llm/runtime/` | trusted LLM orchestration, retry/failover, errors, streaming, and client delivery | | `internal/transport/httpserver/` | generic process-level HTTP server, health/readiness, recovery, and shutdown | | `internal/config/` | standalone YAML parsing, defaults, validation, and Snapshot construction | | `internal/config/snapshot/` | immutable runtime configuration and deterministic fingerprinting | | `internal/configsync/` | authenticated full-Snapshot distribution and hot-update source | | `internal/security/` | transport-neutral authentication and authorization contracts | | `internal/quota/` | backend-neutral request/token windows and concurrency leases | | `internal/telemetry/` | logs, metrics, traces, and request-pipeline observation | | `internal/storage/` | control-plane storage contracts, migrations, and database/memory implementations | | `internal/admin/` | management API for keys, quotas, models, routes, providers, settings, and observability | | `internal/bootstrap/` | explicit catalogs, runtime candidate construction, reconciliation, and process wiring | | `nyro.go`, `cmd/` | unified CLI: `nyro proxy`, `nyro serve`, and operator tools | The implemented data-plane architecture is described in [docs/design/architecture.md](docs/design/architecture.md). Nyro-owned runtime infrastructure lives under `internal/platform/`: | Path | Responsibility | |---|---| | `internal/platform/database/sqlite/` | caller-owned, pure-Go SQLite connection policy; no application schema or migration ownership | | `internal/platform/state/` | binary-safe String/TTL semantic API, SQLite persistence, and a limited RESP2/RESP3 Redis-compatible server | | `internal/platform/observe/` | lossless OTLP batch persistence, indexed SQLite queries, retention, and an OTLP/HTTP protobuf receiver | State and Observe remain separate internal platform packages assembled by `nyro serve`; they use dedicated `state.db` and `observe.db` files when embedded. ## Library mapping (Rust → Go) | Rust | Go | Notes | |---|---|---| | axum | `net/http` + **Chi** (`github.com/go-chi/chi/v5`) | generic server infrastructure plus explicitly mounted LLM, Admin, and WebUI handlers | | tokio | goroutines + channels | native | | reqwest | `net/http` | | | sqlx (sqlite/pg/mysql) | **GORM** (`gorm.io/gorm`) + sqlite/postgres drivers | Go models and explicit migration logic are the schema sources of truth; automatic migration is opt-in | | serde | `encoding/json` + struct tags | | | tracing | `log/slog` | stdlib | | `inventory` | explicit constructors + immutable catalogs | Protocol codecs and provider drivers are enumerated by Bootstrap; imports have no Nyro registration side effects | **SQLite without CGO:** GORM's default sqlite driver (`gorm.io/driver/sqlite`) pulls `mattn/go-sqlite3` (CGO). To keep the build pure-Go (no C toolchain, clean cross-compile), use `github.com/glebarez/sqlite` — a GORM driver backed by `modernc.org/sqlite`. Reusable infra modules use the same pure-Go `modernc.org/sqlite` driver directly through `database/sql` and do not depend on GORM. ## Build & run ```bash go build ./... go run . serve --auto-migrate # everything: control plane + embedded data plane go test ./... go vet ./... ``` `nyro serve` alone is a complete, usable nyro: the management API and WebUI on `127.0.0.1:19531`, an embedded data plane on `127.0.0.1:19530`, a Redis-compatible State Engine on `127.0.0.1:16379`, and an OTLP/HTTP Observe Engine on `127.0.0.1:14318`. Local databases default to `~/.nyro/data/{config,state,observe}.db`. The embedded data plane subscribes to config over an in-process pipe — the same ConfigSync client used by a remote node feeds snapshots through the Reconciler, Kernel Host generation, and trusted LLM Runtime. This preserves one activation path without opening a config-sync TCP port for the embedded data plane. The two roles can also be split, for horizontal scaling or to keep the node holding credentials out of the traffic path: ```bash go run . serve --disable-proxy --sync-listen 127.0.0.1:19532 # control plane only go run . proxy --server 127.0.0.1:19532 # extra data plane node go run . proxy --config ./nyro.yaml # standalone: no server, no DB ``` Quota State defaults to process-local memory. To share request, token, and concurrency quota state across gateway replicas, configure the same reachable Redis URL on every node (directly in standalone YAML or through config-sync): ```yaml settings: state: type: redis url: "${NYRO_REDIS_URL}" ``` Only `redis://` URLs are currently supported; they may include username, password, and database. Because the connection is plaintext, use loopback, a trusted private network, or an independently encrypted tunnel. External Redis must be version 7.0 or newer. Startup probes the request, token, and concurrency quota paths before installing the backend; the Redis account must permit the commands documented in the standalone configuration reference. An unavailable or incapable standalone backend fails startup. A failed ConfigSync hot update is rejected while the last-known-good backend continues serving; later pushed snapshots remain eligible for activation. Request quotas are admitted atomically across replicas that share Redis. A clean `429` denial does not consume quota, while an admitted request counts even if its upstream exchange fails. Token quotas remain response-settled, so concurrent requests can exceed a strict token boundary before usage arrives. Extreme Redis `WATCH` contention can fail one request closed with `503` without marking State unhealthy. The embedded Redis-compatible listener is separately controlled by `nyro serve` flags and is intentionally limited to Nyro's State needs. See [standalone configuration](docs/schema/config.md) and [embedded infrastructure databases](docs/schema/database.md). Off-host config-sync needs a join token (`--sync-token`) or mTLS — nyro refuses to serve upstream credentials over an unauthenticated plaintext network port. See [config-sync transport and mTLS](docs/security/config-sync-mtls.md). ## Go WebUI The Go admin control plane has its own management console at `go/webui/`, targeting the Go admin API schema directly (upstreams, routes, consumers, settings, logs, stats). It is a separate app from the root `webui/` (Rust implementation, kept running in parallel until cutover — see `go/docs/cutover.md`). **Dev workflow** — run the frontend against a live admin API with hot reload: ```bash cd go/webui npm install npm run lint npm run build cd .. go run . serve --webui-dir ./webui/dist ``` **Release-embedding workflow** — bake the built assets into the `nyro` binary so no external `--webui-dir` is needed at deploy time: ```bash cd go/webui && pnpm install && pnpm run build # produces go/webui/dist cd .. rm -rf internal/webui/dist mkdir -p internal/webui/dist cp -R webui/dist/. internal/webui/dist/ go build -tags webui_embed -o bin/nyro . ``` The embed build tag is `webui_embed` (see `internal/webui/embed_enabled.go` and `internal/webui/embed_disabled.go`). The equivalent `make` targets are `go-webui-build`, `go-webui-embed-assets`, `go-webui-embed-build`, and `go-webui-embed-run` (see the repo-root `Makefile`).