* feat(edge-registry): ADR-102 — surface Cognitum cog catalog via /api/v1/edge/registry
Adds a new sensing-server endpoint that fetches and caches the canonical
Cognitum app registry at
https://storage.googleapis.com/cognitum-apps/app-registry.json (105 cogs
across 11 categories as of v2.1.0). RuView previously had no live
awareness of the catalog — the README's capability table was hand-
curated and went stale as Cognitum shipped new cogs (the registry was
last updated 6 days ago).
ADR:
* docs/adr/ADR-102-edge-module-registry.md — full design, response
shape, configuration flags, failure modes, and a 12-row security
review covering SSRF, response inflation, ?refresh abuse, stale-serve
semantics, TLS, cache poisoning, JSON-panic resistance, etc.
Code:
* v2/.../edge_registry.rs — EdgeRegistry struct + UreqFetcher +
MockFetcher trait + 7 unit tests. RwLock<Option<CachedEntry>> with
stale-on-error fallback. MAX_PAYLOAD_BYTES=8 MiB, 10s wire timeout.
* v2/.../main.rs — constructs Option<Arc<EdgeRegistry>> at startup,
registers GET /api/v1/edge/registry handler, wires Extension layer.
Handler runs the blocking ureq fetch via tokio::task::spawn_blocking
so the async runtime stays free.
* v2/.../cli.rs / main.rs Args — three new flags (per user request to
"allow the registry to be disabled or changed"):
--edge-registry-url <URL> (env RUVIEW_EDGE_REGISTRY_URL)
--edge-registry-ttl-secs <N> (env RUVIEW_EDGE_REGISTRY_TTL_SECS)
--no-edge-registry (env RUVIEW_NO_EDGE_REGISTRY)
When --no-edge-registry is set or the URL is empty, the endpoint
returns 404.
Cargo.toml: adds ureq (rustls), sha2, thiserror as direct deps.
README:
* New collapsed "🧩 Edge Module Catalog" section with the full 105-cog
table generated from the registry, grouped by category with practical
one-line descriptions (e.g. "Spots irregular heartbeats and abnormal
heart rhythms", "Detects walking problems and scores fall risk").
Links to https://seed.cognitum.one/store and the local appliance
/cogs page. Sits between the HF model section and How It Works.
Tests (7/7 pass):
first_call_hits_upstream_and_caches
ttl_expiry_triggers_refetch
force_refresh_bypasses_fresh_cache
stale_serve_on_upstream_failure_after_cached_success
no_cache_no_upstream_returns_error
upstream_invalid_json_is_treated_as_error
upstream_sha256_is_deterministic
Security highlights (full review in ADR-102 §"Security review"):
- The registry is metadata-only; per-cog binary signatures (ADR-100)
remain the trust root for installs. A compromised registry can
mislead a human reader but cannot ship malicious binaries.
- 8 MiB cap + 10s timeout + Option<Arc<...>> via Extension layer means
the endpoint can't be used to exhaust memory or pin tokio threads.
- Stale-on-error responses carry an explicit `stale: true` field so
upstream outages are visible to consumers rather than silently
masked.
- Endpoint sits behind the existing RUVIEW_API_TOKEN bearer gate when
set, otherwise unauthenticated (registry contents are public anyway).
* chore: refresh Cargo.lock for ureq/sha2/thiserror deps added by ADR-102