Nazwa `astroklient` zostaje zarezerwowana dla przyszłej wersji produkcyjnej;
obecna, demonstracyjna nazywa się od teraz `astroklient-demo`.
Zmiana obejmuje nazwy plików (astroklient-demo.yaml, astroklient-demo-stack.yaml,
README-astroklient-demo.md), Deployment i Service, etykiety selektorów, obraz
w rejestrze, alias w image-updaterze, wpisy w kustomization i odsyłacze w README.
`data-demo` i `logic-demo` zostają bez zmian — nie niosły starej nazwy.
Sprawdzone: `kubectl kustomize astrololo` składa komplet 27 zasobów, obraz to
astrololo-astroklient-demo, żadnego zdublowanego przyrostka ani gołego
`astroklient` nie zostało.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zmiana wobec pierwszej wersji: demo NIE pracuje już na produkcyjnej warstwie
danych. Ma własną logikę i własne dane, na osobnym udziale astrololo-demo,
pustym na starcie. Oryginalne bazy są dla demo nieosiągalne — nie przez
uprawnienia, tylko dlatego, że nie ma do nich drogi.
DLACZEGO OSOBNA JEST TEŻ WARSTWA LOGICZNA. Zna ona JEDEN adres warstwy danych,
więc astroklient korzystający z produkcyjnej logiki i tak trafiłby na produkcyjne
bazy. Izolacja musi sięgnąć obu warstw naraz, inaczej nie ma jej wcale. Jedyną
różnicą logic-demo wobec produkcyjnej jest DATA_URL — i to jest cała izolacja,
więc wpisanie tam „logic" cofnęłoby ją jednym słowem. Stąd komentarz przy tej
linii i sąsiedztwo obu plików.
PULE PER KONTO. Każde konto demo dostaje własny podkatalog na tym udziale,
niewidoczny dla pozostałych — w liście plików i w wynikach wyszukiwania. Konta
są listą `login:sekret` w sekrecie, bo jedno wspólne oznaczałoby wspólną pulę,
czyli klientów oglądających nawzajem swoje wgrania.
Ten sam OBRAZ co produkcja dla data i logic — różni je wyłącznie konfiguracja.
Osobny obraz to drugi kod do utrzymania i pewność, że kiedyś się rozjadą.
Logika demo nie dostaje kluczy do modeli językowych: astroklient nie umie o nie
prosić, więc nie ma powodu, żeby leżały w tym podzie.
Runbook opisuje IMPORT PULI KLIENTA do pełnej aplikacji — sedno całego układu,
bo klient przechodzący na pełną wersję nie może stracić wgrań. Jego pula to jeden
katalog: sprawdzenie kolizji nazw, kopiowanie z -n (nigdy nadpisania bazy
produkcyjnej), bez pliku stanu (produkcja ma własny), a na końcu świadome
włączenie w zakładce Pliki. Nie włączają się same, bo przeniesienie jest
czynnością techniczną, a decyzja o użyciu należy do właściciela.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deployment + Service (ClusterIP), wpięcie w kustomization, obraz dopisany do
image-updatera i runbook.
Obraz na liście image-updatera CELOWO od razu: obraz, którego tam nie ma, nigdy
się nie podbije, choćby CI go budowało — tak przez chwilę wisiał render na :latest.
Token międzywarstwowy i klucz łącza brane z TYCH SAMYCH sekretów co prezentacja:
demo nie jest furtką omijającą ochronę warstwy logicznej. Hasło demo natomiast
z OSOBNEGO sekretu astrololo-demo — demo odcina się jego skasowaniem, bez ruszania
kont głównej aplikacji.
Limit żądań niższy niż w pełnej aplikacji (60/min): demo bywa udostępniane szerzej,
a każde zapytanie sięga do treści baz.
Runbook zaczyna się od ostrzeżenia, bo to jedyna rzecz, którą trzeba pamiętać za
każdym razem: demo pracuje na PRODUKCYJNEJ warstwie danych, więc kto dostaje adres,
czyta oryginalne bazy, a jego wgrania trafiają do produkcyjnego zbioru. Pełna
izolacja wymagałaby osobnej warstwy danych i nie jest dziś zrobiona.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Treść powstała jako commit na gałęzi feat/pliki-zapis JUŻ PO zmergowaniu jej
PR-a, więc nigdy nie trafiła na master — został tam wyłącznie manifest.
Sam manifest jest w porządku (readOnly zdjęte), brakowało tylko opisu, co z tym
zrobić po stronie NAS-a.
Runbook zawiera: komendy midclt otwierające udział z bazami na zapis, sprawdzenie
przez exportfs (bo konfiguracja udziału i stan eksportu to dwie różne rzeczy),
ostrzeżenie, że przeładowanie musi iść przez zero replik zamiast rollout restart,
oraz listę kontrolną po wdrożeniu — z wyszukiwaniem jako punktem NAJWAŻNIEJSZYM,
bo to jedyne miejsce, w którym ta zmiana mogłaby przejść niezauważona.
Osobno wyliczone, co świadomie tracimy: DAN-25 dawał gwarancję, że baz nie da się
zmienić przez NFS, i tych dwóch rzeczy nie da się mieć naraz.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Manifest, wpięcie w kustomization i runbook krok po kroku.
DECYZJE ZAPISANE W MANIFEŚCIE, ŻEBY NIE TRZEBA ICH BYŁO ODTWARZAĆ Z GŁOWY:
local-path, NIE NFS. Postgres zakłada semantykę blokad i fsync, której NFS nie
gwarantuje — to klasyczne źródło uszkodzenia bazy przy nagłym restarcie. Ceną
jest przywiązanie do węzła; przy luście odtwarzalnym z Excela to akceptowalne.
strategy: Recreate. Wolumen jest ReadWriteOnce, a dwa procesy Postgresa na jednym
katalogu danych to uszkodzona baza — rolling próbowałby wstać z nowym podem,
zanim stary zejdzie.
PGDATA w PODKATALOGU wolumenu: katalog główny potrafi zawierać wpisy systemu
plików, a initdb odmawia pracy w niepustym katalogu.
Wersja PRZYPIĘTA i poza image-updaterem: podbicie majora wymaga migracji katalogu
danych, więc nie może się zdarzyć samo, w nocy, przy okazji builda aplikacji.
C.UTF-8 zamiast pl_PL.UTF-8: dopasowanie tekstu robimy przez unaccent i pg_trgm,
nie przez collation, a pl_PL wymagałby obrazu z wygenerowanymi lokalizacjami.
DATA_PROVIDER zostaje na `excel`. Postgres można wdrożyć i obejrzeć BEZ ryzyka
dla działającego wyszukiwania; przełączenie to osobna, późniejsza decyzja.
SPRAWDZONE PRZED ODDANIEM: `kubectl kustomize astrololo` składa komplet 21
zasobów bez błędu, YAML parsuje się poprawnie, audyt odwołań potwierdza, że
jedynym brakującym sekretem jest astrololo-postgres (oba klucze), a DSN
postgresql+psycopg:// jest rozpoznawany przez SQLAlchemy z psycopg 3.
NIE SPRAWDZONE: skrypt inicjalizujący nie biegł przeciwko prawdziwemu Postgresowi
— na maszynie, na której to powstawało, nie ma Dockera. Zapisane wprost
w runbooku; krok 4 jest tam prawdziwym testem.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Pod presentation nie wstawał: CreateContainerConfigError, „failed to create
subPath directory for volumeMount state".
MÓJ BŁĄD, NIE ZAGADKA. Udział z bazami jest wyeksportowany `ro: true`
z `root_squash` — ustawiłem to runbookiem DAN-25 i sam tam napisałem ostrzeżenie,
że po tej zmianie nikt nic nie wgra. Dokładając wolumen na konta zmieniłem
`readOnly` przy MONTOWANIU w podzie i uznałem sprawę za załatwioną, nie sprawdzając
eksportu po stronie serwera.
Przyczyna była zresztą podwójna i za każdym razem ta sama:
1. kubelet nie mógł utworzyć podkatalogu, bo udział jest tylko do odczytu,
2. a gdyby nawet mógł — kontenery działają jako root, a root_squash mapuje
roota na nobody, więc aplikacja i tak nie zapisałaby tam pliku kont.
ROZWIĄZANIE BEZ RUSZANIA DAN-25: osobny, mały udział /mnt/Tank1/astrololo-state,
zapisywalny, zawężony do tych samych trzech węzłów, z mapall_user na
nieuprzywilejowanego użytkownika (bezpieczniejsze niż no_root_squash, bo nie
oddaje roota). Udział z bazami ZOSTAJE tylko do odczytu.
Przy okazji znika subPath, czyli znika potrzeba, żeby kubelet cokolwiek zakładał —
katalog istnieje, bo jest korzeniem udziału.
Runbook README-stan-prezentacji.md zawiera test zapisu Z WĘZŁA do wykonania PRZED
wdrożeniem — dokładnie ten, którego zabrakło za pierwszym razem.
UWAGA: DAN-27 (zarządzanie plikami baz) uderzy w tę samą ścianę, bo wymaga zapisu
do udziału z BAZAMI. Ten commit tego nie rozwiązuje i celowo nie rusza DAN-25 —
to osobna decyzja, opisana na końcu runbooka.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Konfiguracja argocd-image-updater istniała tylko w klastrze (ręczne `kubectl
apply`, brak w git). Skutkiem był PRE-24: `render` zbudował się i zdeployował raz,
ale kolejne buildy nie schodziły — bo nie było go na JAWNEJ liście obserwowanych
obrazów CRD, a nigdzie nie dało się tego podejrzeć ani odtworzyć.
Zrzucam manifest (z `render` już w środku, 4 obrazy: data/logic/presentation/
render) do `astrololo/image-updater.yaml` + opis w README.
Plik CELOWO nie jest w kustomization.yaml: resource stoi w ns `argocd` (poza
namespace docelowym aplikacji), a to konfiguracja kontrolera wdrażającego tę
aplikację — nakładany ręcznie (`kubectl apply -f astrololo/image-updater.yaml`),
w repo dla odtwarzalności i historii. Dodanie nowej usługi = dopisanie wpisu tutaj.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Domyka PRE-16 po stronie manifestow. Kod (astrololo#21) potrafi juz szyfrowac
ruch miedzy warstwami AES-256-GCM; tu dokladamy klucze i wymuszenie.
- astrololo-link: nowy sekret z dwoma kluczami (LINK_KEY_PRESENTATION_LOGIC,
LINK_KEY_LOGIC_DATA), tworzony POZA repo jak pozostale. Osobny klucz na pare
rozmowcow: przejecie klucza prezentacji nie otwiera warstwy danych. Logika
bierze oba, prezentacja i dane wylacznie swoj (secretKeyRef).
- LINK_ENCRYPTION_REQUIRED=true we wszystkich trzech: bez klucza pod NIE wstaje,
a klient nie wysyla niczego. Fail-closed w obie strony jest celowy — usluga,
ktora wstala i po cichu nie szyfruje, jest gorsza niz CrashLoop, bo awarii
nie widac.
README: sekcja o sekrecie astrololo-link (tworzenie, wymiana, restart calej
trojki naraz) oraz uczciwa nota, ze szyfrowane sa ciala, nie naglowki — sciezka
i token jada czytelnie, ale sam token bez klucza nic nie daje.
Sprawdzone: kubectl kustomize + apply --dry-run=server przechodza dla obu
profili. Pelna instrukcja wdrozenia: docs/wdrozenie-pre16.md w repo astrololo.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Do tej pory aplikacja chodzila po zwyklym http, a logowanie szlo przez HTTP
Basic — czyli haslo leciało siecia w postaci trywialnej do podsluchania.
Poza bezpieczenstwem TLS naprawia DWIE funkcje zepsute dzis z tego samego
powodu: geolokalizacja przegladarki („Tu i teraz") i kopiowanie promptu do
schowka dzialaja wylacznie w secure context, wiec po http po prostu odmawialy.
Wlasne CA, nie Let's Encrypt — klaster stoi w LAN (traefik trzyma LoadBalancera
na 192.168.1.x), wiec walidacja HTTP-01 nie ma jak dojsc z internetu, a DNS-01
wymagalby trzymania w klastrze tokena API do domeny.
Co dochodzi:
- tls.yaml — Issuer selfSigned -> korzen CA (10 lat, importowany raz do
przegladarki) -> Issuer CA -> certyfikat serwera (90 dni, odnawiany sam).
Krotki termin certyfikatu serwera jest CELOWY: odnawianie sprawdza sie wtedy
co kwartal, a nie dopiero za dekade.
- ingress.yaml — wejscie po https + naglowki (HSTS rok, nosniff, frameDeny)
oraz osobny Ingress na porcie 80 robiacy przekierowanie. Rozdzielone
swiadomie: gdyby redirect wisial na tym samym routerze co wejscie po https,
odsylalby sam do siebie w kolko.
- presentation: Service NodePort -> ClusterIP. NodePort byl druga,
nieszyfrowana droga do aplikacji, czyli obejsciem calego PRE-16.
Zmiana wymagajaca uwagi: TRUST_PROXY=true. Za Ingressem bezposrednim rozmowca
jest zawsze pod Traefika, wiec bez tego limit zadan liczylby WSZYSTKICH do
jednego wiadra i pierwsza osoba, ktora go wyklika, odcielaby pozostalych.
Sprawdzone: Ingress i Middleware przechodza `kubectl apply --dry-run=server`
na zywym klastrze (CRD traefik.io/v1alpha1 sa, entrypointy web/websecure sa).
Obiekty cert-managera zwalidowane offline wobec oficjalnego schematu z CRD
v1.21.0 z additionalProperties=false — literowka w nazwie pola zostalaby
zlapana. `kubectl kustomize` przechodzi dla obu profili (bazowego i swisseph).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Domyslnie dziala model LOKALNY i nic nie opuszcza sieci. Zeby dalo sie wybrac
w UI OpenAI albo Anthropic, logika potrzebuje ich kluczy.
- LLM_PROVIDER=local, LOCAL_BASE_URL, LOCAL_MODEL, LLM_TIMEOUT, LLM_MAX_TOKENS
jawnie (nie sa tajne),
- OPENAI_API_KEY i ANTHROPIC_API_KEY z osobnego sekretu `astrololo-llm`.
Sekret jest OPCJONALNY (optional: true) — inaczej niz `astrololo-auth`. Auth to
zabezpieczenie i ma zatrzymac pody, gdy go brak; klucze do chmury to funkcja,
wiec ich brak nie moze wywracac wdrozenia. Bez nich pody startuja normalnie,
tylko chmura jest niedostepna.
Osobny sekret, a nie doklejenie do astrololo-auth, zeby klucze LLM dalo sie
wymieniac bez dotykania hasla i tokenu miedzywarstwowego.
Zwalidowane kubectl kustomize; w wyniku nadal zadnego `kind: Secret`.
Instrukcja tworzenia i podmiany kluczy w astrololo/README.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Domyka od strony wdrozenia ochrone dodana w aplikacji (PR #10 w repo astrololo).
Bez tych zmiennych aplikacja startuje OTWARTA, a wystawia tresc oryginalnych
baz interpretacyjnych.
- presentation: APP_PASSWORD + INTERNAL_TOKEN z sekretu, APP_USER i
RATE_LIMIT_PER_MIN jawnie (nie sa tajne).
- logic, data: INTERNAL_TOKEN z sekretu — bez niego da sie ominac logowanie,
uderzajac wprost w warstwe nizej. Warstwa danych oddaje SUROWE wiersze,
wiec to najwrazliwszy punkt.
WARTOSCI SEKRETOW CELOWO NIE TRAFIAJA DO REPO — to GitOps, wiec zostalyby
w historii gita na zawsze i przekreslily caly sens tej zmiany. Manifesty
tylko odwoluja sie do sekretu `astrololo-auth`, tworzonego poza repo — ta sama
konwencja co istniejacy `gitea-registry`.
UWAGA: sekret jest WYMAGANY (bez `optional: true`), wiec pody nie wstana,
dopoki go nie utworzysz. To swiadome: lepsza widoczna awaria niz cichy start
bez ochrony. Instrukcja tworzenia i zmiany hasla w astrololo/README.md.
Przy okazji: poprawiony mylacy komentarz przy EXCEL_DIR — pliki baz ida
z NFS, nie z obrazu (montaz je nadpisuje).
Zwalidowane `kubectl kustomize` dla bazy i profilu swisseph: sekrety trafiaja
do wlasciwych uslug, a w wyniku nie ma zadnego `kind: Secret`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>