Serving & writing a UI client — guide
Class:
guide· Function:serve. Architecture:serve-design.md. Two how-tos: (A) drive the two doors from a client, (B) build a UI over gRPC.
A · Hit the two doors
Native HTTP — point the stock tool at the gateway, unmodified:
# the sparse index advertises the public download URL (file-rust)
curl -s http://localhost:8081/rust-prod/index/config.json
# → {"dl":"https://holger.example.com/rust-prod/crates","api":null}
# per-crate index is text/plain NDJSON with real SHA-256 cksums
curl -si http://localhost:8081/rust-prod/index/se/rd/serde | head
# health
curl http://localhost:8081/healthz # → ok
gRPC — the holger-native surface (holger.v1). Regenerate stubs after editing the proto (never at build time, never needs protoc):
cargo xtask gen-proto # rewrites server/lib/src/generated/holger.v1.rs, idempotent
B · Write a UI client over gRPC
Source pointers: proto/holger.proto (contract), server/lib/src/grpc.rs (service impl), server/lib/src/object.rs (RemoteHolger, the reference tonic client), server/lib/src/auth.rs (auth surface). A UI should use gRPC, not the native-HTTP door (that door exists for stock package managers, not for you to reimplement).
The canonical pattern is nornir's own viz (nornir/src/viz/remote.rs): an egui app that loads everything over a tonic channel, one blocking call per click, a bearer token stamped by an interceptor. holger's UI is the same shape.
Which RPC backs which view
| view | RPC | notes |
| browse / list | RepositoryService::ListArtifacts |
name_filter substring; page_token cursor; rows are ArtifactEntry |
| open / download | FetchArtifact |
whole blob; NOT_FOUND if absent |
| download w/ progress | StreamArtifact |
64 KB ArtifactChunks → drive a progress bar |
| push | PutArtifact |
PERMISSION_DENIED on a read-only repo or a failed RBAC check |
Connect + authenticate
Open a tonic channel to :50051, add a request interceptor that stamps the bearer token (OIDC) — mirror RemoteHolger and the nornir viz interceptor. For mTLS, present the client cert configured against holger's client_ca (the chain is verified once at the TLS handshake, and against ron_tls.crl when the endpoint names one: a revoked certificate fails the handshake; see auth-guide). Then each view is one RPC call as above.
The operator UI (package + bin holger-server-ui)
server/ui is an egui app built from facett components + theme, connecting over gRPC. Build with --features gui. It is driven headlessly by a real robot-UI test (server/ui/tests/robot_ui_app.rs, egui_kittest, feeding the nornir test matrix) — see testing-guide. Don't hard-code colours; pull from the facett theme so every pane reads as one product.