Demo ma być rozdawane szeroko i różnym osobom, więc pierwsza wersja — jedno konto
na produkcyjnej warstwie danych — nie nadawała się do użycia: każdy dostawałby
dostęp do oryginalnych baz, a wgrania jednego klienta widzieliby wszyscy.
IZOLACJA OD PRODUKCJI. Warstwa danych i logiczna demo są osobne (manifesty w repo
deploy). Osobna musi być TEŻ LOGICZNA, bo zna ona jeden adres warstwy danych —
demo korzystające z produkcyjnej logiki i tak trafiłoby na produkcyjne bazy.
PULE PER KONTO w warstwie danych. Zapytanie i lista plików niosą nazwę puli;
puste = cały udział, czyli produkcja działa dokładnie jak dotąd i o pulach nic
nie wie. Nazwa puli przechodzi przez sito dopuszczające wyłącznie znaki bezpieczne
w nazwie katalogu — „../..” albo ukośnik wyprowadziłyby zapytanie wprost do cudzych
baz, więc sito ZAMIENIA podejrzane znaki zamiast ufać, że nikt ich nie poda.
PULA MUSI BYĆ W KLUCZU CACHE ZAPYTAŃ. Bez tego wynik policzony dla jednego konta
trafiłby z cache do drugiego — cicha wymiana treści baz między klientami,
niewidoczna w logach i nie do wykrycia z zewnątrz. Osobny test tego pilnuje.
PULA WYNIKA Z LOGINU, nigdy z żądania. Klient warstwy logicznej jest budowany
per żądanie i związany z pulą zalogowanej osoby; gdyby nazwa przychodziła
z formularza, wystarczyłoby podstawić cudzy login. Test wysyła `tenant`, `user`
i `login` w polach formularza i sprawdza, że nie mają na nią wpływu.
Pulę wstrzykujemy w INSTANCJĘ klienta, nie w sygnatury metod. Argumentem trzeba
by ją przeprowadzić przez protokół DataSource i build_report — kod, który o kontach
nie ma prawa nic wiedzieć — a każde nowe wywołanie byłoby okazją, żeby o nią
zapomnieć i sięgnąć nie tam.
Konta demo to lista `login:sekret` (DEMO_USERS), bo jedno wspólne konto oznaczałoby
wspólną pulę. Format i skrypt haseł te same, co w głównej aplikacji.
Pula klienta to JEDEN KATALOG, więc przejście na pełną wersję nie oznacza utraty
wgrań — procedurę importu opisuje runbook w repo deploy.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bazy sa rdzeniem produktu i wlasnie zostaly kupione — a aplikacja nie miala
ZADNEGO uwierzytelniania. Prezentacja to NodePort, wiec kazdy w LAN wchodzil
bez logowania, a `/search` oddawal surowe wiersze do 50 000 na zapytanie.
Bazy mogly wyjsc przez sama aplikacje, bez udzialu jakiegokolwiek LLM.
- prezentacja: HTTP Basic (APP_USER/APP_PASSWORD) + limit zadan na IP
(RATE_LIMIT_PER_MIN, domyslnie 120/min). Limit dziala TAKZE przed
uwierzytelnieniem, zeby zgadywanie hasla i sondowanie API nie bylo darmowe.
- logika i dane: token miedzywarstwowy X-Astrololo-Token (INTERNAL_TOKEN) —
bez niego dalo sie ominac logowanie, uderzajac wprost w warstwe nizej.
Warstwa danych oddaje surowe wiersze, wiec to najwrazliwszy punkt.
- /search: gorny limit 50 000 -> 5000 (tyle realnie uzywa build_report).
Publiczne /api/query zostaje na 200.
- /health celowo publiczny (sondy k8s go nie uwierzytelnia).
- swiadomie nie logujemy tresci zadan ani promptow — logi to kolejny nosnik.
Fail-open przy braku konfiguracji (zgodnosc wstecz i dev), ale z GLOSNYM
ostrzezeniem przy starcie, zeby nikt nie wdrozyl tego w przekonaniu, ze jest
chroniony. Wlaczenie w produkcji wymaga ustawienia sekretow w repo deploy.
Testy: 12 (prezentacja, nowy katalog + job w CI) i 5 (logika). Zweryfikowane
na zywym stosie: bez hasla 401, z haslem 200, logika wprost bez tokenu 401,
z tokenem 200, /health 200, limit 50000 odrzucony (422), a prezentacja nadal
liczy horoskop przez logike.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pierwsza wersja mostu horoskop -> sygnifikatory -> baza (zalążek LOG-16/18/19).
- logic/significators.py: z pozycji generuje tokeny w składni bazy (planeta
[Su, znak [Tau...), pyta warstwę danych o rekordy z tokenem planety i zawęża
do tych, które wspominają też jej znak ("planeta w swoim znaku"); odsiewa szum.
- logic /chart/report: nowy endpoint (pozycje -> raport dopasowań z interpretacjami).
- logic DataClient.search: parametr fields (lżejszy payload).
- data: naprawa str.contains regex=True -> regex=False (sygnifikatory zawierają
[ + itd., metaznaki regex); podniesiony górny limit zapytania (le=50000).
- prezentacja: strona /interpret (formularz -> wyszukane interpretacje per obiekt)
+ nawigacja.
Zweryfikowano end-to-end na realnym pliku (Encyclopaedia of Medical Astrology,
53969 wierszy): dla horoskopu 30.04.1984 znaleziono m.in. Sun w Taurus 46,
Mars w Scorpio 57, Saturn w Scorpio 61 dopasowań; przykłady: "[Su in [Tau" ->
"the bump of amativeness prominent". 15 testów przechodzi.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Trzy niezależne usługi FastAPI komunikujące się przez HTTP/JSON, każda
zna tylko adres warstwy bezpośrednio pod nią:
- presentation (:8000) — strona WWW + formularz
- logic (:8001) — reguły biznesowe, pośrednik
- data (:8002) — wyszukiwanie danych za interfejsem DataProvider
Warstwa danych: czytanie setek plików .xlsx z wykrywaniem nagłówka i
mapowaniem układu kolumn na schemat kanoniczny, z 4-poziomowym cache
(schemat L1, Parquet L2, wyniki zapytań L3, odwrócony indeks L4) i
unieważnianiem po odcisku pliku. Gotowa ścieżka migracji do SQL
(ingest/to_sql.py + SqlDataProvider, przełączane przez DATA_PROVIDER).
Zawiera docker-compose, Makefile, generator danych przykładowych.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>