feat(cli): enroll with an auth key, through the daemon (#175) - #290
Merged
Merged
Conversation
|
📘 Docs preview for this PR: https://waired-docs--pr-290-du3sdaff.web.app Rebuilt on each push; the preview channel auto-expires in 7 days. |
waired#976 gave the control plane auth keys — the credential a headless host presents instead of a person completing a browser sign-in. This is the client half. The key is a credential for the DAEMON's enrollment, never a selector for the local one. chooseEnrollRoute gains an authKey fact that outranks every local selector: a run carrying a key reaches the daemon or fails saying why. Falling back would drop the credential on the floor and register a device whose capabilities the control plane never learns — the exact failure #175 exists to remove. Six new route-table cases pin that, including "--auth-key overrides --bypass-mode". Value resolution is a pure function (resolveAuthKey): the key itself, "file:/path/to/key" like Tailscale's --authkey, or $WAIRED_AUTH_KEY when the flag is omitted, with surrounding whitespace always trimmed — a key written with `echo ... > key` carries a newline, and one that fails only because of an invisible character is miserable to debug. The file leg takes an injected reader that records the path it was asked for, plus one test through the real os.ReadFile so the production wiring is exercised. On the wire, RunInit sends auth_key only when there is one (an older control plane decodes with DisallowUnknownFields and would 400 every interactive enrollment on an empty field) and, when the create response carries a registration_ticket, skips OnLoginURL and the entire poll loop: the session was authorized inside that call. Offering a login URL nobody can open is worse than useless on a headless host. The second half of enrollment is now enrollWithTicket, shared by both ways of obtaining the ticket. A refused key fails immediately rather than falling through to a browser flow that would hang until the poll timeout. LoginStartRequest.AuthKey carries it to the daemon. That type lives in internal/management, not proto/, so there is no tag and no CP-side bump; and /login/start is a mutating verb, which the management server's write guard already confines to the local IPC socket — the key never crosses the TCP listener. Removed: --google-sa-login, --impersonate-sa, --oidc-id-token, --oidc-audience and cmd/waired/oidc_grant.go. Those four shipped in the production CLI (only --bypass-mode was buildflag-gated) and drove a second, capability-less enrollment implementation. installtest's `oidc` mode becomes `authkey` on all three OSes: the host still mints the SA id_token with gcloud, but now exchanges it once for a reusable auth key at the CP's dev issuer, and the guest enrols with the daemon UP. The leg therefore exercises the journey a real headless install takes, instead of stopping the daemon to force a path production never uses. The Hyper-V edge verify moves the same way. Two legs deliberately keep the old shape: * --daemon-engine (macOS/Windows) still completes an ordinary login out-of-band with the raw token. Its whole point is watching the resident executor install the engine while init is mid-flight, and an auth key authorizes the session in the create call — collapsing the window there is nothing left to observe. * --bypass-mode stays. The testnet container uses it, and flipping that needs waired#982's terraform plumbing deployed first. It goes in the follow-up. Also adds a gitleaks rule for waired_ak_ + 64 hex: the default ruleset has no pattern for it, and this repo is public. Verified: go build/vet/test, -tags prod build+vet, make verify-cross, golangci-lint (0 issues), docs-site i18n 32 pairs in sync, decision-log guard. Not verified locally: the 3-OS installtest legs themselves, which need the dev control plane — CI runs them on this PR. Refs #175 Signed-off-by: gen16k <gen16k@gmail.com>
gen16k
force-pushed
the
feat/175-auth-key
branch
from
July 27, 2026 19:20
9f014c7 to
660d735
Compare
gen16k
added a commit
that referenced
this pull request
Jul 27, 2026
`waired init` on a host that already has an identity was the last input that selected the standalone enrollment implementation. The daemon could not renew credentials for a device it had already enrolled -- Start returned its idempotent no-op -- so re-auth had nowhere else to go. The control plane needed nothing: EnrollDevice already matches an existing Device on the machine key and renews the row (renewDeviceTx, "#115 Phase C"), and the renewal path is exempt from the device cap. So this is agent wiring only, no proto change and no CP deploy. LoginStartRequest gains `reauth`. The daemon honours it by skipping the already-active short-circuit, and by rebuilding the live session afterwards rather than publishing a second one: the running session is using the credentials that were just replaced, and activate refuses to publish over a current session anyway. That rebuild is the same teardown-then-activate the node-key rotator has always done, so it is now one function sharing one mutex with the rotator instead of two copies that could race. chooseEnrollRoute therefore loses routeLocal and, with it, every fact that used to steer: no input picks an implementation any more, because there is only one. What remains is "is the agent there", and the table test is now the full input space rather than a sample of it. The code behind the removed route is unreachable from this commit but still present; #175's next PR deletes it together with the `waired init` flags that exist only to steer it. Splitting it that way keeps this change reviewable as a behaviour change. Two defects fixed on the way: * `--auth-key` on an already-enrolled host used to print a successful sign-in for a run that renewed nothing (routeDaemon -> the daemon's no-op). It now re-authenticates. * waitForBenchmark read `disabled` and `stopped` as "engine is up, a download must be in flight" and waited out the full ten-minute deadline before giving up -- on a host with no model and no intention of getting one. waitForBundledModel has always treated those two as terminal (init_pull.go:97); now both do. Measured cost: ten minutes per installtest leg, three legs, every PR. Found because #290's harness migration made the daemon path the one CI takes. An agent predating `reauth` ignores the field and answers with the no-op status. The CLI names that skew ("the background service is too old to renew that sign-in") instead of reporting the protocol symptom or, worse, success. Refs #175 Signed-off-by: gen16k <gen16k@gmail.com>
gen16k
added a commit
that referenced
this pull request
Jul 27, 2026
Nothing reaches it any more. PR-1 (#271) removed the implicit fallback, #290/#291 removed --google-sa-login and --bypass-mode, and the daemon re-auth that landed just before this removed the last selector. What is left is ~3,300 lines that cannot run. Deleted: * cmd/waired/main.go's runInitBody tail -- everything after the route switch (~410 lines), plus chooseListenAddr, printInitSuccessBox, claudeRouteEligible and startAgentServiceBestEffort. * internal/setup/init.go (Init/InitOptions/InitResult) and deploy.go's Deploy/Puller/PullEvent. What survives deploy.go is the Ollama detection both sides still use, so the file is renamed ollama_detect.go rather than left named after what it no longer does. * The CLI-local halves of the journey: offerBenchmark, ensureBundledEngine, promptInference, promptOllamaSource, applyBundledModelSelection, initStepLabels, cliPullProgressSink, flagBoolPtr, and the GNOME AppIndicator installer. * Seven `waired init` flags that existed only to steer it: --listen, --endpoint, --skip-deploy, --start-agent, --no-wait-model, --ollama-source, --reset-config. Removed outright, so a script still passing one fails at the flag rather than being quietly ignored. Both docs-site languages updated; no installer passes any of them (checked). Kept, because they are shared and easy to delete by mistake: setup.Enroll (the daemon's enrollFunc), setup.Integration (`waired link`, `waired doctor`), confirmRenew, handStateToServiceUser (also called by `runtimes install`), benchmarkWithScanner and waitForBundledModel. --skip-claude-route is NOT removed even though nothing reads it now: install.ps1:1841 passes it, so deleting the flag would fail every Windows install that used -SkipClaudeProxy. Two gaps this makes permanent, neither caused by it -- both have been unreachable for as long as routeDaemon has been the path every install takes, and both are filed rather than buried: * #294 -- the daemon-driven init never writes Claude Code managed settings, and the installers dropped their own `waired claude enable` step on the understanding that init does it. So a CLI install finishes unrouted, and --skip-claude-route opts out of something that was not going to happen. The routing sentinel does not see this: it exercises the gateway directly, not Claude Code's settings. * #295 -- nothing installs the GNOME AppIndicator host extension any more; the waired-tray package's Suggests is what is left. internal/setup's package doc described three phases run in-process by `waired init`; it now describes what the package actually is, which is the enrollment/integration/detection pieces the daemon and the CLI share. Fixes #175 Signed-off-by: gen16k <gen16k@gmail.com>
gen16k
added a commit
that referenced
this pull request
Jul 27, 2026
`waired init` on a host that already has an identity was the last input that selected the standalone enrollment implementation. The daemon could not renew credentials for a device it had already enrolled -- Start returned its idempotent no-op -- so re-auth had nowhere else to go. The control plane needed nothing: EnrollDevice already matches an existing Device on the machine key and renews the row (renewDeviceTx, "#115 Phase C"), and the renewal path is exempt from the device cap. So this is agent wiring only, no proto change and no CP deploy. LoginStartRequest gains `reauth`. The daemon honours it by skipping the already-active short-circuit, and by rebuilding the live session afterwards rather than publishing a second one: the running session is using the credentials that were just replaced, and activate refuses to publish over a current session anyway. That rebuild is the same teardown-then-activate the node-key rotator has always done, so it is now one function sharing one mutex with the rotator instead of two copies that could race. chooseEnrollRoute therefore loses routeLocal and, with it, every fact that used to steer: no input picks an implementation any more, because there is only one. What remains is "is the agent there", and the table test is now the full input space rather than a sample of it. The code behind the removed route is unreachable from this commit but still present; #175's next PR deletes it together with the `waired init` flags that exist only to steer it. Splitting it that way keeps this change reviewable as a behaviour change. Two defects fixed on the way: * `--auth-key` on an already-enrolled host used to print a successful sign-in for a run that renewed nothing (routeDaemon -> the daemon's no-op). It now re-authenticates. * waitForBenchmark read `disabled` and `stopped` as "engine is up, a download must be in flight" and waited out the full ten-minute deadline before giving up -- on a host with no model and no intention of getting one. waitForBundledModel has always treated those two as terminal (init_pull.go:97); now both do. Measured cost: ten minutes per installtest leg, three legs, every PR. Found because #290's harness migration made the daemon path the one CI takes. An agent predating `reauth` ignores the field and answers with the no-op status. The CLI names that skew ("the background service is too old to renew that sign-in") instead of reporting the protocol symptom or, worse, success. Refs #175 Signed-off-by: gen16k <gen16k@gmail.com>
gen16k
added a commit
that referenced
this pull request
Jul 27, 2026
Nothing reaches it any more. PR-1 (#271) removed the implicit fallback, #290/#291 removed --google-sa-login and --bypass-mode, and the daemon re-auth that landed just before this removed the last selector. What is left is ~3,300 lines that cannot run. Deleted: * cmd/waired/main.go's runInitBody tail -- everything after the route switch (~410 lines), plus chooseListenAddr, printInitSuccessBox, claudeRouteEligible and startAgentServiceBestEffort. * internal/setup/init.go (Init/InitOptions/InitResult) and deploy.go's Deploy/Puller/PullEvent. What survives deploy.go is the Ollama detection both sides still use, so the file is renamed ollama_detect.go rather than left named after what it no longer does. * The CLI-local halves of the journey: offerBenchmark, ensureBundledEngine, promptInference, promptOllamaSource, applyBundledModelSelection, initStepLabels, cliPullProgressSink, flagBoolPtr, and the GNOME AppIndicator installer. * Seven `waired init` flags that existed only to steer it: --listen, --endpoint, --skip-deploy, --start-agent, --no-wait-model, --ollama-source, --reset-config. Removed outright, so a script still passing one fails at the flag rather than being quietly ignored. Both docs-site languages updated; no installer passes any of them (checked). Kept, because they are shared and easy to delete by mistake: setup.Enroll (the daemon's enrollFunc), setup.Integration (`waired link`, `waired doctor`), confirmRenew, handStateToServiceUser (also called by `runtimes install`), benchmarkWithScanner and waitForBundledModel. --skip-claude-route is NOT removed even though nothing reads it now: install.ps1:1841 passes it, so deleting the flag would fail every Windows install that used -SkipClaudeProxy. Two gaps this makes permanent, neither caused by it -- both have been unreachable for as long as routeDaemon has been the path every install takes, and both are filed rather than buried: * #294 -- the daemon-driven init never writes Claude Code managed settings, and the installers dropped their own `waired claude enable` step on the understanding that init does it. So a CLI install finishes unrouted, and --skip-claude-route opts out of something that was not going to happen. The routing sentinel does not see this: it exercises the gateway directly, not Claude Code's settings. * #295 -- nothing installs the GNOME AppIndicator host extension any more; the waired-tray package's Suggests is what is left. internal/setup's package doc described three phases run in-process by `waired init`; it now describes what the package actually is, which is the enrollment/integration/detection pieces the daemon and the CLI share. Fixes #175 Signed-off-by: gen16k <gen16k@gmail.com>
gen16k
added a commit
that referenced
this pull request
Jul 28, 2026
…#297) * refactor(cli): delete the standalone enrollment implementation (#175) Nothing reaches it any more. PR-1 (#271) removed the implicit fallback, #290/#291 removed --google-sa-login and --bypass-mode, and the daemon re-auth that landed just before this removed the last selector. What is left is ~3,300 lines that cannot run. Deleted: * cmd/waired/main.go's runInitBody tail -- everything after the route switch (~410 lines), plus chooseListenAddr, printInitSuccessBox, claudeRouteEligible and startAgentServiceBestEffort. * internal/setup/init.go (Init/InitOptions/InitResult) and deploy.go's Deploy/Puller/PullEvent. What survives deploy.go is the Ollama detection both sides still use, so the file is renamed ollama_detect.go rather than left named after what it no longer does. * The CLI-local halves of the journey: offerBenchmark, ensureBundledEngine, promptInference, promptOllamaSource, applyBundledModelSelection, initStepLabels, cliPullProgressSink, flagBoolPtr, and the GNOME AppIndicator installer. * Seven `waired init` flags that existed only to steer it: --listen, --endpoint, --skip-deploy, --start-agent, --no-wait-model, --ollama-source, --reset-config. Removed outright, so a script still passing one fails at the flag rather than being quietly ignored. Both docs-site languages updated; no installer passes any of them (checked). Kept, because they are shared and easy to delete by mistake: setup.Enroll (the daemon's enrollFunc), setup.Integration (`waired link`, `waired doctor`), confirmRenew, handStateToServiceUser (also called by `runtimes install`), benchmarkWithScanner and waitForBundledModel. --skip-claude-route is NOT removed even though nothing reads it now: install.ps1:1841 passes it, so deleting the flag would fail every Windows install that used -SkipClaudeProxy. Two gaps this makes permanent, neither caused by it -- both have been unreachable for as long as routeDaemon has been the path every install takes, and both are filed rather than buried: * #294 -- the daemon-driven init never writes Claude Code managed settings, and the installers dropped their own `waired claude enable` step on the understanding that init does it. So a CLI install finishes unrouted, and --skip-claude-route opts out of something that was not going to happen. The routing sentinel does not see this: it exercises the gateway directly, not Claude Code's settings. * #295 -- nothing installs the GNOME AppIndicator host extension any more; the waired-tray package's Suggests is what is left. internal/setup's package doc described three phases run in-process by `waired init`; it now describes what the package actually is, which is the enrollment/integration/detection pieces the daemon and the CLI share. Fixes #175 Signed-off-by: gen16k <gen16k@gmail.com> * refactor(cli): drop two comments that outlived their code helpers.go is deleted, and runInitBody is no longer the 460-line body its doc comment described. Refs #175 Signed-off-by: gen16k <gen16k@gmail.com> --------- Signed-off-by: gen16k <gen16k@gmail.com>
gen16k
added a commit
that referenced
this pull request
Jul 28, 2026
`waired init` on a host that already has an identity was the last input that selected the standalone enrollment implementation. The daemon could not renew credentials for a device it had already enrolled -- Start returned its idempotent no-op -- so re-auth had nowhere else to go. The control plane needed nothing: EnrollDevice already matches an existing Device on the machine key and renews the row (renewDeviceTx, "#115 Phase C"), and the renewal path is exempt from the device cap. So this is agent wiring only, no proto change and no CP deploy. LoginStartRequest gains `reauth`. The daemon honours it by skipping the already-active short-circuit, and by rebuilding the live session afterwards rather than publishing a second one: the running session is using the credentials that were just replaced, and activate refuses to publish over a current session anyway. That rebuild is the same teardown-then-activate the node-key rotator has always done, so it is now one function sharing one mutex with the rotator instead of two copies that could race. chooseEnrollRoute therefore loses routeLocal and, with it, every fact that used to steer: no input picks an implementation any more, because there is only one. What remains is "is the agent there", and the table test is now the full input space rather than a sample of it. The code behind the removed route is unreachable from this commit but still present; #175's next PR deletes it together with the `waired init` flags that exist only to steer it. Splitting it that way keeps this change reviewable as a behaviour change. Two defects fixed on the way: * `--auth-key` on an already-enrolled host used to print a successful sign-in for a run that renewed nothing (routeDaemon -> the daemon's no-op). It now re-authenticates. * waitForBenchmark read `disabled` and `stopped` as "engine is up, a download must be in flight" and waited out the full ten-minute deadline before giving up -- on a host with no model and no intention of getting one. waitForBundledModel has always treated those two as terminal (init_pull.go:97); now both do. Measured cost: ten minutes per installtest leg, three legs, every PR. Found because #290's harness migration made the daemon path the one CI takes. An agent predating `reauth` ignores the field and answers with the no-op status. The CLI names that skew ("the background service is too old to renew that sign-in") instead of reporting the protocol symptom or, worse, success. Refs #175 Signed-off-by: gen16k <gen16k@gmail.com>
gen16k
added a commit
that referenced
this pull request
Jul 28, 2026
…#296) `waired init` on a host that already has an identity was the last input that selected the standalone enrollment implementation. The daemon could not renew credentials for a device it had already enrolled -- Start returned its idempotent no-op -- so re-auth had nowhere else to go. The control plane needed nothing: EnrollDevice already matches an existing Device on the machine key and renews the row (renewDeviceTx, "#115 Phase C"), and the renewal path is exempt from the device cap. So this is agent wiring only, no proto change and no CP deploy. LoginStartRequest gains `reauth`. The daemon honours it by skipping the already-active short-circuit, and by rebuilding the live session afterwards rather than publishing a second one: the running session is using the credentials that were just replaced, and activate refuses to publish over a current session anyway. That rebuild is the same teardown-then-activate the node-key rotator has always done, so it is now one function sharing one mutex with the rotator instead of two copies that could race. chooseEnrollRoute therefore loses routeLocal and, with it, every fact that used to steer: no input picks an implementation any more, because there is only one. What remains is "is the agent there", and the table test is now the full input space rather than a sample of it. The code behind the removed route is unreachable from this commit but still present; #175's next PR deletes it together with the `waired init` flags that exist only to steer it. Splitting it that way keeps this change reviewable as a behaviour change. Two defects fixed on the way: * `--auth-key` on an already-enrolled host used to print a successful sign-in for a run that renewed nothing (routeDaemon -> the daemon's no-op). It now re-authenticates. * waitForBenchmark read `disabled` and `stopped` as "engine is up, a download must be in flight" and waited out the full ten-minute deadline before giving up -- on a host with no model and no intention of getting one. waitForBundledModel has always treated those two as terminal (init_pull.go:97); now both do. Measured cost: ten minutes per installtest leg, three legs, every PR. Found because #290's harness migration made the daemon path the one CI takes. An agent predating `reauth` ignores the field and answers with the no-op status. The CLI names that skew ("the background service is too old to renew that sign-in") instead of reporting the protocol symptom or, worse, success. Refs #175 Signed-off-by: gen16k <gen16k@gmail.com>
This was referenced Jul 28, 2026
gen16k
added a commit
that referenced
this pull request
Aug 2, 2026
The leg has not run an assert since 2026-07-28. It dies 17 seconds in:
[installtest] --daemon-engine supports IT_ENROLL_MODE=oidc only (got 'authkey')
#290 replaced installtest's `oidc` enrol mode with `authkey` across the
workflow and all three OS drivers, and said so in its own body -- "Two legs
deliberately keep the old shape: --daemon-engine (macOS/Windows) still
completes an ordinary login out-of-band with the raw token". Linux is
missing from that sentence and from the change: the diff to this file
touched three comments and left the guard demanding a mode that no longer
exists (installtest-enroll.sh accepts authkey|interactive, and
--google-sa-login was deleted in the same PR).
Reverting the workflow is not an option, and nothing else has to move.
it_enroll_daemon_path never depended on the ambient enrol mode; it depends
on IT_IMPERSONATE_SA, gcloud, and the CP's /v1/login/oidc-grant, which is
exactly what authkey mode already requires. The leg keeps the RAW SA token
and completes the login out-of-band rather than spending the auth key --
the same shape installtest-macos.sh and installtest-windows.ps1 have used
since #290.
This matters more after #138: no installer pre-installs the engine on any
OS now, so `waired init` is the only thing that puts one on a host. Of its
two engine-install paths, --inference covers ensureDaemonPathEngine and
this leg is the only e2e that reaches runSetupEngineInstall -- the
wizard-driven half, which on Linux therefore had no live coverage at all.
Also refreshes the comments #290 left behind: the header still claimed the
engine is "installed by install.sh (--inference)" (false since #138) and
still explained --auth-key as a local-enroll selector, which #290 itself
made wrong. Plus two labels that drifted the same way -- run.sh's
"(oidc|bypass|interactive)" mode list and a Windows failure string reading
"waired init (oidc)" on the auth-key branch.
Verified: shellcheck (daemon-engine lib clean; the SC2015/SC1091 info
findings in enroll.sh/run.sh are pre-existing at untouched lines), bash -n,
pwsh parse. The leg itself needs the dev control plane -- dispatched
installtest-inference.yml (os=linux) on this branch.
Fixes #388
Signed-off-by: gen16k <gen16k@gmail.com>
gen16k
added a commit
that referenced
this pull request
Aug 2, 2026
The leg has not run an assert since 2026-07-28. It dies 17 seconds in:
[installtest] --daemon-engine supports IT_ENROLL_MODE=oidc only (got 'authkey')
#290 replaced installtest's `oidc` enrol mode with `authkey` across the
workflow and all three OS drivers, and said so in its own body -- "Two legs
deliberately keep the old shape: --daemon-engine (macOS/Windows) still
completes an ordinary login out-of-band with the raw token". Linux is
missing from that sentence and from the change: the diff to this file
touched three comments and left the guard demanding a mode that no longer
exists (installtest-enroll.sh accepts authkey|interactive, and
--google-sa-login was deleted in the same PR).
Reverting the workflow is not an option, and nothing else has to move.
it_enroll_daemon_path never depended on the ambient enrol mode; it depends
on IT_IMPERSONATE_SA, gcloud, and the CP's /v1/login/oidc-grant, which is
exactly what authkey mode already requires. The leg keeps the RAW SA token
and completes the login out-of-band rather than spending the auth key --
the same shape installtest-macos.sh and installtest-windows.ps1 have used
since #290.
This matters more after #138: no installer pre-installs the engine on any
OS now, so `waired init` is the only thing that puts one on a host. Of its
two engine-install paths, --inference covers ensureDaemonPathEngine and
this leg is the only e2e that reaches runSetupEngineInstall -- the
wizard-driven half, which on Linux therefore had no live coverage at all.
Also refreshes the comments #290 left behind: the header still claimed the
engine is "installed by install.sh (--inference)" (false since #138) and
still explained --auth-key as a local-enroll selector, which #290 itself
made wrong. Plus two labels that drifted the same way -- run.sh's
"(oidc|bypass|interactive)" mode list and a Windows failure string reading
"waired init (oidc)" on the auth-key branch.
Verified: shellcheck (daemon-engine lib clean; the SC2015/SC1091 info
findings in enroll.sh/run.sh are pre-existing at untouched lines), bash -n,
pwsh parse. The leg itself needs the dev control plane -- dispatched
installtest-inference.yml (os=linux) on this branch.
Fixes #388
Signed-off-by: gen16k <gen16k@gmail.com>
gen16k
added a commit
that referenced
this pull request
Aug 2, 2026
… (#389) The leg has not run an assert since 2026-07-28. It dies 17 seconds in: [installtest] --daemon-engine supports IT_ENROLL_MODE=oidc only (got 'authkey') #290 replaced installtest's `oidc` enrol mode with `authkey` across the workflow and all three OS drivers, and said so in its own body -- "Two legs deliberately keep the old shape: --daemon-engine (macOS/Windows) still completes an ordinary login out-of-band with the raw token". Linux is missing from that sentence and from the change: the diff to this file touched three comments and left the guard demanding a mode that no longer exists (installtest-enroll.sh accepts authkey|interactive, and --google-sa-login was deleted in the same PR). Reverting the workflow is not an option, and nothing else has to move. it_enroll_daemon_path never depended on the ambient enrol mode; it depends on IT_IMPERSONATE_SA, gcloud, and the CP's /v1/login/oidc-grant, which is exactly what authkey mode already requires. The leg keeps the RAW SA token and completes the login out-of-band rather than spending the auth key -- the same shape installtest-macos.sh and installtest-windows.ps1 have used since #290. This matters more after #138: no installer pre-installs the engine on any OS now, so `waired init` is the only thing that puts one on a host. Of its two engine-install paths, --inference covers ensureDaemonPathEngine and this leg is the only e2e that reaches runSetupEngineInstall -- the wizard-driven half, which on Linux therefore had no live coverage at all. Also refreshes the comments #290 left behind: the header still claimed the engine is "installed by install.sh (--inference)" (false since #138) and still explained --auth-key as a local-enroll selector, which #290 itself made wrong. Plus two labels that drifted the same way -- run.sh's "(oidc|bypass|interactive)" mode list and a Windows failure string reading "waired init (oidc)" on the auth-key branch. Verified: shellcheck (daemon-engine lib clean; the SC2015/SC1091 info findings in enroll.sh/run.sh are pre-existing at untouched lines), bash -n, pwsh parse. The leg itself needs the dev control plane -- dispatched installtest-inference.yml (os=linux) on this branch. Fixes #388 Signed-off-by: gen16k <gen16k@gmail.com>
gen16k
added a commit
that referenced
this pull request
Sep 6, 2026
…ents stop contradicting the file they are in (#1261) The Strix Halo arms rest on upstream facts that move without notice, and #1250 wrote the recheck list for them. Three things it did not cover. ggml-org/llama.cpp#27856 (open, checked 2026-09-06) has qwen4exp — the architecture behind the Flash-Next entry #1259 just shipped — collapsing 3.5-4x in decode once context passes ~1k on HIP/gfx1151, plateauing at 5.5-6.1 tok/s where CUDA decays only mildly. It moves with the VENDORED LLAMA.CPP version rather than with ollama's release or its ROCm overlay, so it is on a different recheck axis from the four ollama threads already listed and ollama's release notes will never mention it. It also lands on the arm nobody was watching. The Windows arm already names Vulkan, so it never reaches HIP; the LINUX arm prefers ROCm, and the #290 probe cannot see this failure — it falls back only on positive evidence of CPU residency (size_vram == 0), and a model that is on the GPU but 4x slow at depth is not that. Conservative by design, but the failure shape is outside the arm. No Linux Strix Halo is in the fleet, so this is an upstream report, not an observation here; the note says so. Two stale comments in the same file, both left by #1247 correcting one copy of a figure and not the other: - BackendROCm's doc calls the Windows ROCm overlay ~350 MiB. WantsROCm, sixty lines below it, already carries the measured 247 MB at 0.33.3, as does the knowledge note. - The backend table test names its case "vulkan only (no ROCm on Win APU)". The arm it pins says the opposite in as many words — "Vulkan, because it is FASTER here — not because ROCm is absent" — because ROCm does engage gfx1151 under Windows. The assertion was right; the name was the last copy of the claim #1233 disproved. No behaviour change: comments, one test case name, and a knowledge-note section. Refs #1233 Refs #1247 Refs #1255 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0167iiPnKQz1qcuz2bhNBGep Signed-off-by: gen16k <gen16k@users.noreply.github.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.
waired#976 gave the control plane auth keys — the credential a headless host presents instead of a person completing a browser sign-in. This is the client half, and it removes the first of the two remaining local-enrollment selectors.
The key is a credential, not a route selector
chooseEnrollRoutegains anauthKeyfact that outranks every local selector: a run carrying a key reaches the daemon or fails saying why. It must never fall back — falling back drops the credential on the floor and registers a device whose capabilities the control plane never learns, which is the exact failure #175 exists to remove. Six new table cases pin it, including--auth-key overrides --bypass-mode.Value resolution
resolveAuthKeyis a pure function over three forms:--auth-key waired_ak_…--auth-key file:/path/to/keyps$WAIRED_AUTH_KEYSurrounding whitespace is always trimmed: a key written with
echo … > keycarries a newline, and a key that fails only because of an invisible character is a miserable thing to debug. The file leg takes an injected reader that records the path it was asked for, plus one test through the realos.ReadFileso the production wiring is actually exercised.On the wire
auth_keyis sent only when there is one. An older control plane decodes withDisallowUnknownFields, so an empty field would 400 every interactive enrollment.registration_ticket,RunInitskipsOnLoginURLand the entire poll loop — the session was authorized inside that call. Offering a login URL nobody can open is worse than useless on a headless host. There is a test asserting the poll count is exactly zero.enrollWithTicket, shared by both ways of obtaining the ticket.LoginStartRequest.AuthKeycarries it to the daemon. That type lives ininternal/management, notproto/— no tag, no CP-side bump. And/login/startis a mutating verb, which the management server's write guard already confines to the local IPC socket, so the key never crosses the TCP listener.Removed
--google-sa-login,--impersonate-sa,--oidc-id-token,--oidc-audienceandcmd/waired/oidc_grant.go. Worth noting: only--bypass-modewasbuildflag-gated — those four shipped in the production CLI, driving a second, capability-less enrollment implementation.installtest:
oidc→authkey, on all three OSesThe host still mints the SA id_token with gcloud (the guests have no gcloud), but now exchanges it once for a reusable auth key at the CP's dev issuer, and the guest enrols with the daemon up. So the leg exercises the journey a real headless install takes, instead of stopping the daemon to force a path production never uses. The Hyper-V edge verify moves the same way.
The key is minted per run and reused across guests — both the fleet case a real operator has, and what keeps every guest in one network for the Tier-3 overlay ping.
Two legs deliberately keep the old shape
--daemon-engine(macOS/Windows) still completes an ordinary login out-of-band with the raw token. Its whole point is watching the resident executor install the engine while init is mid-flight; an auth key authorizes the session in the create call, so there would be no window left to observe.--bypass-modestays. The testnet container uses it, and flipping that needs waired#982's terraform plumbing deployed first. It goes in the follow-up, together withbuild/agent-bootstrap.sh.Secrets
Adds a gitleaks rule for
waired_ak_+ 64 hex. The default ruleset has no pattern for it, this repo is public, and a leaked key admits a device to someone's network until it expires or is revoked. Doc examples use shortwaired_ak_...placeholders that do not match, so no allowlist is needed.Verification
go build ./...,go vet ./...,go test ./...— greengo build -tags prod ./...+go vet -tags prod ./...+go test -tags prod ./internal/buildflag/...— greenmake verify-cross— greengolangci-lint run— 0 issuesdocs-site:i18n-sync --check→ 32 pairs in sync (EN + JA both written, not just hash-accepted)scripts/ci/decision-log-guard.py— greenNot verified locally: the 3-OS installtest legs, which need the dev control plane. CI runs them on this PR — that is the real test of the migration. The
POST /test/auth-keyissuer they call is live onapp.dev.waired.netas of waired#978's deploy.New tests:
resolveAuthKey(13 cases incl. every failure), the real-os.ReadFilewiring, the old-CP error classifier, six route-table cases, and three wire-level tests — auth key skips the poll loop, no key omits the field entirely, a refused key never enrolls.Refs #175