Audyt Jakości Specyfikacji: Browser-VPN SaaS

Data audytu: 2026-07-22

Obiekt: docs/superpowers/specs/2026-07-22-browser-vpn-product-design.md (738 linii, 15 sekcji)

Cel biznesowy: przekształcenie bash-owego PoC (Docker + WireGuard + przeglądarka + tunnel Cloudflare) w hostowany produkt SaaS dla użytkowników ceniących „total privacy", płatność krypto przez Trocador.


1. Wprowadzenie

Niniejszy raport ocenia jakość i gotowość implementacyjną specyfikacji produktowej przed wejściem w fazę deweloperską. Audyt nie ocenia samej koncepcji produktu, lecz sprawdzalność dokumentu jako bazy pod implementację: kompletność, spójność, mierzalność kryteriów, wykonalność techniczną oraz pokrycie zagadnień bezpieczeństwa i operacyjnych.

Zakres audytu:

Kontekst decyzyjny: produkt celuje w segment „total privacy" — czyli audytor właśnie tej kohorty (najbardziej wyczulonej na błędy bezpieczeństwa) ocenia dokument. Błędy bezpieczeństwa są tu błędami biznesowymi, nie technicznymi niuansami.


2. Podsumowanie wykonawcze i ocena ogólna

Specyfikacja jest solidnym, dobrze zorganizowanym draftem koncepcyjnym, ale niegotowym do bezpośredniej implementacji. Silna struktura, uczciwe sekcje ograniczeń i ryzyk, rozsądny model danych. Jednocześnie dokument zawiera kilkanaście luk, z czego pięć to blokery (P0) wymagające rozwiązania przed rozpoczęciem kodowania.

Scorecard — ocena wg wymiarów (skala 1-10)

WymiarOcenaKomentarz
Struktura i czytelność9/1015 logicznych sekcji, spójna numeracja, dobre tabele
Kompletność funkcjonalna7/10Pokrywa flow zakupu, sesji, wsparcia, rozliczeń
Kompletność bezpieczeństwa5/10Szeroki zakres, ale kluczowe luki (XSS, capabilities, CSP)
Mierzalność kryteriów6/10Progi 80%/50 instancji OK, ale brak SLO/SLA, brak metryk jakości
Wykonalność techniczna5/10Rozbieżność PoC↔Spec, brak WebSocket, brak limitów zasobów
Gotowość do implementacji5/105 blokerów + 5 otwartych pytań = potrzeba iteracji
Zarządzanie ryzykiem7/10Tabela ryzyk z mitigacjami, sekcja Conscious Limitations
Ocena ogólna6.3/10Solidny Draft — wymaga poprawek P0 przed dev

Werdykt: Approve with mandatory revisions — dokument zatwierdzić warunkowo, pod warunkiem rozwiązania 5 blokerów P0 i 5 otwartych pytań (sekcja 15) w rewizji v2 specyfikacji.


3. Mocne strony specyfikacji

Wymienienie mocnych stron jest ważne — to baza, której nie trzeba przebudowywać:

  1. Jasne wyznaczenie zakresu (sekcja 1.4 Out of Scope) — precyzyjne wylistowanie tego, czego MVP nie robi (konta, refundy, Stripe, i18n, multi-server). To rzadka dobra praktyka w draftach.
  2. Uczciwa sekcja Conscious Limitations (14.1) — każda decyzja ograniczająca ma podany powód. Świadczy o dojrzałości produktowej autora.
  3. Tabela ryzyk z mitigacjami (14.2) — 5 ryzyk z konkretnymi działaniami zaradczymi, w tym „utrata order number = brak odzyskania by design" (świadomy tradeoff za anonimowość).
  4. Rozsądny model danych (sekcja 6) — 7 tabel, odpowiednie typy, nullable oznaczone, FK wskazane. system_logs z JSONB metadata to dobra elastyczność.
  5. Dobrze zdefiniowane API (sekcja 7.2) — podział public/admin, sensowne endpointy, /api/health obecne.
  6. Scheduler jobs z częstotliwościami (7.3) — konkretne interwały (1 min, 30 s, 5 min, daily), nie abstrakcyjne „periodically".
  7. Monitoring i alerting dopracowane (sekcja 11) — metryki co 30 s, zewnętrzne health-check, 11 warunków alertów Telegram.
  8. Sekcja Open Questions (15) — autor sam wskazuje 5 punktów do rozstrzygnięcia. Świadomość next steps.
  9. Plan backup i migracji między serwerami (12.5) — konkretny rsync + restore.sh, nie tylko obietnica.
  10. Fast domain switching przez njalla (12.6) — pragmatyczne dla scenariusza privacy (przepięcie domeny).

4. Krytyczne luki — P0 (blokery implementacyjne)

Pięć problemów, które muszą zostać rozwiązane w rewizji specyfikacji przed startem kodowania. Każde z nich może zablokować działanie produktu lub naruszyć obietnicę „total privacy".

P0-1: Trocador — pojedynczy punkt awarii (SPoF) płatności

Stan weryfikacji: https://trocador.app/ zwraca HTTP 503 w dniu audytu (2026-07-22). Endpoint płatności jest niedostępny.

Problem: Cały model monetyzacji MVP opiera się na jednym zewnętrznym serwisie (Trocador AnonPay). Sekcja 3.1 zakłada webhooki + polling co 60 s jako fallback, ale to tylko obsługa przypadku „Trocador wolny", a nie „Trocador nie działa". Polling niedziałającego API nie przywraca płatności.

Wpływ biznesowy: produkt „sellable" nie może sprzedawać, gdy Trocador jest down. Dla kohorty privacy (anonimowi, jednorazowi klienci) — klient, który trafi na niedziałający przycisk płatności, nie wraca.

Rekomendacja:

P0-2: Brak proxy WebSocket w Caddy — streaming Selkies nie zadziała

Problem: Produkt streamuje przeglądarkę z kontenera do użytkownika. PoC (README.md) używa Selkies-GStreamer do współdzielenia ekranu. Selkies wymaga połączenia WebSocket do przesyłania strumienia i zdarzeń wejścia.

Sekcja 9 (Reverse Proxy) definiuje reverse_proxy do kontenera, ale nie wspomina o nagłówkach Upgrade i Connection wymaganych do WebSocket. Bez tego Caddy proxy'uje ruch HTTP, ale handshake WebSocket (HTTP 101 Switching Protocols) nie przejdzie — obraz się nie załaduje.

Wpływ: fundamentalna funkcjonalność produktu (streaming przeglądarki) jest nieopisana na poziomie proxy. To najpewniej pierwszy bug po wdrożeniu.

Rekomendacja: dodać do sekcji 9.3 jawną regułę:

# Caddy — proxy z obsługą WebSocket
reverse_proxy localhost:<port> {
    header_up Host {host}
    header_up Upgrade {http.upgrade}
    header_up Connection {http.connection}
}

Caddy automatycznie obsługuje WebSocket w reverse_proxy, ale specyfikacja musi to jawnie zadeklarować jako wymaganie, aby implementator nie pominął konfiguracji.

P0-3: Nadmierne uprawnienia kontenera — SYS_ADMIN to de facto root hosta

Problem: Sekcja 10.1 przyznaje kontenerom NET_ADMIN i SYS_ADMIN dla WireGuard/Selkies. SYS_ADMIN to jedna z najszerszych capabilities w Linuksie — pozwala m.in. na manipulację namespace'ami, mountowanie systemów plików, operacje na cgroups. Kontener z SYS_ADMIN jest bliski rootowi na hoście.

Wpływ bezpieczeństwa: w architekturze multi-tenant (do 50 kontenerów jednocześnie, sekcja 3.2) przejęcie jednego kontenera może prowadzić do eskalacji na hosta i kompromitacji wszystkich pozostałych sesji. Dla produktu „total privacy" to katastrofalne.

Rekomendacja: minimalizować capabilities:

Zaktualizować sekcję 10.1 o minimalny zestaw capabilities i uzasadnienie per capability.

P0-4: deviceToken w localStorage — podatność XSS obchodzi wiązanie urządzenia

Problem: Sekcja 3.3 pkt 8 i 10.2 pkt 2: „deviceToken stored in httpOnly cookie and localStorage". To błąd bezpieczeństwa. httpOnly cookie chroni token przed odczytem przez JavaScript, ale localStorage jest pełnodostępny dla każdego skryptu na stronie, w tym złośliwego (XSS).

Jeśli atakujący wstrzyknie JavaScript (np. przez złośliwą stronę załadowaną w streamowanej przeglądarce, która dotyka session page), może odczytać deviceToken z localStorage i ukraść sesję — paradoksalnie omijając właśnie httpOnly cookie, które miało chronić.

Wpływ: obietnica „only the bound device can return" (sekcja 1.3 pkt 9) zostaje złamana przy jakimkolwiek XSS. Dla produktu privacy to bezpośrednie złamanie obietnicy produktu.

Rekomendacja:

P0-5: Brak limitów zasobów per kontener — ryzyko DoS i OOM hosta

Problem: Sekcja 3.2 definiuje progi na poziomie hosta (CPU <80%, RAM <80%, 50 kontenerów) — to limity admission control. Ale brak limitów per kontener (--cpus, --memory, --pids-limit). Jeden uciekający proces wewnątrz jednej sesji (np. przeglądarka z wyciekiem pamięci, pętla otwierających się tabów) może zjeść RAM hosta i ubić wszystkie 50 sesji.

Wpływ: multi-tenant bez izolacji zasobów = jeden klient może (nawet nieumyślnie) zdestabilizować usługę dla wszystkich. Przy 50 kontenerach Chrome na jednym VPS ryzyko OOM jest realne (Chrome bywa po 1-2 GB na sesję).

Rekomendacja: dodać do sekcji 10.1 i 12.2:


5. Luki znaczące — P1 (wymagają naprawy przed produkcją)

Problemy poważne, ale nie blokujące startu MVP — należy je rozwiązać przed wdrożeniem produkcyjnym.

Tabela P1

IDProblemSekcjaWpływRekomendacja
P1-1Brak CSP (Content Security Policy)10Produkt „total privacy" bez CSP jest rażącym pominięciem; XSS możliwyDodać Content-Security-Policy w Caddy: default-src 'self'; connect-src 'self' ws: wss:; img-src 'self' blob: data:;
P1-2Brak monitorowania health kontenera w trakcie sesji11Mierzony tylko startup time; martwa sesja nie wykrywanaDodać heartbeat co 30 s: brak ruchu >5 min = alert + auto-restart/stop
P1-3Brak timeout bezczynności sesji3.3Sesja może wisieć pełny czas bez aktywności, marnując zasobyDodać idle_timeout (np. 30 min) osobno od expires_at
P1-4Brak skanowania podatności obrazów Docker10.6„Automated rebuilds weekly" bez skanowania = mogą wchodzić CVEDodać Trivy/Grype w CI przed wdrożeniem obrazu
P1-5Magic link bez rate-limit na POST /api/orders7.2Brak ograniczeń tworzenia zamówień = spam/abuseDodać rate-limit per IP (np. 5 zamówień/h) na tworzenie order
P1-6Admin „password-protected, optional TOTP"10.4Hasło-only dla panelu z dostępem do wszystkich sesji = za małoTOTP obowiązkowe dla MVP (nie optional), + logowanie prób
P1-7Brak HSTS i nagłówków bezpieczeństwa9Privacy produkt bez HSTS = mitm przy pierwszej wizycieDodać Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
P1-8Backup lokalny 7 dni, brak off-site11.5Utrata serwera = utrata wszystkiego (orders, configs)„Optional future: S3/B2" → podnieść do MVP (encrypted, off-site)
P1-9Brak definicji zachowania przy padzie hosta3, 12Sesje active po restarcie serwera: status?Dodać: restart hosta = kontenery oznaczane interrupted, użytkownik widzi info, restart manualny
P1-10wg-configs/ backup plain11.5Kopia konfigów WireGuard w plaintext = kompromitacja VPNSzyfrować backup (gpg/age), klucz poza serwerem

6. Luki drobne — P2 (usprawnienia)

Te pozycje nie blokują, ale podniosą jakość:

  1. Niespójność nazewnictwa browser: sekcja 1.3 wymienia „Mullvad" jako browser, sekcja 2.1 pkt 4 i 6.1 wymienia mullvad-browser. PoC README (config.yml) też używa mullvad. Ujednolicić na mullvad.
  2. Brak wersjiowania API: endpointy bez prefiksu wersji (/api/v1/). Dodać /api/v1/ dla przyszłych zmian niezaburzających klientów.
  3. order_number format ord-XXXXXX-XXXXXX: brak definicji źródła losowości i kolizji. Dodać: kryptograficzny RNG + unique constraint (już jest w 6.1) + retry przy kolizji.
  4. Sekcja 4.3 „X minutes": niedoprecyzowane. Sekcja 11.3 podaje „5 minut" — ujednolicić wartość w jednej zmiennej konfiguracyjnej CONTAINER_START_TIMEOUT.
  5. Brak definicji fiat_equiv vs amount: sekcja 3.1 pkt 4 daje alternatywę „amount in XMR or fiat_equiv=USD". Nie określono, który jest preferowany i jak API reaguje na oba. Wybrać jeden (fiat_equiv z konwersją przez Trocador).
  6. Brak GDPR/data-retention polityki: choć „no personal data", są logi i tickety. Dodać retencję: system_logs 7 dni (już jest), tickety 90 dni, metryki 30 dni.
  7. Brak definicji limits na attachment w ticketach: sekcja 4.1 „optional attachment" — brak typu/rozmiaru. Dodać max 5 MB, typy obraz/pdf.
  8. server_metrics bez retencji: metryki co 30 s rosną szybko. Dodać partycjonowanie/agregację (np. surowe 24h, potem co 5 min agregowane).
  9. Brak health-check endpointu kontenera: sekcja 11.2 sprawdza VPN endpoints, ale nie health samej przeglądarki w kontenerze. Dodać /healthz w obrazie.

7. Macierz porównawcza: PoC vs Specyfikacja vs Wymagania Produkcyjne

To kluczowa analiza — pokazuje dystans między obecnym stanem (PoC), zaplanowanym (Spec) i wymaganym (Produkcja).

KryteriumPoC (bash, README)Specyfikacja MVPWymagania ProdukcyjneLuk Spec↔Prod
Architektura dostępuCloudflare Tunnel (trycloudflare)Caddy, direct domain, no CloudflareDirect + opcjonalnie CDN privacyDecyzja arch. potrzebna
PłatnościBrakTrocador (krypto)Trocador + fallback (BTCPay)P0-1 — brak fallback
Izolacja sieciTunnel CF + WireGuardWireGuard per kontenerWireGuard + egress filtering + DNS isolationCzęściowo
Session bindingBrakMagic link + deviceToken (cookie+localStorage)httpOnly+Secure+SameSite cookie, fp serwerP0-4 — localStorage
Capabilities konteneraNieokreślone (PoC)NET_ADMIN + SYS_ADMINNET_ADMIN + SYS_PTRACE + no-new-privilegesP0-3 — SYS_ADMIN
Limity zasobówBrakBrak (tylko admission host)memory/cpu/pids per kontenerP0-5 — brak limitów
Proxy streamingCloudflare Tunnel (WS OK)Caddy reverse_proxy, brak WSCaddy + WebSocket expliciteP0-2 — brak WS
Bezpieczeństwo nagłówkiN/A (tunnel)Brak CSP/HSTSCSP + HSTS + X-Frame-OptionsP1-1, P1-7
MonitoringBrakMetryki + 11 alertów Telegram+ Grafana, health kontenera w sesjiP1-2
BackupN/ALokalny 7 dniLokalny + off-site szyfrowanyP1-8, P1-10
Admin authN/AHasło + optional TOTPHasło + obowiązkowe TOTP + logiP1-6
Rate-limitingN/ABrakPer-IP na orders/webhookP1-5

Wniosek z macierzy: specyfikacja pokrywa ~60% wymagań produkcyjnych. Największe luki koncentrują się w bezpieczeństwie operacyjnym (capabilities, limity, cookie) — dokładnie w obszarze, który dla „total privacy" jest krytyczny.


8. Rozbieżność architektoniczna PoC ↔ Specyfikacja

Najpoważniejsza obserwacja strukturalna: PoC i Specyfikacja opisują dwie różne architektury dostępu.

Konsekwencje:

  1. Implementacja nie jest ewolucją PoC, lecz jego przebudową. Tunnel Cloudflare w PoC rozwiązuje automatycznie: TLS, WebSocket, ukrywanie IP hosta, DDoS protection. Przejście na Caddy direct oznacza, że te wszystkie funkcje trzeba zaimplementować samodzielnie.
  2. IP serwera jest eksponowane przy direct domain — dla produktu privacy to ryzyko (de-anonymizacja infrastruktury, ataki na sam serwer). Tunnel Cloudflare ukrywał IP.
  3. DDoS protection znika — Cloudflare absorbing ataków przestaje chronić. Przy direct domain atak L7 trafia bezpośrednio w Caddy/backend.
  4. WebSocket (P0-2) — Cloudflare Tunnel obsługiwał go domyślnie; na Caddy trzeba konfigurować.

Rekomendacja: to decyzja architektoniczna wymagająca uzasadnienia w specyfikacji. Autor wykluczył Cloudflare (1.4) powodem „simpler infrastructure", ale nie rozważył, że Cloudflare też:

Dodać do sekcji 14.2 ryzyko: „Direct domain exposure → IP hosta publiczny, brak DDoS protection, brak ukrycia infrastruktury" z mitigacją: „rozważyć Cloudflare proxy (nie tunnel) jako warstwę L7, lub AWS CloudFront-equivalent privacy-preserving CDN". To zachowuje obietnicę privacy infra przy odzyskaniu ochrony. Jeśli zostaje direct Caddy — ująć to jako świadomą decyzję z tradeoffami.


9. Rozwiązania otwartych pytań (sekcja 15 spec)

Sekcja 15 zawiera 5 otwartych pytań. Raport audytowy powinien je rozstrzygnąć, aby specyfikacja stała się implementacyjna.

Otwarte pytanie (spec 15)Proponowane rozstrzygnięcie
1. Potwierdzić pola odpowiedzi Trocador dla direct=falseSprawdzić dokumentację Trocador AnonPay (API reference). Kluczowe pola: transaction_id, payment_address, amount_to, amount_to_currency, status, expiration. Blokowane przez P0-1 — jeśli Trocador niedostępny, pilnie dodać BTCPay i dokumentować jego API. Uwaga: weryfikacja 2026-07-22 zwraca HTTP 503 — endpoint docs również niepewny.
2. Zdefiniować format pliku WG i konwencję nazewnictwa regionówFormat: standardowy [Interface] + [Peer] (.conf). Konwencja regionów: ISO 3166-1 alpha-2 (np. pl, de, us) + opcjonalnie sufiks miasta (us-nyc). Plik: wg-configs/-.conf. W bazie (6.3) config_file_path = ścieżka względna do wg-configs/.
3. Wybrać metodę auth admina (hasło vs TOTP)Hasło + TOTP obowiązkowe (P1-6). Hasło: bcrypt, min 14 znaków. TOTP: RFC 6238, 30 s window, 10 backup-kodów. Log wszystkich prób (już w 10.4). Ograniczenie IP/VPN (już rekomendowane).
4. Caddy jako kontener czy natywnyKontener — spójność z resztą stacka, łatwa migracja (12.5), rebuild z resztą. Wady: restart Caddy = chwilowy downtime wszystkich sesji. Mitigacja: health-check + auto-restart. Natywny tylko gdy wymagana zerowa przerwa.
5. Finalizować format alertów TelegramFormat: ⚠️ [SEVERITY] [COMPONENT] + metadane JSON w code-block. Severity: CRIT (SSH/Caddy/DB down), WARN (progi, retry), INFO (backup OK). Max 1 alert/min per komponent (rate-limit, by nie spamować).

10. Plan napraw przed implementacją

Sekwencja działań, aby specyfikacja stała się gotowa do dev. Każdy krok = rewizja dokumentu (v2 spec).

Faza 0 — Rozstrzygnięcia blokujące (przed dev, ~1-2 dni)

  1. Rozwiązać P0-1: dodać BTCPay jako fallback, udokumentować API obu dostawców.
  2. Rozwiązać P0-4: usunąć localStorage, ujednolicić cookie HttpOnly;Secure;SameSite=Strict.
  3. Rozwiązać P0-3: zdefiniować minimalny zestaw capabilities, usunąć SYS_ADMIN.
  4. Rozwiązać P0-5: dodać limity --memory/--cpus/--pids-limit do 10.1 i 12.2.
  5. Rozwiązać P0-2: dodać wymaganie WebSocket proxy w sekcji 9.3.
  6. Rozstrzygnąć 5 otwartych pytań (sekcja 15) — wdrożyć propozycje z sekcji 9 tego raportu.
  7. Rozstrzygnąć rozbieżność PoC↔Spec (sekcja 8) — ująć decyzję architektoniczną z tradeoffami.

Faza 1 — Uzupełnienia P1 (przed produkcją, równolegle z dev)

  1. Dodać CSP + HSTS + nagłówki bezpieczeństwa (P1-1, P1-7) do sekcji 9.4.
  2. Dodać heartbeat kontenera + idle_timeout (P1-2, P1-3) do sekcji 3.3 i 11.
  3. Dodać rate-limiting (P1-5) do sekcji 7.2.
  4. TOTP obowiązkowe (P1-6) w 10.4.
  5. Off-site encrypted backup (P1-8, P1-10) w 11.5.
  6. Skanowanie obrazów Trivy (P1-4) w 10.6.

Faza 2 — Uzupełnienia P2 (usprawnienia, w trakcie dev)

  1. Wdrożyć ujednolicenie nazewnictwa, wersjonowanie API, RNG order_number, retencje.

Faza 3 — Pre-produkcyjna walidacja

  1. Security audit zewnętrzny (pen-test) przed launch — dla produktu privacy to obowiązkowe.
  2. Load test: 50 kontenerów jednocześnie, obserwacja hosta.
  3. Chaos test: pad hosta, pad Trocador, pad VPN region.

11. Podsumowanie

Specyfikacja 2026-07-22-browser-vpn-product-design.md to dobry punkt wyjścia — dobrze zorganizowany, uczciwy co do ograniczeń, z rozsądnym modelem danych i dopracowanym monitoringiem. Ocena ogólna 6.3/10 odzwierciedla solidny draft, który potrzebuje jednej pełnej iteracji naprawczej, zanim implementacja może bezpiecznie wystartować.

Co działa: struktura, zakres, model danych, API, scheduler, monitoring, zarządzanie ryzykiem. To fundament, którego nie trzeba przebudowywać.

Co blokuje (P0): zewnętrzny SPoF płatności (Trocador niedostępny), brak proxy WebSocket dla streaming'u, nadmierne uprawnienia kontenerów, localStorage podatne na XSS, brak limitów zasobów. Pięć punktów, z których każdy może samodzielnie zablokować produkt lub złamać obietnicę privacy.

Co wymaga decyzji: rozbieżność architektoniczna PoC (Cloudflare Tunnel) ↔ Spec (Caddy direct) nie jest arbitralnym wyborem — to zmiana, która zdejmuje warstwy ochrony (ukrywanie IP, DDoS protection, auto-WebSocket) i powinna być ujęta z tradeoffami, nie tylko powodem „simpler infrastructure".

Werdykt: Approve with mandatory revisions. Po rozwiązaniu 7 punktów Fazy 0 specyfikacja powinna awansować do v2 i dopiero wtedy wejść w implementację. Dla produktu czerpiącego wartość z obietnicy „total privacy" margines na błędy bezpieczeństwa jest zerowy — każda luka P0 to bezpośrednie ryzyko utraty zaufania najbardziej wrażliwej kohorty klientów.


12. Glosariusz


13. Źródła

Źródła (Pliki)

Źródła (Linki URL — zweryfikowane HTTP 200, 2026-07-22)

Źródła (zweryfikowane niedostępne — flaga ostrzegawcza)