This guide covers how to create and modify compliance implementations (plugins) for the darnit framework. Read this if you want to add controls to the existing OpenSSF Baseline implementation or create a new compliance framework plugin.
All controls MUST be defined in the implementation's TOML configuration file. Python code is NOT the source of truth for control metadata.
- New controls are defined entirely in TOML with passes, metadata, severity, and help URLs
- CEL expressions in TOML follow documented escaping rules (see CEL Reference)
- TOML controls overwrite any Python-registered controls
Each control is defined under [controls."CONTROL-ID"] in the TOML file:
[controls."MS-DOC-01"]
name = "ReadmeExists"
description = "Project must have a README file"
tags = { level = 1, domain = "DOC", documentation = true }
docs_url = "https://example.com/mystandard#MS-DOC-01"
help_md = """Create a README.md file in the repository root.
**Remediation:**
1. Create README.md with project description
2. Include usage instructions
"""| Field | Required | Purpose |
|---|---|---|
name |
Yes | Short identifier |
description |
Yes | Human-readable description |
tags |
Yes | Must include level (integer) and domain (string) |
docs_url |
No | Link to the standard's documentation |
help_md |
No | Markdown help text shown on failure |
when |
No | Preconditions for when this control applies |
Controls can declare preconditions. If not met, the control is skipped (not failed):
[controls."MS-CI-01"]
name = "SecureWorkflowInputs"
description = "Workflows handle untrusted inputs safely"
when = { ci_provider = "github" }The when keys are matched against project context from .project/project.yaml.
Controls define one or more [[passes]] entries. The sieve pipeline executes them in order, stopping at the first conclusive result.
Check if any listed file exists:
[[controls."MS-SEC-01".passes]]
handler = "file_exists"
files = ["SECURITY.md", ".github/SECURITY.md", "docs/SECURITY.md"]Run a CLI command and evaluate the result:
[[controls."MS-AC-01".passes]]
handler = "exec"
command = ["gh", "api", "/orgs/$OWNER"]
pass_exit_codes = [0]
fail_exit_codes = [1]
output_format = "json"
expr = 'output.json.two_factor_requirement_enabled == true'
timeout = 30Variable substitution: $PATH (local repo), $OWNER, $REPO, $BRANCH, $CONTROL.
Search file contents with regex:
[[controls."MS-DOC-02".passes]]
handler = "pattern"
file_patterns = ["SECURITY.md", ".github/SECURITY.md"]
content_patterns = { security_contact = '([\w.-]+@[\w.-]+\.\w+|security\s*contact)' }
expr = 'output.any_match'Always returns INCONCLUSIVE with steps for human review:
[[controls."MS-DOC-02".passes]]
handler = "manual"
steps = [
"Open SECURITY.md",
"Verify it contains a clear contact method",
"Confirm the contact method is monitored",
]Controls can define declarative remediation actions:
[controls."MS-SEC-01".remediation]
safe = true
dry_run_supported = true
[controls."MS-SEC-01".remediation.file_create]
path = "SECURITY.md"
template = "security_policy"
overwrite = false[controls."MS-BR-01".remediation.exec]
command = ["git", "tag", "-s", "v1.0.0"]
success_exit_codes = [0]
timeout = 30[controls."MS-AC-01".remediation.api_call]
method = "PUT"
endpoint = "/repos/$OWNER/$REPO/branches/$BRANCH/protection"
payload_template = "branch_protection_payload"Update .project/project.yaml after remediation:
[controls."MS-SEC-01".remediation.project_update]
set = { "security.policy.path" = "SECURITY.md" }Templates are content blocks used by remediation actions:
[templates.security_policy]
description = "Standard SECURITY.md template"
content = """# Security Policy
## Reporting a Vulnerability
Report vulnerabilities by emailing security@$OWNER.example.com.
We will respond within 48 hours.
"""Templates support $OWNER and $REPO variable substitution.
When built-in pass types aren't enough, write custom sieve handlers.
from typing import Any
from darnit.sieve.handler_registry import HandlerContext, HandlerResult, HandlerResultStatus
def my_handler(config: dict[str, Any], context: HandlerContext) -> HandlerResult:
"""Check something and return a result."""
return HandlerResult(
status=HandlerResultStatus.PASS,
message="Check passed",
confidence=1.0,
evidence={"key": "value"},
)config: All fields from the TOML[[passes]]entry (minushandler,shared,phase)context: Framework-provided context (repo path, control ID, evidence, project context)
Register from your implementation class:
from darnit.sieve.handler_registry import get_sieve_handler_registry
def register_sieve_handlers(self):
registry = get_sieve_handler_registry()
registry.set_plugin_context(self.name)
registry.register("my_check", "deterministic", my_handler,
description="My custom check")
registry.set_plugin_context(None) # Always clear when doneThen reference in TOML:
[[controls."MS-LIC-01".passes]]
phase = "deterministic"
handler = "my_check"
file_extensions = [".py", ".js"] # → config["file_extensions"]| Status | Meaning | When to use |
|---|---|---|
PASS |
Control satisfied | Positively verified compliance |
FAIL |
Control NOT satisfied | Positively verified non-compliance |
INCONCLUSIVE |
Cannot determine | Let the next handler try |
ERROR |
Handler malfunction | Your handler broke (not the control) |
Register your implementation via Python entry points in pyproject.toml:
[project.entry-points."darnit.implementations"]
my-framework = "my_package:register"The register() function returns an instance of your implementation class:
# my_package/__init__.py
def register():
from .implementation import MyImplementation
return MyImplementation()To expose custom tools to AI assistants, implement register_handlers() on your implementation class:
def register_handlers(self) -> None:
from darnit.core.handlers import get_handler_registry
from . import tools
registry = get_handler_registry()
registry.set_plugin_context(self.name)
registry.register_handler("my_tool", tools.my_tool)
registry.set_plugin_context(None)Then reference in TOML:
[mcp.tools.my_tool]
handler = "my_tool"
description = "Run my custom tool"The OpenSSF Baseline implementation (packages/darnit-baseline/) is the canonical example:
| File | What to learn |
|---|---|
openssf-baseline.toml |
TOML config with 62 controls, templates, context definitions |
src/darnit_baseline/implementation.py |
ComplianceImplementation class |
src/darnit_baseline/__init__.py |
register() function |
pyproject.toml |
Entry point configuration |
- CEL Reference — CEL expression syntax and pitfalls
- Tutorial: Add a New Control — Step-by-step walkthrough
- Tutorial: Create a New Implementation — Build a plugin from scratch
- Testing Guide — Testing your implementation
- Back to Getting Started