Repository navigation
feat(gateway)!: clock writes take the control token, a watch its computer's read token, and a waiting write names a computer that stopped watching (FD-5472) - #634
Closed
earakely-scale wants to merge 2 commits into
Conversation
…ter's read token, and a waiting write names a computer that stopped watching (FD-5472) - /clock/set-time, /clock/set-rate, /clock/advance and /clock/clear are control routes: anyone who could reach a gateway could move its clock, an agent with a shell included. Core already sends the token (DeployedEnv.invoke); a gateway without one, as a Harbor export's, stays open. - /clock/watch takes the control token or a declared computer's read token, and a computer's token names that computer (401 without a token, 403 for an unknown one or another name). /clock/state and /clock/time stay open. - A write given wait_followers_s doesn't wait for a computer whose last watch is older than a hold and a reconnect (ClockFollowers.QUIET_S): it names it at once, its 504 carrying when each computer last watched. One that hasn't watched yet is still waited for. - multi_env_computers_test checks the gateway's control token is in no file, environment or command line on a computer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… quiet; name the token scan's size limit (FD-5472) stale_after only woke on an acknowledgement, so a computer that went quiet during the wait (its last watch 29 s before a 30 s wait) still held the write for all of it. It now also wakes at the next quiet deadline of a computer it waits for. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
|
Closing: we're deferring the control-token half of FD-5472 (clock writes behind the control token, watches bound to each computer's read token), which Hark's CUA work doesn't need yet. Revisiting it should also cover moving tokens out of environment variables. The fail-fast part, which doesn't touch tokens, moved to #636. The branch stays for when FD-5472 comes back. |
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.
Part of FD-5420: a computer's own clock follows the gateway's universe clock. This is FD-5472. Each watch on
/clock/watchis a computer's acknowledgement of the version it shows, so a watch must come from that computer. And nothing but core may move the clock.What changes
/clock/set-time,/clock/set-rate,/clock/advanceand/clock/clearare now amongCONTROL_ROUTES.DeployedEnv.invoke, which already sends the env's control headers./clock/watchtakes the control token or a declared computer's read token (CLOCK_FOLLOWER_TOKENS). A computer's token must name that computer asfollower./clock/stateand/clock/timestay open. They show only the universe's time, its rate and which version each computer shows. Two outside readers depend on them: the agentic-DQ skill uses/clock/stateas its liveness probe, and psi_bench's event feed finds gateways with it.ClockFollowers.QUIET_S) has lost its connection.wait_followers_snow names such a computer at once instead of waiting out its full timeout. One that goes quiet during the wait ends it at its quiet deadline.followers: each computer's last shown version and when it last watched.The design's accepted gap stands: the session user can read the computer's control token today, so it could open watches as its own computer. Minting a separate read token later closes it.
Outside callers
--gateway-token/set_gateway(url, token=...), and a gateway it finds on the host gives its own, from its container's environment./clock/set-timewith no token. Their gateways run without one, so nothing changes for them.Testing
Unit:
tst/unit/env/gateway/control_token_test.pycovers the clock's writes among the control routes:clock_watch_test.pycovers:CI's unit jobs pass, and all 32 checks are green on 1614208.
The write token stays off computers.
multi_env_computers_test.pynow scans the deployed computer's container for the gateway's control token, as root and as the session user. It checks every file under/etc,/opt,/home,/root,/tmp,/run,/var,/srvand/usr/local, and every process's environment and command line. The token is in none of them.Integration, on an EC2 host (x86_64), with local sandboxes:
gateway_test.py's clock tests pass: the clock extension, its rate accuracy, the time triggers, the autonomous trigger andsync_env_clock.test_register_env_triggers_local.py,test_next_env_event_hook_local.pyandtest_advance_to_next_event_hook_local.py, 8 tests.multi_env_computers_test.py's gateway test passes, the token scan included.E2E against the branch's gateway image, with two real follower processes long-polling under their read tokens:
set-time,set-rate,advance,clearwithout a token;set-timewith a computer's read tokenset-timewith the control token/clock/state,/clock/timewithout a tokenSIGSTOP) for 36 s, then an advance waiting up to 10 sDocs
The clock extension's page should cover:
🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge; no new blocking issues were found.Summary
The PR protects clock writes with the gateway's control token and ties each watch to its computer's read token.
Diagram
%%{init: {'theme': 'neutral'}}%% flowchart TD W[Clock write with control token] --> C[Change clock and answer held watches] C --> Q{Wait requested?} Q -->|No| OK[Return success] Q -->|Yes| A[Check each computer's shown version] A -->|All caught up| OK A -->|Still behind| T[Wait for a watch, quiet deadline, or write deadline] T --> A A -->|No further wait needed| E[Return 504 naming computers still behind]Reviews (7) · Last reviewed commit: "fix(gateway): a waiting write wakes when..." · Reviewed by Greptile