| # RMI Backend β 2026 Architecture Design |
|
|
| ## Why this exists |
|
|
| The current backend (`/root/backend/`) has grown organically: |
| - `main.py` is 10,305 lines |
| - 124 router files flat in `app/routers/` |
| - 14 RAG modules scattered at top of `app/` |
| - `token_scanner.py` is 4,109 lines |
| - `x402_tools.py` is 5,817 lines |
| - Cross-cutting concerns (redis, auth, errors) duplicated across modules |
| - Domain logic entangled with FastAPI |
| - No tests, no type safety, no clear boundaries |
|
|
| 15 mechanical refactor tasks would patch symptoms. This design fixes the architecture. |
|
|
| ## Target Layout |
|
|
| ``` |
| /root/backend/ |
| βββ pyproject.toml uv + ruff + mypy + pytest config (single source) |
| βββ .pre-commit-config.yaml ruff + mypy + size cap + gitleaks |
| βββ Dockerfile |
| βββ alembic/ async migrations |
| β |
| βββ app/ |
| β βββ main.py <100 lines: app factory + lifespan + middleware ONLY |
| β βββ config.py pydantic-settings, env loading |
| β β |
| β βββ core/ cross-cutting, NO business logic |
| β β βββ logging.py structlog JSON + correlation ID |
| β β βββ errors.py AppError hierarchy + FastAPI handlers |
| β β βββ redis.py async client + get_redis() Depends() |
| β β βββ db.py async SQLAlchemy session |
| β β βββ auth.py JWT decode + role guards |
| β β βββ lifespan.py startup/shutdown |
| β β βββ middleware.py CORS, rate limit, correlation ID |
| β β βββ websocket.py WS connection manager |
| β β βββ tracing.py OpenTelemetry + Langfuse v4 init |
| β β βββ http.py async httpx client |
| β β βββ pagination.py cursor-based |
| β β βββ metrics.py Prometheus /metrics endpoint (P0 v3) |
| β β βββ legacy.py @legacy decorator for deprecation tracking |
| β β |
| β βββ api/ HTTP transport, thin routes |
| β β βββ deps.py shared Depends (current_user, redis, etc) |
| β β βββ v1/ |
| β β β βββ public/ no auth β scanner, wallet, token, pricing, health |
| β β β βββ auth/ JWT β portfolio, alerts, intel, profile |
| β β β βββ admin/ admin β users, system, ops |
| β β β βββ x402/ paid β tools, tokens, wallets, defi, security |
| β β β βββ mcp/ MCP β tools.py |
| β β βββ ws/ WebSocket |
| β β βββ alerts.py |
| β β |
| β βββ domain/ pure business logic, NO FastAPI imports |
| β β βββ scanner/ core + honeypot + rugcheck + holders + contract + deployer + models + service |
| β β βββ wallet/ analyzer + labels + behavior + models + service |
| β β βββ token/ discovery + supply + models + service |
| β β βββ rag/ embeddings + chunking + search + ingest + firehose + feedback + agentic + evaluation + tracing + router + permanence + models + service |
| β β βββ x402/ facilitator + tokens + enforcement + settlement + models + service |
| β β βββ intel/ feeds + narratives + graph + models + service |
| β β βββ scam/ classifier + patterns + models + service |
| β β βββ databus/ client + chains(96) + models + service |
| β β βββ bulletin/ board + models + service |
| β β |
| β βββ infra/ external integrations |
| β β βββ ollama.py |
| β β βββ langfuse.py |
| β β βββ vector_store.py |
| β β βββ chains/ evm + solana + bitcoin + base + ... |
| β β βββ apis/ coingecko + etherscan + birdeye + goplus + arkham + dune + ... |
| β β βββ providers/ ollama + openrouter + huggingface + ... |
| β β |
| β βββ agents/ v3 NEW β bounded-task AI agent loops (M1) |
| β β βββ loop.py frozen: TaskInput/LoopBudget/TaskOutput Pydantic models |
| β β βββ fact_store.py Redis-backed long-term memory |
| β β βββ kill_switches.py budget enforcers (iterations, tokens, wall, spend) |
| β β βββ executor.py bounded loop runner |
| β β |
| β βββ mcp/ v3 NEW β MCP mesh v2 (M2) |
| β β βββ manifest.py frozen: MCPToolManifest Pydantic model |
| β β βββ registry.py FastAPI on :8643 β list/resolve tools |
| β β βββ auth.py gopass-backed auth scopes |
| β β βββ servers/ opencti + dify + n8n FastMCP servers |
| β β |
| β βββ middleware/ v3 NEW β Starlette middleware classes (M7) |
| β β βββ cost_tracking.py frozen: per-tenant/per-route cost middleware |
| β β βββ rate_limit_v2.py per-tier sliding window |
| β β βββ auth_deps.py JWT/API key on legacy routes |
| β β βββ security_headers.py CSP, HSTS, X-Frame-Options |
| β β βββ shadow_traffic.py v1/v2 response diffing |
| β β |
| β βββ workers/ background jobs (separate from API) |
| β βββ firehose.py |
| β βββ scanner_queue.py |
| β βββ ingest_cron.py |
| β βββ cleanup.py |
| β |
| βββ docs/ |
| β βββ adr/ v3 NEW β Architecture Decision Records (M6) |
| β βββ runbooks/ v3 NEW β incident response (M6) |
| β βββ postmortems/ v3 NEW β incident learnings (M6) |
| β βββ oncall/ v3 NEW β rotation doc (M6) |
| β βββ load/ v3 NEW β k6 baseline reports (P2 #26) |
| β βββ contract/ v3 NEW β Pact contract specs (P2 #27) |
| β βββ plans/ |
| β βββ UNFUCK-V1.md Frozen β superseded |
| β βββ UNFUCK-V2.md Frozen β superseded |
| β βββ UNFUCK-V3.md ACTIVE β AI-Forward Ship Edition (this document's parent) |
| β βββ INTEGRATION-LOG.md append-only worklog of v3 execution |
| β |
| βββ tests/ |
| βββ conftest.py |
| βββ unit/ |
| β βββ core/ 33+ core tests |
| β βββ domain/ 58+ domain tests |
| βββ e2e/ v3 NEW β Playwright (P2 #25) |
| βββ contract/ v3 NEW β Pact (P2 #25) |
| βββ load/ v3 NEW β k6 scripts (P2 #25) |
| βββ rag/ |
| βββ eval_suite.py v3 NEW β retrieval quality regression (M3) |
| βββ prompts/ |
| βββ test_snapshots.py v3 NEW β prompt regression |
| ``` |
|
|
| ## Key Design Principles |
|
|
| 1. **STRICT LAYERING.** `api β domain β infra`. Never reverse. Domain knows nothing about HTTP. |
| 2. **ONE SOURCE OF TRUTH for cross-cutting.** redis/auth/errors/logging live in `core/` exactly once. Routes import, never redefine. |
| 3. **HARD SIZE CAP.** 500 lines per file. Enforced in pre-commit. No 4,109-line `token_scanner.py` ever again. |
| 4. **THIN ROUTES.** Routes parse β call service β return. No business logic in HTTP layer. |
| 5. **DOMAIN = PURE PYTHON.** `domain/scanner/` can be unit tested without spinning up FastAPI. This is the test that proves the architecture. |
| 6. **WORKERS SEPARATED.** Background jobs don't pollute the API. firehose, scanner_queue, ingest_cron live in `workers/`. |
| 7. **PYDANTIC V2 EVERYWHERE.** Every domain has `models.py`. No `dict` types crossing boundaries. |
| 8. **ASYNC-ONLY.** No sync I/O in handlers. Same shape for the whole codebase. |
| 9. **OBSERVABILITY BY DEFAULT.** structlog JSON + correlation ID + OTel + Langfuse in `core/tracing.py`. Every endpoint instrumented without opt-in. |
| 10. **STRANGLER FIG MIGRATION.** New skeleton co-exists with old code. Old `main.py` keeps importing the old routers. New routes added alongside. Per-domain cutover, not big-bang. |
|
|
| ## Migration Order |
|
|
| | Order | Domain | Why | |
| |-------|--------|-----| |
| | 0 | `rag_engine` shim | unblock prod crash, temp until `app/rag/` lands | |
| | 1 | `core/` | foundation everyone depends on | |
| | 2 | `infra/` | external integrations domain depends on | |
| | 3 | `alerts` | smallest, well-bounded, has WS + JWT + redis β proves full pattern | |
| | 4 | `wallet` | high-value, used by frontend | |
| | 5 | `token` | high-value | |
| | 6 | `scanner` | biggest (4,109 lines), do last when pattern is mature | |
| | 7 | `x402` | payment system, critical, mature pattern by then | |
| | 8 | `intel`, `scam`, `databus`, `bulletin` | long tail | |
| | 9 | `rag` consolidation (was 14 files) | last because it's the most coupled | |
|
|
| --- |
|
|
| # THE 14-POINT MODERN BUILDER BAR (v3 β non-negotiable quality gate) |
|
|
| Every file that touches the unfuck MUST clear this. Pre-commit enforces what can be enforced. |
|
|
| ``` |
| # Standard Status Enforcement |
| 1 Pydantic v2 strict (ConfigDict) PARTIAL pre-commit mypy |
| 2 async-only handlers DONE pre-commit AST scan |
| 3 <500 lines/file (pre-commit) DONE pre-commit file-size-cap |
| 4 pre-commit hooks (ruff+mypy+gitleaks+ |
| detect-secrets+bandit+file-size+pytest) PARTIAL install on dev + CI |
| 5 typed errors (18/18) β currently 4/18 4/18 pre-commit AST scan |
| 6 1 type per layer (apiβdomainβcoreβinfra) PARTIAL architecture test |
| 7 structlog everywhere DONE pre-commit no-print() |
| 8 event bus (Redis pub/sub) PARTIAL manual review |
| 9 per-domain /health/{domain} PARTIAL manual review |
| 10 per-route cost tracking NONE M7 middleware (P2) |
| 11 no from-main-import DONE pre-commit AST scan |
| 12 single get_redis() DONE pre-commit no-new-get-redis |
| 13 OTel trace coverage >85% 0% M4 wiring (P1) |
| 14 Langfuse span coverage >95% LLM calls PARTIAL M4 wiring (P1) |
| ``` |
|
|
| **Items 5, 10, 13, 14 are the v3 additions.** |
|
|
| --- |
|
|
| # THE 30 MUST-DO ITEMS (v3 β ACTIVE PLAN) |
|
|
| ## P0 β Week 1 (32h total β 16h human + 16h M3) |
|
|
| | # | Item | Hours | Owner | Status | File | |
| |---|------|-------|-------|--------|------| |
| | 1 | main.py lifespan merge crash fixed | 1 | human | β
DONE | `main.py` (94 lines) | |
| | 2 | Audit 53 uncommitted modifications | 3 | human | β³ TODO | `git diff HEAD` | |
| | 3 | 65 broken crons β Tailscale IP | 4 | human | β³ TODO | crons 167.86.116.51 β 100.100.18.18 | |
| | 4 | /metrics endpoint | 2 | M3 | β³ TODO | `app/core/metrics.py` (NEW) | |
| | 5 | AlertManager β Telegram | 2 | M3 | β³ TODO | `docker-compose.yml` + `alertmanager.yml` | |
| | 6 | Embedder dual-dim wrapper (M3) | 8 | M3 | β³ TODO | `app/rag/embedder.py` (NEW) | |
| | 7 | WalletService proper async impl | 6 | M3 | β³ STUBS DONE, REAL PENDING | `app/domain/wallet/service.py` | |
| | 8 | Frontend SPA migrate Contaboβnetcup | 2 | human | β³ TODO | `/var/www/rmi/` | |
| | 9 | GHOST_ADMIN_KEY from gopass | 1 | human | β³ TODO | `.env` | |
| | 10 | Load avg 14.19 β identify cause | 3 | human | β³ TODO | Erigon or Ollama? | |
|
|
| ## P1 β Week 2 (37h total β 3h human + 34h M3) |
|
|
| | # | Item | Hours | Owner | Status | File | |
| |---|------|-------|-------|--------|------| |
| | 11 | GlitchTip self-host + wire SDK | 4 | M3 | β³ TODO | `docker-compose.glitchtip.yml` (NEW) | |
| | 12 | OTel auto-instrumentation (M4) | 6 | M3 | β³ TODO | `app/core/tracing.py` wire into lifespan | |
| | 13 | GitHub Actions CI | 8 | M3 | β³ TODO | `.github/workflows/ci.yml` (NEW) | |
| | 14 | Renovate + Dependabot | 2 | M3 | β³ TODO | `.github/dependabot.yml` (NEW) | |
| | 15 | Trivy in CI | 2 | M3 | β³ TODO | `.github/workflows/ci.yml` | |
| | 16 | Semgrep SAST in CI | 2 | M3 | β³ TODO | `.github/workflows/ci.yml` | |
| | 17 | Gitleaks + Trufflehog | 2 | M3 | β³ TODO | `.pre-commit-config.yaml` + CI | |
| | 18 | Vault rotation script (gopass) | 3 | human | β³ TODO | `scripts/rotate_secrets.sh` (NEW) | |
| | 19 | Rate limit middleware on all /api/v1/* | 4 | M3 | β³ TODO | `app/core/middleware.py` | |
| | 20 | Auth Depends() on legacy routes | 4 | M3 | β³ TODO | `app/core/middleware.py` | |
|
|
| ## P2 β Week 3 (46h total β 12h human + 34h M3) |
|
|
| | # | Item | Hours | Owner | Status | File | |
| |---|------|-------|-------|--------|------| |
| | 21 | Security headers (CSP, HSTS, X-Frame) | 3 | M3 | β³ TODO | `app/core/middleware.py` | |
| | 22 | Backup restore drill | 3 | human | β³ TODO | R2 bucket test | |
| | 23 | 4 Grafana dashboards | 5 | M3 | β³ TODO | `grafana/dashboards/*.json` | |
| | 24 | Container memory limits (all 36) | 3 | M3 | β³ TODO | `docker-compose.yml` | |
| | 25 | tests/{e2e,contract,load}/ scaffolding | 4 | M3 | β³ TODO | `tests/e2e/` `tests/contract/` `tests/load/` | |
| | 26 | k6 baselines (top 50 routes) | 6 | M3 | β³ TODO | `tests/load/baseline.js` | |
| | 27 | Pact contracts + openapi-generator SDK | 5 | M3 | β³ TODO | `tests/contract/` | |
| | 28 | Top 10 ADRs | 5 | human | β³ TODO | `docs/adr/0001-0010.md` | |
| | 29 | Runbooks + postmortem + oncall | 4 | human | β³ TODO | `docs/runbooks/` `docs/postmortems/` `docs/oncall/` | |
| | 30 | Agent Loop Spec productionization (M1) | 8 | M3 | β³ TODO | `app/agents/loop.py` (NEW, frozen on creation) | |
|
|
| ## P3 β Defer until revenue |
|
|
| Multi-region read replicas. SOC2 audit. Paid third-party pen-test. SBOM distribution. AsyncAPI WebSocket spec. Frontend RUM (Sentry browser SDK, web vitals). Pyroscope continuous profiling. OWASP ZAP. |
|
|
| --- |
|
|
| # THE 8 AI-FORWARD MODULES (v3) |
|
|
| ## M1: Agent Loop Productionization (P2 #30) |
|
|
| **Deliverable: `app/agents/loop.py` (frozen on creation)** |
|
|
| ```python |
| from pydantic import BaseModel, Field, ConfigDict |
| |
| class TaskInput(BaseModel): |
| model_config = ConfigDict(strict=True, frozen=True) |
| task_id: str = Field(..., pattern=r"^\d+-[a-z0-9-]+$") |
| description: str = Field(..., max_length=500) |
| context_budget_tokens: int = Field(default=8000, le=32000) |
| allowed_tools: list[str] = Field(..., min_length=1) |
| success_criteria: str = Field(..., max_length=300) |
| verify_command: str = Field(..., max_length=200) |
| |
| class LoopBudget(BaseModel): |
| model_config = ConfigDict(strict=True, frozen=True) |
| max_iterations: int = Field(default=20, le=100) |
| max_tokens: int = Field(default=50_000, le=500_000) |
| max_wallclock_seconds: int = Field(default=900, le=3600) |
| max_spend_usd: float = Field(default=5.0, le=50.0) |
| |
| class TaskOutput(BaseModel): |
| task_id: str |
| files_touched: list[str] |
| lines_added: int |
| lines_removed: int |
| verify_passed: bool |
| worklog_entry: str |
| spend_usd: float |
| iterations_used: int |
| ``` |
|
|
| **Kill switches** (every iteration): |
| - max_iterations β€ 20 (max 100) |
| - max_tokens β€ 50K (max 500K) |
| - max_wallclock β€ 900s (max 3600s) |
| - max_spend β€ $5 (max $50) |
| - verify_command_failed_3x β human review |
| |
| **fact_store companion** (`app/agents/fact_store.py`): |
| - Redis key: `fact:{namespace}:{key}` |
| - TTL: 86400s |
| - Loaded at loop start, written at loop end |
| - Prevents re-discovery of known facts |
|
|
| **Acceptance gate: 7 consecutive days zero runaway.** |
|
|
| ## M2: MCP Mesh v2 (P2 #24, D9) |
|
|
| **Deliverable: `app/mcp/manifest.py` (frozen on creation)** |
|
|
| ```python |
| from pydantic import BaseModel, Field |
| from typing import Any |
| |
| class MCPToolManifest(BaseModel): |
| name: str = Field(..., pattern=r"^[a-z_]+:[a-z_]+$") # e.g. "rmi-netcup:docker_ps" |
| version: str = Field(..., pattern=r"^\d+\.\d+\.\d+$") |
| server: str = Field(..., pattern=r"^[a-z-]+$") |
| description: str = Field(..., max_length=200) |
| input_schema: dict[str, Any] |
| output_schema: dict[str, Any] |
| auth_scope: str = Field(..., pattern=r"^[a-z_]+:[a-z_]+$") # gopass scope |
| deprecated: bool = False |
| successor: str | None = None |
| ``` |
|
|
| **Mesh registry on netcup `:8643`** with: |
| - GET /tools (list all with filters) |
| - GET /tools/{name} (full manifest) |
| - POST /resolve (name+MAJOR version β executor endpoint) |
|
|
| **3 unwired servers to wire in D9:** |
| - OpenCTI (40 tools) β wire with FastMCP |
| - Dify (18 tools) β wire with FastMCP |
| - n8n (20 tools) β wire with FastMCP |
|
|
| **Acceptance gate: 332+ tools versioned, all gopass-backed auth, OpenCTI+Dify+n8n wired.** |
|
|
| ## M3: RAG Dual-Dim Wrapper (P0 #6) |
|
|
| **Deliverable: `app/rag/embedder.py` (wraps frozen `app/crypto_embeddings.py`)** |
| |
| ```python |
| from enum import Enum |
| |
| class EmbedderBackend(Enum): |
| BGE_M3 = "bge-m3" # 1024d, legacy |
| QWEN3_4B = "qwen3-embedding:4b" # 2048d, target |
| |
| class DualDimEmbedder: |
| def __init__(self, backend: EmbedderBackend): |
| self.backend = backend |
| self.dim = 1024 if backend == EmbedderBackend.BGE_M3 else 2048 |
| |
| async def embed(self, texts: list[str]) -> list[list[float]]: |
| return await self._ollama_embed(texts, self.backend.value) |
| |
| async def reindex_collection(self, name: str, target: EmbedderBackend) -> bool: |
| old = DualDimEmbedder(EmbedderBackend.BGE_M3) |
| new = DualDimEmbedder(target) |
| # 1. Read all docs from collection |
| # 2. Re-embed with new backend |
| # 3. Write to new collection {name}_v2 |
| # 4. Atomic swap: {name} β {name}_legacy, {name}_v2 β {name} |
| # 5. Verify: query 10 known docs, confirm retrieval |
| return True |
| ``` |
| |
| **The math:** |
| | Metric | bge-m3 | qwen3-4b | Delta | |
| |--------|--------|----------|-------| |
| | Dimensions | 1024 | 2048 | +2x | |
| | RAM (loaded) | 1.2GB | 2.0GB | +800MB | |
| | Embed latency | ~80ms | ~140ms | +60ms | |
| | Retrieval (nDCG@10) | 0.71 | 0.83 (est) | +12% | |
| | Reindex time | β | 6h overnight | one-time | |
| | Storage/coll | ~50MB | ~100MB | +650MB total | |
| |
| **Strategy: collection-by-collection overnight, smallest first, verify each morning.** If <8%, defer. |
| |
| **Acceptance gate: All 13 collections reindexed, eval harness +8% nDCG@10, bge-m3 unloaded.** |
| |
| ## M4: Observability Triangle (P1 #11, #12, P2 #21) |
| |
| **Three pillars wired into the 94-line lifespan:** |
| |
| ```python |
| # main.py additions (~15 lines to existing 94) |
| from contextlib import asynccontextmanager |
| from app.core.tracing import setup_otel |
| from app.core.langfuse import langfuse |
| from app.core.errors import init_glitchtip |
| |
| @asynccontextmanager |
| async def lifespan(app: FastAPI): |
| _otel = setup_otel() # OTel: httpx, redis, asyncpg, neo4j |
| init_glitchtip() # Sentry-compatible SDK |
| await app.state.redis.initialize() |
| yield |
| _otel.shutdown() |
| langfuse.flush() |
| await app.state.redis.close() |
| |
| app = FastAPI(lifespan=lifespan) |
| ``` |
| |
| **Pillars:** |
| - **OTel**: auto-instrument httpx/redis/asyncpg/neo4j, export to local OTel collector |
| - **Langfuse**: `@observe()` decorator on every LLM call, >95% coverage |
| - **GlitchTip**: Sentry-compatible SDK, DSN from gopass, captures every unhandled exception |
| |
| **Acceptance gate: 7 consecutive days triangle lit.** |
| |
| ## M5: CI/CD Standards (P1 #13-17, P1 #19, P1 #20) |
| |
| **Pipeline stages:** |
| ``` |
| PRE-COMMIT (local): ruff + mypy + gitleaks + detect-secrets + bandit + file-size + pytest-smoke |
| CI (GH Actions): lint + test-full + build + trivy + semgrep + gitleaks-history + grype |
| DEPLOY (blue-green): build-green β start β health-gate β swap-router β verify-5xx β keep-blue-10min |
| ROLLBACK (auto): swap-back-to-blue on 5xx spike β page β freeze-deploys-1h |
| ``` |
| |
| **Justfile deploy target:** |
| ```makefile |
| deploy: |
| docker compose -f docker-compose.green.yml build |
| docker compose -f docker-compose.green.yml up -d |
| ./scripts/health-check.sh http://127.0.0.1:8001/health 60 |
| ./scripts/swap-upstream.sh green |
| ./scripts/watch-errors.sh 600 0.01 |
| docker compose -f docker-compose.blue.yml stop |
| ``` |
| |
| ## M6: ADRs + Runbooks + Postmortems + Oncall (P2 #28, #29) |
| |
| **Top 10 ADRs (write these first):** |
| 1. Why FastAPI over Litestar/Flask/Django |
| 2. Why 5 DBs (Postgres+Redis+CH+Neo4j+Qdrant) |
| 3. Why strangler-fig over rewrite |
| 4. Why bge-m3 β qwen3-embedding:4b (M3) |
| 5. Why self-host GlitchTip over Sentry SaaS |
| 6. Why single-VPS blue-green over k3s |
| 7. Why gopass over Vault/Doppler/AWS SM |
| 8. Why Hermes cron over systemd/Airflow |
| 9. Why MCP mesh over direct API calls |
| 10. Why Tailscale over WireGuard/ZeroTier |
| |
| **ADR Template:** |
| ``` |
| # ADR-XXXX: [Title] |
| ## Status: Accepted | Rejected | Superseded |
| ## Date: YYYY-MM-DD |
| ## Decider: @cryptorugmunch |
| ## Context: What we're solving |
| ## Decision: What we chose |
| ## Alternatives Considered: What we rejected |
| ## Consequences: Easier / Harder / Mitigations |
| ``` |
| |
| ## M7: Cost Tracking + SLOs + Error Budgets (P2 #21) |
| |
| **Per-tenant cost middleware (`app/middleware/cost_tracking.py`):** |
| ```python |
| class CostTrackingMiddleware(BaseHTTPMiddleware): |
| async def dispatch(self, request, call_next): |
| tenant = request.headers.get("X-Tenant-ID", "anonymous") |
| t0 = time.monotonic() |
| response = await call_next(request) |
| elapsed_ms = (time.monotonic() - t0) * 1000 |
| cost = self._estimate_cost(route, response, elapsed_ms) |
| await self._buffer_cost(tenant, route, cost, elapsed_ms) |
| return response |
| ``` |
|
|
| **SLO Targets:** |
| | Service | SLO | Error budget | |
| |---------|-----|--------------| |
| | /api/v1/* p99 latency | < 500ms | 43min/mo | |
| | /api/v2/* p99 latency | < 200ms | 43min/mo | |
| | /health uptime | β₯ 99.5% | 3.6h/mo | |
| | /api/* error rate (5xx) | < 0.1% | 43min of 5xx/mo | |
| | RAG retrieval success | > 95% | 43min of failures/mo | |
| | LLM calls timeout rate | < 1% | 7.2h of timeouts/mo | |
|
|
| **Auto-freeze deploys on 4x burn:** |
| ```yaml |
| - alert: SLOBurnRate4x |
| expr: (sum(rate(http_requests_total{status=~"5.."}[1h])) / sum(rate(http_requests_total[1h]))) > 0.02 |
| for: 2m |
| annotations: |
| webhook: "http://127.0.0.1:8642/ci/freeze" |
| ``` |
|
|
| ## M8: AI Tool Wiring Matrix |
|
|
| | Task | Best tool | Worst tool | |
| |------|-----------|------------| |
| | Bounded code edit (1 file, <50 lines) | **aider** | claude-code | |
| | Multi-file refactor (3β10 files) | **claude-code** | aider | |
| | Codebase search | **Bloop** (when installed) | grep | |
| | Test generation | **claude-code** | aider | |
| | Doc generation (ADR, runbook) | **GLM-5.2** | aider | |
| | Security review | **Semgrep** (not LLM) | any LLM | |
| | Dependency upgrade | **Renovate** (not LLM) | any LLM | |
| | Prompt engineering | **GLM-5.2 + DSPy** | claude-code | |
| | RAG eval | Custom harness + LLM-as-judge | any LLM alone | |
| | Long-context (β₯100K) | **Kimi K2.7** (256K) | DeepSeek (slow) | |
| | Vision | **Gemini 2.5-pro** | text-only LLM | |
| | Fast small (β€500 tokens) | **Groq** | DeepSeek | |
| | Autonomous (bounded loop) | **Hermes** | claude-code | |
|
|
| --- |
|
|
| # 10 INCIDENT SCENARIOS (v3) |
|
|
| ## v2's 7 (preserved) |
| 1. rmi-redis crashes at 3am β auto-restart |
| 2. Disk full on netcup β AlertManager page at 80% |
| 3. Postgres corruption β restore from nightly backup |
| 4. SSL cert expiry β Cloudflare manages, Tailscale renew |
| 5. OOM kill on rmi-backend β Docker limit + py-spy/memray |
| 6. DDoS on rugmunch.io β Cloudflare "Under Attack" mode |
| 7. DeepSeek balance hits $0 β failover to Ollama Cloud/Gemini |
|
|
| ## v3's 3 NEW (AI-specific) |
|
|
| ### Scenario 8 β Agent Loop Runaway |
| **Symptom:** LLM spend spikes ($50+/h). Hermes logs show task iterating >50Γ. |
| **Detect:** Daily spend alert (M7) at $20/h. Hermes /cron/health shows iterations >20. |
| **Mitigate:** `pkill -f "hermes.*task_id=X"`. Disable cron job. |
| **Recover:** Audit input contract β likely missing success_criteria or verify_command. |
| **Root cause:** vague description or missing verify_command. |
| |
| ### Scenario 9 β RAG Poisoning |
| **Symptom:** RAG retrieval returns adversarial content. User reports "the AI told me to send funds to X." |
| **Detect:** Langfuse span shows suspicious source. Eval harness flags quality regression. |
| **Mitigate:** Disable affected collection. Roll back FAISS index. |
| **Recover:** Add source allowlist to ingest step. |
| **Root cause:** open ingestion endpoint or untrusted RSS feed. |
| |
| ### Scenario 10 β MCP Tool Compromise |
| **Symptom:** Unusual gopass access pattern. MCP tool called from unexpected IP. Lateral movement. |
| **Detect:** gopass audit log. CrowdSec flags anomalous SSH. Tailscale ACL violation. |
| **Mitigate:** `gopass rm rmi/infra/compromised/scope` + rotate. Revoke Tailscale node. Block IP. |
| **Recover:** Audit all gopass accesses in last 30 days. Rotate ALL keys. |
| **Root cause:** over-broad auth_scope or leaked Tailscale key. SEV1. |
|
|
| --- |
|
|
| # FROZEN FILE MANIFEST v3 (14 entries) |
|
|
| | # | File | Why frozen | |
| |---|------|------------| |
| | 1 | `main.py` | Front door (was 8475, now 94) | |
| | 2 | `_legacy_main.py` | Strangler-fig target | |
| | 3 | `app/core/redis.py` | Single Redis source | |
| | 4 | `app/core/config.py` | Env var loader | |
| | 5 | `app/databus/core.py` | 96-chain fetch interface | |
| | 6 | `app/rag/pipeline.py` | RAG pipeline (frozen for M3) | |
| | 7 | `app/crypto_embeddings.py` | bge-m3 embedder (replaced via wrapper) | |
| | 8 | `app/api/v1/__init__.py` | Router registry | |
| | 9 | `docker-compose.yml` | All 36 containers | |
| | 10 | `app/core/tracing.py` | OTel (not wired β M4) | |
| | 11 | `app/agents/loop.py` | M1 deliverable (frozen on creation) | |
| | 12 | `app/mcp/server.py` | M2 deliverable (frozen on creation) | |
| | 13 | `app/core/metrics.py` | P0 deliverable (frozen on creation) | |
| | 14 | `app/middleware/cost_tracking.py` | M7 deliverable (frozen on creation) | |
|
|
| **Rule:** Modified only via bounded-task delegation with Task ID in `/home/z/my-project/worklog.md`. Out-of-process edits reverted. |
|
|
| --- |
|
|
| # THE MATH (v3 BRUTAL TRUTH) |
|
|
| ``` |
| Solo With M3 |
| P0 + P1 + P2 hours 115h 115h |
| Available budget (21dΓ8h) 168h 168h human + 152h M3 = 320h |
| Buffer (30% incident tax) β50h β50h human, β46h M3 |
| Effective capacity 118h 224h |
| NET (capacity β work) +3h +109h |
| β Phase 4 fits here |
| |
| Plus 20h passive waits (eval soaks, mesh verifications) |
| β Solo: 138h needed vs 118h available β slips to Day 25 |
| β M3: 138h needed vs 224h available β fits comfortably |
| ``` |
|
|
| **Verdict: Solo, v3 is a 25-day plan. With M3 parallel, v3 is a 21-day plan.** |
|
|
| --- |
|
|
| # 90-DAY ROADMAP (after the unfuck) |
|
|
| ## Month 2 β Agent Mesh + RAG Eval + SDK |
| - Agent mesh: Hermes orchestrating multiple claude-code/aider in parallel |
| - RAG eval harness: automated retrieval-quality regression on every push |
| - SDK v1: TypeScript + Python clients from OpenAPI spec |
|
|
| ## Month 3 β Multi-Region + SOC2 + Paid Pilots |
| - Multi-region read replicas (Contabo β Cloudflare LB) |
| - SOC2 prep (ADRs + runbooks = 60% of evidence) |
| - 3 paid pilots, $500/mo each, 60-day commitment |
|
|
| ## Month 4+ β Hire or Stay Solo |
| - If 3 pilots + 1 conversion β hire senior backend engineer |
| - If pilots but no conversion β focus on conversion |
| - If no pilots β pivot |
|
|
| --- |
|
|
| # What Ships This Pass (Foundation) |
|
|
| 1. β
Fix crash β `rag_engine` re-export shim, backend healthy (Phase 0) |
| 2. β
`pyproject.toml` β uv + ruff + mypy strict + pytest |
| 3. β
`.pre-commit-config.yaml` β ruff + mypy + size cap (500) + gitleaks |
| 4. β
`app/core/` β 11 modules, each <200 lines |
| 5. β
`app/api/v1/__init__.py` β router aggregator |
| 6. β
`main.py` β 94 lines (Phase 0 just restored) |
| 7. β
Verify: backend boots, all 1249 routes respond, health 200 |
| 8. β
Commit + deploy (`4a8a16d`) |
|
|
| ## What Does NOT Ship This Pass (v3 P0-P2) |
|
|
| - Migrating alerts/wallet/token/scanner to new `domain/`. That's the current unfuck. |
| - The 15 mechanical refactors. Replaced with the layered architecture. |
| - Deleting old code. Strangler fig β old stays until domain is migrated. |
| - **v3 new**: M1 Agent Loop, M2 MCP Mesh, M3 RAG dual-dim, M4 Observability Triangle, M5 CI/CD, M6 ADRs, M7 Cost+SLOs, M8 AI Tool Matrix. |
|
|
| ## Phase 2: Alerts Vertical Slice (proves the pattern) β COMPLETE |
|
|
| After foundation lands, migrate `alerts` end-to-end as the reference: |
| ``` |
| app/domain/alerts/ |
| βββ models.py # Alert, AlertRule, Notification β Pydantic v2 |
| βββ repository.py # async SQLAlchemy queries |
| βββ service.py # business logic, pure Python |
| βββ broadcaster.py # WebSocket broadcast helper |
| |
| app/api/v1/auth/alerts.py # thin route: parse β call service β return |
| ``` |
|
|
| This proves the pattern works: domain is pure Python, route is <100 lines, can be unit tested without HTTP. |
|
|