Traducción en curso
Esta página se refleja automáticamente desde el original en inglés hasta que llegue una traducción al español. El contenido está en inglés — la navegación y la interfaz ya están traducidas.
Architecture
A reference for the moving parts. Useful before you self-host, before you contribute, and before you debug anything operational.
Production service shape
Veytics runs three backend roles from one versioned application image:
veytics.com -------> OSINT / Core API -----> markets, aircraft, vessels, users
|
+-----------> News Aggregator ------> sources, canonical stories, AI copy
admin.veytics.com -> Admin Console
+-> Core admin API
+-> Aggregator admin API
+-> News Publisher --> Telegram, Threads, X, Meta, Bluesky
News Aggregator -> transactional outbox -> Redis Stream -> consumers
All services ----> PostgreSQL (shared during the first migration phase)docs.veytics.com remains an independent static VitePress application. The combined backend shown below remains available as ATLAS_SERVICE_ROLE=all for local development and rollback.
Legacy all-in-one compatibility shape
┌─────────────────┐
│ User browser │
│ React + Cesium │
└────────┬────────┘
│ HTTPS (REST, ~30s polling)
▼
┌────────────────────────────────────────────┐
│ FastAPI backend (uvicorn) │
│ ┌──────────────┐ │
│ │ HTTP routes │ │
│ └──────┬───────┘ │
│ │ │
│ ┌──────▼──────────────────────────┐ │
│ │ Services layer (httpx clients) │ │
│ └──┬──────┬──────┬───────┬──────┬─┘ │
└──────┼──────┼──────┼───────┼──────┼─────────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
EODHD Finnhub OpenSky AISstream Stripe
┌─────────────┐ ┌──────────┐
│ 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: Product and marketing routes only. Administration is not bundled into this application; nginx redirects the former
/adminpath tohttps://admin.veytics.com/. - 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: 10 locales (en, de, es, fr, pl, pt, ru, uk, vi, zh) 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.
Admin console — React + Vite (apps/admin/)
- Independent origin: all operator tools live at
admin.veytics.com. - Project surfaces: News Publisher, News Aggregator, Veytics Terminal, and Platform Runtime are selected independently, leaving room for future microservice consoles.
- Terminal tools: user and subscription management, plans, audit events, runtime/API/AI usage, social and translation health, email campaigns, trial invites, AI chat controls and strategies, feedback, feature flags, Telegram, source management, social review queues, and reel tooling.
- API boundary: the browser reaches core, aggregator, and publisher through
/service/core,/service/news, and/service/publishing; provider secrets never enter the client bundle.
Backend services — FastAPI (backend/)
- Stack: Python 3.11+, FastAPI, SQLAlchemy, Alembic, httpx (async), Poetry.
- Entry: backend/main.py, selected with
ATLAS_SERVICE_ROLE=core|aggregator|publisher.allis the compatibility role. - 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). Note: the Settings two-factor/TOTP toggle is a UI placeholder only — 2FA is not yet implemented server-side.
- News delivery: canonical story writes and service events commit together; a durable outbox dispatches events to a replayable Redis Stream.
- Publishing: social credentials exist only in the publisher process. The editorial queue supports drafts, ready items, schedules, retries, and delivery history.
- Live updates: The browser gets fresh quotes and alerts by polling REST endpoints (~30s); alert conditions are evaluated server-side on a schedule. There is no browser WebSocket — backend/routes/websocket.py is a no-op stub.
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+.
- Veytics 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 |
AISSTREAM_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_ADMIN_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 Veytics-account (one shared upstream account for all Veytics 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.