Repository navigation
Stop tagging when the clock cannot place packets in their epochs - #376
Merged
Merged
Conversation
Epochs advance on the executor's monotonic clock from the chain origin it announces, the wall-clock reading at chain start, and a verifier maps a capture's wall time to an epoch from that origin. Two clock conditions make that mapping wrong and were not detected: a chain started while the host clock was not ready announces an origin that may be wrong, and a wall-clock step or a suspend afterwards moves wall time away from the monotonic time the epochs follow. The executor kept tagging and reported attribution as available although honest packets mapped to the wrong epoch. The key schedule now reads the host clock readiness at chain start and measures the drift between wall and monotonic time elapsed since the origin. While the origin is untrusted (clock_unready, for the chain's life) or the drift exceeds min(epoch length / 2, 1 s) (clock_drift), no key signs: CurrentKey, the one decision point of both the pure-Go and the kernel signing path, returns nothing, so the pure-Go tagger stops at once and a kernel tagger's slot is removed at its next refresh. The schedule also never signs with the key of an epoch below one it has already signed with. Attribution carries the two reasons, the dispatcher accepts them, a node that tags packets refuses new runs with FailedPrecondition while either holds, and a node whose tagging mode is none admits them. The bound is a safety threshold, not a measured uncertainty bound; recovery is a restart with a ready clock, which starts a new chain. GitHub issue #72.
ctfbruce
marked this pull request as ready for review
October 5, 2026 08:49
This was referenced Oct 5, 2026
ctfbruce
marked this pull request as draft
October 5, 2026 11:03
added 2 commits
October 5, 2026 13:13
The pure-Go tagger read the schedule's current key once to decide whether to tag a packet and again, through the per-packet tag derivation, to derive the tag. With the schedule refusing any epoch below the highest one it has already signed with, another signing path reaching the next epoch between the two reads turned a valid packet into a datagram write error on a healthy clock. The tag is now derived from the key the first read returned; the epoch fence is unchanged. The two clock reasons an executor may report now have their own attribution states in the dispatcher's health metrics and the executors_attribution_state labels, so every registered executor is counted in exactly one state again, and the nodes table of the command names them. The protocol comment, the documentation of a transient drift that returns within the bound and of the kernel removal timing are corrected to match the behaviour. GitHub issue #72.
Take the disclosure-lag gauges, the counter-attachment and resource metrics and the admin account recovery from main. The attribution state series keeps the clock_unready and clock_drift labels next to the states main lists, in the metrics table, the exporter's label list and the two tests that cover them; the other metrics tests from main stay as they are. GitHub issue #72.
ctfbruce
marked this pull request as ready for review
October 5, 2026 12:47
This was referenced Oct 6, 2026
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The executor previously kept tagging after an unready chain origin, a wall-clock step or a suspend could make its epochs disagree with capture wall time. The schedule now refuses signing while the origin is untrusted or wall/monotonic drift exceeds min(epoch length / 2, 1 s), and never returns an epoch below one it already signed with. Both signing paths use that decision; the pure-Go tagger derives each packet tag from the exact key returned by it.
clock_unreadyandclock_driftappear in executor admission, capability reports, the CLI and dispatcher health metrics. A transient drift that returns within the bound recovers automatically; an unready origin or persistent drift needs a restart with a ready clock. Existing runs can continue untagged. Kernel removal happens at the next scheduled refresh, and a failed removal holds disclosure back and retries. This threshold is a safety policy, not a measured receiver-clock uncertainty bound.Validation: focused Aspire race tests cover injected clock steps/suspend, epoch fencing, admission, capability/CLI output and metric accounting; pinned protocol generation reproduces the committed bindings. Latest-main address, status and tag behavior is preserved. Complete candidate CI is required before merge.
Related to #72. The broader receiver/capture-time trust contract remains open.