Bare wanctl prints help; wanctl start enrolls the device - #69
Merged
Conversation
Running `wanctl` with no arguments logged in and then registered the machine as a controlled device. Someone on a controller-only box who typed it to see what the command does got that box enrolled -- which is not what anyone means by running a program to read its help. Bare `wanctl` now prints the usage text plus one line of local status and returns. It reads no relay setting, asks no first-run question, and writes nothing, so a fresh config directory is left exactly as found. The liveness probe behind that status line no longer creates agent.lock either: a directory with no lock file has never run an agent, which answers the question without touching the disk. `wanctl start` absorbs the login step, so onboarding a device stays one command. `wanctl login` is unchanged and still never starts an agent. `wanctl up` is removed rather than aliased: nothing outside the bare path used it -- not the installers, not the Android app, not the Windows supervisor or the service units. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SpPAyRQzTAw7MbmJqZdm7e
Daily-AC
added a commit
that referenced
this pull request
Sep 9, 2026
`wanctl update` stopped the agent, swapped the binary and started a new one while the old process was still shutting down. Terminating is asynchronous and cmdStop returned as soon as the signal was delivered, so the new agent hit a config-dir lock the dying one still held, exited, and left the machine with nothing running -- after the parent had already printed "✓ 服务已转后台". A device dropped off the relay for fifty minutes that way. cmdStop now waits for the lock to come free, up to ten seconds, and fails loudly rather than letting the caller replace the binary and start a second agent behind a first that will not leave. The agent gained the other half: it retries a held lock for up to five seconds instead of exiting at once, because the process that started it has already reported success. Its message no longer blames itself either. `wanctl start` records the child's pid before the child locks anything, so an agent that lost the race read its own number out of agent.pid and reported "another agent is already running (pid <itself>)", sending the reader after the process that was printing the message. The auto-update paths were checked and need no change: on Unix the lock is closed before syscall.Exec and re-acquired by the new image in the same process, on Windows it is released before the successor is spawned, and the Scheduled Task supervisor waits for the child to exit before running the next one. Also the status hint #69 missed: "未运行" now says `wanctl start`. Claude-Session: https://claude.ai/code/session_01SpPAyRQzTAw7MbmJqZdm7e Co-authored-by: 张以琳 <zhangyilin@thunder.com.cn> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
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.
Summary
Bare
wanctlstops being an action. It prints the help plus one line of localstatus and returns;
wanctl startdoes the enrolling.main.go— the no-argument path printsusageandlocalStatusLine(). It nolonger runs the first-run relay gate, so it asks nothing and writes nothing.
The
upcase,cmdUpand the now-unusedrunRelayCommandare gone, andupis out of
relayCommands.daemon.go—cmdStart(ctx)logs in when there is no token, via theenrollForStartvariable so a test can drive the rest of start without aportal.
localStatusLine()is the new status line.internal/config/lock.go—AgentRunningopensagent.lockwithoutO_CREATE. Asking whether an agent runs must not create the file that recordsthat one ever did; no lock file also answers the question.
internal/config/store.go's reference to the token "saved bywanctl up".Root cause
main.godispatchedlen(os.Args) < 2tocmdUp, which enrolled if there wasno token and then called
cmdStart. Reading the help and onboarding a devicewere the same keystroke.
cmdStartreinforced it by refusing without a tokenand telling the user to "先运行
wanctl(无参)完成登录授权", so the documentedway to enroll was the same command someone would type to look around.
wanctl loginwas already correct — it takes a credential and starts nothing —so only the device half moved.
upis removed rather than kept as an alias. I checked every caller the briefnamed:
grep -rn cmdUpfinds only the bare path; the Android app runsWanctl.run(…, "login", "--code", …)andAgentServiceexecsagent; neitherscripts/install.sh.innorscripts/install.ps1.ininvokes wanctl at all afterinstalling;
cmdSuperviserunsagent --managed;service.gowrites units thatrun
agent.Doc files touched
For the portal re-sync after deploy:
README.mdanddocs/android*.mdalready saidwanctl startwhere it mattersand needed no change; their remaining bare-
wanctlmentions are about PATHresolution, not enrollment.
firstrun.gonever told anyone to run barewanctl.Test plan
The bare-invocation test builds the binary and runs it against a temp config
directory with no relay configured, then asserts exit 0, the help and the status
line on stdout, and that none of
token,agent.pid,agent.lock,cert.pem,key.pemordevice_idwas created. Running with no relay is deliberate: it iswhat proves the first-run question is not reached.
TestStartEnrollsWhenThereIsNoTokenstubs the login to fail and asserts startattempted it exactly once, returned that failure, and recorded no agent.
TestStartWithAnAgentAlreadyRunningDoesNothingholds the lock and asserts thelogin is not re-run.
TestLoginNeverRecordsAnAgentasserts login leaves noagent.pidand noagent.lock.Full gate clean:
gofmt -l .,go vet ./...,go test ./...,go test -tags lark ./...,go vet -tags lark ./..., Windows and Androidbuilds,
GOOS=linux go vet ./....Migration
Devices:
wanctl start. Controllers:wanctl login. Agents already running areunaffected.
internal/portal/changelog/v0.8.1.mdcarries the same note for theportal's release sheet.
🤖 Generated with Claude Code
https://claude.ai/code/session_01SpPAyRQzTAw7MbmJqZdm7e