rugmuncher-backend / DESIGN.md
Crypto Rug Muncher
clean slate: removing all old data, fresh build
6993919
|
Raw
History Blame Contribute Delete
7.75 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
β”‚   β”‚
β”‚   β”œβ”€β”€ 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 + ...
β”‚   β”‚   └── providers/          ollama + openrouter + huggingface + ...
β”‚   β”‚
β”‚   └── workers/                background jobs (separate from API)
β”‚       β”œβ”€β”€ firehose.py
β”‚       β”œβ”€β”€ scanner_queue.py
β”‚       β”œβ”€β”€ ingest_cron.py
β”‚       └── cleanup.py
β”‚
└── tests/
    β”œβ”€β”€ conftest.py
    β”œβ”€β”€ unit/domain/
    └── integration/api/v1/

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

What Ships This Pass (Foundation)

  1. Fix crash β€” rag_engine re-export shim, backend healthy
  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 that still imports OLD routers (zero breakage)
  6. app/main.py β€” rewritten to ~100 lines, calls lifespan + middleware from core/, mounts new aggregator
  7. Verify: backend boots, all 757 routes respond, health 200, no import errors
  8. Commit + deploy

What Does NOT Ship This Pass

  • Migrating alerts/wallet/token/scanner to new domain/. That's Phase 2.
  • The 15 mechanical refactors. Replaced with the layered architecture.
  • Deleting old code. Strangler fig β€” old stays until domain is migrated.

Phase 2: Alerts Vertical Slice (proves the pattern)

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.

When alerts is shipped and verified in prod, the same pattern is applied to wallet, token, scanner, etc.