Appearance
Architecture
A reference for the moving parts. Useful before you self-host, before you contribute, and before you debug anything operational.
High-level shape
┌─────────────────┐
│ User browser │
│ React + Cesium │
└────────┬────────┘
│ HTTPS, WebSocket
▼
┌────────────────────────────────────────────┐
│ FastAPI backend (uvicorn) │
│ ┌──────────────┐ ┌─────────────────┐ │
│ │ HTTP routes │ │ WebSocket route │ │
│ └──────┬───────┘ └────────┬────────┘ │
│ │ │ │
│ ┌──────▼───────────────────▼──────┐ │
│ │ Services layer (httpx clients) │ │
│ └──┬──────┬──────┬───────┬──────┬─┘ │
└──────┼──────┼──────┼───────┼──────┼─────────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
Finnhub EODHD OpenSky MT Stripe
MarineTraffic
┌─────────────┐ ┌──────────┐
│ PostgreSQL │ │ Redis │
└─────────────┘ └──────────┘Components
Frontend — React + Vite (frontend/)
- Stack: React, Vite, plain JavaScript (no TypeScript), Tailwind, React Router.
- Entry: frontend/src/App.jsx.
- Workspace root: OSINTDashboard.jsx — orchestrates all panels.
- Routing: Two protected routes (
/,/dashboard/:tab), three public (/login,/register,/pricing), one admin (/admin). - State: Local component state + a small set of context providers; no global Redux/MobX. Server state is fetched per panel via small hooks (e.g., useMarketData.js).
- i18n: 6 locales in frontend/src/i18n/. All keys mirrored across all locales.
- 3D globe: Cesium, accessed via the public
VITE_CESIUM_ION_TOKEN. - Build output: Static SPA — deployable behind any CDN.
Backend — FastAPI (backend/)
- Stack: Python 3.11+, FastAPI, SQLAlchemy, Alembic, httpx (async), Poetry.
- Entry: backend/main.py.
- Routes: Modular under backend/routes/ — one file per surface (auth, market, news, alerts, ai_chat, vessels, aircraft, …).
- Services: backend/services/ — one file per upstream provider, all
httpx.AsyncClient-based with explicit timeouts andasyncio.Semaphorefor rate limits. - Auth: JWT access tokens (short-lived) + refresh tokens (HTTP-only cookie). 2FA via TOTP.
- WebSocket: backend/routes/websocket.py for live quote streams and alert delivery.
Database — PostgreSQL
- Schemas: users, watchlists, alerts, screens, graphs, dashboards, portfolios, subscriptions.
- Migrations: Alembic —
alembic upgrade headafter every backend update. - Recommended version: 14+. Earlier versions miss features like generated columns used in some indexes.
Cache — Redis
- What it caches:
- Short-TTL upstream responses (quotes, OHLC, fundamentals) so the backend doesn't hammer Finnhub / EODHD on every request.
- Rate-limit counters per user and per upstream provider.
- WebSocket session state.
- Recommended version: 6+.
- Atlas does not run without Redis — the rate-limit middleware refuses to start if
REDIS_URLdoesn't connect.
Documentation — VitePress (apps/docs/)
- Independent app: static deploy, separate from frontend and backend.
- Glossary auto-gen: generate-glossary-docs.mjs reads
frontend/src/components/onboarding/glossary.jsand writes a single auto-managed page. - Screenshot capture: capture-screenshots.mjs drives Playwright against the running frontend.
Request lifecycle
For a typical "give me AAPL quote" request:
- Frontend —
useMarketData('AAPL')fires. - HTTP —
GET /api/market/quote?symbol=AAPLwith the access-token cookie. - Backend route — backend/routes/market.py authenticates, rate-limits via Redis, then calls the service layer.
- Service — finnhub.py checks Redis cache; on miss, calls Finnhub with
httpx.AsyncClient, populates cache, returns. - Response — JSON to the frontend.
- Render — Market panel displays the quote with a
realtime/delayedbadge based on the response timestamp.
For a live stream, the WebSocket channel replaces step 2–5 with a long-lived subscription, and the service layer publishes ticks as they arrive.
Secrets boundary
A non-negotiable rule: the frontend never has a backend-provider key. The split:
| Variable | Side | Public? |
|---|---|---|
EODHD_API_KEY | Backend | No |
OPENSKY_PASSWORD | Backend | No |
MARINETRAFFIC_API_KEY | Backend | No |
STRIPE_SECRET_KEY | Backend | No |
SECRET_KEY (JWT) | Backend | No |
DATABASE_URL | Backend | No |
VITE_API_URL | Frontend | Yes (bundled) |
VITE_CESIUM_ION_TOKEN | Frontend | Yes (bundled) |
Anything with a VITE_* prefix is bundled into the JS payload and is therefore world-readable. Use Cesium Ion tokens scoped to globe rendering, never an admin token.
Scaling considerations
- Backend is stateless beyond Redis sessions — horizontal scale by adding uvicorn replicas behind a load balancer with sticky WebSocket sessions.
- PostgreSQL is the common bottleneck before the upstream providers are. Read replicas help for reporting workloads.
- Redis is small and fast — a single instance handles many users. Replicate for HA.
- Upstream providers rate-limit per Atlas-account (one shared upstream account for all Atlas users on a hosted instance). Self-hosted instances control their own quota.
Where to read more
- Self-hosting — deployment shapes.
- Data providers — what each upstream is for.
- API keys — how to obtain each credential.
- Stripe billing — billing webhook flow.
- Repo: source code is the most authoritative reference. Read the route file for the surface you care about; route files are deliberately short.