Extending
Writing custom rules
The built-in rule set is deliberately closed. This is the sanctioned way to add the sixteenth rule: write it yourself as a WebAssembly module, and Garboard runs it in the gate beside the built-ins.
These are rules, not agents
A module is a pure function from parsed facts to findings. It never touches a language model, never writes anything, and cannot reach the host.
That is a bounded, boring capability on purpose, and it is what lets you answer a diligence question precisely: a customer’s custom rule can read the facts it was handed and return JSON. That is the whole list.
- No network. It cannot make a request.
- No filesystem. It cannot read your disk.
- No host calls. The sandbox is the boundary.
- Deterministic. Same facts in, same findings out — so a custom rule cannot break the determinism the rest of the gate depends on.
What a rule is
Two files sharing a basename, under .garboard/rules/:
.garboard/rules/
instance_size.json # manifest — identity, severity, the docs line
instance_size.wasm # the compiled module
The manifest carries the rule’s identity and severity; the module carries the check. Rule ids are namespaced custom.*, which means they are attributed in the comment and muted exactly like any built-in — there is no separate mechanism for your own rules.
Running them
garboard gate . --rules .garboard/rules
--rules defaults to <path>/.garboard/rules and is skipped when that directory is absent, so a repo without custom rules needs no flag and no configuration.
The ladder applies to your rules too
A custom rule can run in shadow: evaluated and recorded, never commented, never blocking. Write a rule, put it in shadow for a fortnight, look at what it actually caught, and then decide whether to make it live.
This is worth doing for a rule you wrote yourself even more than for a catalog rule. Your intuition about how often your own rule will fire is reliably wrong, and shadow costs nothing to find out.
Languages
Anything that compiles to WebAssembly. TinyGo and Rust are the two with complete worked examples.
Why not just let me add a built-in?
Because a built-in rule is a promise to every user of Garboard, and a custom rule is a decision by you about your repository. Keeping the built-in table closed is what makes the built-in table trustworthy — and it is why a custom rule gets a real sandbox rather than a plugin interface that could do anything.
