Skip to Content
ReferencePolicy linter

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 else

The 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/:

CheckFails when
Directory name is a valid rule IDIt does not match <namespace>.<name> (lowercase letters, digits, underscores, one dot). Later checks for that rule are skipped.
Namespace is not reservedThe namespace is argocd, constitution, proposal, or trustacks.
LengthThe rule ID is longer than 64 characters.
Rule file existsrules/<id>/<id>.rego is missing.
Test file existsrules/<id>/<id>_test.rego is missing.
Package declaredThe rule file has no package line.
Metadata presentThe rule file never mentions rule_metadata. Without it the rule is invisible to the rule inventory and to agent citations.
Metadata key matchesThe 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 runs opa test.
  • Whether tests exist beyond the file. A test file with one trivial test passes lint. The scaffold from trustacks rule new ships 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

Last updated on