Custom Gates
A custom verify-gate — one that isn't in the Tier 1a catalog — currently faces three coupled obstacles. The catalog is intentionally closed; if you need a gate that isn't shipped, the path is "fork and contribute back" rather than "register a plugin."
1. nockup graft inject hard-rejects unknown gate names
The CLI validates [graft.gates].gate and [graft.gates].gate-chain entries against an allowlist (TIER_1A_GATES) at manifest discovery. A name outside that allowlist fails immediately:
[graft.gates].gate `my-custom-gate` in hoon/lib/settle-graft.toml is not a known catalog gate.
Tier 1a (currently shipping): sig-verify-ed25519, sig-verify-schnorr,
manifest-verify, set-membership-verify, bounded-value-verifyThere is no --allow-custom-gate flag today. To get past this you either (a) add your name to TIER_1A_GATES in tools/graft-inject/src/gates.rs in a local fork, or (b) skip the [graft.gates] table entirely and hand-write your gate directly in settle-graft's poke body (the composer leaves hand-written gates untouched — see the apply_gate_selection mismatch guard).
2. vesl-gates.hoon must contain a matching arm
The composer rewrites the default hash-gate block to =/ hash-gate=verify-gate <name>:vesl-gates, so <name> must resolve as an arm in vesl-gates.hoon with the required [note-id=@ data=* expected-root=@] -> ? signature. A name with no matching arm produces a hoonc find . <name>:vesl-gates failure at compile time. If you forked TIER_1A_GATES, you must also ship the hoon arm.
3. vesl_hull::payload_builder_for_gate has no impl for your name
Stock /settle dispatches through SettlePayloadBuilder. The library's payload_builder_for_gate(name) only knows default-hash and manifest-verify; every other name hits the fallback arm, prints a stderr warning, and returns DefaultHashPayloadBuilder — which will dead-deny against your custom gate. You have two ways out:
Wire your impl in the template. The scaffolded
templates/vesl/src/main.rs::build_app_stateowns the dispatch. Replace the call topayload_builder_for_gatewith your own match:rustlet settle_builder: Arc<dyn vesl_hull::SettlePayloadBuilder> = match manifest.gate.as_str() { "my-custom-gate" => Arc::new(MyCustomPayloadBuilder), _ => vesl_hull::payload_builder_for_gate(&manifest.gate), };AppState.settle_builderisArc<dyn SettlePayloadBuilder>, so any user impl works once the template chooses it. The trait shape and an example impl are on Catalog Gates — Implementing aSettlePayloadBuilder.Skip stock
/settleentirely. Mount your ownPOST /settle-my-gateroute viaserve_with_extra_routesand use the per-gate poke builders invesl-core/src/graft_pokes/settle.rsdirectly (e.g.build_settle_note_poke_with_datafor arbitrary payload shapes). The stock/settlewill still dead-deny if anyone hits it, but your route bypasses the dispatcher.
Need a gate the catalog doesn't have?
Most "custom verify-gate" needs are actually shape-validation or authorization checks that belong in a validate-graft prelude or a separate graft's poke arm — not in the verify-gate. The verify-gate is narrow on purpose: it answers "does this payload prove authority over the registered root?" If your need doesn't fit that shape, a graft is usually the right surface.
If you do need a true custom verify-gate, open an issue describing the shape — recurring patterns are candidates for inclusion in the catalog rather than per-user forks.
See Also
- Catalog Gates from Rust — the shipped gates and the
SettlePayloadBuildertrait shape. - Serve — Composing Custom Routes —
serve_with_extra_routesfor the bypass path. - Grafts / Manifest Schema — Gate Selection —
[graft.gates]validation contract.