From 69849cb9e9be8652b80031388cdc7e20f5441c3b Mon Sep 17 00:00:00 2001 From: u1 Date: Tue, 6 Jan 2026 12:33:47 +0100 Subject: [PATCH] chore: initial import --- .gitignore | 4 + README.md | 6 + bot-plan.html | 338 ++++++++++++++++++++++++++++++++++++++++ bot-plan.txt | 99 ++++++++++++ dapp-choice.html | 234 ++++++++++++++++++++++++++++ gitea-k3s-rv32i.md | 150 ++++++++++++++++++ k8s-migracja.md | 167 ++++++++++++++++++++ migration.md | 339 +++++++++++++++++++++++++++++++++++++++++ spot-mvp.html | 211 +++++++++++++++++++++++++ steps.md | 216 ++++++++++++++++++++++++++ t2 | 37 +++++ workflow-api-ingest.md | 171 +++++++++++++++++++++ 12 files changed, 1972 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 bot-plan.html create mode 100644 bot-plan.txt create mode 100644 dapp-choice.html create mode 100644 gitea-k3s-rv32i.md create mode 100644 k8s-migracja.md create mode 100644 migration.md create mode 100644 spot-mvp.html create mode 100644 steps.md create mode 100644 t2 create mode 100644 workflow-api-ingest.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a70187 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +*.swp +*.tmp +*.log diff --git a/README.md b/README.md new file mode 100644 index 0000000..92fd20b --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# trade-doc + +Dokumentacja całego projektu `trade` (migracja k3s/GitOps/CI, log działań). + +- Plan migracji: `migration.md` +- Log działań: `steps.md` diff --git a/bot-plan.html b/bot-plan.html new file mode 100644 index 0000000..fa7aabc --- /dev/null +++ b/bot-plan.html @@ -0,0 +1,338 @@ + + + + + + Bot: analiza wielu par na Solanie + sygnał wejścia (top 10) + + + +
+
+

Bot: analiza wielu par na Solanie + sygnał wejścia (top 10)

+

+ Specyfikacja funkcjonalna: skanowanie wielu rynków, ranking i generowanie sygnałów (z opcją + egzekucji). +

+
+ +
+
+

Cel

+
    +
  • Monitoruje i analizuje dane z kilkudziesięciu par/rynków na Solanie.
  • +
  • + Wylicza sygnał wejścia (long/short lub buy/sell) i wybiera ~10 + najlepszych okazji. +
  • +
  • Opcjonalnie automatycznie składa zlecenia (po dopracowaniu ryzyka).
  • +
+
+ +
+

Założenia / pytania decyzyjne ustalić na start

+
    +
  1. + Czy handlujemy SPOT czy PERPS? +
      +
    • SPOT: swap + ewentualnie limit (zależnie od venue).
    • +
    • PERPS: leverage, funding, osobne ryzyka i mechanika zleceń.
    • +
    +
  2. +
  3. Interwał i horyzont: skaner 5s / 30s / 1m? Sygnał intraday czy swing?
  4. +
  5. Typy sygnałów: momentum, mean reversion, breakout, funding-arb, basis, orderflow.
  6. +
  7. + Ograniczenia: max dźwignia, max ekspozycja per rynek, max łączna ekspozycja, SL/TP. +
  8. +
+
+ +
+

Źródła danych (Solana)

+
    +
  • + RPC: stan kont, transakcje, price feeds, state orderbook/AMM (Helius/Alchemy/QuickNode). +
  • +
  • Oracle: Pyth (ceny), Switchboard (alternatywnie).
  • +
  • + Dane DEX: +
      +
    • AMM (Orca/Raydium): pool state, price, liquidity, tick.
    • +
    • Orderbook (Phoenix/OpenBook): book depth, spread, imbalance.
    • +
    +
  • +
  • + Dane PERPS: +
      +
    • Drift: mark price, oracle, funding, OI, pozycje, margin, perp state (SDK).
    • +
    +
  • +
+
+ +
+

Architektura (proponowana)

+
    +
  1. + Universe (lista rynków) +
      +
    • Start: 30–80 rynków (top wolumen / top OI / ręcznie wybrane).
    • +
    • Aktualizacja listy raz dziennie lub co godzinę.
    • +
    +
  2. +
  3. + Collector (pobieranie danych) +
      +
    • + Polling (bez batch) + cache: ceny (Pyth/oracle), wolumen/OI/funding (perps), + spread/depth (orderbook), liquidity/TVL (AMM). +
    • +
    • + Normalizacja do jednego formatu: + timestamp, symbol/marketId, price, returns, vol, spread, depth, funding, OI, liquidity +
    • +
    +
  4. +
  5. + Feature engineering (cechy) +
      +
    • returns (1m/5m/15m), RSI, EMA cross, ATR/volatility,
    • +
    • orderbook imbalance, spread %, slippage estymowana dla rozmiaru pozycji,
    • +
    • perps: funding (aktualny + trend), basis, utilization, OI change.
    • +
    +
  6. +
  7. + Scoring (ranking i selekcja top N) +
      +
    • wynik = f(signal_strength, liquidity_score, cost_score, risk_score)
    • +
    • filtr: minimalna płynność / maksymalny spread / maks. slippage
    • +
    • wybór: top 10 rynków do wejścia lub do obserwacji
    • +
    +
  8. +
  9. + Execution (składanie zleceń) – dopiero po stabilizacji +
      +
    • tryb “paper” (log + symulacja) → tryb “live” (małe size)
    • +
    • + risk manager przed wysłaniem: +
        +
      • max size per rynek
      • +
      • max łączna ekspozycja
      • +
      • cooldown, max liczba otwartych pozycji
      • +
      • warunki anulowania (np. slippage > X)
      • +
      +
    • +
    +
  10. +
  11. + Observability +
      +
    • logi sygnałów i decyzji
    • +
    • metryki: winrate, MDD, slippage, koszt fee, latency
    • +
    • alerty: RPC errors, divergence oracle/mark, brak danych
    • +
    +
  12. +
+
+ +
+

Jaki dApp jest najlepszy do bota?

+

+ To zależy, czy chcesz SPOT czy PERPS. Kryteria “najlepszy” dla bota: +

+
    +
  • stabilne API/SDK (TS/Python), dobre typy i przykłady,
  • +
  • zlecenia limit/market i sensowna płynność,
  • +
  • bezpieczny model kluczy (delegate / subaccount / ograniczenia),
  • +
  • koszty (fees, slippage) i ryzyka (leverage/funding).
  • +
+ +

Rekomendacja (PERPS, sygnały na 10 rynkach)

+
    +
  • + Drift jest najpraktyczniejszy, bo ma dojrzałe SDK + (@drift-labs/sdk), workflow delegate i spójny system ryzyk (margin/health). +
  • +
  • Jest to spójne z obecnym repozytorium (workflow Drift + delegate).
  • +
+ +

Rekomendacja (SPOT, egzekucja swapów)

+
    +
  • + Jupiter zwykle najlepiej nadaje się do wykonania swapów (routing, + egzekucja, slippage). +
  • +
  • + Jeśli potrzebujesz limit orders na SPOT: Phoenix/OpenBook (większa złożoność: + orderbook + cancel/replace). +
  • +
+ +

Proponowany “hybrid”

+
    +
  • Sygnał niezależny od venue (feature store + ranking).
  • +
  • SPOT: egzekucja przez Jupiter.
  • +
  • PERPS: egzekucja na Drift.
  • +
  • Dane: oracle + metryki DEX + metryki perps.
  • +
+
+ +
+

Minimalny MVP (1–2 tygodnie, bez live trading)

+
    +
  1. Universe 30–50 rynków + konfiguracja w pliku.
  2. +
  3. Collector: ceny + podstawowe metryki per rynek (wspólny format).
  4. +
  5. Ranking: prosty score (momentum + liquidity − cost).
  6. +
  7. + Output co X sekund: +
      +
    • top 10 rynków
    • +
    • strona (long/short)
    • +
    • siła sygnału
    • +
    • est. slippage
    • +
    • uzasadnienie (cechy)
    • +
    +
  8. +
  9. Backtest “na sucho”: zapis sygnałów do CSV/JSONL, analiza w Pythonie.
  10. +
+
+ +
+

Następne kroki

+
    +
  1. Potwierdź: SPOT czy PERPS (czy oba)?
  2. +
  3. Podaj listę par (albo kryterium wyboru).
  4. +
  5. Potwierdź częstotliwość skanowania (np. 10s/30s/60s) i horyzont.
  6. +
  7. Powiedz, czy bot ma tylko sygnalizować, czy też automatycznie tradować.
  8. +
+
+
+
+ + diff --git a/bot-plan.txt b/bot-plan.txt new file mode 100644 index 0000000..0c054d1 --- /dev/null +++ b/bot-plan.txt @@ -0,0 +1,99 @@ +BOT: analiza wielu par na Solanie + sygnał wejścia (top 10) + +Cel +- Zbudować bota, który: + - monitoruje i analizuje dane z kilkudziesięciu par/rynków na Solanie, + - wylicza sygnał wejścia (long/short lub buy/sell) i wybiera ~10 najlepszych okazji, + - opcjonalnie automatycznie składa zlecenia (po wcześniejszym dopracowaniu ryzyka). + +Założenia / pytania decyzyjne (ustalić na start) +1) Czy handlujemy SPOT czy PERPS? + - SPOT: swap + ewentualnie limit (zależnie od venue). + - PERPS: leverage, funding, osobne ryzyka i mechanika zleceń. +2) Interwał i horyzont: skaner 5s / 30s / 1m? Sygnał intraday czy swing? +3) Jakie typy sygnałów: momentum, mean reversion, breakout, funding-arb, basis, orderflow? +4) Jakie ograniczenia: max dźwignia, max ekspozycja per rynek, max łączna ekspozycja, SL/TP? + +Źródła danych (Solana) +- RPC (stan kont, transakcje, price feeds, orderbook/AMM state): Helius/Alchemy/QuickNode (zależnie od budżetu). +- Oracle: Pyth (ceny), Switchboard (alternatywnie). +- Dane DEX: + - AMM (Orca/Raydium): pool state, price, liquidity, tick. + - Orderbook (Phoenix/OpenBook): book depth, spread, imbalance. +- Dane PERPS: + - Drift: mark price, oracle, funding, OI, pozycje, margin, orderbook/perp state (przez Drift SDK). + +Architektura (proponowana) +1) Universe (lista rynków) + - Start: 30–80 rynków (np. top wolumen / top open interest / ręcznie wybrane). + - Aktualizacja listy raz dziennie lub co godzinę. +2) Collector (pobieranie danych) + - Polling (bez batch) + cache: + - ceny (Pyth/oracle), + - wolumen / OI / funding (perps), + - spread/depth (orderbook), + - liquidity/TVL (AMM). + - Normalizacja do jednego formatu: + - timestamp, symbol/marketId, price, returns, vol, spread, depth, funding, OI, liquidity. +3) Feature engineering (cechy) + - returns (1m/5m/15m), RSI, EMA cross, ATR/volatility, + - orderbook imbalance, spread %, slippage estymowana dla rozmiaru pozycji, + - perps: funding (aktualny + trend), basis, utilization, OI change. +4) Scoring (ranking i selekcja top N) + - wynik = f(signal_strength, liquidity_score, cost_score, risk_score) + - filtr: minimalna płynność / maksymalny spread / maks. slippage + - wybór: top 10 rynków do wejścia lub do obserwacji. +5) Execution (składanie zleceń) – dopiero po stabilizacji + - tryb “paper” (log + symulacja) -> tryb “live” (małe size). + - risk manager przed wysłaniem zlecenia: + - max size per rynek, + - max łączna ekspozycja, + - cooldown, max liczba otwartych pozycji, + - warunki anulowania (np. slippage > X). +6) Observability + - logi sygnałów i decyzji, + - metryki (winrate, MDD, slippage, koszt fee, latency), + - alerty (RPC errors, divergence oracle/mark, brak danych). + +Jaki dApp jest najlepszy do bota? +To zależy od tego, czy chcesz SPOT czy PERPS. Praktyczne kryteria “najlepszy” dla bota: +- Czy ma stabilne API/SDK (TS/Python), dobre typy i przykłady? +- Czy obsługuje zlecenia limit/market i ma sensowną płynność? +- Czy pozwala na bezpieczny model kluczy (delegate / subaccount / ograniczenia)? +- Jakie są koszty (fees, slippage) i ryzyka (leverage/funding)? + +Rekomendacja (jeśli celem jest PERPS i sygnały na 10 rynkach) +- Drift (PERPS) jest najpraktyczniejszy dla bota na Solanie, bo: + - ma dojrzałe SDK (@drift-labs/sdk) i workflow delegate (hot key do tradingu), + - umożliwia margin/perps, typowe order types i subaccounty, + - ma jeden spójny system ryzyk (margin, health), co ułatwia kontrolę ekspozycji. +- To jest też spójne z obecnym repozytorium (workflow Drift + delegate + SOL-PERP). + +Rekomendacja (jeśli celem jest SPOT i egzekucja swapów) +- Jupiter (agregator) jest zwykle najlepszy do wykonania swapów na SPOT: + - optymalizuje routing, upraszcza egzekucję, + - łatwo wycenić i policzyć slippage dla różnych par. +- Jeśli potrzebujesz limit orders na SPOT: + - rozważ venue orderbook (Phoenix/OpenBook) albo dApp, które natywnie wspiera limity, + - kosztem większej złożoności (orderbook + cancel/replace). + +Proponowany “hybrid” (często najlepszy w praktyce) +- Sygnał generujesz niezależnie od venue (feature store + ranking), +- SPOT wykonujesz przez Jupiter (najlepsza egzekucja), +- PERPS wykonujesz na Drift (leverage + perps), +- Dane rynkowe mieszasz: oracle + DEX metrics + perps metrics. + +Minimalny MVP (1–2 tygodnie, bez live trading) +1) Universe 30–50 rynków + konfiguracja w pliku. +2) Collector: ceny + podstawowe metryki per rynek (wspólny format). +3) Ranking: prosty score (momentum + liquidity - cost). +4) Output: co X sekund zapis: + - top 10 rynków, strona (long/short), siła sygnału, est. slippage, uzasadnienie (cechy). +5) Backtest “na sucho”: + - zapis sygnałów do CSV/JSONL, później analiza w Pythonie. + +Następne kroki (żeby doprecyzować i dobrać dApp ostatecznie) +1) Potwierdź: SPOT czy PERPS (czy oba)? +2) Podaj listę par (albo kryterium wyboru). +3) Potwierdź częstotliwość skanowania (np. 10s/30s/60s) i horyzont. +4) Powiedz, czy bot ma tylko sygnalizować, czy też automatycznie tradować. diff --git a/dapp-choice.html b/dapp-choice.html new file mode 100644 index 0000000..7f756c1 --- /dev/null +++ b/dapp-choice.html @@ -0,0 +1,234 @@ + + + + + + Jaki dApp najlepszy pod bota (Solana)? + + + +
+
+

Jaki dApp jest najlepszy pod bota (Solana)?

+

+ Jeśli bot ma analizować dziesiątki rynków i generować sygnały wejścia na top 10, to + wybór “dAppa” zależy od tego, czy chcesz PERPS czy SPOT. +

+
+ +
+
+

Najkrótsza odpowiedź top choice

+

+ PERPS: Drift  •  SPOT (egzekucja swapów): + Jupiter (a sygnały liczysz w swoim bocie) +

+
+ +
+

Dlaczego “dApp do swapów” to za mało?

+
    +
  • + “Sygnał” to nie przycisk w UI, tylko proces: dane → cechy → ranking → filtr kosztów → + decyzja. +
  • +
  • + Potrzebujesz stabilnego SDK/API do automatyzacji, a nie tylko + interfejsu dla człowieka. +
  • +
  • + Przy “top 10 z 50 rynków” kluczowe są koszty (spread/slippage/fees) i kontrola ryzyka + (limity ekspozycji), których UI zwykle nie gwarantuje. +
  • +
+
+ +
+

PERPS → Drift (najbardziej praktyczne pod bota)

+
    +
  • Jeden spójny system perps: margin/health, funding, OI, mark vs oracle.
  • +
  • + Naturalny model bezpieczeństwa: authority (cold/Ledger) + delegate/hot key do tradingu. +
  • +
  • + Łatwiej zbudować ranking “okazji” między wieloma rynkami, bo metryki są porównywalne + (np. funding, basis, zmiana OI, momentum). +
  • +
+
+ +
+

SPOT → Jupiter (egzekucja), sygnał po Twojej stronie

+
    +
  • + Jupiter jest świetny do wykonania swapów na wielu parach (routing + wycena slippage). +
  • +
  • + Ale Jupiter nie jest “silnikiem sygnałów” — sygnał i ranking robisz w swoim + collector/scorer. +
  • +
  • + Jeśli potrzebujesz limit orders na SPOT, zwykle dochodzi orderbook (Phoenix/OpenBook) + i robi się to bardziej złożone (cancel/replace, kolejka, częściowe fill). +
  • +
+
+ +
+

Jak wygląda sensowny podział systemu

+
    +
  1. + Dane: oracle (np. Pyth) + metryki DEX/perps + płynność/spread/depth. +
  2. +
  3. + Ranking: score = siła sygnału − koszt (slippage/spread) − ryzyko + (płynność, limity, zmienność). +
  4. +
  5. Selekcja: wybór top 10 (z filtrami minimalnej jakości).
  6. +
  7. + Egzekucja: Drift (PERPS) albo Jupiter (SPOT) + risk manager przed + transakcją. +
  8. +
+
+ +
+

Co musisz doprecyzować (żeby “najlepszy dApp” był jednoznaczny)

+
    +
  • Chcesz sygnały pod PERPS czy SPOT (czy oba)?
  • +
  • Bot ma tylko sygnalizować, czy automatycznie wchodzić?
  • +
  • Interwał skanowania (np. 10s/30s/60s) i horyzont (intraday vs swing).
  • +
  • Docelowy rozmiar pozycji (bo koszt i slippage zależą od size).
  • +
+

+ Tip: jeśli celem jest perps + top 10 sygnałów, zwykle wygrywa Drift; jeśli celem jest + czysty spot-execution, wygrywa Jupiter (ale sygnał nadal jest “Twój”). +

+
+
+
+ + diff --git a/gitea-k3s-rv32i.md b/gitea-k3s-rv32i.md new file mode 100644 index 0000000..d6cc41d --- /dev/null +++ b/gitea-k3s-rv32i.md @@ -0,0 +1,150 @@ +# Gitea na k3s (Traefik + cert-manager + Let’s Encrypt) dla `rv32i.pl` + +Ten dokument instaluje Gitea w k3s: +- Ingress: Traefik +- TLS: cert-manager + Let’s Encrypt (`ClusterIssuer` = `letsencrypt-prod`) +- Host: `rv32i.pl` +- Secret TLS: `rv32i-pl-tls` + +## Ważne (hasła/sekrety) + +Nie wstawiam i nie zalecam trzymania haseł w plikach w repo (`doc/`), bo to zwykle kończy się wyciekiem (git history, backupy, screeny). + +Zamiast tego: +- trzymamy hasła w **Kubernetes Secret** (w klastrze), +- a w dokumentacji zostawiamy **placeholderey** i komendy, które proszą o hasło interaktywnie. + +Jeśli mimo wszystko chcesz „na sztywno” wpisać hasła do pliku, podaj je jawnie w wiadomości — ale to jest zła praktyka. + +## 0) Wymagania + +1) `cert-manager` działa, a `ClusterIssuer` jest gotowy: +```bash +sudo k3s kubectl get clusterissuer letsencrypt-prod +``` + +2) DNS wskazuje na VPS: +```bash +dig +short rv32i.pl A +``` +Oczekiwane: `77.90.8.171` + +3) Porty 80/443 są otwarte z internetu (firewall/ACL u providera). + +## 1) Instalacja Helm (jeśli nie masz) + +```bash +helm version || true +curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | sudo bash +helm version +``` + +## 2) Namespace + +```bash +sudo k3s kubectl create namespace gitea +``` + +## 3) Secret z kontem admina (bez wpisywania hasła do historii) + +Ten krok tworzy w klastrze sekret `gitea-admin` z loginem/hasłem/email admina. + +```bash +read -rp "Gitea admin username: " GITEA_ADMIN_USER +read -rsp "Gitea admin password: " GITEA_ADMIN_PASS; echo +read -rp "Gitea admin email: " GITEA_ADMIN_EMAIL + +sudo k3s kubectl -n gitea create secret generic gitea-admin \ + --from-literal=username="$GITEA_ADMIN_USER" \ + --from-literal=password="$GITEA_ADMIN_PASS" \ + --from-literal=email="$GITEA_ADMIN_EMAIL" +``` + +## 4) Helm values (Ingress + TLS + Postgres w klastrze) + +Utwórz plik `gitea-values.yaml` na VPS (nie musi być w repo): + +```bash +cat <<'YAML' > gitea-values.yaml +ingress: + enabled: true + className: traefik + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + hosts: + - host: rv32i.pl + paths: + - path: / + pathType: Prefix + tls: + - secretName: rv32i-pl-tls + hosts: + - rv32i.pl + +gitea: + admin: + existingSecret: gitea-admin + config: + server: + DOMAIN: rv32i.pl + ROOT_URL: https://rv32i.pl/ + PROTOCOL: http + +persistence: + enabled: true + storageClass: local-path + size: 10Gi + +postgresql: + enabled: true + primary: + persistence: + enabled: true + storageClass: local-path + size: 10Gi +YAML +``` + +## 5) Instalacja Gitea + +```bash +helm repo add gitea-charts https://dl.gitea.com/charts/ +helm repo update + +helm upgrade --install gitea gitea-charts/gitea \ + -n gitea \ + -f gitea-values.yaml +``` + +## 6) Sprawdzenie + +```bash +sudo k3s kubectl -n gitea get pods -o wide +sudo k3s kubectl -n gitea get ingress -o wide +sudo k3s kubectl -n gitea get certificate -o wide +sudo k3s kubectl -n gitea get order,challenge +``` + +Wejdź na: +- `https://rv32i.pl` + +Jeśli `Certificate` nie robi się `Ready=True`, to najczęściej: +- port 80 nie dochodzi z internetu (Let’s Encrypt HTTP-01), +- Ingress nie ma `className: traefik`, +- DNS nie wskazuje na VPS. + +## 7) (Opcjonalnie) dostęp do haseł po czasie + +Jeśli chcesz podejrzeć login/email z sekretu (hasło też da się odczytać, ale rób to ostrożnie): +```bash +sudo k3s kubectl -n gitea get secret gitea-admin -o jsonpath='{.data.username}' | base64 -d; echo +sudo k3s kubectl -n gitea get secret gitea-admin -o jsonpath='{.data.email}' | base64 -d; echo +``` + +## 8) Uwaga o Git przez SSH + +Na VPS port 22 zajmuje systemowy OpenSSH. Na start korzystaj z klonowania przez HTTPS. +Jeśli chcesz klonować przez SSH z Gitei: +- najprościej wystawić Gitea SSH na innym porcie (np. 2222) i zrobić NodePort/LoadBalancer, +- albo zmapować port przez Traefik TCP (wymaga dodatkowej konfiguracji). + diff --git a/k8s-migracja.md b/k8s-migracja.md new file mode 100644 index 0000000..f7573a6 --- /dev/null +++ b/k8s-migracja.md @@ -0,0 +1,167 @@ +# Plan migracji na Kubernetes (mikroserwisy + CI/CD) + +## 1) Co jest dziś w repo (stan wejściowy) + +Ten projekt już ma podział “mikroserwisowy” w Docker Compose: + +- **DB stack** (`devops/db/docker-compose.yml`) + - `postgres` (TimescaleDB, port 5432) + - `hasura` (GraphQL Engine, port 8080) + - `pgadmin` (narzędzie dev, port 5050) +- **App stack** (`devops/app/docker-compose.yml`) + - `api` = **trade-api** (Node, port 8787, `devops/api/Dockerfile`) + - `frontend` = **trade-frontend** (Node + statyczny build visualizera, port 8081, `devops/app/frontend/Dockerfile`) + - `ingestor` = worker (Node, `devops/ingestor/Dockerfile`) – opcjonalnie, profil `ingest` +- **One-shot bootstrap** (`devops/tools/bootstrap/docker-compose.yml`) + - `db-init` (aplikuje SQL `devops/db/initdb/001_init.sql`) + - `db-version` / `db-backfill` (migracje “wersji” ticków) + - `hasura-bootstrap` (Node script `devops/db/hasura-bootstrap.mjs`: track tabel + permissions + funkcje) + +Konfiguracja i sekrety dziś są w `tokens/*.json` (gitignored) i są montowane jako pliki do kontenerów. + +## 2) Docelowa architektura na K8s + +Minimalny sensowny podział na K8s (1 namespace lub 2): + +- **timescaledb** (stanowe) → `StatefulSet` + `PVC` + `Service` +- **hasura** → `Deployment` + `Service` +- **trade-api** → `Deployment` + `Service` +- **trade-frontend** → `Deployment` + `Service` + `Ingress` +- **trade-ingestor** → `Deployment` (1 replika) albo `CronJob` (jeśli chcesz uruchamiać okresowo) +- **bootstrap / migracje** → `Job` (odpalane ręcznie albo jako część release’u) + +Opcjonalnie: +- `pgadmin` tylko na dev/staging (nieprodukcyjnie). + +## 3) Proponowane zasoby Kubernetes (mapowanie 1:1 z Compose) + +### 3.1 timescaledb (Postgres) +- `StatefulSet` z `volumeClaimTemplates` (dane w PVC) +- `Service` typu `ClusterIP` (np. `timescaledb:5432`) +- Init schematu: + - *na pierwszy start* można nadal użyć mechanizmu `/docker-entrypoint-initdb.d` (ConfigMap z SQL), + - *dla istniejących wolumenów* potrzebny jest osobny `Job` (odpowiednik `db-init` z compose). + +### 3.2 Hasura +- `Deployment` + `Service` (`hasura:8080`) +- `Secret` na: + - `HASURA_GRAPHQL_ADMIN_SECRET` + - klucz JWT (`HASURA_GRAPHQL_JWT_SECRET` / `HASURA_JWT_KEY`) + - connection string do Postgresa (albo osobno hasło) +- Bootstrap metadanych: + - `Job` uruchamiający `devops/db/hasura-bootstrap.mjs` (odpowiednik `hasura-bootstrap`). + +### 3.3 trade-api +- `Deployment` + `Service` (`trade-api:8787`) +- `readinessProbe`/`livenessProbe`: `GET /healthz` (już istnieje) +- Konfiguracja: + - env: `HASURA_GRAPHQL_URL`, `TICKS_TABLE`, `CANDLES_FUNCTION`, `APP_VERSION`, `BUILD_TIMESTAMP` + - sekret plikowy (jak dziś): `tokens/hasura.json` + `tokens/api.json` → `Secret` montowany do `/app/tokens/*` + +### 3.4 trade-frontend +- `Deployment` + `Service` (`trade-frontend:8081`) +- `Ingress` wystawiający UI na zewnątrz (TLS opcjonalnie) +- `readinessProbe`/`livenessProbe`: `GET /healthz` (już istnieje) +- Sekrety plikowe: + - `tokens/frontend.json` (basic auth do UI) + - `tokens/read.json` (read token do proxy `/api/*`) + - oba jako `Secret` montowany do `/tokens/*` +- `API_UPSTREAM`: `http://trade-api:8787` (Service DNS) + +### 3.5 trade-ingestor +- `Deployment` (zwykle `replicas: 1`) +- Sekrety: + - RPC do Drift (np. `tokens/heliusN.json` / `rpcUrl` / `heliusApiKey`) + - write token do API (`tokens/alg.json`) +- Konfiguracja: + - `MARKET_NAME`, `INTERVAL_MS`, `SOURCE`, `INGEST_API_URL=http://trade-api:8787` +- Uwaga: worker wymaga egress do internetu (Helius RPC + Drift). + +### 3.6 Jobs: db-init / db-version / db-backfill / hasura-bootstrap +Bezpieczny pattern: +- uruchamiane manualnie (kubectl) albo jako “hook” w Helm (pre/post-install) +- odpalane w tym samym namespace co DB/Hasura (żeby DNS działał prosto) + +## 4) Sekrety i konfiguracja (z `tokens/` → K8s) + +Rekomendacja: rozdziel na 2 typy: + +- **Secret** (nie commitować w git): + - `tokens/hasura.json` (adminSecret, jwtKey) + - `tokens/api.json` (api adminSecret) + - `tokens/frontend.json` (basic auth) + - `tokens/read.json` (read token) + - `tokens/alg.json` (write token) + - `tokens/heliusN.json` (RPC token / url) +- **ConfigMap**: + - wersja i parametry nie-wrażliwe: `APP_VERSION`, `BUILD_TIMESTAMP`, `TICKS_TABLE`, `CANDLES_FUNCTION`, `MARKET_NAME`, `INTERVAL_MS` + +Jeśli chcesz GitOps bez trzymania sekretów “na piechotę”, wybierz jedno: +- **Sealed Secrets** (zaszyfrowane sekrety w repo), +- **External Secrets Operator** (Vault / AWS / GCP / Azure), +- “na start” manualne `kubectl create secret ...` per środowisko. + +## 5) Wersjonowanie (v1, v2…) i cutover bez wyłączania DB + +W `scripts/ops/` jest workflow wersjonowania Compose: +- nowa wersja `api+frontend(+ingestor)` działa równolegle +- pisze do **nowej tabeli** (`drift_ticks_vN`) i używa **nowej funkcji** (`get_drift_candles_vN`) + +Na K8s najprościej odwzorować to tak: +- Helm release name albo suffix w nazwach zasobów: `trade-v1`, `trade-v2`, … +- wspólne DB/Hasura pozostają bez zmian +- `Job` “version-init”: + - odpala SQL migracji (odpowiednik `db-version`) + - odpala `hasura-bootstrap` z `TICKS_TABLE` i `CANDLES_FUNCTION` ustawionymi na vN +- “cutover”: + - startujesz `trade-ingestor` vN + - stopujesz `trade-ingestor` v1 + - po czasie robisz `db-backfill` (opcjonalnie) i sprzątasz stare zasoby + +## 6) CI/CD “na gita” (build → deploy po pushu) + +### Opcja A (polecana): GitOps (Argo CD / Flux) +1) CI (GitHub Actions / GitLab CI) buduje obrazy: + - `trade-api` + - `trade-frontend` + - `trade-ingestor` +2) CI publikuje je do registry (np. GHCR) +3) CD (ArgoCD/Flux) automatycznie synchronizuje manifesty/Helm z repo i robi rollout + +Tagowanie obrazów: +- `:sha-` dla każdego commita +- opcjonalnie `:vN` dla release’ów + +Aktualizacja tagów: +- ArgoCD Image Updater / Flux Image Automation **albo** +- CI robi commit do `k8s/` (np. podmienia tag w `values.yaml`) + +### Opcja B (prostsza na start): “kubectl apply” z CI +1) CI buduje i pushuje obrazy +2) CI wykonuje `helm upgrade --install` albo `kubectl apply -k ...` +3) Dostęp do klastra przez sekret w repo (kubeconfig / token) + +## 7) Proponowana sekwencja migracji (checklista) + +1) **Decyzje**: gdzie stoi klaster (EKS/GKE/AKS/k3s), jakie registry, jaki Ingress, jak trzymamy sekrety. +2) **K8s “base”**: namespace, storage class, ingress controller, certy (jeśli TLS). +3) **DB**: wdroż Timescale (StatefulSet + PVC), odpal `db-init` job. +4) **Hasura**: wdroż Hasurę, odpal `hasura-bootstrap` job. +5) **API**: wdroż `trade-api`, sprawdź `/healthz`. +6) **Tokeny**: wygeneruj read/write tokeny (obecnym mechanizmem API) i wgraj je jako Secrets. +7) **Frontend**: wdroż `trade-frontend` + Ingress, sprawdź `/healthz` i UI. +8) **Ingestor**: wdroż `trade-ingestor` (1 replika), potwierdź że ticki wpadają. +9) **CI/CD**: dodaj workflow build+push i deploy (GitOps albo kubectl). +10) **Staging → Prod**: rollout na staging, potem prod. + +## 8) Pytania, które domykają plan + +1) Jaki “git”: **GitHub czy GitLab**? +2) Gdzie ma stać K8s: cloud (EKS/GKE/AKS) czy on-prem/k3s? +3) DB w klastrze (StatefulSet) czy zewnętrzny managed Postgres/Timescale? +4) Czy “po pushu” ma: + - tylko robić rollout na `main`, + - czy tworzyć **preview env per branch/PR**, + - czy startować **nową wersję vN równolegle** (jak `scripts/ops/`)? +5) Jaki dostęp z zewnątrz: domena + TLS, czy wystarczy port-forward / internal? + diff --git a/migration.md b/migration.md new file mode 100644 index 0000000..55a37f7 --- /dev/null +++ b/migration.md @@ -0,0 +1,339 @@ +# Migracja `trade` do k3s + GitOps (pull) na Gitea + CI/CD + +Ten dokument opisuje plan migracji obecnego stacka (Docker Compose) do k3s z CD w modelu **pull-based** (GitOps): klaster sam synchronizuje „desired state” z repo na Gitei, a CI jedynie buduje/publikuje obrazy i aktualizuje repo deploymentu. + +## Status (VPS / k3s) + +Na VPS jest już uruchomione (k3s single-node): + +- Ingress: Traefik (80/443) +- TLS: cert-manager + `ClusterIssuer/letsencrypt-prod` +- Gitea (Ingress `https://rv32i.pl`) + registry (`https://rv32i.pl/v2/`) +- Argo CD w namespace `argocd` +- Gitea Actions runner w namespace `gitea-actions` (act_runner + DinD) +- GitOps repo `trade/trade-deploy` podpięte do Argo: + - `Application/argocd/trade-staging` (auto-sync) + - `namespace/trade-staging`: Postgres/Timescale + Hasura + pgAdmin + Job `hasura-bootstrap` + - `namespace/trade-staging`: `trade-api` + `trade-ingestor` (obrazy z registry `rv32i.pl`) + - `namespace/trade-staging`: `trade-frontend` + Ingress `trade.rv32i.pl` (basic auth, TLS OK) + +Szybka weryfikacja (VPS): + +```bash +KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n trade-staging get deploy trade-api trade-ingestor -o wide +KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n trade-staging logs deploy/trade-ingestor --tail=40 +``` + +### Tokeny `trade-api` (read/write) + +Endpointy w `trade-api` są chronione tokenami z tabeli `api_tokens`: +- `GET /v1/ticks`, `GET /v1/chart` → scope `read` +- `POST /v1/ingest/tick` → scope `write` + +W `staging` tokeny są przechowywane jako K8s Secrets (bez commitowania do gita): +- `trade-staging/Secret/trade-ingestor-tokens`: `alg.json` (write) + `heliusN.json` (RPC) +- `trade-staging/Secret/trade-read-token`: `read.json` (read) +- `trade-staging/Secret/trade-frontend-tokens`: `frontend.json` (basic auth) + `read.json` (proxy do API) + +### Dostęp: Argo CD UI (bez konfliktu z lokalnym Hasurą na 8080) + +Port-forward uruchamiasz „na żądanie” (to nie jest stały serwis). Jeśli lokalnie masz Hasurę na `8080`, użyj `8090`. + +Na VPS: + +```bash +KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n argocd port-forward --address 127.0.0.1 svc/argocd-server 8090:443 +``` + +Na swoim komputerze: + +```bash +ssh -L 8090:127.0.0.1:8090 user@rv32i.pl +``` + +UI: `https://localhost:8090` + +### Argo CD: username / hasło + +- Username: `admin` +- Hasło startowe (pobierz na VPS; nie commituj / nie wklejaj do gita): + +```bash +KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d; echo +``` + +Po pierwszym logowaniu ustaw własne hasło i (opcjonalnie) usuń `argocd-initial-admin-secret`. + +### Runner: log (czy CI działa) + +Na VPS: + +```bash +KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n gitea-actions logs deploy/gitea-act-runner -c runner --tail=80 +``` + +### Portainer (Kubernetes UI): `portainer.rv32i.pl` + +Portainer jest wdrażany przez Argo CD jako osobna aplikacja `portainer` (namespace `portainer`) z Ingressem i cert-managerem. + +Wymagania: +- DNS: rekord A `portainer.rv32i.pl` → `77.90.8.171` + +Weryfikacja na VPS: + +```bash +KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n argocd get application portainer -o wide +KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n portainer get pods,svc,ingress,certificate -o wide +``` + +Jeśli cert-manager „utknie” na HTTP-01 z powodu cache NXDOMAIN w klastrze, pomocne jest zrestartowanie CoreDNS: + +```bash +KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n kube-system rollout restart deploy/coredns +``` + +Jeśli Portainer pokaże ekran `timeout.html` (setup/login timed out), zrestartuj deployment i od razu dokończ inicjalizację: + +```bash +KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n portainer rollout restart deploy/portainer +``` + +### Frontend UI: `trade.rv32i.pl` + +Frontend jest wdrożony w `trade-staging` i ma Ingress na `trade.rv32i.pl` (Traefik + cert-manager). + +Wymagania: +- DNS: rekord A `trade.rv32i.pl` → `77.90.8.171` + +Weryfikacja na VPS: + +```bash +KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n trade-staging get deploy trade-frontend -o wide +KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n trade-staging get ingress trade-frontend -o wide +KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl -n trade-staging get certificate trade-rv32i-pl-tls -o wide +``` + +Status na dziś: +- `Certificate/trade-rv32i-pl-tls` = `Ready=True` (Let’s Encrypt) + +## 0) Stan wejściowy (co dziś jest w repo) + +Projekt ma już logiczny podział „mikroserwisowy”: + +- **DB stack**: `devops/db/docker-compose.yml` + - TimescaleDB/Postgres (`postgres`, port 5432) + - Hasura (`hasura`, port 8080) + - pgAdmin (`pgadmin`, port 5050) – raczej tylko dev/staging +- **App stack**: `devops/app/docker-compose.yml` + - `trade-api` (`api`, port 8787; health: `GET /healthz`) + - `trade-frontend` (`frontend`, port 8081; health: `GET /healthz`) + - `trade-ingestor` (`ingestor`, profil `ingest`) – worker ingestujący ticki +- **One-shot bootstrap**: `devops/tools/bootstrap/docker-compose.yml` + - `db-init`, `db-version`, `db-backfill` (SQL) + - `hasura-bootstrap` (track tabel + permissions) + +Sekrety/konfiguracja lokalnie są dziś w `tokens/*.json` (gitignored) i są montowane jako pliki do kontenerów. + +## 1) Cel migracji + +- Przeniesienie usług do k3s (Kubernetes) bez zmiany logiki aplikacji. +- Wdrożenia przez GitOps (**pull**, bez „kubectl apply” z CI). +- Powtarzalność: jedna ścieżka build → deploy, jednoznaczne tagi/digests. +- Minimalny downtime: możliwość uruchamiania wersji równolegle (v1/v2…), jak w `scripts/ops/*`. + +## 2) Założenia i decyzje do podjęcia (checklista) + +Ustal na start (zaznacz jedną opcję, resztę możesz doprecyzować później): + +1) **CD (pull)**: Argo CD czy Flux + - Argo CD + (opcjonalnie) Image Updater – popularny i „klikany” + - Flux + Image Automation – bardzo „git-first” +2) **Registry obrazów**: Gitea Registry czy osobne (Harbor / registry:2) +3) **Sekrety w GitOps**: manualne seedy (na start) czy docelowo SOPS/SealedSecrets/ExternalSecrets +4) **DB**: w klastrze (StatefulSet + PVC) czy zewnętrzny Postgres/Timescale +5) **Środowiska**: `staging` + `prod`? (opcjonalnie preview per branch/PR) +6) **Ingress/TLS**: Traefik + cert-manager + Let’s Encrypt (lub inny wariant) + +## 3) Docelowa architektura na k3s (mapowanie 1:1) + +Minimalny, praktyczny podział zasobów: + +- `timescaledb` (Postgres/Timescale): `StatefulSet` + `PVC` + `Service` +- `hasura`: `Deployment` + `Service` +- `trade-api`: `Deployment` + `Service` +- `trade-frontend`: `Deployment` + `Service` + `Ingress` +- `trade-ingestor`: `Deployment` (zwykle `replicas: 1`) albo `CronJob` (jeśli ingest ma być okresowy) +- migracje/bootstrap: + - `Job` dla `db-init`/`db-version`/`db-backfill` + - `Job` dla `hasura-bootstrap` + +Ważne: +- `trade-api` i `trade-frontend` mają gotowe endpointy `/healthz` do `readinessProbe`/`livenessProbe`. +- `trade-ingestor` potrzebuje egress do internetu (RPC do Helius/Drift). + +## 4) Podział repozytoriów (rekomendowane) + +Żeby GitOps było czyste i proste: + +1) Repo aplikacji: **`trade`** (to repo) + - kod i Dockerfile: `devops/api/Dockerfile`, `devops/ingestor/Dockerfile`, `devops/app/frontend/Dockerfile` +2) Repo deploymentu: **`trade-deploy`** (nowe repo na Gitei) + - manifesty K8s (Helm chart albo Kustomize) + - overlays per środowisko: `staging/`, `prod/` + +Zasada: CI nie wykonuje deploya do klastra; CI tylko aktualizuje `trade-deploy`, a CD w klastrze to „pulluje”. + +## 5) CI na Gitei (build → push) + +Do wyboru: +- **Gitea Actions** + `act_runner` (najbliżej GitHub Actions) +- alternatywnie Woodpecker/Drone, jeśli już masz w infra + +Zakres CI: +- zbuduj i wypchnij obrazy: + - `trade-api` + - `trade-frontend` + - `trade-ingestor` +- taguj deterministycznie, np.: + - `sha-` (zawsze) + - opcjonalnie `vN` na release +- po push obrazu: zaktualizuj `trade-deploy` (tag/digest) lub zostaw to automatyzacji obrazów (patrz niżej). + +## 6) CD (pull) z klastra: GitOps + +Wariant A (najprostszy operacyjnie): **CI robi commit do `trade-deploy`** +- CI po zbudowaniu obrazu aktualizuje `values.yaml`/`kustomization.yaml` w `trade-deploy` (tag lub digest) i robi commit/push. +- Argo CD / Flux wykrywa zmianę i robi rollout. + +Wariant B (bardziej „autopilot”): **automatyczna aktualizacja obrazów** +- Argo CD Image Updater albo Flux Image Automation obserwuje registry i sam aktualizuje repo `trade-deploy`. +- CI tylko buduje/pushuje obrazy. + +## 7) Sekrety: `tokens/*.json` → Kubernetes Secret (plikowy mount) + +Docelowo w K8s montujesz te same pliki co w Compose: + +- `tokens/hasura.json` → dla `trade-api` (admin do Hasury) +- `tokens/api.json` → dla `trade-api` (admin secret do tokenów API) +- `tokens/read.json` → dla `trade-frontend` (proxy do API) +- `tokens/frontend.json` → dla `trade-frontend` (basic auth) +- `tokens/alg.json` → dla `trade-ingestor` (write token) +- `tokens/heliusN.json` (lub `rpcUrl`) → dla `trade-ingestor` + +Nie commituj sekretów do gita. W GitOps wybierz jedną drogę: +- start: ręczne `kubectl create secret ...` na środowisko +- docelowo: SOPS (age) / SealedSecrets / ExternalSecrets (Vault itp.) + +## 8) Plan wdrożenia (kolejność kroków) + +### Etap 1: Klaster i narzędzia bazowe +1) Namespace’y (np. `trade`, `gitea`, `argocd`/`flux-system`). +2) StorageClass (na start `local-path`, docelowo rozważ Longhorn). +3) Ingress controller (Traefik w k3s) + cert-manager + ClusterIssuer. +4) Registry obrazów (Gitea Registry / Harbor / registry:2). +5) GitOps controller (Argo CD albo Flux) podpięty do `trade-deploy`. + +### Etap 2: DB + Hasura +6) Deploy TimescaleDB (StatefulSet + PVC + Service). +7) Uruchom `Job` `db-init` (idempotent) – odpowiednik `devops/tools/bootstrap:db-init`. +8) Deploy Hasura (Deployment + Service + Secret na admin/jwt/db-url). +9) Uruchom `Job` `hasura-bootstrap` – odpowiednik `devops/tools/bootstrap:hasura-bootstrap`. + +### Etap 3: App stack +10) Deploy `trade-api` (Deployment + Service, env `HASURA_GRAPHQL_URL`, `TICKS_TABLE`, `CANDLES_FUNCTION`). +11) Sprawdź `GET /healthz` po Service DNS i (opcjonalnie) z zewnątrz. +12) Wygeneruj i wgraj tokeny: + - read token dla frontendu (`tokens/read.json`) + - write token dla ingestora (`tokens/alg.json`) +13) Deploy `trade-frontend` + Ingress (TLS opcjonalnie); ustaw `API_UPSTREAM=http://trade-api:8787`. +14) Deploy `trade-ingestor` (`replicas: 1`); potwierdź, że ticki wpadają. + +### Etap 4: CI/CD end-to-end +15) Skonfiguruj runnera CI (Gitea Actions / Woodpecker). +16) Workflow CI: build+push obrazów (i ewentualny commit do `trade-deploy`). +17) Zasady promocji: staging → prod (np. tylko tag/release). + +## 9) Wersjonowanie v1/v2… (równoległe wdrożenia + cutover) + +Repo ma już pattern wersjonowania w Compose (`scripts/ops/*` + `devops/versions/vN.env`). + +Odwzorowanie na K8s: +- osobne release’y Helm (np. `trade-v1`, `trade-v2`) albo osobne namespace’y +- wspólny DB/Hasura bez zmian +- `Job` „version-init”: + - uruchamia SQL `db-version` (tworzy `drift_ticks_vN` i funkcje) + - uruchamia `hasura-bootstrap` z `TICKS_TABLE`/`CANDLES_FUNCTION` ustawionymi na vN +- cutover: + - start `trade-ingestor` vN + - stop `trade-ingestor` v1 + - (opcjonalnie) `db-backfill` jako osobny job + +## 10) Definition of Done (DoD) + +- `trade-api` i `trade-frontend` działają na k3s (proby `Ready=True`, brak crashloopów). +- `trade-ingestor` zapisuje ticki do właściwej tabeli, a UI pokazuje wykres. +- Deploy jest sterowany przez GitOps (zmiana w `trade-deploy` → rollout bez ręcznego `kubectl apply`). +- Sekrety są poza gitem (manualnie lub przez wybraną metodę GitOps). + +## 11) Referencje w repo + +- Compose: `devops/db/docker-compose.yml`, `devops/app/docker-compose.yml`, `devops/tools/bootstrap/docker-compose.yml` +- Workflow end-to-end: `doc/workflow-api-ingest.md` +- Szkic migracji K8s: `doc/k8s-migracja.md` +- Gitea na k3s (Traefik/TLS): `doc/gitea-k3s-rv32i.md` + +## 12) Metoda „superproject” (subrepo) – plan refaktoru repozytoriów + +Cel: utrzymać osobne repo dla usług (API/ingestor/frontend) i osobne repo GitOps (`trade-deploy`), a do tego mieć jedno repo „główne” (superproject) spinające wszystko (np. jako **git submodules**). + +### Wybór repo głównego + +Rekomendacja: **`trade/trade-infra` jako repo główne (superproject)**: +- jest neutralne (infra/ops), nie miesza kodu aplikacji z manifestami GitOps, +- pozwala trzymać jedną „zafiksowaną” wersję całego systemu (SHAs submodułów), +- nie wymaga zmiany Argo CD (który nadal może śledzić `trade/trade-deploy`). + +### Docelowy podział ról repo + +- `trade/trade-api` – kod + Dockerfile dla `trade-api` +- `trade/trade-ingestor` – kod + Dockerfile dla `trade-ingestor` +- `trade/trade-frontend` – kod + Dockerfile dla `trade-frontend` (apps/visualizer + serwer) +- `trade/trade-deploy` – GitOps: Kustomize/Helm dla k3s (Argo CD pull) +- `trade/trade-doc` – dokumentacja całego projektu (`doc/`), w tym log działań +- `trade/trade-infra` – superproject + narzędzia/ops (np. makefile, skrypty, checklisty) + +### Proponowany układ w superprojekcie + +Przykład (ścieżki w `trade/trade-infra`): + +```text +trade-infra/ + api/ -> submodule: trade-api + ingestor/ -> submodule: trade-ingestor + frontend/ -> submodule: trade-frontend + deploy/ -> submodule: trade-deploy + doc/ -> submodule: trade-doc +``` + +### Plan wdrożenia refaktoru (po akceptacji) + +1) Zainicjalizować `trade/trade-infra` jako superproject i dodać submodule do wszystkich subrepo. +2) Wyciąć obecny kod do subrepo: + - API: `services/api/*` + `devops/api/Dockerfile` → `trade-api/` + - Frontend: `apps/visualizer/*` + `services/frontend/*` + `devops/app/frontend/*` → `trade-frontend/` + - Ingestor: `scripts/ingest-drift-oracle.ts` (+ minimalny zestaw zależności i Dockerfile) → `trade-ingestor/` +3) Przenieść dokumentację do `trade-doc` (w tym log: `doc/steps.md`) i zostawić w pozostałych repo linki do docs. +4) Ujednolicić nazwy obrazów i tagowanie: + - `rv32i.pl/trade/trade-api:` + - `rv32i.pl/trade/trade-ingestor:` + - `rv32i.pl/trade/trade-frontend:` +5) CI/CD (Gitea Actions) per repo usługi: + - build → push do registry, + - aktualizacja `trade-deploy` (commit/PR z bumpem tagu/digesta), + - staging auto-sync w Argo (prod manual). +6) Walidacja end-to-end na k3s: + - rollout `trade-staging`, + - UI `https://trade.rv32i.pl`, + - ingest ticków (`trade-ingestor` → `trade-api`). + +Uwaga: lokalnie repo nie ma historii commitów, więc refaktor będzie startem „od zera” w nowych repo (initial commit per subrepo). diff --git a/spot-mvp.html b/spot-mvp.html new file mode 100644 index 0000000..012b410 --- /dev/null +++ b/spot-mvp.html @@ -0,0 +1,211 @@ + + + + + + Bot SPOT na Solanie: skan 10/50 par (MVP) + + + +
+
+

Bot SPOT na Solanie (MVP)

+

+ Analiza dziesiątek par → ranking → wybór TOP 10 sygnałów wejścia. Start na SPOT zwykle + jest bezpieczniejszy i prostszy niż PERPS. +

+
+ +
+
+

Dlaczego SPOT jest lepszy na start praktycznie

+
    +
  • Brak likwidacji (największy “killer” na perpsach).
  • +
  • Prostsze koszty: swap fee + slippage + fee Solany.
  • +
  • Łatwiej zrobić stabilny ranking (płynność / volatility / volume) i testować strategię.
  • +
+
+ +
+

Jak to zrobić praktycznie na Solanie (stack)

+
    +
  • Jupiter jako egzekucja swapów (routing po DEX-ach, najlepsza cena).
  • +
  • Helius jako RPC do wysyłki transakcji + monitoring (potwierdzenia, logi).
  • +
  • + Dane do sygnałów: +
      +
    • najprościej: OHLCV + volume z zewnętrznych market data (szybko),
    • +
    • albo: on-chain (trudniej, ale najbardziej “raw”).
    • +
    +
  • +
+
+ +
+

Zasady bezpieczeństwa na SPOT (żeby bot nie palił kapitału)

+
    +
  1. Filtr płynności: nie wchodź w pary z małą płynnością (slippage zabije edge).
  2. +
  3. Limit slippage: ustaw twardy max (np. 0.3–1% zależnie od tokenów).
  4. +
  5. Cooldown po transakcji: unikniesz “mielenia” (overtrading).
  6. +
  7. Blacklist: wyklucz tokeny z anomaliami (freeze, podatki transferowe, honeypoty itd.).
  8. +
  9. Rozmiar pozycji ∝ płynność: im mniejsza płynność, tym mniejszy notional.
  10. +
+
+ +
+

Prosty wzór rankingu “TOP 10” na SPOT (na start)

+
Score_i = μ_i - (fee_i + slip_i + net_i) - λ · risk_i
+
    +
  • μ_i = przewidywany zwrot z Twojego sygnału (np. momentum / mean reversion)
  • +
  • fee_i = koszt swapów (wejście + wyjście)
  • +
  • slip_i = oczekiwany slippage / price impact (wejście + wyjście)
  • +
  • net_i = koszt sieci / priority (mały, ale rośnie przy częstych trade’ach)
  • +
  • risk_i = kara za ryzyko (np. ATR/volatility)
  • +
  • λ = jak mocno karzesz ryzyko
  • +
+
+ +
+

Kiedy przechodzić na PERPS (Drift)

+

+ Dopiero gdy na SPOT masz stabilny pipeline danych, umiesz liczyć realny koszt wejścia/wyjścia i + masz sensowny EV/winrate — wtedy warto dodać shorty + dźwignię na perpsach. +

+
    +
  • stabilny pipeline danych
  • +
  • realne koszty wejścia/wyjścia (slippage + fees)
  • +
  • sensowny winrate/EV
  • +
  • chęć dodania shortów + dźwigni
  • +
+
+
+
+ + diff --git a/steps.md b/steps.md new file mode 100644 index 0000000..738d21d --- /dev/null +++ b/steps.md @@ -0,0 +1,216 @@ +# steps.md + +Log działań wykonywanych w ramach migracji `trade` → k3s + Gitea + GitOps (pull). +Uwaga: **nie zapisuję sekretów** (hasła, tokeny, prywatne klucze) – jeśli pojawiają się w outputach narzędzi, są pomijane/redagowane. + +## 2026-01-05 + +### Repo: inwentaryzacja i plan +- Przejrzano strukturę repo (`apps/`, `devops/`, `services/`, `doc/`) i istniejące Compose stacki: + - DB: `devops/db/docker-compose.yml` + - App: `devops/app/docker-compose.yml` + - Bootstrap (one-shot): `devops/tools/bootstrap/docker-compose.yml` +- Sprawdzono Dockerfile: + - API: `devops/api/Dockerfile` + - Frontend: `devops/app/frontend/Dockerfile` + - Ingestor: `devops/ingestor/Dockerfile` +- Sprawdzono skrypty wersjonowania Compose (v1/v2…): `scripts/ops/*` + env: `devops/versions/v1.env`. +- Dodano dokument planu migracji: `doc/migration.md`. + +### Gitea: repo GitOps (MCP Gitea) +- Utworzono repo `trade/trade-deploy` (public, `main`, auto-init). +- Dodano szkielet Kustomize: + - `kustomize/base/` (placeholder ConfigMap) + - `kustomize/overlays/staging/` (`namespace: trade-staging`) + - `kustomize/overlays/prod/` (`namespace: trade-prod`) +- Dodano bootstrap manifesty Argo CD Application: + - `bootstrap/argocd/application-trade-staging.yaml` (auto-sync + CreateNamespace) + - `bootstrap/argocd/application-trade-prod.yaml` (CreateNamespace, bez auto-sync) +- Dodano DB stack do `trade-deploy` (manifests + bootstrap): + - Postgres/Timescale: `kustomize/base/postgres/*` + init SQL: `kustomize/base/initdb/001_init.sql` + - Hasura: `kustomize/base/hasura/*` + job `hasura-bootstrap` (track tables/functions) + - Staging: `kustomize/overlays/staging/pgadmin.yaml` + patch Hasury (console/dev mode) + - Zaktualizowano `README.md` w `trade-deploy` o wymagane sekrety i port-forward. + +### VPS: audyt stanu (MCP SSH) +- Host: `qstack` (Debian), k3s działa; dostęp do klastra uzyskany przez `KUBECONFIG=/etc/rancher/k3s/k3s.yaml` (bez sudo). +- Ingress: Traefik działa (LB na `77.90.8.171`, porty 80/443). +- TLS: cert-manager działa; `ClusterIssuer/letsencrypt-prod` jest `Ready=True`. +- Gitea jest zainstalowana w k3s (Helm release `gitea` w namespace `gitea`), Ingress na `rv32i.pl`, cert `rv32i-pl-tls` jest `Ready=True`. +- GitOps controller (Argo CD / Flux): **nie znaleziono** (brak namespace `argocd` i `flux-system`). +- Wdrożenia aplikacji `trade-*` w k3s: **nie znaleziono** (brak deploy/podów z nazwą `trade`). +- Registry: endpoint `https://rv32i.pl/v2/` odpowiada (401 bez autoryzacji). + +### Gitea: audyt stanu (MCP Gitea) +- Zalogowany użytkownik: `u1` (admin). +- Organizacja `trade` istnieje; są utworzone repozytoria (puste, bez commitów/branchy): + - `trade/trade-api` + - `trade/trade-frontend` + - `trade/trade-ingestor` + - `trade/trade-infra` +- Repo `trade-deploy` (manifesty GitOps): **nie znaleziono**. + +### VPS: Argo CD (MCP SSH) +- Zainstalowano Argo CD przez Helm: release `argocd` w namespace `argocd` (`argo/argo-cd`). +- Utworzono `Application` dla staging z `trade-deploy`: + - zastosowano `bootstrap/argocd/application-trade-staging.yaml` + - Argo utworzyło namespace `trade-staging` i zsynchronizowało placeholder ConfigMap `trade-deploy-info`. +- Uwaga: jeśli port `8080` jest zajęty lokalnie (np. przez Hasurę), do port-forward użyj innego portu (np. `8090:443`). + +### VPS: Gitea Actions runner (MCP SSH) +- Wykryto wymaganie: `sudo` wymaga hasła, więc runner postawiony w k3s (bez instalacji binarki na hoście). +- Utworzono namespace `gitea-actions` oraz zasoby dla `act_runner` (DinD sidecar + runner): + - manifest w `trade-deploy`: `bootstrap/gitea-actions/act-runner.yaml` + - sekret `act-runner-registration-token` utworzony w klastrze z tokena rejestracyjnego pobranego z Gitei (API admin; token nie trafia do gita) +- Naprawiono start Dockera w sidecar: + - początkowo próba po unix-sockecie powodowała konflikt (`docker.sock` jako katalog) + - finalnie ustawiono `DOCKER_HOST=tcp://127.0.0.1:2375` (runner ↔ dind po localhost), co uruchomiło runnera poprawnie. + +### Uwaga: seed sekretów (blokada narzędzia) +- Przy próbie seedowania sekretów do `trade-staging` przez MCP SSH pojawił się trwały timeout na każde polecenie (`ssh-mcp/exec`), więc ten krok został opisany w `trade-deploy/README.md` do wykonania ręcznie na VPS. + +### VPS: dostęp SSH kluczem (wymagane hasło) +- Użytkownik poprosił o używanie naszego klucza prywatnego dla VPS: `k/qstack/qstack`. +- Klucz jest zaszyfrowany hasłem (passphrase). Bez podania passphrase nie da się go użyć w trybie nieinteraktywnym (`ssh`/`ssh-keygen` zwraca błąd o niepoprawnym passphrase / brak `ssh-askpass`). +- Żeby kontynuować automatyzację z tej sesji są 2 opcje: + 1) dostarczyć passphrase poza czatem (np. lokalnie w pliku w `tokens/`, gitignored) i używać `SSH_ASKPASS`, albo + 2) wygenerować osobny klucz bez passphrase jako “deploy key” tylko do automatyzacji i dodać jego publiczną część do `~user/.ssh/authorized_keys` na VPS. + +### Następne kroki (do zrobienia) +- Wybrać GitOps controller (Argo CD vs Flux) i zainstalować w k3s. +- Utworzyć repo `trade-deploy` i dodać strukturę Helm/Kustomize (staging/prod). +- Postawić runner CI (Gitea Actions `act_runner` lub alternatywa) i dodać pipeline build+push obrazów. +- Dopiero potem: manifesty K8s dla `trade-api`/`trade-frontend`/`trade-ingestor` + DB/Hasura + joby migracji. + +## 2026-01-06 + +### VPS: SSH kluczem projektu (passphrase) +- Użyto klucza prywatnego `k/qstack/qstack` do połączenia z VPS przez `ssh` (z `SSH_ASKPASS` czytającym passphrase z `tokens/vps-ssh.json`, plik gitignored). +- Skrypt `tokens/ssh-askpass.sh` był tworzony tymczasowo i został usunięty po użyciu (żeby nie ryzykować przypadkowego commita). + +### Doprecyzowanie dostępu i bezpieczeństwo repo +- Dopisano w `doc/migration.md` bieżący status VPS/k3s oraz komendy do Argo CD (port-forward na `8090`) i logów runnera. +- Zaktualizowano `.gitignore`, żeby ignorować sekrety/klucze lokalne: `tokens/*`, `k/`, `argo/pass`, `gitea/token`. + +### k3s: seed sekretów (staging) +- Utworzono sekrety w namespace `trade-staging` (wartości wygenerowane losowo, nie logowane): + - `trade-postgres` (`POSTGRES_*`) + - `trade-hasura` (`HASURA_GRAPHQL_ADMIN_SECRET`, `HASURA_JWT_KEY`) + - `trade-pgadmin` (`PGADMIN_DEFAULT_*`) + +### Argo CD: deploy DB stack (staging) +- Wymuszono refresh aplikacji Argo `trade-staging` (annotation `argocd.argoproj.io/refresh=hard`). +- Status: `trade-staging` = `Synced` / `Healthy`. +- Workloady w `trade-staging` działają: + - `StatefulSet/postgres` = `postgres-0 Running` + - `Deployment/hasura` = `Running` + - `Deployment/pgadmin` = `Running` + - `Job/hasura-bootstrap` = `Completed` (log: `[hasura-bootstrap] ok`) + +### Portainer: dlaczego nie widać k3s (wyjaśnienie) +- Portainer uruchomiony jako kontener Docker domyślnie widzi tylko środowisko Docker; k3s używa `containerd`, więc nie zobaczysz „podów” w `docker ps` ani w Portainerze jako kontenerów Dockera. +- Żeby Portainer widział „wnętrze” k3s trzeba dodać środowisko typu `Kubernetes` w Portainer UI albo postawić Portainera bezpośrednio w klastrze (rekomendowane). + +### Portainer (opcja A): wdrożenie w k3s + Ingress `portainer.rv32i.pl` +- Dodano do GitOps repo `trade/trade-deploy` paczkę Kustomize: `kustomize/infra/portainer` (Namespace+RBAC+PVC+Deployment+Service+Ingress). +- Dodano Argo CD `Application` dla Portainera: `bootstrap/argocd/application-portainer.yaml`. +- Zastosowano `Application` na VPS i potwierdzono status: `portainer` = `Synced` / `Healthy`. +- Ingress utworzony na host `portainer.rv32i.pl` (Traefik). +- cert-manager utworzył `Certificate/portainer-rv32i-pl-tls`, ale ACME jest `pending` dopóki DNS `portainer.rv32i.pl` nie wskazuje na `77.90.8.171` (brak rekordu A w momencie sprawdzania). + +### Portainer: DNS + TLS + odblokowanie „New Portainer installation timed out” +- Potwierdzono, że rekord A `portainer.rv32i.pl` istnieje na autorytatywnych DNS (`dns*.home.pl`) i rozwiązuje się na publicznych resolverach. +- Zrestartowano `Deployment/portainer` w namespace `portainer`, żeby odblokować ekran inicjalizacji (komunikat “timed out for security purposes”). +- cert-manager miał „zawieszone” HTTP-01 self-check przez cache NXDOMAIN w klastrze; zrestartowano `Deployment/coredns` w `kube-system`, po czym certyfikat stał się `Ready=True` (`Certificate/portainer-rv32i-pl-tls`). + +### Registry: token do Gitea Packages + docker login +- Na VPS (z poziomu poda Gitei) wygenerowano token `read:package,write:package` dla użytkownika `u1`: + - polecenie: `gitea admin user generate-access-token --username u1 --scopes "read:package,write:package" --raw` + - token zapisany lokalnie w `tokens/gitea-registry.token` (gitignored; wartość nie jest logowana). +- Wykonano `docker login rv32i.pl` (bez logowania tokena). +- Zaktualizowano `.dockerignore`, żeby nie wysyłać do build context katalogów z sekretami/kluczami (`tokens/*`, `k/`, `gitea/`, `argo/`). + +### Obrazy: build + push (trade-api, trade-ingestor) +- Zbudowano i wypchnięto obrazy do Gitea registry: + - `rv32i.pl/trade/trade-api:k3s-20260106013603` + - `rv32i.pl/trade/trade-ingestor:k3s-20260106013603` +- Tag zapisany lokalnie w `tokens/last-image-tag.txt` (gitignored). + +### GitOps: manifesty k3s dla API + ingestor (trade-deploy) +- Dodano do repo `trade/trade-deploy`: + - `kustomize/base/api/deployment.yaml` + `kustomize/base/api/service.yaml` + - `kustomize/base/ingestor/deployment.yaml` + - aktualizacja `kustomize/base/kustomization.yaml` (dopięcie nowych zasobów). +- Konfiguracja: + - `trade-api` używa `HASURA_GRAPHQL_URL=http://hasura:8080/v1/graphql` i sekretów `trade-hasura` + `trade-api` (admin). + - `trade-ingestor` startuje w trybie `INGEST_VIA=hasura` (bez tokenów API) i używa `trade-hasura` + `trade-ingestor-tokens` (RPC). + +### k3s: seedy sekretów + weryfikacja (staging) +- Utworzono w `trade-staging`: + - `Secret/gitea-registry` (imagePullSecret do `rv32i.pl`) + - `Secret/trade-api` (`API_ADMIN_SECRET`, wygenerowany losowo; nie logowany) + - `Secret/trade-ingestor-tokens` (plik `heliusN.json`; wartość nie logowana) +- Argo CD `Application/trade-staging` po refresh: `Synced` / `Healthy`. +- Pody w `trade-staging` uruchomione: `trade-api` i `trade-ingestor` (`Running`). +- Logi: + - `trade-api` startuje i maskuje sekrety (`***`). + - `trade-ingestor` pobiera ceny i ingestuje ticki; URL RPC jest redagowany (`api-key=***`). + +### Ingest przez API + tokeny read/write +- Zmieniono `trade-ingestor` na `INGEST_VIA=api` (pisze do `trade-api` zamiast bezpośrednio do Hasury); manifest w `trade/trade-deploy`: `kustomize/base/ingestor/deployment.yaml`. +- Utworzono tokeny w `trade-api`: + - `write` (dla ingestora) oraz `read` (dla klientów UI/odczytu) + - tokeny zapisane jako K8s Secrets (wartości nie logowane): + - `trade-staging/Secret/trade-ingestor-tokens`: `alg.json` (write) + `heliusN.json` (RPC) + - `trade-staging/Secret/trade-read-token`: `read.json` (read) +- Wymuszono rollout `Deployment/trade-ingestor` i potwierdzono w logach: + - `ingestVia: "api"`, `writeUrl: "http://trade-api:8787/v1/ingest/tick"`, `auth: "bearer"`. +- Potwierdzono, że `trade-api /v1/ticks` działa z tokenem `read` (bez ujawniania tokena). + +### Frontend: deploy na k3s (staging) + Ingress `trade.rv32i.pl` +- Zbudowano i wypchnięto obraz do Gitea registry: + - `rv32i.pl/trade/trade-frontend:k3s-20260106013603` +- Dodano manifesty do `trade/trade-deploy`: + - `kustomize/base/frontend/deployment.yaml` + `kustomize/base/frontend/service.yaml` + - staging: `kustomize/overlays/staging/frontend-ingress.yaml` (host `trade.rv32i.pl`) + - aktualizacje `kustomize/base/kustomization.yaml` i `kustomize/overlays/staging/kustomization.yaml`. +- Utworzono sekret `trade-staging/Secret/trade-frontend-tokens` (pliki `frontend.json` + `read.json`; wartości nie logowane). +- Argo CD `Application/trade-staging`: `Synced` / `Healthy`; `Deployment/trade-frontend` = `Running`, `/healthz` działa. +- TLS dla `trade.rv32i.pl` jest `pending` dopóki DNS `trade.rv32i.pl` nie wskazuje na `77.90.8.171` (w razie cache NXDOMAIN: restart `kube-system/coredns` jak wcześniej). + +### DNS: `trade.rv32i.pl` (weryfikacja) +- Sprawdzono rekord A `trade.rv32i.pl` na autorytatywnych DNS (`dns*.home.pl`) oraz na publicznych resolverach: **brak odpowiedzi** (rekord nie był jeszcze widoczny na NS), więc cert-manager trzyma `trade-rv32i-pl-tls` w stanie `pending` (NXDOMAIN). +- Uwaga: jeśli w panelu DNS dodasz rekord i nadal masz `NXDOMAIN`, sprawdź czy host nie ma literówki (np. `rade` zamiast `trade`) i czy rekord jest zapisany jako A/CNAME dla właściwej nazwy `trade.rv32i.pl`. + +### DNS/TLS: `trade.rv32i.pl` (finalizacja) +- Użytkownik potwierdził rekord A `trade.rv32i.pl` → `77.90.8.171` na autorytatywnych DNS (`dns.home.pl`). +- Na VPS potwierdzono: + - `Ingress/trade-frontend` ma host `trade.rv32i.pl`. + - `Certificate/trade-rv32i-pl-tls` = `Ready=True`. + - `https://trade.rv32i.pl` odpowiada `HTTP 401` (basic auth) – czyli Ingress + TLS działają. + +### Weryfikacja wdrożenia (MCP SSH) +- `argocd/Application`: `trade-staging` i `portainer` = `Synced` / `Healthy`. +- `trade-staging`: wszystkie workloady `Running` (Postgres/Hasura/pgAdmin/trade-api/trade-ingestor/trade-frontend). +- `trade-ingestor` ma `INGEST_VIA=api` oraz `INGEST_API_URL=http://trade-api:8787` (ingest przez API z tokenem write). + +### Portainer: odblokowanie ekranu `timeout.html` +- Zrestartowano `Deployment/portainer` w namespace `portainer`, żeby ponownie pojawił się kreator inicjalizacji (admin user/password). + +### Gitea: lista repo w organizacji `trade` +- `trade/trade-api` +- `trade/trade-deploy` +- `trade/trade-doc` +- `trade/trade-frontend` +- `trade/trade-infra` +- `trade/trade-ingestor` + +## 2026-01-06 + +### Zmiana: log działań w `doc/` +- Przeniesiono log działań z `steps.md` → `doc/steps.md` (zgodnie z nową zasadą: wszystko projektowe ląduje w `doc/`). + +### Superproject: decyzja + plan (do akceptacji) +- Zaproponowano `trade/trade-infra` jako repo główne (superproject) spinające subrepo (API/ingestor/frontend/deploy/doc). +- Plan refaktoru opisany w `doc/migration.md` (sekcja „Metoda superproject”). +- Użytkownik zaakceptował `trade/trade-infra` jako superproject; do decyzji pozostało: `submodules` vs `subtree` (rekomendacja: submodules, jeśli chcemy zachować niezależne repo + pinowanie wersji). diff --git a/t2 b/t2 new file mode 100644 index 0000000..0f14b60 --- /dev/null +++ b/t2 @@ -0,0 +1,37 @@ +cat > gitea-values.yaml <<'YAML' +ingress: + enabled: true + className: traefik + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + hosts: + - host: rv32i.pl + paths: + - path: / + pathType: Prefix + tls: + - secretName: rv32i-pl-tls + hosts: + - rv32i.pl +gitea: + admin: + existingSecret: gitea-admin + config: + server: + DOMAIN: rv32i.pl + ROOT_URL: https://rv32i.pl/ + PROTOCOL: http +persistence: + enabled: true + storageClass: local-path + size: 10Gi +postgresql: + enabled: true + primary: + persistence: + enabled: true + storageClass: local-path + size: 10Gi +postgresql-ha: + enabled: false +YAML \ No newline at end of file diff --git a/workflow-api-ingest.md b/workflow-api-ingest.md new file mode 100644 index 0000000..c12b7ea --- /dev/null +++ b/workflow-api-ingest.md @@ -0,0 +1,171 @@ +# Workflow: init bazy + app stack + ingest przez API (tokeny + basic auth) + +Docelowy podział na 3 compose’y: +1) `devops/db/docker-compose.yml` → **Postgres/TimescaleDB + Hasura + pgAdmin** (DB stack) +2) `devops/tools/bootstrap/docker-compose.yml` → **one-shot tools** (init SQL + Hasura metadata) +3) `devops/app/docker-compose.yml` → **API + frontend (+ opcjonalny ingestor)** (app stack) + +## TL;DR (local, end-to-end) +1) `npm install` +2) DB: `docker compose -f devops/db/docker-compose.yml up -d` +3) Schema/metadata: `docker compose -f devops/tools/bootstrap/docker-compose.yml run --rm db-init` + `docker compose -f devops/tools/bootstrap/docker-compose.yml run --rm hasura-bootstrap` +4) Tokeny: skopiuj `tokens/*.example.json` → `tokens/*.json` + ustaw RPC w `tokens/heliusN.json` (albo po prostu `cp tokens/helius.json tokens/heliusN.json`) +5) API: `docker compose -f devops/app/docker-compose.yml up -d api` + `curl -sS http://localhost:8787/healthz` +6) Tokeny (revocable): `npm run token:api -- --scopes write --out tokens/alg.json` + `npm run token:api -- --scopes read --out tokens/read.json` +7) Frontend: `docker compose -f devops/app/docker-compose.yml up -d frontend` +8) Ingestor: `docker compose -f devops/app/docker-compose.yml --profile ingest up -d` + +Uwaga: ceny w `drift_ticks` są `NUMERIC` (Hasura zwykle zwraca/oczekuje stringów dla `numeric`); `trade-api` normalizuje je do `number` w `/v1/chart`. + +## Migracja (bez kasowania danych) +Szybka sekwencja do migracji schematu + odpalenia stacka (bez `down -v`) jest w `r1.txt`. + +## Wersjonowanie API/UI (v2, v3...) bez ruszania DB/Hasury +Cel: uruchamiasz nową wersję `api+frontend(+ingestor)` równolegle, na **nowych tabelach**, na **nowych portach**, a potem robisz cutover ingestora. + +Konwencja: +- v1: tabela `drift_ticks`, funkcja `get_drift_candles`, porty `8787/8081` +- vN: tabela `drift_ticks_vN`, funkcja `get_drift_candles_vN`, porty `8787+(N-1)` i `8081+(N-1)` +- wspólne: Postgres/Hasura/pgAdmin + `api_tokens` (tokeny) zostają bez wersjonowania + +Skrypty: `scripts/ops/` (tworzą też env w `devops/versions/vN.env`) +- `bash scripts/ops/version-init.sh 2` migracja DB + create `drift_ticks_v2` + track w Hasurze +- `bash scripts/ops/version-up.sh 2` start `api+frontend` v2 (host: `8788/8082`) +- `bash scripts/ops/version-ingestor-up.sh 2` start ingestor v2 (pisze do v2 API → `drift_ticks_v2`) +- `bash scripts/ops/version-cutover.sh 1 2` start v2 ingestor, potem stop v1 ingestor +- `bash scripts/ops/version-backfill.sh 1 2` backfill ALL: `drift_ticks` → `drift_ticks_v2` +- `bash scripts/ops/version-down.sh 1` usuń stare kontenery v1 (DB bez zmian) +- `bash scripts/ops/version-check.sh 2` healthz/ticks/chart dla v2 + +## Reset (UWAGA: kasuje dane z bazy) +Jeśli uruchomisz `docker compose -f devops/db/docker-compose.yml down -v`, Postgres/Timescale traci cały wolumen (czyli rekordy z `drift_ticks`). + +## Dane logowania (dev) +- Postgres: `postgres://admin:pass@localhost:5432/crypto` +- Hasura: `http://localhost:8080` (admin secret: `tokens/hasura.json` → `adminSecret`) +- pgAdmin: `http://localhost:5050` (login: `admin@example.com`, hasło: `admin`) +- Frontend UI: `http://localhost:8081` (basic auth: `tokens/frontend.json` → `username`/`password`) + +## 0) Start DB stack +Z root repo: +```bash +docker compose -f devops/db/docker-compose.yml up -d +``` +Ten krok tworzy wspólną sieć Dockera `trade-net`, przez którą app stack łączy się do Hasury. + +## 1) Init / upgrade schematu bazy (idempotent) +Użyj zwłaszcza, jeśli masz istniejący wolumen Postgresa: +```bash +docker compose -f devops/tools/bootstrap/docker-compose.yml run --rm db-init +``` +To jest też krok, który trzeba odpalić po zmianach w `devops/db/initdb/001_init.sql` (np. migracja `DOUBLE PRECISION` → `NUMERIC`). + +## 2) Hasura metadata bootstrap (track tabel + permissions) +```bash +docker compose -f devops/tools/bootstrap/docker-compose.yml run --rm hasura-bootstrap +``` + +## 3) Skonfiguruj API + frontend +Utwórz lokalne pliki (gitignored) w `tokens/`: +- `tokens/hasura.json` z `tokens/hasura.example.json` (URL + admin secret dla Hasury, używane przez `trade-api`) +- `tokens/api.json` z `tokens/api.example.json` (sekret admina do tworzenia/revoke tokenów API) +- `tokens/frontend.json` z `tokens/frontend.example.json` (basic auth do UI) +- `tokens/heliusN.json` (RPC dla Drift; albo `heliusApiKey`, albo pełny `rpcUrl`) + +Jeśli odpalasz lokalne skrypty (`npm run token:*`, `npm run ingest:*`) wykonaj też: +```bash +npm install +``` + +## 4) Start API (trade-api) +```bash +docker compose -f devops/app/docker-compose.yml up -d api +``` + +- API: `http://localhost:8787` + +Healthcheck: +```bash +curl -sS http://localhost:8787/healthz +``` + +## 5) Wygeneruj tokeny (revocable) +**Write token** dla ingestora (domyślnie zapisuje do `tokens/alg.json`): +```bash +npm run token:api -- --name algo1 --scopes write --out tokens/alg.json +``` + +Jeśli wcześniej uruchomiłeś `frontend` bez pliku `tokens/read.json`, Docker mógł utworzyć katalog `tokens/read.json`. +Usuń go przed generacją tokena: +```bash +rm -rf tokens/read.json +``` + +**Read token** dla frontendu (proxy wstrzykuje go serwerowo; nie trafia do JS): +```bash +npm run token:api -- --name reader-ui --scopes read --out tokens/read.json +``` + +## 6) Start frontend (UI) +```bash +docker compose -f devops/app/docker-compose.yml up -d frontend +``` + +- Frontend: `http://localhost:8081` (basic auth) + +Dev mode (Vite + proxy `/api` z tokenem z `tokens/read.json`): +```bash +npm run visualizer:dev +``` +- Dev UI: `http://localhost:5173` + +## 7) Uruchom kontener ingestora, który wysyła ticki do API +W ramach app stack (profil `ingest`): +```bash +docker compose -f devops/app/docker-compose.yml --profile ingest up -d +``` + +Alternatywnie (bez Dockera) możesz odpalić ingest lokalnie: +```bash +npm run ingest:oracle -- --ingest-via api --market-name PUMP-PERP --interval-ms 1000 --source drift_oracle +``` +Wymaga: `tokens/heliusN.json` (RPC) + `tokens/alg.json` (write token do API) + działającego `trade-api`. + +Override (przykład): +```bash +MARKET_NAME=PUMP-PERP INTERVAL_MS=250 SOURCE=drift_oracle \ +docker compose -f devops/app/docker-compose.yml --profile ingest up -d +``` + +## 8) Sprawdź, czy rekordy wpadają +Przez API (wymaga read tokena): +```bash +curl -sS "http://localhost:8787/v1/ticks?symbol=PUMP-PERP&limit=5" \ + -H "Authorization: Bearer $(node -p 'require("./tokens/read.json").token')" +``` + +W UI: `http://localhost:8081` → wykres powinien się aktualizować. + +Sprawdź agregacje + wskaźniki (candles + indicators z backendu): +```bash +curl -sS "http://localhost:8787/v1/chart?symbol=PUMP-PERP&tf=1m&limit=120" \ + -H "Authorization: Bearer $(node -p 'require("./tokens/read.json").token')" +``` + +## Checklist (copy/paste) +- `npm install` zainstaluj zależności do `npm run token:*` / `npm run ingest:*` +- `cp tokens/hasura.example.json tokens/hasura.json` skopiuj konfigurację Hasury +- `cp tokens/api.example.json tokens/api.json` skopiuj konfigurację API (adminSecret) +- `cp tokens/frontend.example.json tokens/frontend.json` skopiuj basic auth do UI +- `cp tokens/helius.json tokens/heliusN.json` ustaw RPC dla Drift (fallback jest też `tokens/helius.json`) +- `docker compose -f devops/db/docker-compose.yml up -d` uruchom Postgres/Timescale + Hasura + pgAdmin +- `docker compose -f devops/tools/bootstrap/docker-compose.yml run --rm db-init` zastosuj schemat PG (tabele/indeksy/hypertable) +- `docker compose -f devops/tools/bootstrap/docker-compose.yml run --rm hasura-bootstrap` track tabel + permissions w Hasurze +- `docker compose -f devops/app/docker-compose.yml up -d api` uruchom API +- `curl -sS http://localhost:8787/healthz` sprawdź czy API działa +- `npm run token:api -- --name algo1 --scopes write --out tokens/alg.json` wygeneruj write token dla ingestora +- `npm run token:api -- --name reader-ui --scopes read --out tokens/read.json` wygeneruj read token dla UI/curl +- `docker compose -f devops/app/docker-compose.yml up -d frontend` uruchom frontend +- `docker compose -f devops/app/docker-compose.yml --profile ingest up -d` uruchom ingestora (Drift → API → DB) +- `curl -sS "http://localhost:8787/v1/ticks?symbol=PUMP-PERP&limit=5" -H "Authorization: Bearer $(node -p 'require("./tokens/read.json").token')"` sprawdź czy ticki wpadają +- `curl -sS "http://localhost:8787/v1/chart?symbol=PUMP-PERP&tf=1m&limit=120" -H "Authorization: Bearer $(node -p 'require("./tokens/read.json").token')"` sprawdź candles + wskaźniki z backendu