Repository navigation
NockGuard v0.10.0
v0.10.0 extends the opt-in paired stdio audit (audit.action_pairing: true). Signed action rows now record a digest of each tool definition the server advertised. Operators can pin reviewed definitions with tool_pins and --upstream-id; a pinned tool whose definition is missing, unhashable or changed is hidden from tools/list and its calls are blocked. Each paired proxy run writes signed open, heartbeat and close rows, and verify --completeness reports a per-run assessment next to the integrity verdict. Paired audit now needs a signing key. This release also fixes concurrent Telegram approval routing, the bracketed IPv6 loopback Host on mcp-listen, oversized method and tool names, and a malformed approval_actor latching the required audit writer.
Upgrade notes
audit.action_pairing: truenow needs a signed audit writer (an Ed25519 or HMAC signing key). Without one,nockguard proxyexits withaudit.action_pairing requires a signed audit writer for run lifecycle. v0.9.0 ran pairing unsigned. Policies withoutaction_pairingare not affected.- Paired trails now contain
runrows (open, aheartbeatevery 30 seconds,close) and, on call rows,tool_pin_statuswithtool_hash_versionandtool_definition_hashwhen a definition was observed. Scripts that count rows or match onrecord_typewill see them. These rows use the v2 format v0.9.0 already validates, so a v0.9.0verifystill checks their integrity;--completenessneeds v0.10.0. - With pairing on, when the upstream exits or the proxy gets SIGINT, SIGTERM, SIGHUP or SIGQUIT, the proxy stops accepting input, waits a bounded time for accepted calls, records
outcome_unknownfor calls still in flight and then writes the runcloserow. With pairing off, shutdown is unchanged. tool_pinsandstrict_tool_pinsare new agent policy keys. A policy that uses them does not load on v0.9.0, so roll back with a policy that omits them.--upstream-idis required only when the agent's policy setstool_pinsorstrict_tool_pins. The ID must be 1 to 128 bytes with no whitespace or control characters, and when pins are listed it must name an upstream undertool_pins; the same rule applies totool_pinskeys when the policy loads. A pin policy also needsaudit.action_pairing: true(the policy fails to load without it) and a signing key.--audit-best-effortis refused with pins, andmcp-listen,mcp-httpandmcp-gatewayrefuse to start with a pin policy.verify --completenesskeeps the integrity exit status:BROKEN,INCOMPLETEorUNASSESSABLEdo not change the exit code. Readcompleteness[].verdictfrom--json. Thecompletenessarray and the textCOMPLETENESS:lines appear only when the integrity verdict isPROTECTED; on any other verdict, or a key or read error, there is no completeness output, so checkverdictfirst. It checks a single trail and cannot be combined with--all.- MCP requests whose method or tool name is longer than 256 bytes are now refused with JSON-RPC
-32600and recorded asmethod-deny.
Added
- Tool-definition digests for opt-in paired stdio audit. Each tool in a complete client
tools/listlisting gets a SHA-256 digest of its RFC 8785 canonical JSON (jcs-rfc8785-v1), kept in a session tool catalog. Signed v2 call rows record the observed hash and atool_pin_status(unpinned,unobservedorunhashable). The digest identifies the definition the server advertised, not how the server behaves. Only a complete single-page listing attaches a hash to later calls; calls after a paginated listing or afternotifications/tools/list_changedstayunobserved. No raw definition or schema is written to the trail. If the catalog cannot be built,tools/liststill returns the normal filtered listing. - Operator tool pins for the stdio proxy.
tool_pinsmaps a stable upstream ID, passed with--upstream-id, and an exact tool name to a reviewedjcs-rfc8785-v1SHA-256 digest. A pinned tool whose definition is missing, unhashable or changed is hidden fromtools/listand blocked ontools/call, even without strict mode.strict_tool_pins: truealso blocks allowed tools that have no pin. Signed rows record the expectedtool_pin_hashnext to the observed hash, withmatched,mismatch,unobserved,unhashableorunpinnedstatus. Re-listing and call approval never change a pin; only a policy edit does. Afternotifications/tools/list_changed, re-list from the first page or reconnect. Pinned tools behind a paginated listing stay blocked. With pins, atools/listrequest that cannot be matched to a live listing (such as an unknown continuation cursor) or whose upstream page is invalid or cannot join the catalog build is refused with JSON-RPC-32603and recorded asmethod-denywith reasontool_catalog_refresh_required. - Signed run lifecycle rows for opt-in paired stdio audit. Each run has a fresh random run nonce and writes a durable
openrow before reading input, aheartbeatevery 30 seconds and, after a bounded shutdown drain, acloserow that commits to the run's preceding row count and signature. A write or sync failure prevents a clean close. verify --completenessadds a per-run assessment to a single trail's integrity verdict.LIMITEDis the best result: the signed, checkpointed observation stream recorded for that run is internally complete. It does not prove that every attempted call was recorded or that a remote side effect completed. A recent open run isIN_PROGRESS; an open run with no heartbeat for 90 seconds, or an action with no terminal observation, isINCOMPLETE; future-dated heartbeats and mixed legacy and v2 trails areUNASSESSABLE; legacy-only trails areUNVERIFIED; invalid run structure isBROKEN. Completeness is reported only when integrity verification passes (PROTECTED); with any other integrity result (TAMPERED,NO_ROWS,UNANCHORED) or a key or read error, there is no completeness output. Completeness is separate from integrity and does not change the exit code.
Changed
- The proxy waits at most one second for the audit writer to close on exit. If a write is still in progress, it says so and points to
verify --completeness.
Fixed
- Route Telegram approval callbacks to the matching held request when calls wait concurrently.
mcp-listenaccepts the bracketed IPv6 loopback Host[::1]without a port.- Reject MCP method and tool names exceeding 256 bytes before forwarding, with bounded audit diagnostics that preserve trail verification.
- A required audit write rejected for a malformed
approval_actorno longer latches the writer or creates the.audit-failingmarker; nothing is written, and later calls proceed. Real storage failures still latch.