A personal AI astrology web app. It computes your exact natal chart from the sky at your birth using the Swiss Ephemeris — mathematically, with no third-party astrology API — then interprets it: your Big Three, an interactive chart wheel, elemental balance, numerology, gemstones, deep AI readings (love / career / growth), and a daily transit forecast renewed each dawn.
The interpretation layer is provider-agnostic: switch between Ollama (local, free) and OpenAI (cloud) from the Settings page with no code change — the same astrological data flows to whichever engine you pick.
- Accurate, API-independent chart engine. Planets, houses (Placidus),
Ascendant/MC and aspects are computed with
pyswissephusing the Moshier model — no ephemeris data files, no external service. - Step-by-step onboarding wizard with an animated starfield and a constellation progress rail: name → birth date → birth time (with a "don't know" option that gracefully skips houses) → birthplace (live city → coordinates via Open-Meteo geocoding) → gender → reveal.
- Interactive natal wheel. A hand-drawn SVG chart — zodiac ring, house cusps, aspect lines, planets. Tap any planet for a popup with its sign, house, degree and archetype.
- Structured-JSON AI. The backend feeds your compact chart into a system
prompt and the model must return a single JSON object; each section
(
love_analysis,career_analysis,daily.affirmation, …) is parsed into its own panel. LLM values are coerced to text at the boundary, so a model that nests an object where a string was expected can never break the UI. - Daily transit engine. Live planetary positions are compared with your natal chart for a transit reading, a cosmic do / don't, and a morning affirmation — generated once per day and cached.
- Cosmic glassmorphism theme. Midnight blue, deep purple, neon gold; blurred glass surfaces, gold hairlines, twinkling stars, Cinzel / Cormorant Garamond / Inter. Fully responsive to mobile.
| Criterion | Status |
|---|---|
| City → coordinates resolved automatically | ✅ live geocoding (Open-Meteo) |
| Natal chart responsive on mobile | ✅ single-column at ≤640px, scaled wheel |
| OpenAI ↔ Ollama switch with no code change | ✅ provider stored per-user, swapped in Settings |
| Chart computed once and stored | ✅ cached on AstroProfile.chart |
| Daily reading generated once per day | ✅ HoroscopeLog keyed by (user, kind, day) |
Backend — Python · FastAPI · SQLAlchemy 2 · SQLite (WAL) ·
pyswisseph · httpx ·
PyJWT · bcrypt · cryptography (AES-256-GCM vault).
Frontend — React 18 · TypeScript · Vite · React Router 6. No heavy chart libraries: the natal wheel is bespoke SVG and the starfield is a canvas.
AI — pluggable provider layer: OpenAI (response_format: json_object) or
Ollama (format: "json").
Birth data ──> Swiss Ephemeris ──> natal chart (JSON, cached on the profile)
│
┌──────────────────────┼───────────────────────┐
▼ ▼ ▼
panel builder LLM context daily transits
(Big 3, elements, (compact chart sent (live sky vs natal,
numerology, stones) to OpenAI / Ollama cached per day)
│ as a system prompt) │
└──────────────► structured JSON ◄────────────┘
parsed into panels
- The chart is computed once at onboarding and stored on the profile, so every dashboard load is instant.
- AI readings and the daily forecast are cached in
HoroscopeLogto control cost (readings until regenerated; daily per calendar day).
- User — credentials, AI provider choice (
ollama/openai), model names. - AstroProfile — 1:1 with user: name, birth date/time, place + coordinates,
timezone, gender, and the computed
chartJSON. - HoroscopeLog — cached AI output (
kind=readings|daily,day). - Vault — AES-256-GCM encrypted secrets (the OpenAI API key).
The app runs on two processes in development (Vite proxies the API), or as a single FastAPI process in production (FastAPI serves the built SPA).
cd backend
python -m venv .venv
.venv/Scripts/activate # Windows
# source .venv/bin/activate # macOS / Linux
pip install -r requirements.txt
python -m uvicorn app.main:app --host 127.0.0.1 --port 8300The database, secret key and vault key are created on first run under
backend/data/ (git-ignored). On Windows, tzdata (in requirements.txt)
provides the IANA timezone database that zoneinfo needs.
cd frontend
npm install
npm run dev # http://localhost:5300 (proxies /api → :8300)cd frontend && npm run build # outputs frontend/dist
# restart the backend — it auto-serves dist at http://127.0.0.1:8300Open Settings:
- Ollama (default): set the host (e.g.
http://localhost:11434) and pick a model. "Test connection" lists the models Ollama has pulled. Free and private. - OpenAI: paste an API key (stored encrypted) and choose a model.
Switching is a per-user setting — no redeploy, no code change.
backend/
app/
astrology/ engine.py (Swiss Ephemeris), signs.py, numerology.py, profile.py
ai/ provider.py (OpenAI/Ollama dispatch + JSON coercion), prompts.py
routers/ auth, onboarding (+ geocode), astro (profile/readings/daily), settings
models.py db.py vault.py security.py geocode.py main.py
frontend/
src/
components/ NatalChart, Starfield, Modal, GemIcon, AppHeader, PasswordInput…
pages/ Landing, Auth, Onboarding (wizard), Dashboard, Settings
lib/ api.ts, auth.tsx, zodiac.ts
styles/ theme.css (cosmic tokens)
- Passwords hashed with bcrypt; sessions are stateless JWT (HS256).
- The OpenAI key is encrypted at rest with AES-256-GCM; the key never leaves the server and is returned to the client only as a "saved" flag.
- All inbound bodies are validated with Pydantic; emails with
email-validator. - LLM output is treated as untrusted and coerced to safe text before render.
Apache-2.0.