# Implementation Proof: operator dashboard foundation, funds desk, and operator controls Status: open Opened: 2026-04-04 ## Target outcome The next turn is complete only when operators can answer from one real dashboard: - are we making money - are we trading - can I safely control the system - what exactly is broken if the answer is no That answer must come from the live BTC/EURe system itself rather than from stitched-together `curl`, `kubectl`, and SQL by hand. This is an operator-surface turn built on top of the already-proven data and execution loop. It should make the live system easier to supervise without loosening spendable truth or silently widening the trade hot path. ## Hypothesis `unrip` becomes materially easier and safer to run once it has a dedicated dashboard service that: 1. reads PostgreSQL for durable history and current portfolio metrics 2. reads service `/state` and `/healthz` for live freshness and controls 3. shows operators the current EUR portfolio value, current profit view, current funds position, current alerts, and current control state in one place 4. exposes the routine non-destructive control actions that already exist 5. makes stale or unhealthy state obvious without requiring log-diving If operators still have to reconstruct balances, alerts, arming state, and recent activity from multiple terminals, the system remains too operationally expensive for routine funded use. ## Scope - [I010] Operator dashboard foundation: dedicated dashboard pod plus backend API that reads PostgreSQL and service `/state`, with a global live status bar, arm/disarm controls, stale-state display, and `Funds` as the default home page. - [I011] Funds desk: current balances, EUR-equivalent value, deposit-handle reveal with retrieval timestamps, emergency stop plus withdraw-all control path, deposit/withdraw history, trade-driven asset-change history, and flexible withdrawal forms by asset and EUR equivalent. - [I014] Strategy and system operator pages: active strategy settings, skip reasons, alert state, service health, topic lag, persistence health, and cluster-operator stats. ## Turn-shaping rule This turn is about truthful operator surfaces, not new trading logic. - The browser must read one dashboard backend, not Kafka, not PostgreSQL directly, and not a fan-out of service control APIs. - The dashboard backend may read PostgreSQL, service HTTP state, Kafka, and narrow read-only cluster metadata. - If the dashboard shows a control, that control must either be real and wired or clearly absent. No fake buttons. - Frontend polling is prohibited. Live dashboard updates must use WebSockets. ## Non-goals - No new strategy logic, dynamic fee logic, or benchmark-aware thresholding. - No full quote analytics workbench or hindsight trade analytics page beyond the narrow recent-quotes and successful-trades views named in this turn. - No browser-direct Kafka, Redpanda, or PostgreSQL access. - No public internet ingress, auth stack, or multi-user permission system in this turn. - No live-funds-moving withdrawal submit path without explicit user approval during implementation. - No dashboard widgets backed only by invented or hand-maintained data. - No polling-based live refresh loops. ## Required runtime behavior ### Dashboard topology - A dedicated `operator-dashboard` service must exist in cluster runtime. - The dashboard frontend must be served by the dashboard service itself or by a tightly coupled companion in the same deployment shape. - The dashboard backend must be the only browser-facing integration point. - The browser must use one authenticated WebSocket connection to the dashboard backend for live updates. ### Authentication skeleton The dashboard backend must have authentication and authorization hooks in place from the first turn even if the initial implementation is permissive. Required behavior: - REST API routes must pass through backend auth middleware. - WebSocket connection establishment must also pass through backend auth checks. - The current implementation may resolve to an always-true local operator identity, but the code path must already return a structured auth context rather than bypass auth entirely. - The frontend must learn auth state from the backend, not hard-code a local assumption. ### Profitability-first operator view The dashboard must revolve around the question `are we making money`. At minimum the default view must make it obvious: - current total portfolio value in EUR terms - profit or loss versus the deposit-time baseline - profit or loss versus simply holding the original deposits - a compact attribution split between market move and trading contribution using the truthful data available in this turn - whether trading is currently active, including recent trade count and last successful trade time If some part of the answer is still provisional because the system does not yet track every fee or per-trade realized settlement delta, the dashboard must say that explicitly rather than implying complete precision. ### Global status bar The dashboard must show a stable top-level status strip with at least: - active pair - latest reference price and freshness - latest inventory snapshot and freshness - active alert count and highest visible severity - strategy armed state - executor armed state - current total portfolio value in EUR terms If one of these inputs is stale or unavailable, the bar must show that explicitly rather than quietly rendering old values. The status bar should update from the backend WebSocket feed rather than browser polling. ### Funds home page `Funds` must be the default landing page. Its top section must answer `are we making money` before it answers lower-level operator questions. It must show at least: - current total portfolio value in EUR terms - profit or loss versus the deposit-time baseline - profit or loss versus simple hold of the original deposits - current spendable balances by asset - current EUR-equivalent value by asset and in total - latest credited deposit inventory - pre-credit funding visibility and current funding handles - recent deposit and withdrawal records - recent trade-driven asset changes - the timestamps that tell the operator how fresh each section is The dashboard may show funding observations, but it must not present pre-credit observations as spendable. If the profit view excludes some fee classes or cannot yet provide per-trade realized net profit, that limitation must be visible in the page copy. ### Quote and trade activity The dashboard must include a narrow operator-facing activity surface that is intentionally smaller than the future analytics workbench. At minimum it must show: - the 10 most recent incoming quotes - live updates for that recent-quote list through the backend WebSocket feed - a list of all successful trades with pagination The recent-quote list may be minimal as long as it is real, current, and useful. For example: - quote time - pair or asset direction - request kind - input and output amounts when available The successful-trades list must be durable-history-backed rather than in-memory-only. ### Strategy and operator surfaces The dashboard must show: - current strategy threshold and notional settings - recent decisions and skip reasons - current alert state and recent alert transitions - service-by-service health and pause state - writer offsets, persistence health, and relevant freshness timestamps - read-only cluster/operator facts needed to understand whether the app pods are healthy ### Controls The dashboard must expose routine controls through the backend, not by requiring the operator to hand-craft HTTP calls. At minimum this turn must support some real control actions end to end, such as: - service refresh where available - pause or resume on safe services - funding observer pause or resume - withdrawals freeze or unfreeze - withdrawal estimate requests If arm/disarm or drain controls are shown for strategy or executor, they must be explicit about their risk class and confirmation requirements. ### Risk boundary for treasury actions This turn may prepare operator-visible withdrawal estimation and confirmation surfaces, but it must not silently add a one-click live-funds path. Any control that can move funds or materially alter live execution risk must remain: - explicitly identified as risky - confirmation-gated - subject to separate user approval before live use ## Required durable and live sources The dashboard backend must use: - PostgreSQL for durable history and portfolio metrics - service `/state` or `/healthz` for live state and freshness - Kafka on the backend side for live event fan-out to WebSocket clients where appropriate - optionally a narrow read-only cluster source for pod or deployment readiness Kafka remains the event backbone underneath the system, but the browser must not depend on Kafka directly. The backend may consume Kafka specifically to avoid polling and to drive live quote, price, inventory, alert, and trade updates into the dashboard WebSocket stream. ## Definition of done - The live cluster still runs the previously proven BTC/EURe loop with unchanged spendable truth. - A real dashboard service is deployed and reachable by port-forward or equivalent operator access. - `Funds` is the default page and makes the current profit view explicit, including current EUR value, deposit-baseline comparison, hold comparison, and current balances from real sources. - Strategy or system views show live operator state from real sources, including alerts and service health. - The dashboard exposes a recent-quotes section that updates live over WebSockets with no polling. - The dashboard exposes a durable successful-trades list with pagination. - Backend auth is present for both REST and WebSocket paths, even if the initial auth provider is permissive. - At least one safe control action is executed through the dashboard backend and reflected in the underlying service state. - A stale or paused condition induced safely is visible in the dashboard without manual SQL or log parsing. - The dashboard clearly distinguishes durable history from live current state. - Tests cover backend aggregation and control routing logic. - Remaining risky or intentionally deferred controls are named plainly. ## Failure conditions - The dashboard is only a static shell over mocked or copied data. - The browser has to call five different service APIs directly to work. - Balances or alerts shown in the dashboard disagree with PostgreSQL or live service state. - Freshness is hidden, forcing the operator to guess whether a value is current. - Live updates depend on frontend polling instead of WebSockets. - The dashboard has no backend auth path and would need auth bolted on later. - The turn adds risky treasury controls without explicit approval and confirmation design. - Profitability remains buried in generic metric cards or requires the operator to reconstruct the answer manually. - The dashboard ships without enough runtime truth to replace routine manual `curl` and SQL work. ## Current real - The first funded BTC/EURe live loop is already proven and archived. - Pre-credit BTC funding visibility, durable alert records, and rollout-safe armed-state persistence are now live. - PostgreSQL contains the durable trade, inventory, funding, alert, and portfolio-metric history needed for a dashboard backend. - Operators still depend on manual terminal workflows to inspect that truth.