The bright line
A rule pack expresses exactly one shape: keywords + regex over code/string regions + a templated claim. Anything that needs more is a Go detector.
Three component kinds are structurally reserved for Go and the assembler, and a rule can
never emit them:
local-model-file (binary header parsers own it), rag-pipeline
(synthesized downstream), and application (the scan root).
The split is enforced, not advisory. The rule compiler rejects a pack that reaches past
this envelope at startup — before any file is scanned.
The embedded ruleset
Packs live underrules/<category>/, one file per provider, and are compiled into the
binary with go:embed. That makes the default detection vocabulary offline by
construction and versioned with each release.
Inspecting the effective ruleset
airom rules list prints the ruleset AIROM will actually run — embedded packs plus any
--rules overlays, post-merge — with each rule’s originating layer.
List the effective ruleset
LAYER column reads embedded for a built-in rule, or the overlay’s file path for a
rule an overlay added or overrode. It is the fastest way to answer “which pack is
actually producing this finding?”
Overlays: --rules
--rules loads an additional pack file. It is repeatable, and files apply in flag order.
Layer two overlays on top of the embedded set
Add — a rule ID that does not exist yet
Add — a rule ID that does not exist yet
New IDs in an overlay must be namespaced by the overlay’s own
pack name. A pack
declaring pack: mycorp can add mycorp/internal-gateway, not openai/something.Override — a rule ID that matches an existing rule
Override — a rule ID that matches an existing rule
The overlay rule replaces the existing rule wholly. There is no field-level
merging: your rule must be complete and passes the same validation.
Disable — an entry of just the ID plus disable: true
Disable — an entry of just the ID plus disable: true
disable is overlay-only, and the target
must exist — disabling a rule that isn’t there is an error, never a silent no-op.--rules list, later files win. A third layer — an
OCI-distributed remote registry — is a reserved slot for v2 and pairs with the
signing/trust-policy work; it is not implemented.
Validating a pack: lint and test
Both commands take exactly one pack file and need no Go toolchain — which is the point.
Rule authors are not expected to be Go programmers.
- airom rules lint
- airom rules test
Validates a pack against the lint contract and its fixture coverage: every rule
needs at least one positive and one negative fixture case.
Lint a pack
rules/models/openai.yaml uses
rules/models/testdata/openai/. Failures print with file, line, rule ID, and reason, and
the command exits non-zero.
Validation is strict by design. An unknown field is a parse error, not a shrug:
Selecting the rule engine: --select
All rule packs execute inside a single detector whose ID is ruleengine. --select
takes a detector selection expression, so you can run only the declarative layer:
Run only the rule engine
airom detectors explain ruleengine shows the compiled selector, and the rule count it
carries:
Explain the rule-engine detector
rules:98 reflects the effective set. Add an overlay with two new rules and
airom detectors list --rules mycorp.yaml reports rules:100 — a quick confirmation
that your pack was actually loaded.
airom detectors list and airom detectors explain honor --select and --rules, so
what they print is what a scan with those same flags will run.How a rule runs
Compilation happens once at process startup: every layer is parsed and merged, the whole lint contract is validated (any violation aborts startup with the offending pack, rule, and reason), every regex is compiled, and one Aho–Corasick trie is built over every pack’s keywords. Then, per file:1
Selector gate
Language and size are checked from compiled rule metadata — before any content is
read.
2
Region classification
A per-language lexer splits the file into code, comment, and string regions.
3
Keyword prefilter
The trie runs over code + string regions only. No keyword hit means the file is done,
at roughly memcpy speed. This eliminates the overwhelming majority of files.
4
Regex execution
Only rules whose keywords hit run their regex, and only within their declared regions.
5
Templating
Matches template into findings: claim, occurrence (with fields from named groups and
captured params), and relation claims. The detection method is always
source-code-analysis.Rules are self-invalidating
The SHA-256 of the effective compiled ruleset participates in the cache namespace:version: field is informational: rules-as-data
invalidate on content, which eliminates the forgotten-version-bump stale-cache bug.
Next
Writing Rules
The full YAML schema, why keywords are mandatory, and the fixture-driven test loop.
CLI Reference
Every command and global flag, including
--rules and --select.