Instruction file imported from DrEggdwarf/Anvil (
.github/instructions/backend.instructions.md). Copyright stays with the author.
Backend conventions — Anvil
Full architecture in CLAUDE.md. Full ADR history in ai/context/decisions.md.
Adding a route — use /new-endpoint prompt
.github/prompts/new-endpoint.prompt.md walks through the full checklist:
model → bridge method → API route → test.
Python conventions
from __future__ import annotationsen première ligne de chaque fichier- Imports : stdlib → third-party → local (ruff
I001) - Type hints sur tout : paramètres, valeurs de retour, attributs de classe
- Pydantic v2 :
Field(...)obligatoire sur chaque champ de requête —max_lengthsur lesstr,ge/lesur lesint,max_lengthsur leslist
Critical rules
CompilationBridgeonly (ADR-017): all compilation (ASM/C/C++/Rust/Go) goes throughbridges/compilation.py. Never spawn compilers directly in routes.- Binary data = hex strings (ADR-010): encode all bytes as hex (
"9090"for\x90\x90). Bridge converts hex↔bytes. - MCP dict rule (ADR-022): any endpoint intended for MCP must return a structured
dictwith asummaryfield — never a raw string. pyproject.tomlis the only dep source (ADR-019): don't create or editrequirements*.txt.- SubprocessManager only (ADR-020): never call
os.systemorsubprocess.rundirectly.
Session lifecycle
POST /api/sessions {bridge_type} → {session_id, token}
...use /api/{mode}/{session_id}/*...
DELETE /api/sessions/{session_id}
Token from session create is required for WS: /ws/{type}/{session_id}?token=<token>.
Bridge checklist
- Subclass
BaseBridge, implementstart / stop / health / execute - Call
self._require_ready()at top of every action method - Sanitize all user input via
core/sanitization.py - Register bridge: import it in
core/lifecycle.py - For large bridges, use mixin pattern (see
bridges/pwn/,bridges/rizin/)
API package structure
Large routers are split into packages:
api/gdb/ → __init__.py, _deps.py, execution.py, breakpoints.py, inspection.py
api/pwn/ → __init__.py, _deps.py, upload.py, elf.py, tools.py
__init__.pycreates the router and includes sub-routers_deps.pyhas shared helpers:_get_*_bridge(),_resolve_path(),_workspace- Path sandboxing (ADR-025): ALL endpoints accepting file paths MUST use
_resolve_path(session_id, raw_path)
Agent tools package
agent/tools/ → __init__.py, spec.py, handlers.py, registry.py, dispatch.py
handlers.py: bridge shims (async functions taking Session + kwargs)registry.py: ToolSpec list +tools_for_modules()filteringdispatch.py:dispatch_tool_call()entry point- Security:
_resolve_agent_path()sandboxes LLM-controlled paths
Test conventions
- Fixture
async_client(httpx) for API tests,mock_session_managerfor bridge tests - All bridges mocked via
sys.modulesinconftest.py— CI has no GDB/rizin/pwntools - No
@pytest.mark.asyncioneeded —asyncio_mode = "auto"in pyproject.toml