Skip to content

Stop tagging when the clock cannot place packets in their epochs - #376

Merged
ctfbruce merged 6 commits into
mainfrom
b72/clock-attribution
Oct 7, 2026
Merged

ctfbruce merged 6 commits into
mainfrom
b72/clock-attribution

Conversation

@ctfbruce

@ctfbruce ctfbruce commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

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_unready and clock_drift appear 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.

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
ctfbruce marked this pull request as ready for review October 5, 2026 08:49
@ctfbruce ctfbruce added the Ready for review Reviewed, green candidate; maintainer review and dependency checks still required. label Oct 5, 2026
@ctfbruce
ctfbruce marked this pull request as draft October 5, 2026 11:03
@ctfbruce ctfbruce removed the Ready for review Reviewed, green candidate; maintainer review and dependency checks still required. label Oct 5, 2026
TheodorAdrienIsaak Mattli 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
ctfbruce marked this pull request as ready for review October 5, 2026 12:47
@ctfbruce ctfbruce added the Ready for review Reviewed, green candidate; maintainer review and dependency checks still required. label Oct 5, 2026
@ctfbruce
ctfbruce merged commit f345d9f into main Oct 7, 2026
16 checks passed
@vincent10400094
vincent10400094 deleted the b72/clock-attribution branch October 8, 2026 00:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ready for review Reviewed, green candidate; maintainer review and dependency checks still required.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant