# engine-swisseph (silnik B — AGPL, izolowany) Osobna, **opcjonalna** usługa będąca drugim silnikiem efemeryd (LOG‑27). Liczy pozycje przez **pyswisseph / Swiss Ephemeris** i służy jako **wyrocznia walidacyjna / tryb porównawczy** (LOG‑25/26) dla naszego silnika własnego. ## ⚠️ Licencja Ten komponent jest **AGPL‑3.0** (bo linkuje Swiss Ephemeris) — patrz [LICENSE](LICENSE). Jest **wydzielony jako osobny proces** i wołany przez HTTP, więc nie „zaraża" permisywnej reszty systemu. **Nie wchodzi do dystrybucji zamkniętego produktu.** ## API - `POST /positions` → `{when_utc, lat, lon, objects?}` → pozycje (ten sam kształt co silnik własny) - `GET /health` Tryb Moshiera (`FLG_MOSEPH`) — bez plików efemeryd, zero konfiguracji. ## Build obrazu ```bash docker compose --profile comparison build engine-swisseph ``` Dockerfile jest **wieloetapowy** i to nie jest ozdobnik: `pyswisseph` to rozszerzenie C, a na PyPI (2.10.3.2) gotowe wheels kończą się na **cp311** i obejmują wyłącznie **i686/x86_64**. Dla Pythona 3.12 oraz dla arm64 pip musi kompilować ze źródeł, więc sam `python:3.12-slim` (bez kompilatora) build wywracał. Kompilacja idzie w etapie `builder` (`build-essential`), a do obrazu finalnego trafia już tylko gotowy wheel — runtime zostaje czysty i mały. Pierwszy build trwa ~1–2 min, kolejne idą z cache warstw. Build kończy się sanity-checkiem (`import swisseph`), żeby niedziałający silnik wykrzaczył build, a nie dopiero pierwszy request. ## Uruchomienie (tylko profil porównawczy / dev / CI) ```bash pip install -r requirements.txt # wymaga kompilatora C (patrz wyżej) uvicorn app.main:app --port 8003 ``` Następnie w warstwie logicznej ustaw `ENGINE_SWISSEPH_URL=http://localhost:8003`, aby włączyć silnik B (tryb dual-run i testy kontraktowe silnika B).