Geopolitical and Macro Analyst for the upcoming big crisis of 2026 https://read.markets
Find a file
Giorgio Gilestro 7364d11ffe deploy: add prod compose overlay (no host port, joins intranet network)
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>
2026-05-22 21:26:55 +01:00
alembic phase D milestones 1+2: referral system + paid-access gate 2026-05-21 23:25:35 +01:00
app brand: rename product to "Read the Markets" (read.markets) 2026-05-22 19:39:38 +01:00
config add ECB Data Portal source; group-aware stale thresholds 2026-05-15 23:13:58 +01:00
tasks phase G: data minimisation + passwordless auth + DeepSeek-first LLM 2026-05-18 14:16:57 +01:00
tests brand: rename product to "Read the Markets" (read.markets) 2026-05-22 19:39:38 +01:00
.dockerignore initial commit — cassandra v0.1 2026-05-15 21:56:10 +01:00
.env.example initial commit — cassandra v0.1 2026-05-15 21:56:10 +01:00
.gitignore initial commit — cassandra v0.1 2026-05-15 21:56:10 +01:00
alembic.ini initial commit — cassandra v0.1 2026-05-15 21:56:10 +01:00
docker-compose.prod.yml deploy: add prod compose overlay (no host port, joins intranet network) 2026-05-22 21:26:55 +01:00
docker-compose.yml phase G: data minimisation + passwordless auth + DeepSeek-first LLM 2026-05-18 14:16:57 +01:00
Dockerfile initial commit — cassandra v0.1 2026-05-15 21:56:10 +01:00
pyproject.toml phase G: data minimisation + passwordless auth + DeepSeek-first LLM 2026-05-18 14:16:57 +01:00
README.md deploy: add prod compose overlay (no host port, joins intranet network) 2026-05-22 21:26:55 +01:00

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:

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 / — dashboard
  • GET /portfolio/{name} — portfolio detail
  • GET /news — news feed
  • GET /log — strategic-log archive
  • GET /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).