Three pre-launch audit findings: launch_seed.py — destructive cleanup is now OPT-IN Previously `launch_seed.py --yes` truncated KOL history (divergence / holdings / posts older than 30d) by default — an accidental run erased unrecoverable data, and abort_if_live_user_state() only guards on subscriptions/bindings, not KOL history. Now nothing is deleted unless --wipe is passed; the safe path is --seed-only (pure fetch). Bare/--yes without --wipe refuses and prints guidance. /api/health/deep — monitor the price feeds, not just scrapers The deep healthcheck only watched the (redundant) Trump scrapers + DB, so a dead Binance/HL price feed — which silently stops ALL tp_sl_monitor stop-loss / take-profit firing on live trades — left health green. Added per-feed liveness (binance.last_tick_at, hl_price_feed.last_tick_at) with a 180s boot grace so startup doesn't false-503. Body now includes price_feeds[]. Single-process enforcement (multi-worker safety) The backend is single-process by design (in-memory scheduler, replay cache, tp_sl table, price_store). systemd unit lacked the --workers 1 + rationale that supervisor.conf already had; added it. Added a runtime advisory file lock (app.main._acquire_singleton_lock): only the leader starts background tasks; extra workers serve HTTP reads only and log CRITICAL. health/deep now reports is_leader so the misconfig is visible to monitors. 72 tests pass. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Backend deployment + uptime monitoring
This folder collects the moving pieces that turn the backend from "runs on my laptop" into "runs 24/7 with auto-restart and external alerting".
What the bot needs to be reliable
- Auto-restart on crash — handled by systemd (
Restart=always) or Docker Compose (restart: unless-stopped). Pick one. - Health endpoints — exposed by
app/main.py:GET /api/health(shallow): 200 as long as the FastAPI process is alive. Used by Docker's internalHEALTHCHECK.GET /api/health/deep: 200 only if DB ping works AND at least one Truth Social scraper has polled in the last 90s. Used by external uptime monitors.
- Dual-source scraper —
truth_social.py(CNN archive) +trumpstruth.py(RSS). Both insert intopostswith the sameexternal_idhash, so duplicates are dropped. Whoever sees the post first wins. - External uptime monitor — pings the deep health endpoint from outside the box. If we lose internet or the box is on fire, we get alerted. Recommended: UptimeRobot (free tier covers this).
Option A — Docker Compose (recommended)
docker-compose.yml is already configured with:
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/api/health"]
interval: 15s
timeout: 5s
retries: 5
start_period: 30s
Deploy steps on a fresh server:
git clone <repo>
cd backend
cp .env.example .env
# Fill in: SERVICE_USER_POSTGRES, SERVICE_PASSWORD_POSTGRES, FRONTEND_URL,
# ENCRYPTION_KEY, AI_API_KEY, HL_MAINNET=true
docker compose up -d
docker compose logs -f api # tail logs
To verify auto-restart works:
docker compose kill api # simulate crash
sleep 12 # wait for compose to relaunch
curl http://localhost:8000/api/health # should return {"status":"ok"}
Option B — systemd (bare-metal, no Docker)
Use trumpsignal-api.service from this folder.
# 1. Code lives at /opt/trumpsignal/backend
sudo useradd -m -d /opt/trumpsignal trumpsignal
sudo -u trumpsignal git clone <repo> /opt/trumpsignal/backend
cd /opt/trumpsignal/backend
sudo -u trumpsignal python3.11 -m venv venv
sudo -u trumpsignal venv/bin/pip install -r requirements.txt
# 2. Configure
sudo -u trumpsignal cp .env.example .env
sudo -u trumpsignal nano .env # fill in secrets
# 3. Install + start the service
sudo cp deploy/trumpsignal-api.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now trumpsignal-api
# 4. Tail logs
sudo journalctl -u trumpsignal-api -f
Test auto-restart:
sudo systemctl kill --signal=SIGKILL trumpsignal-api
sleep 12
curl http://localhost:8000/api/health # should return {"status":"ok"}
UptimeRobot setup
- Sign up at https://uptimerobot.com (free tier = 50 monitors, 5-min interval).
- Add new monitor:
- Type: HTTP(s)
- URL:
https://api.trump-signal.bitnews.day/api/health/deep(replace with your real domain) - Monitoring interval: 5 min
- Alert contacts: add your email and/or Telegram
- UptimeRobot will alert you when the deep healthcheck returns 503 — i.e. when both scrapers have been silent for >90s, OR when the DB is unreachable.
Why deep, not shallow
The shallow /api/health returns 200 even if the scraper has died but the
FastAPI process is still serving HTTP. A monitor pointed at the shallow
endpoint would happily report "all green" while the bot silently misses
posts. Always point external monitors at /api/health/deep.
Reading the deep healthcheck
{
"status": "ok",
"now": "2026-04-25T07:35:01.123456+00:00",
"db_ok": true,
"db_error": null,
"scrapers": [
{ "name": "cnn", "last_poll": "2026-04-25T07:34:58Z", "age_sec": 3, "last_error": null },
{ "name": "trumpstruth", "last_poll": "2026-04-25T07:34:52Z", "age_sec": 9, "last_error": null }
],
"freshest_age_sec": 3,
"problems": []
}
status: "degraded" + HTTP 503 means at least one item in problems is
non-empty. The most common failure modes:
db: <error>— Postgres/SQLite can't be reached. CheckDATABASE_URL.scrapers: all stale (freshest=Ns)— both upstream feeds silent. Check network egress, then check if bothix.cnn.ioandtrumpstruth.orgare responding from the box:curl -sI https://ix.cnn.io/data/truth-social/truth_archive.json
Burst-protection note (systemd)
The unit file caps restarts at 5 within 60s, so a hard misconfiguration (missing env var, etc.) stops the boot loop instead of thrashing forever. After fixing the underlying issue:
sudo systemctl reset-failed trumpsignal-api
sudo systemctl start trumpsignal-api