RMI Platform
feat(v3): ship new system β€” skip _legacy_main, mount v1 routers
3cf0daf
|
Raw
History Blame Contribute Delete
28.4 kB

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)

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)

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)

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:

# 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:

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):

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:

- 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.