Talus License Server — Ops Runbook
Gdzie co stoi, jak odtworzyć, jak testować failover. Sekrety NIGDY nie są
w tym pliku — tylko wskaźniki na ~/.secrets/talus/.
Topologia (stan na 2026-09-18)
┌──────────────────────────────┐
talus binary ──●──► │ PRIMARY │
(failover list │ talus-license-server │──┐
w license.rs) │ Cloudflare Worker (free) │ │
└──────────────────────────────┘ │ ┌─────────────────────────┐
┌──────────────────────────────┐ ├──► │ TURSO (libSQL, free) │
talus binary ──●──► │ FAILOVER │──┘ │ talus-licenses │
(drugi element │ talus-license-failover │ │ aws-eu-west-1 (Irlandia)│
listy) │ Cloudflare Worker (free) │ └─────────────────────────┘
└──────────────────────────────┘
┌──────────────────────────────┐
│ D1 talus-licenses │ FROZEN SNAPSHOT (backup,
│ 77c07193-…-616a25ad9fb9 │ nie obsługuje ruchu)
└──────────────────────────────┘
| Komponent | URL / lokalizacja | Storage | Rola |
|---|---|---|---|
| Primary | https://talus-license-server.metaforicmail.workers.dev |
Turso (wspólny) | ruch produkcyjny |
| Failover | https://talus-license-failover.metaforicmail.workers.dev |
Turso (wspólny) | automatyczny zapas |
| Baza | https://talus-licenses-bartoszosiej.aws-eu-west-1.turso.io |
— | jedyne źródło prawdy |
| D1 snapshot | Cloudflare D1 talus-licenses (id 77c07193-9bc8-44e5-a31f-616a25ad9fb9) |
— | zamrożona kopia z 2026-09-18 |
| Klucz podpisujący | ~/.secrets/talus/license-keys/signing_key.json (0600) |
— | NIGDY nie opuszcza maszyny |
Oba serwery to ten sam kod (license-server/src/) — wybór storage robi
src/db.js: jest binding DB → D1; są TURSO_* → Turso po HTTP. Failover
nie ma bindingu D1 (wrangler.failover.toml), primary ma go obecnie
zakomentowanego w wrangler.toml (więc też używa Turso).
Sekrety (wszystkie 0600, poza repo)
| Plik | Gdzie używane |
|---|---|
~/.secrets/talus/admin_token |
primary + failover (ADMIN_TOKEN), issue/revoke scripts |
~/.secrets/talus/totp_secret |
primary + failover (TOTP_SECRET) — ten sam seed, obie panele działać muszą |
~/.secrets/talus/turso_platform_token |
Platform API (turso.tech) — zarządzanie bazami |
~/.secrets/talus/turso_db_token |
token RW do bazy talus-licenses (exp. 1 rok) → sekret TURSO_AUTH_TOKEN na obu workerach |
Zmiana sekretu na workerze:
cd license-server
export CLOUDFLARE_API_TOKEN=$(cat ~/.cloudflare_token)
export CLOUDFLARE_ACCOUNT_ID=$(cat ~/.cloudflare_account | head -1)
npx wrangler secret put ADMIN_TOKEN # primary
npx wrangler secret put ADMIN_TOKEN -c wrangler.failover.toml
Kluczowe komendy
cd license-server
export CLOUDFLARE_API_TOKEN=$(cat ~/.cloudflare_token)
export CLOUDFLARE_ACCOUNT_ID=$(cat ~/.cloudflare_account | head -1)
# deploy obu serwerów (ten sam kod, różne configi)
npx wrangler deploy
npx wrangler deploy -c wrangler.failover.toml
# health / stan
curl https://talus-license-server.metaforicmail.workers.dev/api/v1/health
curl https://talus-license-failover.metaforicmail.workers.dev/api/v1/health
curl -H "Authorization: Bearer $(cat ~/.secrets/talus/admin_token)" \
https://talus-license-server.metaforicmail.workers.dev/api/v1/admin/stats
Test failover (end-to-end, wykonany 2026-09-18 — OK)
# 1. wydaj klucz testowy
TALUS_KEYGEN_DIR=$HOME/.secrets/talus/license-keys \
license-keygen/target/release/talus-keygen issue \
--tier enterprise --organization "Hartwell Labs Test" --seats 1 --json
# 2. aktywuj z CELOWO martwym primary — binarka musi przeskoczyć na failover
TALUS_LICENSE_SERVER="https://127.0.0.1:1" \
TALUS_LICENSE_SERVER_FAILOVER="https://talus-license-failover.metaforicmail.workers.dev" \
./target/debug/process-monitor license activate "<KEY>"
# oczekiwane: "[talus] activation server ... unreachable; trying failover"
# "[talus] ✓ license activated successfully"
# 3. dowód wspólnego storage: aktywacja widoczna na PRIMARY (inny worker!)
scripts/revoke-license.sh # i revoke z poziomu primary
curl -H "Authorization: Bearer $(cat ~/.secrets/talus/admin_token)" ... /admin/stats
# 4. sprzątanie: deactivate binarką, revoke klucza, (opcjonalnie) unregister
Odtwarzanie z katastrofy
Padł primary worker → nic nie robisz: binarka sama przeskakuje na failover (wbudowany w liście serwerów).
Padło konto Cloudflare (oba workery) → wystaw ten sam kod gdziekolwiek indziej (Deno Deploy / Vercel / drugie konto CF):
- import
license-server/src/index.js(Web-standard fetch, bez zależności), - env:
TURSO_DATABASE_URL, sekretyTURSO_AUTH_TOKEN,ADMIN_TOKEN,TOTP_SECRET,LICENSE_PUBLIC_KEY_HEX(wartość jest w wrangler.toml), - gotowe — baza i tak jest na zewnątrz (Turso), workers są wymienne.
Padła baza Turso → przywróć z D1-snapshot (dane od 2026-09-18 wprawdzie już nowsze mogą istnieć tylko w Turso — dlatego cotygodniowy eksport!):
# eksport aktualnego stanu Turso (rób to co tydzień — cron)
npx wrangler d1 export talus-licenses --remote --output=backup.sql # stare narzędzie dla D1
# dla Turso: turso db shell talus-licenses .dump > backup.sql
# odtworzenie: schema.sql + backup.sql przez /v2/pipeline (analogicznie do migracji)
Rollback primary na D1 (awaria Turso, użycie zamrożonego snapshotu):
- w
wrangler.tomlodkomentuj[[d1_databases]], usuń[vars] TURSO_DATABASE_URL, - usuń sekret
TURSO_AUTH_TOKENz primary workera, npx wrangler deploy— primary z powrotem czyta D1 (stan z momentu snapshotu),- binarki klientów:
TALUS_LICENSE_SERVER_FAILOVER=""wyłącza failover (żeby nie wskakiwały na serwer z innym stanem).
Granice free tier (2026-09)
| Usługa | Limit | Ryzyko |
|---|---|---|
| Cloudflare Workers free | 100k req/dzień | przy 100+ aktywnych licencjach spokojnie; ataki zlewają się w rate limiter (5/5min/machine) |
| Turso free | 500 baz, 5 GB storage, 1 mld row-reads/mies. | dla license-server zapas ogromny; token exp. 1 rok → ustawić przypomnienie na rotację ~2027-09 |
| Turso lokacje | tylko 6 lokacji AWS (w tym Irlandia) | brak WAW — akceptowalne |
| Deno Deploy free | 1 mln req/dzień | nieużywane (failover na CF); opcja awaryjna |
Znane kompromisy / ryzyka
- Primary i failover na tym samym koncie Cloudflare — wytrąca awarię
D1/runtime, NIE awarii konta. Migracja na drugie konto = deploy
wrangler.failover.tomlz innym tokenem + ponowne sekrety (5 min). - Turso
batch()nie jest atomowy (D1 jest) — używane tylko w akcjach admin (revoke/unrevoke/fulfill); kolejność statements i tak jest zachowana, a operacje są idempotentne. - Jeden wspólny admin_token/TOTP seed na obu serwerach — kompromis dla
identycznego UX panelu; rotacja = dwa
wrangler secret put. - Token Turso wygasa po roku — kalendarz: rotacja przed 2027-09-18.