Graft is an open-source, extensible Detection-as-Code (DaC) platform written in Python >= 3.13, managed via uv (build-backend = "uv_build", never hatchling), and built around Hexagonal Architecture (Ports & Adapters), Strict TDD, and a Stdlib-First runtime policy. Never add, install, or import any new Python package or OS tool without explicit maintainer approval. The Driving Core (src/graft/core/) is 100% engine-agnostic—it never imports cloud SDKs, SIEM clients, or HTTP transports; Driven Engine Adapters (src/graft/engines/<engine>/) adapt external SIEM APIs to Core port protocols. Every platform capability is drivable from the graft CLI with structured --json output for deterministic agent and CI consumption.
| Track | Hub & Primary Guides |
|---|---|
| Architecture | docs/architecture.md — Hexagonal boundaries, content tracks, and GitOps lifecycle |
| Developers | docs/developers/README.md — framework.md · extending_engines.md · quality_and_standards.md |
| Analysts | docs/analysts/README.md — rule_authoring.md · recipes.md · replay_testing.md · visibility_and_matrix.md |
| Operators | docs/operators/README.md — adoption.md · gitops_reconciliation.md · cicd_and_infrastructure.md · security.md |
| Engines | docs/engines/README.md — Co-located engine guides at src/graft/engines/<engine>/docs/README.md (discover via uv run graft --json engines) |
| Agent Skills | .agents/skills/ — scaffold-rule · scaffold-dataset · scaffold-tests · review-rule |
| Path | Layer & Responsibility | Allowed Imports |
|---|---|---|
src/graft/core/models/ |
Domain Models: Immutable @dataclass(frozen=True) structures (RuleEnvelope, DatasetEnvelope, ManagedState, CompilationResult). |
Python stdlib and graft.core.models only. |
src/graft/core/ports/ |
Port Contracts: @runtime_checkable typing.Protocol interfaces (EngineAdapter, DatasetPort, RuleCompilerPort, RuleDeployerPort, ManagedEnginePort, ReplayHarnessPort). |
Python stdlib, graft.core.models, and graft.core.ports only. |
src/graft/core/ |
Driving Core Services: YAML loaders/dumpers, Draft 2020-12 SchemaValidator, STIX MitreValidator, reconcilers (Dataset, CustomRule, GitOps), GitBlameExtractor, EngineRegistry, and exporters. |
models, ports, sibling core modules, stdlib, and approved runtime deps in pyproject.toml. Never imports graft.cli, graft.engines, or HTTP clients (except mitre_validator.py for explicit STIX updates). |
src/graft/engines/<engine>/ |
Driven Engine Adapters: Self-contained vendor packages (engine.yaml, schemas/, docs/README.md, .env.example, REST client, compiler, deployer, dataset sync, managed sync, and replay harness). |
graft.core, own engine package (graft.engines.<engine>), stdlib, and approved runtime deps. Never imports graft.cli or another engine adapter. |
src/graft/cli/ |
Driving CLI Adapter: argparse entry point (main.py), core handlers (commands_core.py), dynamic engine router (engine_controller.py), and scaffolder (scaffold.py). |
graft.core, sibling cli modules, stdlib, and approved runtime deps. Never statically imports graft.engines.<engine>.* (routes through EngineRegistry and EngineAdapter). |
datasets/ & rulesets/<engine>/ |
Declarative Content: 2-block datasets (datasets/<name>.yaml), 5-block custom rules (custom/*.yaml), vendor state (managed/index.yaml), 4-block managed rules (managed/<rule>.yaml), and _archived/. |
Validated by src/graft/core/schemas/ and src/graft/engines/<engine>/schemas/. |
tests/ |
Partitioned Test Suite: tests/unit/ (100% mock-isolated, engine-agnostic, <1s), tests/engines/<engine>/ (mock-transport contract tests), and tests/integration/ (gated by --run-integration). |
Enforced by test_architecture_layers.py and test_documentation_integrity.py. |
Malformed YAML, invalid UTF-8 bytes, missing file paths, or unexpected API payloads must produce actionable errors—never unhandled KeyError, IndexError, AttributeError, TypeError, or UnicodeDecodeError.
- Typed Domain Exceptions: Parsers in
loader.py, engine manifest loaders, andengine_registry.pymust raiseRuleLoadError,DatasetLoadError,ManagedManifestLoadError,EngineError, orScaffoldError. - Strict
--jsonError Contract: When--jsonis passed on the CLI, every failure exit path must emit valid JSON onstdout({"success": false, "error": "..."}) before exiting non-zero. - Prove It: Every parser or CLI failure fix ships with a regression test in
tests/unit/core/test_adversarial_inputs.py.
- Red: Before implementing a feature, bug fix, or parser change, write a failing unit test (
tests/unit/), engine contract test (tests/engines/<engine>/), or adversarial test (test_adversarial_inputs.py). - Green: Write the minimal production code in
src/graft/required to pass the test. - Refactor: Clean up duplication, enforce strict typing (
mypy --strict), and verify architectural gates while keeping all tests green.
Trigger, inspect, and test every transformation from the CLI without writing throwaway scripts or requiring live cloud credentials:
uv run graft --json engines— Introspect registered engines, capability flags, environments, and schema paths.uv run graft --json lint [paths...]— Validate datasets, custom rules, managed manifests,%<name>.valuereferences, and 1-to-1 ID uniqueness offline.uv run graft --json export datasets | catalog | matrix— Inspect active datasets (after UTCttl:YYYY-MM-DDfiltering), Git-attributed catalogs, and ATT&CK Navigator layers.uv run graft --json <engine> render [paths...] [--all]— Synthesize engine-native queries (e.g., full rule with injected metadata) completely offline.uv run graft --json <engine> verify | test | diff | apply | pull— Execute remote compilation, staging replay, and GitOps reconciliation.
RuleEnvelope, DatasetEnvelope, and ManagedState are mechanically checked against src/graft/core/schemas/*.schema.json by test_architecture_layers.py. When adding or changing an envelope field, update the JSON schema, frozen dataclass in src/graft/core/models/, YAML loader/dumper in loader.py, and engine query synthesizer/deconstructor so load -> dump -> load and synthesize -> deconstruct remain lossless.
- Solve problems with Python >= 3.13 standard libraries first (
urllib.request,argparse,dataclasses,subprocess,csv,hashlib,uuid,tomllib,ast). - Runtime dependencies are locked in
pyproject.tomland enforced bytest_architecture_layers.py. Never add or install any dependency or OS tool without explicit maintainer approval. Dev tools (pytest,mypy,ruff) live in[dependency-groups.dev]and are never imported insrc/graft/.
-
Taxonomy & Archival: Datasets (
datasets/<name>.yaml, 2-block, plural snake_case<context>_<entity_plural>,"index"reserved, inline# ... ttl:YYYY-MM-DDUTC expiry), custom rules (rulesets/<engine>/custom/*.yaml, 5-block), and managed content (managed/index.yaml+ optional 4-blockmanaged/<rule>.yaml). Files/folders starting with_(_archived/) are excluded from linting and exports; archived datasets clear remote rows to()with description"Deprecated on Graft". -
GitOps Order:
diffandapplyalways run in order: 1. Datasets$\rightarrow$ 2. Custom Rules$\rightarrow$ 3. Managed Content (scoped to branch changes by default;--allfor full catalog drift). Missing staging credentials duringgraft <engine> testemit[WARNING]and exit0unless--require-stagingis passed. -
Logging, Comments & Markdown: Core reconcilers (
graft.reconciler) ownINFOCRUD andERRORlogs; engine adapters (graft.<engine>.*) emitDEBUG/WARNINGonly (print()is banned by RuffT20). Write comments only to explain why or security constraints (zero obvious comments, boilerplate docstrings, or AI trailers). Use hyphens (-) for markdown lists and Mermaidflowchart TD/LRwith quoted node labels (ID["Label (Detail)"]) for diagrams.
-
CLI Command / Subcommand: Register in
main.py(plusRESERVED_CORE_COMMANDSinengine_registry.py) orengine_controller.py$\rightarrow$ supportjson_output: bool = Falseon both success and error paths$\rightarrow$ add engine-agnostic unit tests intests/unit/cli/and engine CLI tests intests/engines/<engine>/. -
Engine Adapter: Run
uv run graft new engine <name>$\rightarrow$ declare capabilities and env vars insrc/graft/engines/<name>/engine.yaml$\rightarrow$ implementEngineAdapterand enabled ports (includingrender_ruleanddeconstruct_rule)$\rightarrow$ adddocs/README.md,.env.example, and contract tests intests/engines/<name>/. -
Envelope Field: Update JSON schema in
src/graft/core/schemas/$\rightarrow$ update frozen dataclass insrc/graft/core/models/$\rightarrow$ update loader and serializer inloader.py$\rightarrow$ runuv run pytest tests/unit/core/test_architecture_layers.py.
Before completing any task or opening a PR, all five verification gates must return 0 errors:
uv run ruff check .
uv run ruff format --check .
uv run mypy --strict src tests
uv run pytest tests/unit tests/engines
uv run graft lint- Branching & Scoped Commits: Never commit directly to
main(git checkout -b <branch-name>). Format commits as<scope>: <description>(lowercase scope, imperative one-liner, no trailing period, nofeat:/fix:/chore:/update:, no AI trailers). Pushes touching.github/workflows/requireworkflowOAuth scope or SSH. - Parallel Agents & Shared Files: Isolate concurrent agents in separate worktrees/branches (
share/branch). Keep edits to shared routing/loader files (main.py,commands_core.py,engine_controller.py,loader.py) small and surgical, re-reading target lines immediately before editing. Validatepyproject.toml,engine.yaml, and*.schema.jsonimmediately after any edit.