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.