# SatelliteIndex.io — project guide (condensed from the founding spec, see docs/SPEC-summary.md) **Mission**: the definitive public intelligence layer for everything operating, launched, proposed, licensed, decaying or changing in Earth orbit — Bloomberg Terminal × FlightRadar24 × Crunchbase for orbital infrastructure. Not a simple satellite tracker. ## Architecture (never couple the website to third-party sources) ``` External sources → connector workers → raw snapshots (SI_DATA_DIR/raw, gzip + raw_records) → normalization → entity resolution → canonical Postgres → derived analytics (matviews, stats_snapshots, search_index, events) → FastAPI /api/v1 → Next.js ``` - **Backend** `src/satelliteindex/` (Python 3.12, FastAPI, SQLAlchemy Core + asyncpg, Alembic, sgp4, APScheduler, Redis). - `connectors/` framework (`BaseConnector.run()` = bookkeeping, hashing, circuit breaker; `execute()` per connector) — CelesTrak GP (`celestrak_gp`), CelesTrak groups (`celestrak_groups`), SATCAT (`celestrak_satcat`), `derived_analytics`. Registry: `connectors/__init__.py`. - `orbital/` SGP4 (vectorised `SatrecArray`), TEME→geodetic, orbit classification (documented metric `orbit_class`). - `services/` entity resolution (`SatIndex`, NORAD → COSPAR → name; never auto-merge ambiguous), classification (curated `registry/constellations.yaml`), positions service (in-memory propagator, 30 s cache), events (dedupe keys), Redis cache/locks. - `api/routers/` satellites, search, stats/rankings/density, orbit positions, constellations, operators, countries, launches/sites/debris/reentries, events, misc (sources, status, methodology, sitemap, view beacon), admin (`x-si-admin-token`). - CLI `si`: `migrate | seed | run [--file group=path] | status | stats | position | schedule | api | backup`. - **Frontend** `apps/web/` (Next 16, Tailwind v4, Three/R3F globe). Guide: `docs/FRONTEND-GUIDE.md`. - **Database**: `migrations/versions/0001_initial.py` (plain SQL). Internal ids are prefixed ULIDs (`sat_…`); NORAD/COSPAR are *source* identifiers. Orbital history is append-only (`orbital_elements`), `orbital_state` = latest per object. Materialized views refresh hourly. ## Hard rules (from the spec) - No hardcoded satellite counts, source results, timestamps, fake analytics or placeholder charts. Missing → "Unavailable". - Never overwrite orbital history, discard provenance, merge ambiguous satellites silently, or delete entities because a source went quiet (empty/short upstream responses are *suspicious*, not "no data"). - Never expose credentials (Space-Track, DB, Scrapfly…) or internal cluster addresses. Admin behind token. Rate limits on search/positions. - Derived analytics (orbit class, mission type, constellation membership, activity score, orbital density) are labelled derived with a documented, versioned methodology (`metric_definitions`, `/methodology`). Never present them as safety metrics. Never invent collision probabilities or precise reentry locations. - Mobile-first (390/430/768/1440), no horizontal overflow, ≥ 44 px targets, DOM order = visual order. Design: premium, scientific, dark, data-dense — no crypto-dashboard look, no wall of rounded cards. - Units: km, km/s, kg, degrees, minutes, UTC. ISO 3166 country codes. - Every feature: real data, error + loading states, mobile + desktop, source attribution, performance, accessibility, logging, tests, deployment. ## Local development ``` uv venv --python 3.12 .venv && uv pip install --python .venv/bin/python -e ".[dev]" # backend deps cp .env.example .env # local Postgres `satelliteindex`, Redis db 4 .venv/bin/si migrate && .venv/bin/si seed .venv/bin/si run celestrak_satcat && .venv/bin/si run celestrak_gp && .venv/bin/si run celestrak_groups && .venv/bin/si run derived_analytics .venv/bin/si api # http://127.0.0.1:8311 (docs at /api/v1/docs) pnpm install && pnpm dev:web # http://localhost:8310 pytest # tests/ (fixtures in tests/fixtures, no live network by default) ``` CelesTrak blocks a second download of the same GP group within 2 h (HTTP 403 "has not updated") — the connector treats it as *unchanged*; `si run celestrak_gp --file active=/path/active.json` ingests a local snapshot. ## Production **Since 2026-09-14: OVH server BHS128b** (`ssh BHS128b`, Ubuntu 24.04), outside `mld`: PM2 under systemd (`~/apps/pm2.satelliteindex.config.cjs`, generated from the mld manifest kept in `~/apps/.manifests/`) — `satelliteindex-api` (uvicorn 127.0.0.1:8311), `satelliteindex-scheduler` (`si schedule`), `satelliteindex-web` (Next :8310). Postgres 17 + Redis native; code `~/apps/satelliteindex` (uv Python 3.12 venv), data in `~/satelliteindex-data/`. Public route: MacLustr Tunnel (BHS64 Caddy) `https://www.satelliteindex.io → wg1 10.67.0.62:8310`, apex redirected. Release = rsync + `uv pip install -e .` + `si migrate` + web build + `pm2 restart` (exact command in `docs/DEPLOY.md`). Former home: M2M32b via `mld` (manifest `deploy/satelliteindex.mld.json`), kept as a cold copy.