The VPS deployment sits behind Nginx Proxy Manager on a pre-existing `intranet` Docker bridge network. The overlay drops the host port binding from the base compose, switches uvicorn to listen on port 80 inside the container (uniform NPM upstreams), and joins the app to both `default` (for db/redis) and `intranet` (for NPM ingress). Apply with: docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d Local dev (compose without the overlay) is unchanged — still binds the host port from CASSANDRA_PORT in .env. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2.2 KiB
Read the Markets
Containerised macro-strategy dashboard — hourly market data, RSS news, Trading 212 portfolio, and an AI-generated strategic log written by Cassandra, the in-product seer. Read-only by design.
Production:
- Landing: https://read.markets
- App: https://app.read.markets
The Python package is still named cassandra and several internal identifiers (cookie names, advisory-lock keys, CASSANDRA_TOKEN env var, CSS filename) keep the legacy name on purpose — renaming them would invalidate live sessions / locks / configs for no user benefit. See app/branding.py for the brand single-source-of-truth.
Quick start
cp .env.example .env # fill in API keys; set CASSANDRA_TOKEN if exposing
docker compose up --build # db + app + scheduler + daily backup sidecar
open http://localhost:8000/
Production (VPS)
Apply the prod overlay so the app has no host port binding and joins the
existing intranet Docker network (where Nginx Proxy Manager lives):
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build
Then point NPM at upstream readmarkets-app-1:80.
Architecture
- app (FastAPI + Jinja2 + HTMX) — web dashboard on port 8000
- scheduler (APScheduler) — hourly ingestion jobs (market, news, portfolio, AI log)
- db (MariaDB 11) — quotes, headlines, portfolio snapshots, strategic logs, job runs
- backup (sidecar) — daily mariadb-dump to
./backup/
See /home/gg/.claude/plans/ok-i-think-this-tidy-lake.md for the design plan.
Config
| File | Purpose |
|---|---|
config/default.toml |
Universal data tables: indicator groups, RSS feeds, keyword presets |
config/portfolio.toml |
User-specific portfolios (overrides default.toml) |
.env |
Secrets and runtime knobs — mounted read-only into containers |
Endpoints
GET /— dashboardGET /portfolio/{name}— portfolio detailGET /news— news feedGET /log— strategic-log archiveGET /api/health— job status (last success / failure per job)
All authenticated routes require Authorization: Bearer $CASSANDRA_TOKEN if the env is set; if unset, the app is open (LAN-only mode).