diff --git a/kb/decisions/gokapi-storage-e2e-siec.md b/kb/decisions/gokapi-storage-e2e-siec.md new file mode 100644 index 0000000..857fd2c --- /dev/null +++ b/kb/decisions/gokapi-storage-e2e-siec.md @@ -0,0 +1,54 @@ +--- +okf: "0.1" +type: decision +visibility: private +status: active +updated: 2026-07-09 +links: + - ../services/gokapi.md + - ../runbooks/gokapi-cutover.md +--- + +# Gokapi — decyzje: storage, szyfrowanie E2E, siec + +## Storage — lokalny dysk VPS (nie S3) + +Decyzja Oskara: bez S3, dysk lokalny VPS. VPS ma tylko 80 GB SSD dzielone z +npm/outline/joplin/ai-cluster/fleet-prometheus — więc **ograniczenia +rozmiaru + brak globalnego domyślnego expiry to jedyna ochrona dysku**: + +- `GOKAPI_MAX_FILESIZE=5120` (5 GB/plik; upstream default to 100 GB — za + dużo na ten dysk). +- `GOKAPI_MIN_FREE_SPACE=2048` (2 GB headroom zanim Gokapi odmówi uploadu; + upstream default 400 MB, za mało przy dzielonym dysku). +- **Gokapi NIE MA globalnego domyślnego expiry/limitu pobrań** — to wybór + per-upload w formularzu web. Nie da się tego wymusić przez env var ani + config. Praktyka: przy każdym uploadzie ustawiać rozsądne wartości (np. + **7 dni / 10 pobrań**), żeby wygasające linki faktycznie czyściły dysk. + +## Szyfrowanie E2E — WŁĄCZONE (decyzja Oskara) + +Gokapi ma 3 poziomy szyfrowania (żaden / lokalny / **end-to-end**). Wybór +robi się w kroku "Encryption" wizardu `/setup` przy pierwszym starcie — nie +ma env vara. Level 3 (E2E) = plik szyfrowany w przeglądarce przed uploadem, +serwer nigdy nie widzi treści w plaintext. Uwaga upstream: implementacja +szyfrowania nie była niezależnie audytowana; Firefox ma problemy z +pobieraniem zaszyfrowanych plików (znane ograniczenie, nie nasz bug). + +Klucz szyfrowania trafia do `config.json` w `/app/config` — **to jest część, +którą trzeba backupować**, inaczej utrata configu = utrata dostępu do już +zaszyfrowanych plików. + +## Sieć — bind tylko na Tailscale, nigdy 0.0.0.0 + +Port 53842 binduje się **wyłącznie** na Tailscale IP VPS-a +(`TAILSCALE_BIND_IP=100.95.58.48`), nigdy na `0.0.0.0`. Publiczny adres +Hetznera (`135.181.153.108`) w ogóle nie widzi tego portu — jedyna droga na +zewnątrz to `npm@VPS` (TLS na 443) → `share.okit.pl`. npm i gokapi żyją na +tym samym hoście jako osobne stacki compose; npm dociera do gokapi przez +Docker hairpin NAT po tym samym realnym interfejsie (ten sam trik co +`fleet-prometheus`/`nextcloud` — `127.0.0.1` by tu NIE zadziałało). +`GOKAPI_TRUSTED_PROXIES=172.16.0.0/12` mówi Gokapi, żeby ufał +`X-Forwarded-For` z tego zakresu (podsieć mostka Docker), bo źródłowy IP po +hairpinie to brama bridge'a, nie prawdziwy klient. + diff --git a/kb/runbooks/gokapi-cutover.md b/kb/runbooks/gokapi-cutover.md new file mode 100644 index 0000000..02ec904 --- /dev/null +++ b/kb/runbooks/gokapi-cutover.md @@ -0,0 +1,51 @@ +--- +okf: "0.1" +type: runbook +visibility: private +status: active +updated: 2026-07-09 +links: + - ../services/gokapi.md + - ../decisions/gokapi-storage-e2e-siec.md +--- + +# Gokapi — cutover checklist + +## Cutover checklist (przy deployu — NIE teraz, ten PR to tylko config) + +1. `git pull` na VPS. +2. `mkdir -p /opt/homelab/data/gokapi/{data,config}` na VPS. +3. `cp services/gokapi/env.example services/gokapi/.env`, potwierdzić + `TAILSCALE_BIND_IP` (`tailscale ip -4` na żywym hoście — powinno być + `100.95.58.48`). +4. **Cloudflare**: rekord A `share.okit.pl` → `135.181.153.108`, **DNS + only** (szara chmurka, bez proxy Cloudflare — spójne z resztą wpisów + okit.pl/kapala.org w tym repo). +5. **Wildcard `*.okit.pl` na npm@VPS** — **NAJPIERW SPRAWDZIĆ, czy już + istnieje**. Kontekst: wildcard `*.okit.pl` DNS-01 jest już zrobiony na + `npm@PIHA` (cert #51, ważny do 2026-10-05, token Cloudflare + `npm-dns01-all-zones` — Zone:DNS:Edit, All zones). Sesja + `docs/sessions/2026-07-07-okit-wildcard.md` zostawiła to jako TODO: + *"Drugi npm na VPS (outline/joplin/agents tam) — analogiczny wildcard + `*.okit.pl`?"* — to jest ten moment, żeby to sprawdzić/zrobić. + Jeśli brak: npm@VPS → SSL → New Certificate → Let's Encrypt → domeny + `*.okit.pl` + `okit.pl` → DNS Challenge → Cloudflare → wkleić token + `npm-dns01-all-zones` (ten sam token co PIHA, All-zones więc obejmuje + okit.pl). Pułapka z poprzedniej sesji: literówka `.okit.pl` zamiast + `okit.pl` w drugim polu dawała Internal Error. +6. **vhost w npm@VPS**: nowy Proxy Host, domena `share.okit.pl`, Forward + Hostname/IP = `100.95.58.48` (Tailscale IP VPS, TAILSCALE_BIND_IP), port + `53842`, SSL = cert wildcard `*.okit.pl` z kroku 5, Force SSL + HTTP/2. +7. `docker compose -f services/gokapi/docker-compose.yml up -d`. +8. Otworzyć `https://share.okit.pl/setup` i przejść wizard: + - Database: SQLite (domyślnie, wbudowane). + - Webserver: potwierdzić public URL `https://share.okit.pl`. + - Authentication: Username/Password, ustawić **prawdziwe** hasło admina + (nie ma tu env vara — to jedyny moment, żeby to zrobić w UI). + - Storage: **Local** (nie S3 — decyzja Oskara). + - Encryption: **Level 3 / End-to-End** (decyzja Oskara — E2E ON). +9. Przy każdym uploadzie: ustawiać rozsądny expiry/limit pobrań ręcznie + (patrz sekcja Storage wyżej — brak globalnego defaultu w Gokapi). +10. `./healthcheck.sh` z hosta VPS, potem z zewnątrz: `curl -I + https://share.okit.pl/`. + diff --git a/kb/services/gokapi.md b/kb/services/gokapi.md new file mode 100644 index 0000000..a66f75e --- /dev/null +++ b/kb/services/gokapi.md @@ -0,0 +1,51 @@ +--- +okf: "0.1" +type: service +visibility: private +status: active +updated: 2026-07-09 +links: + - ../decisions/gokapi-storage-e2e-siec.md + - ../runbooks/gokapi-cutover.md +--- + +# Gokapi (publiczne udostępnianie plików) + +Lekki self-hosted "Firefox Send" alternative — link do jednego pliku, na +zewnątrz, z limitem pobrań/czasu. **Osobny serwis od Nextclouda, celowo.** + +| | Nextcloud | Gokapi | +|---|---|---| +| Rola | twierdza — prywatna, mesh/`kapala.org` | publiczne dzielenie — `okit.pl` | +| Dostęp | Tailscale/LAN only, zero public ingress | publiczny internet, przez npm@VPS | +| Host | PIHA | **VPS** (Hetzner, publiczny) | +| Użycie | sync telefon/desktop, KB source | "wyślij komuś link do pliku" | + +Host: **VPS** (Hetzner, `135.181.153.108`) — publiczny serwis na publicznym +hoście, węzły domowe nietknięte. `owner_node: vps`. + +## Stack + +| Kontener | Obraz | Rola | +|---|---|---| +| `gokapi` | `f0rc3/gokapi:v2.2.4` | cała aplikacja (Go binary + wbudowana baza SQLite), port 53842 | + +Wersja przypięta na `v2.2.4` (aktualny stable na 2026-07-09, zweryfikowane +przez github.com/Forceu/Gokapi releases + tagi `f0rc3/gokapi` na Docker +Hub). **TODO PRZY DEPLOYU**: potwierdzić, czy nie wyszła nowsza wersja, +zanim `docker compose up`. + +## Backup + +- `/opt/homelab/data/gokapi/data` — pliki użytkowników. **Ulotne z + założenia** (linki wygasają), nie wymaga backupu. +- `/opt/homelab/data/gokapi/config` — `config.json` + klucz szyfrowania E2E. + **To backupować** — utrata = utrata dostępu do configu (hasło admina, + ustawienia) i do zaszyfrowanych plików, jeśli klucz trzymany lokalnie. + Sugerowana retencja: dołączyć do tej samej pętli nocnej co inne configi na + VPS (mały rozmiar, plik+SQLite). + +## Rejestracja w repo (zrobione w tym PR) + +- `hosts/vps/services.yaml` — wpis `gokapi`. +- `inventory/topology.yaml` — `gokapi` w liście serwisów `vps`. diff --git a/services/gokapi/README.md b/services/gokapi/README.md deleted file mode 100644 index 7083303..0000000 --- a/services/gokapi/README.md +++ /dev/null @@ -1,119 +0,0 @@ -# Gokapi (publiczne udostępnianie plików) - -Lekki self-hosted "Firefox Send" alternative — link do jednego pliku, na -zewnątrz, z limitem pobrań/czasu. **Osobny serwis od Nextclouda, celowo.** - -| | Nextcloud | Gokapi | -|---|---|---| -| Rola | twierdza — prywatna, mesh/`kapala.org` | publiczne dzielenie — `okit.pl` | -| Dostęp | Tailscale/LAN only, zero public ingress | publiczny internet, przez npm@VPS | -| Host | PIHA | **VPS** (Hetzner, publiczny) | -| Użycie | sync telefon/desktop, KB source | "wyślij komuś link do pliku" | - -Host: **VPS** (Hetzner, `135.181.153.108`) — publiczny serwis na publicznym -hoście, węzły domowe nietknięte. `owner_node: vps`. - -## Stack - -| Kontener | Obraz | Rola | -|---|---|---| -| `gokapi` | `f0rc3/gokapi:v2.2.4` | cała aplikacja (Go binary + wbudowana baza SQLite), port 53842 | - -Wersja przypięta na `v2.2.4` (aktualny stable na 2026-07-09, zweryfikowane -przez github.com/Forceu/Gokapi releases + tagi `f0rc3/gokapi` na Docker -Hub). **TODO PRZY DEPLOYU**: potwierdzić, czy nie wyszła nowsza wersja, -zanim `docker compose up`. - -## Storage — lokalny dysk VPS (nie S3) - -Decyzja Oskara: bez S3, dysk lokalny VPS. VPS ma tylko 80 GB SSD dzielone z -npm/outline/joplin/ai-cluster/fleet-prometheus — więc **ograniczenia -rozmiaru + brak globalnego domyślnego expiry to jedyna ochrona dysku**: - -- `GOKAPI_MAX_FILESIZE=5120` (5 GB/plik; upstream default to 100 GB — za - dużo na ten dysk). -- `GOKAPI_MIN_FREE_SPACE=2048` (2 GB headroom zanim Gokapi odmówi uploadu; - upstream default 400 MB, za mało przy dzielonym dysku). -- **Gokapi NIE MA globalnego domyślnego expiry/limitu pobrań** — to wybór - per-upload w formularzu web. Nie da się tego wymusić przez env var ani - config. Praktyka: przy każdym uploadzie ustawiać rozsądne wartości (np. - **7 dni / 10 pobrań**), żeby wygasające linki faktycznie czyściły dysk. - -## Szyfrowanie E2E — WŁĄCZONE (decyzja Oskara) - -Gokapi ma 3 poziomy szyfrowania (żaden / lokalny / **end-to-end**). Wybór -robi się w kroku "Encryption" wizardu `/setup` przy pierwszym starcie — nie -ma env vara. Level 3 (E2E) = plik szyfrowany w przeglądarce przed uploadem, -serwer nigdy nie widzi treści w plaintext. Uwaga upstream: implementacja -szyfrowania nie była niezależnie audytowana; Firefox ma problemy z -pobieraniem zaszyfrowanych plików (znane ograniczenie, nie nasz bug). - -Klucz szyfrowania trafia do `config.json` w `/app/config` — **to jest część, -którą trzeba backupować**, inaczej utrata configu = utrata dostępu do już -zaszyfrowanych plików. - -## Sieć — bind tylko na Tailscale, nigdy 0.0.0.0 - -Port 53842 binduje się **wyłącznie** na Tailscale IP VPS-a -(`TAILSCALE_BIND_IP=100.95.58.48`), nigdy na `0.0.0.0`. Publiczny adres -Hetznera (`135.181.153.108`) w ogóle nie widzi tego portu — jedyna droga na -zewnątrz to `npm@VPS` (TLS na 443) → `share.okit.pl`. npm i gokapi żyją na -tym samym hoście jako osobne stacki compose; npm dociera do gokapi przez -Docker hairpin NAT po tym samym realnym interfejsie (ten sam trik co -`fleet-prometheus`/`nextcloud` — `127.0.0.1` by tu NIE zadziałało). -`GOKAPI_TRUSTED_PROXIES=172.16.0.0/12` mówi Gokapi, żeby ufał -`X-Forwarded-For` z tego zakresu (podsieć mostka Docker), bo źródłowy IP po -hairpinie to brama bridge'a, nie prawdziwy klient. - -## Backup - -- `/opt/homelab/data/gokapi/data` — pliki użytkowników. **Ulotne z - założenia** (linki wygasają), nie wymaga backupu. -- `/opt/homelab/data/gokapi/config` — `config.json` + klucz szyfrowania E2E. - **To backupować** — utrata = utrata dostępu do configu (hasło admina, - ustawienia) i do zaszyfrowanych plików, jeśli klucz trzymany lokalnie. - Sugerowana retencja: dołączyć do tej samej pętli nocnej co inne configi na - VPS (mały rozmiar, plik+SQLite). - -## Cutover checklist (przy deployu — NIE teraz, ten PR to tylko config) - -1. `git pull` na VPS. -2. `mkdir -p /opt/homelab/data/gokapi/{data,config}` na VPS. -3. `cp services/gokapi/env.example services/gokapi/.env`, potwierdzić - `TAILSCALE_BIND_IP` (`tailscale ip -4` na żywym hoście — powinno być - `100.95.58.48`). -4. **Cloudflare**: rekord A `share.okit.pl` → `135.181.153.108`, **DNS - only** (szara chmurka, bez proxy Cloudflare — spójne z resztą wpisów - okit.pl/kapala.org w tym repo). -5. **Wildcard `*.okit.pl` na npm@VPS** — **NAJPIERW SPRAWDZIĆ, czy już - istnieje**. Kontekst: wildcard `*.okit.pl` DNS-01 jest już zrobiony na - `npm@PIHA` (cert #51, ważny do 2026-10-05, token Cloudflare - `npm-dns01-all-zones` — Zone:DNS:Edit, All zones). Sesja - `docs/sessions/2026-07-07-okit-wildcard.md` zostawiła to jako TODO: - *"Drugi npm na VPS (outline/joplin/agents tam) — analogiczny wildcard - `*.okit.pl`?"* — to jest ten moment, żeby to sprawdzić/zrobić. - Jeśli brak: npm@VPS → SSL → New Certificate → Let's Encrypt → domeny - `*.okit.pl` + `okit.pl` → DNS Challenge → Cloudflare → wkleić token - `npm-dns01-all-zones` (ten sam token co PIHA, All-zones więc obejmuje - okit.pl). Pułapka z poprzedniej sesji: literówka `.okit.pl` zamiast - `okit.pl` w drugim polu dawała Internal Error. -6. **vhost w npm@VPS**: nowy Proxy Host, domena `share.okit.pl`, Forward - Hostname/IP = `100.95.58.48` (Tailscale IP VPS, TAILSCALE_BIND_IP), port - `53842`, SSL = cert wildcard `*.okit.pl` z kroku 5, Force SSL + HTTP/2. -7. `docker compose -f services/gokapi/docker-compose.yml up -d`. -8. Otworzyć `https://share.okit.pl/setup` i przejść wizard: - - Database: SQLite (domyślnie, wbudowane). - - Webserver: potwierdzić public URL `https://share.okit.pl`. - - Authentication: Username/Password, ustawić **prawdziwe** hasło admina - (nie ma tu env vara — to jedyny moment, żeby to zrobić w UI). - - Storage: **Local** (nie S3 — decyzja Oskara). - - Encryption: **Level 3 / End-to-End** (decyzja Oskara — E2E ON). -9. Przy każdym uploadzie: ustawiać rozsądny expiry/limit pobrań ręcznie - (patrz sekcja Storage wyżej — brak globalnego defaultu w Gokapi). -10. `./healthcheck.sh` z hosta VPS, potem z zewnątrz: `curl -I - https://share.okit.pl/`. - -## Rejestracja w repo (zrobione w tym PR) - -- `hosts/vps/services.yaml` — wpis `gokapi`. -- `inventory/topology.yaml` — `gokapi` w liście serwisów `vps`.