From cd50796883fc4354c1eeb0e615bc34494fec800c Mon Sep 17 00:00:00 2001 From: GPU Kitchen delivery Date: Tue, 29 Sep 2026 01:45:50 +0000 Subject: [PATCH] Promote v0.1.18 to production --- gpuk | 86 ++++++++++++++++++++++++++++++++++++++++++--- latest.json | 18 +++++----- latest.json.minisig | 6 ++-- 3 files changed, 94 insertions(+), 16 deletions(-) diff --git a/gpuk b/gpuk index 4e4a774..37313eb 100644 --- a/gpuk +++ b/gpuk @@ -87,6 +87,15 @@ arch_asset() { esac } +# [INS-19] A (re)install has just replaced the binary under a daemon that may be +# running: `systemctl enable --now` is a no-op on an active unit and would leave the +# OLD process in memory, speaking the old protocol to the new controller. Enable, then +# restart — which also starts a daemon that was not running. +start_daemon() { + systemctl enable "$SERVICE_NAME" + systemctl restart "$SERVICE_NAME" +} + install_binary() { # [local-path] if [ -n "${1:-}" ]; then [ -f "$1" ] || die "binary not found: $1" @@ -630,7 +639,7 @@ cmd_install() { echo " for automatic admission or administrator approval." fi fi - systemctl enable --now "$SERVICE_NAME" + start_daemon echo "==> $SERVICE_NAME enabled and started" echo echo "Done. The worker daemon is running${ENROLL_TOKEN:+ and enrolled}." @@ -640,7 +649,7 @@ cmd_install() { fi # ── Controller: start the daemon, then bring up the app container ── - systemctl enable --now "$SERVICE_NAME" + start_daemon echo "==> $SERVICE_NAME enabled and started" # The pull is the long part of a first install (a multi-GB image) and workerd # runs docker with its output captured, so pulling from inside `apply` is @@ -1110,6 +1119,67 @@ cmd_apply() { # Worker: the signed-binary self-update is DRIVEN FROM THE CONTROLLER (its Update # button → POST /api/nodes/:id/host/update → cmd:host_update → verified swap). # There is no local unverified swap path. +# [INS-03] `gpuk update` moves the WHOLE host to the release the signed channel +# vouches for: the root daemon as well as the app container. Updating only the +# container leaves the co-located worker on the previous protocol, which the new +# controller refuses — and a refused worker cannot receive the UI's Update either. +# The binary is held to the same chain as install.sh's: minisign signature by the +# pinned key, trusted comment naming the build the channel designates for this +# architecture, never older than the daemon already installed. Sets DAEMON_UPDATED. +DAEMON_UPDATED=0 +update_daemon() { + _base=$(channel_field "$CHANNEL_DOC" workerBase) + case "$(uname -m)" in + x86_64|amd64) _bkey="workerBuildIdX86_64" ;; + aarch64|arm64) _bkey="workerBuildIdAarch64" ;; + *) die "unsupported architecture: $(uname -m)" ;; + esac + _build=$(channel_field "$CHANNEL_DOC" "$_bkey") + if [ -z "$_base" ] || [ -z "$_build" ]; then + die "the signed release channel names no gpu-kitchen-worker build for $(uname -m) — refusing to leave the daemon on another release (OPS-20)" + fi + _installed=$("$BIN_DEST" --version 2>/dev/null | sed -n 's/^gpu-kitchen-worker //p' | head -1) + if [ "$_installed" = "$_build" ]; then + echo "==> worker daemon already on build $_build" + return 0 + fi + case "$_installed" in + [0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-*) + # buildId = YYYYMMDDHHMMSS-: the timestamp orders builds. + [ "${_build%%-*}" -ge "${_installed%%-*}" ] \ + || die "the channel's gpu-kitchen-worker $_build is OLDER than the installed $_installed — refusing a downgrade" ;; + esac + _url="$_base/$(arch_asset)" + echo "==> gpu-kitchen-worker ${_installed:-(unknown build)} → $_build" + command -v curl >/dev/null || die "curl is required to download the daemon" + curl -fL --progress-bar "$_url" -o "$BIN_DEST.new" \ + || { rm -f "$BIN_DEST.new"; die "cannot download $_url"; } + if [ "${GPUK_CHANNEL_INSECURE:-}" = "1" ]; then + echo "WARNING: GPUK_CHANNEL_INSECURE=1 — $_url NOT verified" >&2 + else + require_release_key + curl -fsSL "$_url.minisig" -o "$BIN_DEST.new.minisig" \ + || { rm -f "$BIN_DEST.new" "$BIN_DEST.new.minisig"; die "no signature at $_url.minisig — refusing an unsigned daemon binary"; } + _comment=$("$MINISIGN" -V -m "$BIN_DEST.new" -x "$BIN_DEST.new.minisig" -P "$CHANNEL_PUBKEY" 2>/dev/null \ + | sed -n 's/^Trusted comment: //p' | head -1) + rm -f "$BIN_DEST.new.minisig" + [ "$_comment" = "gpu-kitchen-worker@$_build" ] \ + || { rm -f "$BIN_DEST.new"; die "$_url is signed for '${_comment:-nothing}', not gpu-kitchen-worker@$_build — refusing it (OPS-20)"; } + echo "==> signature verified (build $_build)" + fi + chmod 0755 "$BIN_DEST.new" + mv "$BIN_DEST.new" "$BIN_DEST" + DAEMON_UPDATED=1 +} + +# The running daemon still executes the replaced binary until it restarts. +restart_updated_daemon() { + [ "$DAEMON_UPDATED" -eq 1 ] || return 0 + systemctl restart "$SERVICE_NAME" \ + || die "the daemon binary was updated but $SERVICE_NAME did not restart — run: systemctl restart $SERVICE_NAME" + echo "==> $SERVICE_NAME restarted on the new build" +} + cmd_update() { need_root _want=""; _check=0 @@ -1123,8 +1193,12 @@ cmd_update() { _image=$(manifest_image) if [ -z "$_image" ]; then - echo "This is a worker node. Worker self-update is driven from the controller UI" - echo "(the node's Update button), which pushes a minisign-verified binary swap." + # A worker node: the daemon is the whole install. The controller UI's Update + # button drives the same swap, but only for a worker it still accepts. + [ "$_check" -eq 0 ] || { echo "This is a worker node: 'gpuk update' moves its daemon to the channel's build."; return 0; } + channel_fetch || die "cannot read the release channel at $CHANNEL_URL" + update_daemon + restart_updated_daemon return 0 fi @@ -1193,10 +1267,14 @@ cmd_update() { else echo "==> already on $_current — re-pulling and recreating" fi + # The daemon follows the release it is pinned to — only the channel's own one + # carries a designated, signed build. + [ "$_want" != "$_latest" ] || update_daemon GPUK_MANIFEST_PATH="$MANIFEST" "$BIN_DEST" apply \ || die "the update did not apply — the [worker] lines above say why. A manifest refused for a field an older release wrote is repaired by re-running the same install command with --reset-manifest: the file is archived beside itself and rebuilt from this install's settings." + restart_updated_daemon } # ── uninstall ────────────────────────────────────────────────────────────────── diff --git a/latest.json b/latest.json index 3c70b64..768be7e 100644 --- a/latest.json +++ b/latest.json @@ -1,14 +1,14 @@ { - "version": "v0.1.17", - "semver": "0.1.17", + "version": "v0.1.18", + "semver": "0.1.18", "controllerImage": "repo.byterain.io/gpukitchen/gpukitchen-controller", "controllerImageEnterprise": "repo.byterain.io/gpukitchen-private/gpukitchen-controller-ee", - "controllerImageDigest": "sha256:106ff37f3df93fdccaacef9cf0a8c5a54c8864a3e936c5ab5f0911489b6e6b99", - "controllerImageDigestEnterprise": "sha256:d296b9bd91701f1072d553bac80b814e9a60ec12d2033168a1ecdfa8b0ea47da", - "gpukScriptSha256": "40286416f58d4807c18d62b8dda988ef87714e32c35fe1343e8b4de3fad36dcf", - "workerBuildIdX86_64": "20260928224355-94265bf9a70de6d50abe4cf0bee543cabc99740a", - "workerBuildIdAarch64": "20260928224355-94265bf9a70de6d50abe4cf0bee543cabc99740a", - "workerBase": "https://repo.byterain.io/api/packages/gpukitchen/generic/gpu-kitchen-worker/v0.1.17", - "gpukScript": "https://repo.byterain.io/api/packages/gpukitchen/generic/gpu-kitchen-channel/v0.1.17/gpuk", + "controllerImageDigest": "sha256:018b2f62519c2b8856022855dadff5cc43b6c23a70d0511e4475bb52fbc89df5", + "controllerImageDigestEnterprise": "sha256:401e646d750d22102c4b5110418650bea551255b4fe830b99e789ac1a104aa54", + "gpukScriptSha256": "0e7acfa5d01ec83c4436304d166565937c1c682ebf5557b6d488f9a4192ba901", + "workerBuildIdX86_64": "20260929011028-3858aae6ec272ae95a02c1860382802fedef55c9", + "workerBuildIdAarch64": "20260929011028-3858aae6ec272ae95a02c1860382802fedef55c9", + "workerBase": "https://repo.byterain.io/api/packages/gpukitchen/generic/gpu-kitchen-worker/v0.1.18", + "gpukScript": "https://repo.byterain.io/api/packages/gpukitchen/generic/gpu-kitchen-channel/v0.1.18/gpuk", "releaseNotes": "https://repo.byterain.io/gpukitchen/channel" } diff --git a/latest.json.minisig b/latest.json.minisig index b9b0ee7..eb2124e 100644 --- a/latest.json.minisig +++ b/latest.json.minisig @@ -1,4 +1,4 @@ untrusted comment: signature from minisign secret key -RUQ7BKXJqGX2jaBPnMPv5YyHwVbk6zcwDS7X4JofaAAfbWXjj5agxD8DS53UDyYUo17RWDjxO/QJRrutyKjW8vAxH0UYxzBGBQs= -trusted comment: gpu-kitchen channel v0.1.17 -ZA76R/yyFGS2Os823ISwjzZLnEkLJcpO3GUtx7f4YsDRwkDPxSVyR6z+SO4lxwsrM4i6ksg9MZIDqGfvoGerCQ== +RUQ7BKXJqGX2jYPHJGXskiWTpLMBRffCSFrkWtpw8ijANWROGOzw6yUQoEV/HkogGFDnWfmPsvJTLrU1mYxUsdCy57uw/JCoPwI= +trusted comment: gpu-kitchen channel v0.1.18 +OAAL3XLadiHW6lZ8PM3JBjVhSbyWANB0X4ERB1HOg4gzTg08EoiviEB55WCb8r1kNFwGAVZl18oHXejsoUy4Ag==