Architecture for Contributors¶
This page supplements the Concepts Overview with contributor-focused guidance: package layout, extension points, and key design decisions to understand before making changes. Read the Concepts Overview first for the component model and execution lifecycle.
Package Layout¶
The rampart/ source tree is organized by concern: foundational types live in core/, while each extension point gets its own subpackage (attacks/, probes/, evaluators/, drivers/, converters/, surfaces/, payloads/, reporting/). The pyrit_bridge/ package groups common PyRIT integration code (see PyRIT Bridge below), and pytest_plugin/ provides the pytest integration.
- For the component model and how the pieces fit together at runtime, see the Concepts Overview.
- For the public symbols exported from each package, see the API Reference index.
- For where to put new code, see Extension Points below.
- The authoritative layout is always the source tree itself — browse
rampart/on GitHub.
Key Design Decisions¶
Protocols Over ABCs¶
RAMPART uses @runtime_checkable protocols for extension points that consumers implement (AgentAdapter, Session, Evaluator, Surface, PromptDriver). This means:
- No inheritance required — any class with the right methods satisfies the protocol
- Type-checked at development time by ty
- Verifiable at runtime with
isinstancechecks
BaseExecution is the exception — it's an ABC because it owns the lifecycle skeleton and subclasses share real implementation.
Factory Classes (Attacks, Probes)¶
Attacks and Probes are static factory classes that construct execution objects. They:
- Provide a clean, discoverable API:
Attacks.xpia(...),Probes.behavior(...) - Handle input coercion (e.g.,
coerce_driverfor flexible trigger input) - Return
BaseExecution, hiding the concrete execution class
When adding a new attack or probe, you add a static factory method — not a new class that users instantiate directly.
Evaluator Polarity¶
Evaluators are polarity-free. They report whether a condition was detected, not whether it's good or bad. The attack/probe factory applies the correct polarity:
resolve_as_attack: detected → UNSAFEresolve_as_probe: detected → SAFE
This allows the same evaluator (e.g., ToolCalled) to be used in both attack and probe contexts.
Execution Lifecycle Ownership¶
BaseExecution owns all cross-cutting concerns:
- Event dispatch — ON_PRE_EXECUTE, ON_POST_EXECUTE, ON_ERROR
- Timing — wall-clock duration
- Error handling — all exceptions from
_execute_asyncare caught and converted to ERROR results - Handler registration — framework-level handlers (result collection) are injected automatically
Subclasses implement only _execute_async and strategy_name. They should not catch InfrastructureError — the base class handles it.
Pytest Plugin¶
pytest_plugin/ integrates RAMPART with pytest:
plugin.py— hook registrations (configure, collection, sessionfinish, terminal summary, optionalpytest_testnodedown)._session.py— session-scoped state container (RampartSession), trial-group aggregates, sink registry, idempotency and incomplete-run flags._collection.py— per-testResultCollectorand theContextVar-based handler that captures results from executions._xdist.py— pytest-xdist support: detection helpers, JSON-safe serialization ofResultobjects, controller-side merge, and conftest-scanning sink discovery. Workers serialize their results intoconfig.workeroutput; the controller deserializes viapytest_testnodedownand emits a single unified report. See Parallel Execution for the data flow and trust boundary.
PyRIT Bridge¶
PyRIT is RAMPART's upstream dependency for converters and prompt generation. Its import chain is heavy, so:
- PyRIT-related logic is grouped under
rampart/pyrit_bridge/ - Lazy imports inside functions are used to defer the cost
- This boundary keeps RAMPART's core import fast
Extension Points¶
| Extension Point | Protocol/ABC | Where to add |
|---|---|---|
| New attack strategy | Subclass BaseExecution |
rampart/attacks/ |
| New probe strategy | Subclass BaseExecution |
rampart/probes/ |
| New evaluator | Implement Evaluator protocol |
rampart/evaluators/ |
| New prompt driver | Implement PromptDriver protocol |
rampart/drivers/ |
| New attack surface | Implement Surface protocol |
rampart/surfaces/ |
| New converter | Implement PayloadConverter protocol |
rampart/converters/ |
| New report sink | Implement ReportSink protocol |
rampart/reporting/ |
| New payload format | Extend payload system | rampart/payloads/ |
See Extending RAMPART for step-by-step guides.
Module Import Conventions¶
- Import from the package root (
rampart.core,rampart.attacks) when the symbol is exported in__init__.py - Within the same package, import from the specific module to avoid circular imports
- Internal modules are prefixed with
_(e.g.,_xpia.py,_single_turn.py) — they are not part of the public API