--- okf: "0.1" type: session-log visibility: private status: active updated: 2026-06-17 links: [] --- # Sesja 2026-06-17 — KB foundations (etap 1 maili) ## Cel Zbudowanie fundamentu filaru maili: pgvector spine na SOLARIA, zamrożona koperta, pakiet domeny `kb-mail`, testy. Bez live ingestu, bez bulk Gmaila, bez indexera — sam fundament pod kolejne etapy. --- ## Architektura KB — ustalona w tej sesji (docs) ### 4 warstwy × 4 filary + rdzeń ``` [ 4. Agent interdyscyplinarny ] NL query + analityka (LLM) ← wspólne [ 3. Join + Enrich ] entity resolution · graf · OwnTracks ← wspólne [ 2. Preprocess + Index ] → pgvector / → SQL ← per filar [ 1. Ingest ] Maile Dokumenty Zdjęcia Transakcje ← per filar ``` OwnTracks = **klucz czasoprzestrzenny w warstwie 3**, nie osobny filar (ciągła funkcja czas→miejsce). ### Decyzje zamknięte - Spine: **Postgres + pgvector** (nie Qdrant) na SOLARIA - Embed: **bge-m3** (multilingual, długi kontekst, lepszy pod polski) - Maile: **dwa żywe źródła** — Fastmail (JMAP, primary) + Gmail (IMAP; konto zostaje jako śmieciowe/2FA) - Bulk historyczny Gmail (jednorazowy) — etap 2 - Reguła: **archiwizuj wszystko, indeksuj selektywnie** (filtr na wejściu do indeksu, nie archiwum) - Załączniki: **II tura** (MVP = czysty tekst + nagłówki) - Warstwa 3 startuje jako **cienki graf encji**; federacja przy zapytaniu — później - Dokumenty: **Nextcloud** (drive, WebDAV) + **Paperless-ngx** (skany/faktury/OCR) - Koperta: zamrożona addytywna — `id / source / ts(UTC) / geo / raw_ref / entities[]` ### Decyzje otwarte - **Transakcje (filar #4):** OPEN ISSUE — agregator PSD2 (GoCardless / Tink) dla mBank + Revolut, ale zgoda SCA wygasa co 90 dni → brak pełnego bezobsługowego sync; fallback: CSV/MT940. Do decyzji przy starcie filaru #4. --- ## Etap 1 — ZBUDOWANY ### services/kb-postgres - Obraz: `pgvector/pgvector:pg16` - SOLARIA, port **5433** (5432 zarezerwowany na potencjalny lokalny postgres) - Named volume: `kb_postgres_data` - Init SQL (`init/001_envelope.sql`): `CREATE EXTENSION vector` + tabela `envelope` - Per-host override: `hosts/solaria/runtime/kb-postgres/docker-compose.override.yml` — `mem_limit: 4g` - `inventory/topology.yaml` + `hosts/solaria/services.yaml` — wpisy dodane ### Zamrożona koperta (001_envelope.sql + Envelope dataclass) ```sql CREATE TABLE envelope ( id TEXT PRIMARY KEY, source TEXT NOT NULL, -- fastmail | gmail | ... ts TIMESTAMPTZ NOT NULL, geo JSONB, -- null dla maili; warstwa 3 uzupełnia raw_ref TEXT NOT NULL, -- ścieżka do .eml w archiwum entities JSONB NOT NULL DEFAULT '[]' ); ``` `Envelope` dataclass waliduje `ts.tzinfo is not None` w `__post_init__`. ### packages/kb-mail — nowa konwencja shared lib Lokalizacja: `packages//` (nie `services/`) — biblioteki reużywalne przez joby. Instalacja w Dockerfile: `COPY packages/kb-mail/ /packages/kb-mail/ && pip install /packages/kb-mail/`. | Moduł | Co robi | |-------|---------| | `envelope.py` | `@dataclass Envelope`, walidacja tz-aware ts | | `db.py` | `insert_envelope` (ON CONFLICT DO NOTHING) + `get_envelope` (asyncpg) | | `archive.py` | `save_eml` — append-only, `asyncio.to_thread` na write, `FileExistsError` na duplikat | structlog JSON w każdym module (`structlog.get_logger(__name__)`); konfiguracja po stronie konsumenta (biblioteka nie konfiguruje structlog). ### Testy - 15 unit testów (nie-integration): model, archiwum, sanity SQL — wszystkie zielone bez DB - 5 integration testów (`@pytest.mark.integration`): round-trip DB, duplikat, geo, entities — wymagają `KB_TEST_DSN` - `conftest.py`: fixture `db_conn` = asyncpg connection + BEGIN/ROLLBACK na każdy test ### Poprawki spójności (code-review sesji) 1. **README deploy**: pierwotna wersja miała `docker compose --env-file /opt/homelab/config/…` — pomijała override file i miała złą ścieżkę. Naprawiono: README dokumentuje trzy ścieżki (standardowa przez `deploy.sh solaria`, first-time setup `.env` obok compose file, manual z dwoma `-f`), zgodnie z `deploy-node.sh` L65–72. 2. **.gitignore**: reguła `*.env` (L3) już łapie `services/kb-postgres/.env` — potwierdzono `git check-ignore`. Nic nie dodano. --- ## Higiena git - Cała praca w worktree `task/kb-foundations` — master czysty przez cały czas - 2 commity na branchu po zakończeniu sesji --- ## Commits ``` 31c64d5 feat(kb-mail): fundament — pgvector spine, koperta, archiwum, pakiet domeny docs(kb): etap 1 fundament done + session log 2026-06-17 ``` ## Files changed (etap 1) ``` services/kb-postgres/docker-compose.yml services/kb-postgres/service.yaml services/kb-postgres/env.example services/kb-postgres/healthcheck.sh services/kb-postgres/init/001_envelope.sql kb/services/kb-postgres.md hosts/solaria/runtime/kb-postgres/docker-compose.override.yml hosts/solaria/services.yaml inventory/topology.yaml packages/kb-mail/pyproject.toml packages/kb-mail/src/kb_mail/{__init__,envelope,db,archive}.py packages/kb-mail/tests/{conftest,test_envelope,test_archive,test_db,test_migration}.py kb/subsystems/kb-overview.md (etap 1 done, konwencja packages/) CLAUDE.md (sekcja Shared Python Libraries) docs/sessions/2026-06-17-kb-foundations.md ``` ## Następny krok **Etap 2: jednorazowy bulk importer Gmail** — mbox/Takeout → archiwum. Wejście: plik `.mbox` lub katalog Maildir z eksportu Google Takeout. Wyjście: `.eml` w archiwum + wiersze `envelope` w DB. Jako one-shot job (nie serwis), reużywa `packages/kb-mail`.