6876c0c280
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>
47 lines
1.7 KiB
Desktop File
47 lines
1.7 KiB
Desktop File
[Unit]
|
|
Description=TrumpSignal API + Truth Social poller
|
|
After=network-online.target postgresql.service
|
|
Wants=network-online.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=trumpsignal
|
|
Group=trumpsignal
|
|
WorkingDirectory=/opt/trumpsignal/backend
|
|
EnvironmentFile=/opt/trumpsignal/backend/.env
|
|
# MUST stay --workers 1. The backend is single-process BY DESIGN: APScheduler,
|
|
# scanner_state, the signed-request replay cache, tp_sl_monitor's open-trade
|
|
# table and price_store are all in-memory and NOT shared across processes.
|
|
# With >1 worker the scheduler runs in every worker (duplicate scans / ingest /
|
|
# trades), the scanner toggle split-brains, and TP/SL only sees same-worker
|
|
# trades. A runtime singleton lock in app.main now refuses to start background
|
|
# tasks in extra workers as a backstop, but the correct config is one worker.
|
|
# To scale horizontally, move scheduler leadership + scanner state + replay
|
|
# cache to Redis/DB FIRST. (Mirrors deploy/supervisor.conf.)
|
|
ExecStart=/opt/trumpsignal/backend/venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 1
|
|
|
|
# Auto-restart on any exit (crash, OOM, segfault, manual kill).
|
|
# RestartSec=10 = wait 10s between restart attempts so we don't hammer a broken state.
|
|
Restart=always
|
|
RestartSec=10
|
|
|
|
# Burst protection: if we crash > 5 times in 60s, give up (likely a real config bug,
|
|
# not transient). Reset by `systemctl reset-failed trumpsignal-api`.
|
|
StartLimitIntervalSec=60
|
|
StartLimitBurst=5
|
|
|
|
# Hardening
|
|
NoNewPrivileges=yes
|
|
ProtectSystem=strict
|
|
ProtectHome=yes
|
|
ReadWritePaths=/opt/trumpsignal/backend
|
|
PrivateTmp=yes
|
|
|
|
# Logging — journald captures stdout/stderr; view with `journalctl -u trumpsignal-api -f`
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
SyslogIdentifier=trumpsignal-api
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|