fix: add org revocation CLI#131
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 114fe83247
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| fn enqueueOrgControl(self: *SwimProtocol, data: []const u8) void { | ||
| if (data.len > Org.ORG_REVOKE_WIRE_SIZE) return; | ||
| if (self.org_control_count >= self.org_control_queue.len) return; |
There was a problem hiding this comment.
Handle revocations past the queue capacity
When $MESHGUARD_CONFIG_DIR/revoked/ contains more than 16 .revoke files, loadOrgRevocations can return up to 64 entries and loadPendingOrgRevocations iterates all of them, but this guard silently drops every entry after org_control_queue fills. Those dropped signed revocations are still reported as pending but are never broadcast during that meshguard up, so fleets with more than 16 revoked org certs leave the later revocations unenforced on other peers. Consider retaining/retrying overflow entries or matching the queue capacity to the loader capacity instead of returning silently.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
This PR adds first-class CLI support for creating and propagating signed org certificate revocations (OrgCertRevoke), filling the tooling gap described in #126 and aligning docs with the now-implemented workflow.
Changes:
- Add
meshguard org-revoke <node-key-or-path> [--reason <reason>]to sign and persist org revocations under$MESHGUARD_CONFIG_DIR/revoked/*.revoke. - Load pending revocations on
meshguard upand queue them for best-effort SWIM broadcast (and local application when authorized). - Add org revocation helpers + tests, and update CLI/trust/security documentation to remove the “missing tooling” caveat.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main.zig | Adds org-revoke command, queues pending revocations on up, and shows pending revocations in config show. |
| src/identity/org.zig | Introduces org revocation helpers (reason parsing, signing, save/load, tests). |
| src/discovery/swim.zig | Adds an org-control broadcast queue and applies queued revocations locally when authorized. |
| docs/reference/cli.md | Documents the new meshguard org-revoke command and its behavior. |
| docs/guide/trust-model.md | Updates trust model guidance to include org revoke workflow. |
| docs/concepts/security.md | Updates security overview to reference org-revoke as revocation tooling. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // ─── Org Revocations ─── | ||
| const revoke_dir = std.fs.path.join(allocator, &.{ config_dir, "revoked" }) catch null; | ||
| defer if (revoke_dir) |rd| allocator.free(rd); | ||
|
|
||
| if (revoke_dir) |rd| { | ||
| if (std.Io.Dir.openDirAbsolute(zio(), rd, .{ .iterate = true })) |dir| { | ||
| defer dir.close(zio()); | ||
| var revoke_count: usize = 0; | ||
| var iter = dir.iterate(); | ||
| while (iter.next(zio()) catch null) |entry| { | ||
| if (std.mem.endsWith(u8, entry.name, ".revoke")) revoke_count += 1; | ||
| } | ||
| if (revoke_count > 0) { | ||
| try stdout.writeStreamingAll(zio(), "\n"); | ||
| try writeFormatted(stdout, "\x1b[1m─── Org Revocations ({d}) ───────────────────────\x1b[0m\n", .{revoke_count}); | ||
| try writeFormatted(stdout, " {d} pending revocation(s)\n", .{revoke_count}); | ||
| } | ||
| } else |_| {} | ||
| } |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fc7f5554ef
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| while (iter.next()) |entry| { | ||
| if (entry.value_ptr.state == .dead) continue; | ||
| const ep = entry.value_ptr.gossip_endpoint orelse continue; | ||
| self.gossipSend(slot.data[0..slot.len], ep); |
There was a problem hiding this comment.
Re-gossip verified org revocations
This drains locally loaded .revoke files by sending the raw control message only from this node, while handleOrgRevoke only updates the in-memory revoked_nodes set and never re-enqueues or persists a verified revocation. If the revoker's membership view is incomplete during the few broadcast ticks (for example just after meshguard up through a seed, after a partition, or for peers that restart/join after the queue expires), peers only reachable through recipients never learn the revocation and can still accept the revoked org cert. Please re-broadcast verified revokes with duplicate suppression, or carry/persist them through the existing gossip path.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 030a2c3ce8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| self.gossipSend(slot.data[0..slot.len], ep); | ||
| sent_any = true; | ||
| } | ||
|
|
||
| if (sent_any) slot.remaining -|= 1; |
There was a problem hiding this comment.
Keep revokes queued when fan-out sends are dropped
In meshes where pending revokes times alive peers exceeds the send token bucket, gossipSend silently drops over-budget packets but this loop still sets sent_any and consumes a retry for the slot. Because the slot/peer iteration order is stable, the same early sends can consume the available tokens on each retry/readvertise while later peers repeatedly miss the revocation, leaving them accepting the revoked org cert. Have gossipSend report actual success or track per-peer progress before decrementing remaining.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c6917e4dc4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| self.rememberOrgRevoke(rev); | ||
| self.enqueueOrgRevokeBroadcast(rev); | ||
| if (self.isOrgAuthorizedPeer(rev.org_pubkey)) { | ||
| self.applyOrgRevoke(rev, false); |
There was a problem hiding this comment.
Scope org revokes to the issuing org
When a node trusts multiple orgs, this applies any queued revoke from a trusted org to applyOrgRevoke, which records only rev.node_pubkey in the global revoked_nodes set; certAuthorizesPeer/recordedCertAuthorizesPeer then reject that pubkey even when its cert was issued by a different trusted org. With the new org-revoke broadcast path, an admin of org A can therefore evict nodes that are members of org B. Store revocations by (org_pubkey, node_pubkey) and compare them with the certificate/vouch issuer before rejecting.
Useful? React with 👍 / 👎.
Summary
meshguard org-revoke <node-key-or-path> [--reason <reason>]to create signedOrgCertRevokemessages from the local org keypair.0x42wire messages under$MESHGUARD_CONFIG_DIR/revoked/*.revokeand queue them for best-effort SWIM control-plane broadcast onmeshguard up.Closes #126
Validation
zig build test --summary all(110/110)zig build --summary allMESHGUARD_CONFIG_DIR:keygen,org-keygen,org-revoke --reason key-compromised, verified one 138-byte.revokeartifact andconfig showpending revocation outputzig build -Dtarget=x86_64-linux-gnu -Dno-sodium=true --summary allzig build -Dtarget=x86_64-macos -Doptimize=ReleaseFast --summary allzig build -Dtarget=x86_64-freebsd -Doptimize=ReleaseFast --summary allgit diff --check