All checks were successful
CI / Format (push) Successful in 41s
CI / CUDA type-check (push) Successful in 1m34s
CI / Clippy (push) Successful in 2m23s
CI / Test (push) Successful in 5m19s
CI / Build cortex SRPM (push) Has been skipped
CI / Build neuron SRPM (push) Has been skipped
CI / Publish cortex to COPR (push) Has been skipped
CI / Publish neuron to COPR (push) Has been skipped
CI / Bump version in source (push) Has been skipped
The router is a TLS client to cortexes; the router->cortex hop crosses the helexa->operator boundary carrying the client's bearer. This pins that hop to an enrolled cert. Trust mechanism (the open question): per-cortex enrolled trust anchor. Each [[cortexes]] entry gets an optional `tls_ca` — a PEM CA (or self-signed cert) the cortex's TLS cert must chain to. When set, the router builds a client that trusts ONLY that anchor (platform roots disabled), so the cortex must present the expected cert and a rogue endpoint with any other (even publicly-valid) cert is rejected at the handshake. Enrolment = the operator hands helexa the cortex's cert, referenced by path in router config. This is the natural model for self-hosted operators behind their own nginx/private CA, and reuses the reqwest public API (no custom rustls verifier, no new TLS backend). - `RouterState` now holds a per-cortex `reqwest::Client` map (`client_for`), replacing the single shared client; poller and dispatch use the per-cortex client. `build_client(tls_ca)` is the builder. - Fail closed: a `tls_ca` that can't load omits the cortex from the client map — it's never polled or routed to, rather than silently degrading to unpinned TLS. The poller treats a missing client (and a rejected handshake) as a failed poll, so #72's existing reachability debounce excludes it. Tests (`tls.rs`, 4): a live tokio-rustls HTTPS server proves a client enrolled with the server's cert is accepted (200) while clients pinned to a different cert — or using default roots — are rejected; the poller marks a wrong-cert cortex unreachable while a correctly-enrolled one is reachable; a missing pin file disables the cortex (fail closed); garbage PEM is rejected at build. Existing suites updated for the per-cortex client + new config field. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F6o3ddqmYNh9kzdwq6eowh
40 lines
1.6 KiB
TOML
40 lines
1.6 KiB
TOML
# helexa-router.example.toml — example configuration
|
|
#
|
|
# Copy to helexa-router.toml and adjust for your environment.
|
|
#
|
|
# Environment variable overrides use the HELEXA_ROUTER_ prefix with __
|
|
# separators:
|
|
# HELEXA_ROUTER_ROUTER__LISTEN=0.0.0.0:8088
|
|
|
|
[router]
|
|
# Plaintext listener. Operator/edge nginx terminates client TLS in front of
|
|
# the router — the router never owns an inbound TLS listener.
|
|
listen = "0.0.0.0:8088"
|
|
|
|
# How often (seconds) to refresh each cortex's health + /v1/models topology.
|
|
# poll_interval_secs = 10
|
|
|
|
# -- Downstream cortexes -------------------------------------------------
|
|
# Each [[cortexes]] entry is an operator-run cortex the router may dispatch
|
|
# to. The router forwards the client's bearer verbatim (auth stays at
|
|
# cortex) and routes on capacity (preferring matching `region`).
|
|
#
|
|
# Outbound TLS pinning (optional): set `tls_ca` to a PEM trust anchor that
|
|
# enrols this cortex — the CA (or self-signed cert) its TLS cert must chain
|
|
# to. The router then trusts ONLY that anchor for this cortex (platform
|
|
# roots disabled), so the router->cortex hop (which carries the client's
|
|
# bearer) reaches the cert you expect and a rogue endpoint presenting any
|
|
# other cert is rejected at the handshake. A cortex whose `tls_ca` fails to
|
|
# load is disabled (fail closed). Omit `tls_ca` for a publicly-trusted cert
|
|
# or plaintext http:// on a private (e.g. WireGuard) network.
|
|
|
|
# [[cortexes]]
|
|
# name = "lair-cafe"
|
|
# endpoint = "https://cortex.lair.cafe"
|
|
# region = "eu-west"
|
|
# tls_ca = "/etc/helexa-router/pins/lair-cafe.pem"
|
|
|
|
# [[cortexes]]
|
|
# name = "example-operator"
|
|
# endpoint = "https://cortex.example.com"
|