Agave validator
+Source of truth dla chain state, snapshotow i lokalnego RPC.
+diff --git a/apps/visualizer/diagram.html b/apps/visualizer/diagram.html new file mode 100644 index 0000000..f810b49 --- /dev/null +++ b/apps/visualizer/diagram.html @@ -0,0 +1,12 @@ + + +
+ + +Source of truth dla chain state, snapshotow i lokalnego RPC.
+Pushuje account updates, sloty, tx i block meta po wg0.
Metryki pluginu i gRPC do Prometheusa i diagnostyki.
+Bierze live feed z Yellowstone i zamienia go na dane wewnetrzne.
+Transformuje dane i zapisuje je do DB lub feedow uslugowych.
+Czyta RPC Agavy i buduje stan DLOB, ktory trafia do Redis.
+Serwuje gotowy widok DLOB dla API i wizualizacji.
+Konsumuje feed i decyduje o akcjach tradingowych.
+Wybiera write path: RPC, TPU albo Jito.
Trwaly storage, stan aplikacji, historia, pozycje i GraphQL.
+Cache, pub/sub i szybki stan DLOB dla serwisow read-side.
+Warstwa produktu. UI pokazuje dane, API agreguje odpowiedzi dla klienta.
+Monitoring, obsluga klastra, deployment z Gitea i obserwowalnosc.
+
+ To jest aktualny rozklad obiektow w klastrze mevnode_bot:
+ workloady, uslugi i typy ekspozycji. Tu widac juz konkretne
+ Deployment, StatefulSet, ClusterIP,
+ NodePort i stan gotowosci.
+
Service metrics-server na porcie 443.
Service kube-dns z DNS wewnatrz klastra.
Provisioning lokalnego storage dla PVC na pojedynczym nodzie.
+Service portainer-agent wystawiony jako NodePort 30778 -> 9001.
Uzywany przez agenta do wewnetrznego cluster discovery.
+Service trade-frontend: NodePort 30081 -> 8081.
Service trade-api na porcie 8787.
Ingest pipeline bez osobnego Service. Przetwarza dane w tle.
+Service dlob-redis na porcie 6379.
Service dlob-server na porcie 6969. Zalezy od poprawnie napelnionego stanu DLOB.
Nie ma Service. To komponent zalezn y od zdrowego Agave RPC i bootstrapu Drift account reads.
+Workery pochodnych widokow orderbooka, bez ekspozycji zewnetrznej.
+Service postgres na 5432, headless dla stabilnej tozsamosci StatefulSet.
Service hasura na porcie 8080.
+ Ten układ rozdziela warstwę validatora od warstwy aplikacyjnej. Na sol + trzymasz tylko Agave, Yellowstone i lokalne RPC. Na bot działa + single-node k3s z frontendem, API, DLOB, strategiami i bazami danych. +
+Źródło stanu chaina, snapshotów i lokalnego read/write RPC.
+127.0.0.1:8899 RPC10.91.0.1:10000 Yellowstone gRPC10.91.0.1:8999 Prometheus plugin metricsBot może później używać lokalnego RPC przez proxy lub iść bezpośrednio przez TPU/Jito.
+10.91.0.1:10000RPC, TPU albo Jito
+ Yellowstone gRPC daje live account updates, sloty i transakcje.
+ Agave RPC zostaje do odczytów punktowych typu
+ getAccountInfo, getMultipleAccounts,
+ getHealth i getLatestBlockhash.
+
+ Wysyłka transakcji nie idzie przez Yellowstone. To robi
+ RPC, TPU albo Jito.
+ Dzięki temu read plane i write plane są od siebie operacyjnie odseparowane.
+
+ Wszystko aplikacyjne i webowe ląduje na mevnode_bot.
+ Wszystko validatorowe zostaje na mevnode_sol.
+ To upraszcza debug, upgrade i kontrolę ryzyka.
+
+ Docelowy przepływ: mevnode_sol -> wg0 -> yellowstone-consumer / dlob-publisher -> redis / postgres -> api -> frontend.
+ Osobno działa execution path: strategy-engine -> tx-router -> RPC / TPU / Jito.
+
+ Ta strona opisuje faktyczny przepływ runtime dla waszej appki: browser -> frontend -> API/Hasura -> Postgres/Redis + oraz zależności od Agave RPC i Yellowstone gRPC. To nie jest ogólny diagram architektury, + tylko mapa wejść, wyjść, formatów danych i tras HTTP/WS używanych przez produkt. +
+Serwuje SPA, obsługuje logowanie, robi proxy do API i Hasury.
+Wykres bierze REST JSON. DLOB live bierze GraphQL WebSocket. Auth idzie przez sesję cookie.
+REST backend. Zwraca candles, przyjmuje tick ingest, zarządza tokenami.
+Frontend subskrybuje Hasurę po GraphQL WS. API czyta i zapisuje przez GraphQL do Hasury.
+publisher 0/1 server 0/1 bo bootstrapping zależy od zdrowego Agave RPC.
+Punktowe odczyty: getAccountInfo, getMultipleAccounts, getSlot, getHealth.
Pushowy stream kont, slotów i transakcji po wg0.
To jest write path. Nie mylić z Yellowstone, który jest tylko read streamem.
+/api i /graphql.+ Najważniejszy podział: chart path = REST JSON przez trade-api, a DLOB live = GraphQL WS przez Hasurę. + To oznacza, że nie cały frontend jedzie jednym typem transportu. Produkt ma dwa równoległe read pathy. +
+| Surface | +Endpoint | +Transport | +Input | +Output | +Where it goes | +
|---|---|---|---|---|---|
| Frontend | +GET /whoami |
+ HTTP JSON | +cookie session | +{ ok, user, mode } |
+ handled directly by trade-frontend |
+
| Frontend | +POST /auth/login |
+ HTTP JSON/form | +username, password |
+ { ok, user } + session cookie |
+ handled directly by trade-frontend |
+
| Frontend | +POST /auth/logout |
+ HTTP JSON | +session cookie | +{ ok: true } |
+ handled directly by trade-frontend |
+
| Frontend proxy | +/api/* |
+ HTTP | +browser request, frontend injects read token | +proxied API response | +trade-api:8787 |
+
| Frontend proxy | +/graphql |
+ HTTP GraphQL | +GraphQL query/mutation | +GraphQL response | +hasura:8080/v1/graphql |
+
| Frontend proxy | +WS /graphql |
+ GraphQL WebSocket | +subscription payload | +live subscription frames | +hasura:8080/v1/graphql |
+
| API | +GET /v1/chart |
+ HTTP JSON | +symbol, tf, limit, optional source |
+ candles + indicators + flow rows | +Hasura function get_drift_candles + table drift_ticks |
+
| API | +POST /v1/ingest/tick |
+ HTTP JSON | +tick payload | +{ ok, id } |
+ writes tick through Hasura into DB | +
| API | +GET /v1/ticks |
+ HTTP JSON | +symbol, optional source, limit, from, to |
+ tick list | +reads drift_ticks through Hasura |
+
| API admin | +POST /v1/admin/tokens |
+ HTTP JSON | +name, scopes | +new API token | +stored in DB via Hasura | +
| API admin | +POST /v1/admin/tokens/revoke |
+ HTTP JSON | +token id | +revocation status | +updates token row in DB | +
| Chain read | +Agave RPC |
+ JSON-RPC | +account/state requests | +point reads | +mevnode_sol |
+
| Chain live | +Yellowstone gRPC |
+ gRPC streaming | +subscription config + token | +account/slot/tx stream | +mevnode_sol |
+
{
+ "ok": true,
+ "symbol": "SOL-PERP",
+ "tf": "1m",
+ "bucketSeconds": 60,
+ "candles": [
+ {
+ "time": 1710000000,
+ "open": 132.1,
+ "high": 133.2,
+ "low": 131.8,
+ "close": 132.9,
+ "volume": 412,
+ "oracle": 132.7,
+ "flow": { "up": 0.46, "down": 0.41, "flat": 0.13 },
+ "flowRows": [1, 1, 0, -1],
+ "flowMoves": [0.2, 0.1, 0, 0.3]
+ }
+ ],
+ "indicators": {
+ "sma20": [{ "time": 1710000000, "value": 131.4 }],
+ "ema20": [{ "time": 1710000000, "value": 131.8 }],
+ "bb20": { "upper": [], "lower": [], "mid": [] },
+ "rsi14": [],
+ "macd": { "macd": [], "signal": [] }
+ }
+}
+ {
+ "ts": "2026-03-13T11:20:00.000Z",
+ "market_index": 0,
+ "symbol": "SOL-PERP",
+ "oracle_price": "132.70",
+ "mark_price": "132.91",
+ "oracle_slot": 406125100,
+ "source": "drift",
+ "raw": {
+ "provider": "internal"
+ }
+}
+ subscription DlobStats($market: String!) {
+ dlob_stats_latest(where: {market_name: {_eq: $market}}, limit: 1) {
+ market_name
+ mark_price
+ oracle_price
+ best_bid_price
+ best_ask_price
+ mid_price
+ spread_abs
+ spread_bps
+ depth_bid_usd
+ depth_ask_usd
+ imbalance
+ updated_at
+ }
+}
+ {
+ "market_name": "SOL-PERP",
+ "bids": [
+ { "price": 132910000, "size": 2500000000 }
+ ],
+ "asks": [
+ { "price": 132930000, "size": 1700000000 }
+ ],
+ "updated_at": "2026-03-13T11:20:01.000Z"
+}
+
+Frontend przelicza to przez:
+- pricePrecision = 1_000_000
+- basePrecision = 1_000_000_000
+
+ Krytyczne rozróżnienie: DLOB live nie idzie z REST API. DLOB do UI idzie przez Hasura GraphQL subscriptions,
+ a chart przez REST JSON z trade-api. To są dwa osobne transporty, dwa osobne modele danych i dwa osobne źródła opóźnień.
+