Architecture¶
Single binary, three listeners¶
grex intentionally uses three TCP listeners so each surface has its own network exposure and (eventually) auth boundary:
┌────────────────────────────────────────────────────────────┐
│ grex │
│ ┌─────────────┐ ┌──────────────┐ ┌───────────────────┐ │
│ │ OpAMP :4320 │ │ UI :8080 │ │ Telemetry :9090 │ │
│ │ /v1/opamp │ │ UI + /api/* │ │ /healthz /readyz │ │
│ │ TLS / mTLS │ │ (open today) │ │ /metrics │ │
│ └──────┬──────┘ └──────┬───────┘ │ /metrics/fleet │ │
│ │ │ │ /debug/pprof? │ │
│ ▼ ▼ └───────────────────┘ │
│ in-memory fleet.Registry │
└────────────────────────────────────────────────────────────┘
Wiring is centralized in cmd/grex/main.go:
- Load
config - Create two Prometheus registries (server vs fleet)
- Construct
fleet.Registrywith heartbeat settings + metrics events - Attach
opamp.Handlerto the registry - Mount
api+uion one HTTP mux server.New(...).Start()for all listeners- Run registry background loop; wait for signal or fatal listener error
Data flow¶
Collector / gateway
│ OpAMP AgentToServer
▼
opamp.Handler ──writes──► fleet.Registry
│
┌───────────────┼────────────────┐
▼ ▼ ▼
api.Handler ui.Handler metrics.FleetCollector
(JSON) (HTML) (scrape-time gauges)
- Writes happen on OpAMP callbacks (and registry eviction ticks)
- Reads happen on API/UI handlers and Prometheus collect
- Registry is concurrency-safe; metrics event hooks must not re-enter the
registry while it holds locks (documented on
fleet.Events)
In-memory state, with an opt-in durability layer underneath¶
fleet.Registry is still the sole source of truth for every live read
path (API, UI, metrics). Correctness after restart depends on:
- Agents reconnecting (direct), or
- Gateways continuing to relay check-ins while grex requests full state
A write-only, opt-in Postgres layer now exists under the registry
(internal/persistence) — durable, but nothing reads from it yet, and it
does no good on its own without a second grex process to reconcile
against. replicaCount is still pinned to 1 in the Helm chart; horizontal
scale of grex itself isn't built. See Persistence for
the write path and the safeguards already in place for when it is.
Library boundaries¶
| Concern | Library / approach |
|---|---|
| OpAMP protocol | open-telemetry/opamp-go server packages |
| Metrics | prometheus/client_golang |
| Config | gopkg.in/yaml.v3 |
| UI | stdlib html/template, vendored htmx, hand-written CSS, go:embed |
| Logging | stdlib log/slog to stderr |
grex does not fork OpAMP; it implements callbacks and the gateway custom capability on top of opamp-go.
Auth boundaries (intended)¶
Design assigns:
- Collectors → OpAMP mTLS (partially implemented)
- Humans/API → UI mTLS then OIDC (issue #11)
- Scrapers → telemetry network policy (no app-level auth)
Today only the OpAMP TLS path is real. See Admin: authentication.