There is no supported way to remove a head iPad peer. Mesh.RemoveHeadIPad exists but is wired to nothing, so removing a peer means hand-editing mesh.yaml on the hub and running hub apply. That was done twice on 2026-08-06 against production.
pkg/mesh/headipad.go:28 implements it:
func (m *Mesh) RemoveHeadIPad(id string) error {
Nothing calls it outside tests. There is no CLI command and no API route:
hydraguard headipad remove <id> does not exist even though the mesh function does.So head iPad peers can be created through supported paths and only removed by editing YAML by hand.
Two removals were needed on 2026-08-06, both on the live hub:
Case 2 is the security one: without a removal path, revoking a WireGuard credential is a manual YAML edit on a production mesh host, which is exactly the operation you want tooling for.
mesh.yaml is re-serialized by hydraguard when it provisions a peer. Between the two removals on the same day the file changed indentation (- id: at column 0 became - id:) and new entries were appended at the end rather than in slot order. Line-based edits that worked in the morning silently matched nothing in the afternoon: grep counts returned 0 and would have been taken as "no peers" by anything scripted against them.
The safe procedure ended up being: back up and checksum, locate exact line numbers by reading the file, delete, diff to prove only the intended lines changed, run hub apply --dry-run and compare the generated PublicKey set against wg show wg0 dump to prove exactly one key drops and none are regenerated, then apply and re-check handshake counts against a baseline. That is a lot of care to expose a function that already exists.
hydraguard headipad add|list|config|remove <id>, matching air.go / neckair.go / venue.go. This alone closes the gap, since RemoveHeadIPad is already implemented and tested.hub apply runs, and a freed address can then be reassigned to a new peer while the old one still claims it. That happened on 2026-08-06: 10.10.200.3 was free in mesh.yaml but still on wg0, so the next provision would have collided.