Policy linter
trustacks rule lint is a fast shape check on the overlay rules in a local
checkout. It catches the mistakes that stop a rule from loading or from
showing up in the rule inventory. It is not a proof system.
trustacks rule lint # every rule under rules/
trustacks rule lint acme.requires_runbook # one rule
trustacks rule lint --overlay ../my-overlay # a checkout somewhere elseThe overlay path defaults to the current directory and must contain
environment-profile.yaml at its root. Exit code is 0 when clean, 1 when
any issue is found.
What it checks
For each directory under rules/:
| Check | Fails when |
|---|---|
| Directory name is a valid rule ID | It does not match <namespace>.<name> (lowercase letters, digits, underscores, one dot). Later checks for that rule are skipped. |
| Namespace is not reserved | The namespace is argocd, constitution, proposal, or trustacks. |
| Length | The rule ID is longer than 64 characters. |
| Rule file exists | rules/<id>/<id>.rego is missing. |
| Test file exists | rules/<id>/<id>_test.rego is missing. |
| Package declared | The rule file has no package line. |
| Metadata present | The rule file never mentions rule_metadata. Without it the rule is invisible to the rule inventory and to agent citations. |
| Metadata key matches | The rule file never contains the rule ID as a quoted string, so the rule_metadata key probably does not match the directory name. |
Then it runs opa check over the scoped rule, or over all of rules/ when
unscoped, and reports any parse or compile error.
The naming checks are the rule-naming standard.
If opa is not on your PATH, lint prints a warning and skips the
opa check pass. The file checks still run. Install OPA so a clean lint
means what it says.
What it does not check
- Whether tests pass. Run
trustacks rule test, which runsopa test. - Whether tests exist beyond the file. A test file with one trivial test
passes lint. The scaffold from
trustacks rule newships exactly that: a baseline test and a TODO for the real one. - Whether your rule is correct or useful. A rule that denies everything lints clean.
- Signatures. Lint never touches a registry or a key.
- Refinement against the constitution. Lint does not compare your rule with constitution rules. It does not need to; see below.
trustacks rule sign does not run lint. It builds, pushes, and signs
whatever is under rules/, after checking every rule ID against the naming
standard. Run lint and test yourself first, or in CI.
Why an overlay cannot weaken the constitution
Overlay denies are added to the constitution’s denies. The runner asks one
question, data.proposal.deny, and gets the union of both. An overlay rule
can add a reason to refuse a proposal. It has no way to remove one, because
there is nothing to override: no allow rule, no waiver, no exception list.
That property holds by how the policy is assembled, not because the linter proves it. Earlier versions of this page said the linter verifies refinement, signatures, citations, and waivers at compile time. It does none of those.
In CI
The CLI ships inside the runner image, with opa alongside it. There is no
separate CLI package. Run it from the image against your checkout:
docker run --rm --user "$(id -u):$(id -g)" \
-v "$PWD:/work" -w /work \
ghcr.io/trustacks/runner:<version> \
sh -c 'trustacks rule lint && trustacks rule test'Use the runner version your workspace runs. The same command works as a CI step. See Rule CLI.
Where to go next
- Rule CLI ·
rule new,test,lint,sign - Rule-naming standard · the ID grammar
- Constitution · the rules your overlay adds to