gabriel / musehub public
05-production-architecture.md markdown
67 lines 4.7 KB
Raw
sha256:7d5985ef251de9f0154f9b185a75cf36174bf73e0c34f5eca133e7bd20224bf4 Merge branch 'feat/opengraph-repo-cards' into dev Human 22 days ago

Section 5 — Production Architecture

Companion to musehub-production-readiness-checklist.md.

Good news first — the app itself is more hardened than the infra around it

Grepping musehub/ turned up real, already-implemented protections that Section 0's infrastructure-level inventory didn't surface:

  • Health checks with real dependency probes: /healthz (musehub/main.py:323) checks both DB and object storage, not just "process is up." Used by deploy.sh for the blue/green health gate.
  • Rate limiting (musehub/rate_limits.py): per-route slowapi limits (wire push 30/min, MCP human/agent/anonymous tiers, asset requests), plus a 300/min global per-IP baseline.
  • Bot throttling (musehub/middleware/bot_throttle.py): blocks unauthenticated scanners on write paths while explicitly exempting authenticated (MSign) traffic and safe GET/HEAD reads — a deliberate, well-reasoned design, not a blunt UA blocklist.
  • Security headers already set in main.py: X-Frame-Options, X-Content-Type-Options, Content-Security-Policy, Strict-Transport-Security — and again at the nginx layer (deploy/nginx-cf.conf): HSTS with includeSubDomains, nosniff, X-Frame-Options: SAMEORIGIN, Referrer-Policy.
  • Deliberate per-route timeouts in nginx — not a single blanket value: 5s for fast health checks, 60s default, 120–300s for heavier routes, 3600s for streaming (MCP/SSE) endpoints. This is intentional, not an oversight.
  • No cookies at all — MSign is header-based (Ed25519 signatures), so cookie flag items (Secure/HttpOnly/SameSite) are N/A, not gaps.

Real gaps

  • Single EC2 instance per environment — no ASG, no multi-AZ. Blue/green happens within one instance (two Docker containers on different ports), not across instances. If the instance itself fails (hardware, AZ outage), there is no automatic failover. This is the single largest intentional SPOF and needs an explicit decision: accept it (documented technical debt, matches the "keep it simple" architecture direction from Section 0) or plan multi-instance/multi-AZ.
  • No graceful shutdown / SIGTERM handling found in musehub/main.py — uvicorn's defaults apply. In-flight requests during a container stop may be dropped rather than drained. Given blue/green already waits for the new slot's /healthz before flipping nginx, the new slot is protected, but the old slot's shutdown during docker rm isn't verified graceful.
  • No autoscaling, no documented justification for fixed capacity. Fixed t3.small (prod) / t3.medium (staging) — inconsistent sizing between environments with no stated reason. Worth either matching sizes or documenting why staging is intentionally larger.
  • No AWS WAF — and none needed the way the checklist assumes. There's no ALB/CloudFront for AWS WAF to attach to. DDoS/abuse protection is Cloudflare's job here (edge proxy in front of everything), which is a reasonable substitution — but Cloudflare's own WAF/rate-limiting rules are unverified from this environment (no Cloudflare API access). Needs Gabriel to check the Cloudflare dashboard for what's actually configured there.
  • No AWS service quota monitoring.
  • Background job idempotency (musehub-runner CI jobs) not verified in this pass — would need a closer read of the runner's job-claiming logic; flagging as unverified rather than assuming.
  • Migration/rolling-deploy compatibility: the deploy pipeline does run migrations before the slot flip (per docs/infrastructure.md's pipeline steps), which is the right shape, but expand-and-contract discipline for schema changes hasn't been reviewed — deferred to Section 8.

Documented intentional single points of failure (per the checklist's explicit ask)

  1. One EC2 instance per environment — no instance-level redundancy.
  2. One self-hosted PostgreSQL instance — no replica, no managed failover (RDS Multi-AZ would solve this; explicitly not in scope per the "keep current architecture" lean from Section 0).
  3. One AWS account, shared with Stori (Section 2).
  4. One Cloudflare account/zone — DNS, TLS, and (per Section 6) possibly object storage all depend on Cloudflare being up and correctly configured.
  5. Deploys depend on one human running a script with a shared credential from a laptop — no redundant deployer today (Section 1/3).

Plan to remove unacceptable ones: not yet decided which of the above are acceptable long-term vs. must-fix-before-launch — this needs Gabriel's input once RPO/RTO/availability targets (Section 0, still open) are defined. A SPOF that's fine for a beta with a handful of users may not be fine at real scale.

File History 1 commit
sha256:be1b2fdf6ab1fc652b3b35a945d0277a36fbf5d96e63692020d36cd7ebe03534 Merge 'docs/security-monitoring-verified' into 'dev' — prop… Human 2 days ago