Context
Follow-up from the UI/UX overhaul (#293). The relay list in ConnectionCards.kt (RelaysCard) currently shows relay URLs as plain text with no per-relay status. Amber shows color-coded relay health (e.g. Reliable / Slow / Unknown). We want a per-relay status indicator (a colored dot + label: connected / connecting / error/offline, and ideally latency or last-seen).
The UI overhaul deliberately kept this out of scope: it was presentation-only, and relay health needs new data, not just new pixels. The shared components from #293 (the KeepListRow leading-slot + the KeepSuccess/KeepError/KeepMuted color tokens) are the rendering primitives this will use.
Architecture constraint (RMP — we rely on ../keep)
Per the RMP architecture bible (rmp-architecture-bible.md, §1.2/§1.5): networking, WebSocket connections, and relay management are owned by the Rust core (../keep), not the native layer. The golden rule is that native must not own business logic.
That means:
- ❌ Do NOT compute relay health in Kotlin (no probing/pinging relays from the Android layer, no native success-rate tracking). That would put networking + derived business state on the native side, violating the boundary and creating logic that diverges from the core.
- ✅ Do expose per-relay health from the keep Rust core via the FFI as state fields. The Android layer only renders what the core provides.
Concretely this is a cross-repo change:
../keep (Rust core): extend the live state to carry per-relay health. Today KeepLiveState exposes an aggregate connectionStatus: ConnectionStatus and peers: List<PeerInfo>. Add a per-relay record (e.g. RelayHealth { url, status, last_connected_ms, rtt_ms? }) computed in the core's relay manager and surfaced on KeepLiveState (or via a getRelayHealth() FFI call), pushed through the existing KeepStateCallback reconciler.
- Regenerate the UniFFI bindings (
keep_mobile.kt) and bump keep.version.
- keep-android (this repo): render the health in
RelaysCard using a status dot in the KeepListRow leading slot, colored from the existing tokens. Pure presentation — map the core's status enum to a color/label, nothing more.
Acceptance criteria
Notes
Context
Follow-up from the UI/UX overhaul (#293). The relay list in
ConnectionCards.kt(RelaysCard) currently shows relay URLs as plain text with no per-relay status. Amber shows color-coded relay health (e.g. Reliable / Slow / Unknown). We want a per-relay status indicator (a colored dot + label: connected / connecting / error/offline, and ideally latency or last-seen).The UI overhaul deliberately kept this out of scope: it was presentation-only, and relay health needs new data, not just new pixels. The shared components from #293 (the
KeepListRowleading-slot + theKeepSuccess/KeepError/KeepMutedcolor tokens) are the rendering primitives this will use.Architecture constraint (RMP — we rely on
../keep)Per the RMP architecture bible (
rmp-architecture-bible.md, §1.2/§1.5): networking, WebSocket connections, and relay management are owned by the Rust core (../keep), not the native layer. The golden rule is that native must not own business logic.That means:
Concretely this is a cross-repo change:
../keep(Rust core): extend the live state to carry per-relay health. TodayKeepLiveStateexposes an aggregateconnectionStatus: ConnectionStatusandpeers: List<PeerInfo>. Add a per-relay record (e.g.RelayHealth { url, status, last_connected_ms, rtt_ms? }) computed in the core's relay manager and surfaced onKeepLiveState(or via agetRelayHealth()FFI call), pushed through the existingKeepStateCallbackreconciler.keep_mobile.kt) and bumpkeep.version.RelaysCardusing a status dot in theKeepListRowleading slot, colored from the existing tokens. Pure presentation — map the core's status enum to a color/label, nothing more.Acceptance criteria
keep.versionbumped to the keep commit that adds the FFI fields; bindings regenerated.Notes
../keep; this issue tracks the keep-android rendering side and the cross-repo coordination.KeepEmptyStatefrom UI/UX overhaul: design tokens, shared components, consistent screens #293.