RheaKitScientific UI surfaces for SwiftUI

Architecture

How RheaKit routes data from backend to store to view, with one state spine and explicit recovery behavior.

Architecture

RheaKit uses a layered architecture: views observe shared state, shared state is fed by a single API client, and the API client handles transport and authentication concerns.

Layer diagram

┌──────────────────────────────────────────────────────┐
│                    SwiftUI Views                     │
│  TeamChat · Governor · Tasks · Bio · Dialog · ...   │
├──────────────────────────────────────────────────────┤
│                    RheaStore                         │
│  @MainActor · @Published · GRDB local cache         │
│  Polling: agents, health, proofCount (every 5s)     │
│  On-demand: history, radio, proofs, ontologies      │
├──────────────────────────────────────────────────────┤
│                    RheaAPI                           │
│  Singleton HTTP client · URLSession                 │
│  JWT Bearer + API-key fallback · Typed DTOs         │
├──────────────────────────────────────────────────────┤
│                   AuthManager                        │
│  Keychain-backed JWT · Sign in with Apple           │
│  Skip-login mode for dev · Profile fetch            │
├──────────────────────────────────────────────────────┤
│                  Rhea Backend                        │
│  Fly.io (prod) · localhost:8400 (dev)               │
│  SQL-backed endpoints · In-memory governor state    │
└──────────────────────────────────────────────────────┘

RheaStore: the shared brain

RheaStore is a @MainActor singleton ObservableObject and the package source of truth.

Data tiers

TierWhatRefresh
CoreAgents, health, proof countAutomatic five-second timer
On-demandHistory, radio, proofs, ontologies, sessions, officeLoaded when panes open
EphemeralSSE streams, active dialog stateNever cached

Derived metrics

var totalTokens: Int
var totalCost: Double
var aliveCount: Int
var familyOnline: Bool

Staleness and recovery

Each fetched surface records its last refresh. Views can inspect store.age("proofs") before doing expensive work.

When connection returns after an outage, RheaStore follows a strict recovery order:

  1. Confirm membrane integrity: the backend responds to core agent state.
  2. Refill metabolism: refresh SQL-backed surfaces such as proofs, history, radio, and office.
  3. Accept hard resets honestly: in-memory governor counters may reset to zero after restart.
  4. Resume normal polling.

RheaAPI: transport and typed endpoints

RheaAPI hides raw URLSession calls behind narrow methods that map to real backend capabilities.

MethodEndpointReturns
health()GET /healthHealthSnapshot
agents()GET /agents/status[AgentDTO]
history()GET /cc/history[[String: Any]]
radio()GET /cc/radio[[String: Any]]
proofs()GET /aletheia/proofs[[String: Any]]
ontologies()GET /ontology[[String: Any]]
models()GET /modelsInfraModels
sessions()GET /cc/sessions[[String: Any]]
walletStatus()GET /wallet/status[[String: Any]]
governorAll()GET /governor[String: GovernorAgentStatus]
supervisorSessions()GET /supervisor/sessions[SupervisorSession]

Error handling

public enum RheaAPIError: Error {
    case invalidURL(String)
    case http(Int, String)
    case decode(String)
}

AppConfig: environment routing

RheaKit chooses the backend automatically:

EnvironmentAPI URLAtlas URL
Simulatorhttp://localhost:8400http://localhost:3000
Devicehttps://rhea-tribunal.fly.devsame host

migrateStaleDefaults() replaces saved localhost values when an older dev build lands on a real device.

Local cache

RheaStore maintains a GRDB-backed SQLite cache for offline access.

CREATE TABLE cached_proofs (
    id TEXT PRIMARY KEY,
    claim TEXT NOT NULL,
    tier TEXT,
    agreement_score REAL,
    confidence REAL,
    created_at TEXT,
    data TEXT
);

CREATE TABLE cached_history (
    id INTEGER PRIMARY KEY,
    type TEXT NOT NULL,
    prompt TEXT NOT NULL,
    agreement_score REAL,
    created_at TEXT,
    data TEXT
);

This mirrors the server's durable data, while keeping ephemeral operational surfaces refresh-driven.

Architectural bias

RheaKit deliberately avoids duplicated fetchers and locally improvised caches in individual panes. If you need data in a new view, either add it to the store or make the view explicitly on-demand. Silent shadow state is the fast path to impossible debugging.

On this page