Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.0.33] - 2026-08-01

### Fixed

- Fixed fresh Windows shared-runtime setup so the expected pre-install
`wsl.exe --version` failure is inspected instead of terminating PowerShell
before the pinned Microsoft WSL package can be downloaded and installed.
- Preserved unexpected elevated Windows host-setup failures in the shared
status file so onboarding reports the actionable cause rather than only the
child process exit code.
- Added SHA-256-pinned x64 and arm64 Cloudflare tunnel connectors to the Linux
host archive, selected the connector matching the running host, and made
fresh install/update validation reject incomplete Linux packages.

## [0.0.32] - 2026-08-01

### Fixed
Expand Down Expand Up @@ -941,7 +955,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
notarization, stapled tickets, Gatekeeper verification, persistent
Application Support, and isolated Apple container machines.

[Unreleased]: https://github.com/gitcommit90/1Helm/compare/v0.0.32...HEAD
[Unreleased]: https://github.com/gitcommit90/1Helm/compare/v0.0.33...HEAD
[0.0.33]: https://github.com/gitcommit90/1Helm/compare/v0.0.32...v0.0.33
[0.0.32]: https://github.com/gitcommit90/1Helm/compare/v0.0.31...v0.0.32
[0.0.31]: https://github.com/gitcommit90/1Helm/compare/v0.0.30...v0.0.31
[0.0.30]: https://github.com/gitcommit90/1Helm/compare/v0.0.29...v0.0.30
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ A fresh data directory opens first-run setup. The source runtime defaults to
| `PORT` | `8123` | HTTP/WebSocket control-plane port. |
| `CTRL_DATA_DIR` | `./data` | Databases, routing state, uploads, and non-OCI development/Apple workspace mirrors. |
| `HELM_CHANNEL_COMPUTER_BACKEND` | `apple` on macOS, `oci` on Linux and Windows | Host isolation backend; `native` and `mock` are explicit development/test overrides. |
| `HELM_CHANNEL_MACHINE_IMAGE` | `local/1helm-channel-machine:0.0.32` | Versioned channel-machine image contract. |
| `HELM_CHANNEL_MACHINE_IMAGE` | `local/1helm-channel-machine:0.0.33` | Versioned channel-machine image contract. |

### Agent-first JSON CLI

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "1helm",
"productName": "1Helm",
"version": "0.0.32",
"version": "0.0.33",
"private": true,
"type": "module",
"license": "AGPL-3.0-only",
Expand Down
125 changes: 71 additions & 54 deletions scripts/install-wsl-runtime.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,19 @@ function Fetch-File {
# version/list matches fail unless the NULs are stripped first.
function Get-WslText {
param([Parameter(Mandatory = $true)][string[]]$ArgumentList)
$raw = & $wsl @ArgumentList 2>&1 | Out-String
$code = $LASTEXITCODE
# Windows PowerShell promotes native stderr to ErrorRecord objects. With the
# script-wide Stop policy, an expected nonzero probe (such as --version on a
# genuinely fresh host) otherwise aborts before we can inspect LASTEXITCODE
# and install WSL. Keep the probe non-terminating, then restore fail-closed
# behavior for the surrounding setup transaction.
$previousErrorAction = $ErrorActionPreference
try {
$ErrorActionPreference = "Continue"
$raw = & $wsl @ArgumentList 2>&1 | Out-String
$code = $LASTEXITCODE
} finally {
$ErrorActionPreference = $previousErrorAction
}
Comment on lines +45 to +52

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not trust $LASTEXITCODE when wsl.exe never runs.

$code is set only from $LASTEXITCODE. If & $wsl cannot start (wsl.exe absent from System32, or launch blocked), no native exit code is produced. $LASTEXITCODE then holds a stale value from an earlier native call, or $null on first use. A stale 0 makes the probe report success and makes Get-WslDistributionNames return the error text as distribution names. Default $code to a nonzero value and check $?.

🛡️ Proposed fix to fail closed on launch failures
   $previousErrorAction = $ErrorActionPreference
+  $raw = ""
+  $code = 1
   try {
     $ErrorActionPreference = "Continue"
     $raw = & $wsl `@ArgumentList` 2>&1 | Out-String
-    $code = $LASTEXITCODE
+    $launched = $?
+    $code = $LASTEXITCODE
+    if ($null -eq $code -or (-not $launched -and $code -eq 0)) { $code = 1 }
   } finally {
     $ErrorActionPreference = $previousErrorAction
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$previousErrorAction = $ErrorActionPreference
try {
$ErrorActionPreference = "Continue"
$raw = & $wsl @ArgumentList 2>&1 | Out-String
$code = $LASTEXITCODE
} finally {
$ErrorActionPreference = $previousErrorAction
}
$previousErrorAction = $ErrorActionPreference
$raw = ""
$code = 1
try {
$ErrorActionPreference = "Continue"
$raw = & $wsl `@ArgumentList` 2>&1 | Out-String
$launched = $?
$code = $LASTEXITCODE
if ($null -eq $code -or (-not $launched -and $code -eq 0)) { $code = 1 }
} finally {
$ErrorActionPreference = $previousErrorAction
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/install-wsl-runtime.ps1` around lines 45 - 52, Update the WSL
invocation block around `$raw` and `$code` to initialize `$code` to a nonzero
failure value before calling `& $wsl`, then use `$?` to confirm the command
launched successfully before accepting `$LASTEXITCODE`. Preserve the existing
exit-code handling for successfully launched `wsl.exe`, while ensuring missing
or blocked executables fail closed and cannot reuse a stale `$LASTEXITCODE`.

$text = ($raw -replace [char]0, "").Trim()
return [pscustomobject]@{ ExitCode = $code; Text = $text }
}
Expand Down Expand Up @@ -103,63 +114,69 @@ function Fail-Setup {
}

if ($HostSetup) {
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Fail-Setup "The WSL host setup phase requires administrator approval."
}
Write-SetupStatus -Status "running" -Step "Enabling Windows WSL features..." -Progress 8
$wslFeature = Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
$vmFeature = Get-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform
if ($wslFeature.State -ne "Enabled") { Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux -All -NoRestart | Out-Null }
if ($vmFeature.State -ne "Enabled") { Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform -All -NoRestart | Out-Null }
$wslFeature = Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
$vmFeature = Get-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform
$restartRequired = (Test-RestartRequired $wslFeature) -or (Test-RestartRequired $vmFeature)
$hostTemporary = Join-Path ([System.IO.Path]::GetTempPath()) ("1helm-wsl-host-" + [Guid]::NewGuid().ToString("N"))
New-Item -ItemType Directory -Path $hostTemporary | Out-Null
try {
if (-not (Test-PinnedWslRuntime)) {
$msi = Join-Path $hostTemporary "wsl.candidate.msi"
Write-SetupStatus -Status "running" -Step "Downloading Microsoft WSL installer..." -Progress 12
Fetch-File -Url $wslInstallerUrl -Destination $msi
if ((Get-FileHash -LiteralPath $msi -Algorithm SHA256).Hash.ToLowerInvariant() -ne $wslInstallerSha256) {
Fail-Setup "Microsoft WSL installer did not match 1Helm's pinned SHA-256."
}
$signature = Get-AuthenticodeSignature -LiteralPath $msi
if ($signature.Status -ne [System.Management.Automation.SignatureStatus]::Valid -or $null -eq $signature.SignerCertificate -or
$signature.SignerCertificate.Subject -notmatch '(^|,\s*)CN=Microsoft Corporation(,|$)') {
Fail-Setup "Microsoft WSL installer did not have a valid Microsoft Corporation signature."
}
Write-SetupStatus -Status "running" -Step "Installing Microsoft WSL $wslVersion..." -Progress 18
$installer = Start-Process -FilePath "$env:SystemRoot\System32\msiexec.exe" -ArgumentList @("/i", $msi, "/qn", "/norestart") -Wait -PassThru
# 0 success; 1641/3010 success+reboot; 1638 already installed (same/newer).
# 1603 is a hard fail from msiexec, but on machines that already have the
# pinned WSL build (or UTF-16 made detection fail earlier) the package may
# still leave a working runtime - re-verify before failing closed.
if ($installer.ExitCode -in @(1641, 3010)) { $restartRequired = $true }
elseif ($installer.ExitCode -notin @(0, 1638)) {
if (Test-PinnedWslRuntime) {
Write-Host "Microsoft WSL installer returned $($installer.ExitCode), but pinned WSL $wslVersion is already present; continuing."
} else {
Fail-Setup "Microsoft WSL installer failed with exit code $($installer.ExitCode)."
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Fail-Setup "The WSL host setup phase requires administrator approval."
}
Write-SetupStatus -Status "running" -Step "Enabling Windows WSL features..." -Progress 8
$wslFeature = Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
$vmFeature = Get-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform
if ($wslFeature.State -ne "Enabled") { Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux -All -NoRestart | Out-Null }
if ($vmFeature.State -ne "Enabled") { Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform -All -NoRestart | Out-Null }
$wslFeature = Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
$vmFeature = Get-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform
$restartRequired = (Test-RestartRequired $wslFeature) -or (Test-RestartRequired $vmFeature)
$hostTemporary = Join-Path ([System.IO.Path]::GetTempPath()) ("1helm-wsl-host-" + [Guid]::NewGuid().ToString("N"))
New-Item -ItemType Directory -Path $hostTemporary | Out-Null
try {
if (-not (Test-PinnedWslRuntime)) {
$msi = Join-Path $hostTemporary "wsl.candidate.msi"
Write-SetupStatus -Status "running" -Step "Downloading Microsoft WSL installer..." -Progress 12
Fetch-File -Url $wslInstallerUrl -Destination $msi
if ((Get-FileHash -LiteralPath $msi -Algorithm SHA256).Hash.ToLowerInvariant() -ne $wslInstallerSha256) {
Fail-Setup "Microsoft WSL installer did not match 1Helm's pinned SHA-256."
}
$signature = Get-AuthenticodeSignature -LiteralPath $msi
if ($signature.Status -ne [System.Management.Automation.SignatureStatus]::Valid -or $null -eq $signature.SignerCertificate -or
$signature.SignerCertificate.Subject -notmatch '(^|,\s*)CN=Microsoft Corporation(,|$)') {
Fail-Setup "Microsoft WSL installer did not have a valid Microsoft Corporation signature."
}
Write-SetupStatus -Status "running" -Step "Installing Microsoft WSL $wslVersion..." -Progress 18
$installer = Start-Process -FilePath "$env:SystemRoot\System32\msiexec.exe" -ArgumentList @("/i", $msi, "/qn", "/norestart") -Wait -PassThru

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Windows PowerShell 5.1 Start-Process ArgumentList array quoting spaces in path

💡 Result:

In Windows PowerShell 5.1, the Start-Process cmdlet's -ArgumentList parameter accepts either a single string or an array of strings [1][2]. When you provide an array, the cmdlet joins the elements into a single string by inserting a space between each element [1][3]. Crucially, Start-Process does not automatically handle quoting for array elements that contain spaces [4][5][6]. If an argument contains spaces, the resulting joined string may cause the target process to interpret the arguments incorrectly [7][5]. To pass arguments containing spaces correctly in PowerShell 5.1, you must manually include escaped double quotes around the specific argument within your string or array element [1][2][8]. Example using an array: Start-Process -FilePath "C:\Path\To\Executable.exe" -ArgumentList "FirstArg", ""C:\Path With Spaces\File.txt"", "ThirdArg" Example using a single string: Start-Process -FilePath "C:\Path\To\Executable.exe" -ArgumentList "/arg1 "C:\Path With Spaces\File.txt" /arg3" Official documentation recommends that for the most reliable results, you should use a single string for -ArgumentList and explicitly manage all necessary quotes and escaping [1][2][8]. This avoids potential issues with the automatic space-joining behavior of the array-based approach [5][6]. When using nested quotes, you can use the backtick (`) to escape the double quotes for the target process [1][8].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- candidate script ---'
sed -n '1,230p' scripts/install-wsl-runtime.ps1
printf '%s\n' '--- PowerShell version references ---'
rg -n -i --glob '!node_modules' --glob '!dist' --glob '!build' 'PowerShell|pwsh|powershell.exe|Windows PowerShell|PSVersion' .
printf '%s\n' '--- installer-related references ---'
rg -n -C 3 'Start-Process|GetTempPath|wsl\.candidate\.msi|Test-PinnedWslRuntime|msiexec' scripts

Repository: gitcommit90/1Helm

Length of output: 19099


🌐 Web query:

Microsoft documentation Windows PowerShell 5.1 Start-Process -ArgumentList joins array elements with spaces does not add quotation marks

💡 Result:

In Windows PowerShell 5.1, the Start-Process cmdlet does not automatically add quotation marks to elements in an ArgumentList array [1][2]. Instead, it flattens the array by joining all elements into a single string, separated by a single space [1][3][2]. Because this process does not preserve the structure of the original array or apply automatic quoting, arguments containing spaces or special characters will be misinterpreted by the target executable unless you manually handle the quoting [1][3]. To ensure that arguments containing spaces are correctly passed to a child process, you must include escaped double quotes around those specific elements within your PowerShell code [3][2]. For example, if you need to pass a file path that contains spaces: # Incorrect: The executable will see 'C:\Program' and 'Files\app.txt' as two separate arguments Start-Process -FilePath "app.exe" -ArgumentList "C:\Program Files\app.txt" # Correct: Escaped quotes ensure the path is treated as a single argument Start-Process -FilePath "app.exe" -ArgumentList ""C:\Program Files\app.txt"" As noted in official documentation, for the most predictable results, you can use a single string for ArgumentList that includes all arguments and any necessary escaped quotes, rather than relying on an array [3][2]. Keep in mind that the backtick (`) is used in PowerShell to escape characters (like double quotes) for the command-line string construction [3][2].

Citations:


Quote the MSI path passed to msiexec.exe.

Windows PowerShell 5.1 joins -ArgumentList elements with spaces and does not add quotes. If the elevated account's temporary path contains spaces, msiexec.exe parses the MSI path as separate arguments and the fresh install fails. Use explicit quotes, as at line 196.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/install-wsl-runtime.ps1` at line 147, Update the Start-Process
invocation assigning $installer to explicitly quote the $msi path in the
-ArgumentList passed to msiexec.exe, matching the quoting approach already used
near line 196 so paths containing spaces install correctly.

# 0 success; 1641/3010 success+reboot; 1638 already installed (same/newer).
# 1603 is a hard fail from msiexec, but on machines that already have the
# pinned WSL build (or UTF-16 made detection fail earlier) the package may
# still leave a working runtime - re-verify before failing closed.
if ($installer.ExitCode -in @(1641, 3010)) { $restartRequired = $true }
elseif ($installer.ExitCode -notin @(0, 1638)) {
if (Test-PinnedWslRuntime) {
Write-Host "Microsoft WSL installer returned $($installer.ExitCode), but pinned WSL $wslVersion is already present; continuing."
} else {
Fail-Setup "Microsoft WSL installer failed with exit code $($installer.ExitCode)."
}
}
} else {
Write-SetupStatus -Status "running" -Step "Microsoft WSL $wslVersion is already installed." -Progress 18
}
} else {
Write-SetupStatus -Status "running" -Step "Microsoft WSL $wslVersion is already installed." -Progress 18
}
if ($restartRequired) {
Write-SetupStatus -Status "restart_required" -Step "WSL 2 features are enabled. Restart Windows once, then retry 1Helm computer setup." -Progress 20
exit 10
if ($restartRequired) {
Write-SetupStatus -Status "restart_required" -Step "WSL 2 features are enabled. Restart Windows once, then retry 1Helm computer setup." -Progress 20
exit 10
}
if (-not (Test-PinnedWslRuntime)) { Fail-Setup "Microsoft WSL $wslVersion was installed but could not be verified." }
Write-SetupStatus -Status "running" -Step "Setting WSL 2 as the default..." -Progress 22
$defaultVersion = Get-WslText -ArgumentList @("--set-default-version", "2")
if ($defaultVersion.ExitCode -ne 0) { Fail-Setup "WSL could not set version 2 as the default. $($defaultVersion.Text)" }
} finally {
if (Test-Path -LiteralPath $hostTemporary) { Remove-Item -LiteralPath $hostTemporary -Recurse -Force }
}
Comment on lines +171 to 173

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Make temporary cleanup best-effort so it cannot mask the real error.

The script-wide policy is Stop. If Remove-Item fails, for example because msiexec still holds the MSI, the cleanup error becomes the active exception, reaches the new catch, and Fail-Setup overwrites the status file with the cleanup message. The original elevated failure is then lost in the UI.

♻️ Proposed fix
     } finally {
-      if (Test-Path -LiteralPath $hostTemporary) { Remove-Item -LiteralPath $hostTemporary -Recurse -Force }
+      if (Test-Path -LiteralPath $hostTemporary) {
+        Remove-Item -LiteralPath $hostTemporary -Recurse -Force -ErrorAction SilentlyContinue
+      }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} finally {
if (Test-Path -LiteralPath $hostTemporary) { Remove-Item -LiteralPath $hostTemporary -Recurse -Force }
}
} finally {
if (Test-Path -LiteralPath $hostTemporary) {
Remove-Item -LiteralPath $hostTemporary -Recurse -Force -ErrorAction SilentlyContinue
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/install-wsl-runtime.ps1` around lines 171 - 173, Update the finally
cleanup block around $hostTemporary so Remove-Item is best-effort and cannot
replace the original failure under the script-wide Stop policy. Suppress or
locally handle cleanup errors while retaining the existing Test-Path, recursive,
and forced removal behavior, allowing the original elevated setup error to reach
catch and Fail-Setup unchanged.

if (-not (Test-PinnedWslRuntime)) { Fail-Setup "Microsoft WSL $wslVersion was installed but could not be verified." }
Write-SetupStatus -Status "running" -Step "Setting WSL 2 as the default..." -Progress 22
$defaultVersion = Get-WslText -ArgumentList @("--set-default-version", "2")
if ($defaultVersion.ExitCode -ne 0) { Fail-Setup "WSL could not set version 2 as the default. $($defaultVersion.Text)" }
} finally {
if (Test-Path -LiteralPath $hostTemporary) { Remove-Item -LiteralPath $hostTemporary -Recurse -Force }
exit 0
} catch {
$message = $_.Exception.Message
if (-not $message) { $message = "$_" }
Fail-Setup $message
}
exit 0
}

# Keep the distribution owned by the signed-in Windows account. Only optional
Expand Down
28 changes: 27 additions & 1 deletion scripts/package-linux-host.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env node
import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
import { chmodSync, copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { spawnSync } from "node:child_process";
Expand All @@ -9,6 +9,19 @@ const version = String(JSON.parse(readFileSync(resolve(root, "package.json"), "u
if (!/^\d+\.\d+\.\d+$/.test(version)) throw new Error("package.json must contain a release version");
const dist = resolve(root, "dist");
const output = resolve(dist, `1Helm-${version}-linux-node.tgz`);
const cloudflaredVersion = "2026.3.0";
const cloudflared = [
{
arch: "x64",
asset: "cloudflared-linux-amd64",
sha256: "4a9e50e6d6d798e90fcd01933151a90bf7edd99a0a55c28ad18f2e16263a5c30",
},
{
arch: "arm64",
asset: "cloudflared-linux-arm64",
sha256: "0755ba4cbab59980e6148367fcf53a8f3ec85a97deefd63c2420cf7850769bee",
},
];
const sealed = [
"container/channel-machine.oci.tar",
"container/channel-machine.oci.sha256",
Expand Down Expand Up @@ -43,6 +56,19 @@ try {
copyFileSync(src, join(stage, prefix, rel));
}

const resourcesDir = join(stage, prefix, "resources");
mkdirSync(resourcesDir, { recursive: true });
for (const connector of cloudflared) {
const destination = join(resourcesDir, `cloudflared-linux-${connector.arch}`);
const url = `https://github.com/cloudflare/cloudflared/releases/download/${cloudflaredVersion}/${connector.asset}`;
const download = spawnSync("curl", ["-fsSL", "--proto", "=https", "--tlsv1.2", "--retry", "3", "-o", destination, url], { stdio: "inherit" });
if (download.status !== 0) throw new Error(`Could not download pinned cloudflared for Linux ${connector.arch}`);
const digest = spawnSync("sha256sum", [destination], { encoding: "utf8" });
const actual = digest.status === 0 ? String(digest.stdout || "").trim().split(/\s+/)[0] : "";
if (actual !== connector.sha256) throw new Error(`Pinned cloudflared digest mismatch for Linux ${connector.arch} (got ${actual || "unavailable"})`);
chmodSync(destination, 0o755);
}

const pack = spawnSync("tar", ["-czf", output, "-C", stage, prefix], { stdio: "inherit" });
if (pack.status !== 0) throw new Error("Could not write the Linux host archive");
} finally {
Expand Down
6 changes: 4 additions & 2 deletions site/public/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,8 @@ PACKAGE_VERSION="$("$NODE_LINK/bin/node" -p 'require(process.argv[1]).version' "
&& -r "$RELEASE_STAGE/deploy/1helm-oci-runtime-v1.conf" \
&& -r "$RELEASE_STAGE/container/Containerfile.oci" \
&& -f "$RELEASE_STAGE/container/channel-machine.oci.tar" \
&& -f "$RELEASE_STAGE/container/channel-machine.oci.sha256" ]] \
&& -f "$RELEASE_STAGE/container/channel-machine.oci.sha256" \
&& -x "$RELEASE_STAGE/resources/cloudflared-linux-$NODE_ARCH" ]] \
|| { echo "The verified Linux artifact is missing its complete OCI runtime contract." >&2; exit 1; }
chown -R "$SERVICE_USER:$SERVICE_USER" "$RELEASE_STAGE"
runuser -u "$SERVICE_USER" -- env HOME="$STATE_ROOT" PATH="$NODE_LINK/bin:/usr/bin:/bin" PUPPETEER_SKIP_DOWNLOAD=1 "$NODE_LINK/bin/npm" --prefix "$RELEASE_STAGE" ci
Expand All @@ -193,7 +194,8 @@ if [[ -e "$RELEASE_ROOT" ]]; then
EXISTING_VERSION="$("$NODE_LINK/bin/node" -p 'require(process.argv[1]).version' "$RELEASE_ROOT/package.json" 2>/dev/null || true)"
[[ "$EXISTING_VERSION" == "$VERSION" \
&& -f "$RELEASE_ROOT/container/channel-machine.oci.tar" \
&& -f "$RELEASE_ROOT/container/channel-machine.oci.sha256" ]] \
&& -f "$RELEASE_ROOT/container/channel-machine.oci.sha256" \
&& -x "$RELEASE_ROOT/resources/cloudflared-linux-$NODE_ARCH" ]] \
|| { echo "Existing release directory does not match the verified v$VERSION Linux artifact." >&2; exit 1; }
else
mv "$RELEASE_STAGE" "$RELEASE_ROOT"
Expand Down
9 changes: 7 additions & 2 deletions site/public/update-host.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ STATUS_FILE="$STATE_ROOT/host-update-status.json"
LOCK_FILE="$INSTALL_ROOT/host-update.lock"
SERVICE_NAME="1helm.service"
PORT="8123"
case "$(uname -m)" in
x86_64|amd64) CONNECTOR_ARCH="x64" ;;
aarch64|arm64) CONNECTOR_ARCH="arm64" ;;
*) echo "Unsupported architecture: $(uname -m)" >&2; exit 1 ;;
esac
HOST_CONTRACT_PATHS=(
/usr/libexec/1helm-oci-runtime
/etc/1helm/oci-runtime-v1.conf
Expand Down Expand Up @@ -192,7 +197,7 @@ if [[ "$VERSION_ORDER" != "-1" ]]; then
RELEASE_ROOT="$(readlink -f "$APP_ROOT" 2>/dev/null || true)"
[[ "$RELEASE_ROOT" == "$RELEASES_ROOT/"* && -d "$RELEASE_ROOT" ]] \
|| fail "The current 1Helm release is not inside the verified release store."
[[ -x "$RELEASE_ROOT/site/public/apply-linux-release.sh" && -x "$RELEASE_ROOT/site/public/install-oci-runtime.sh" && -x "$RELEASE_ROOT/site/public/install-linux-units.sh" && -x "$RELEASE_ROOT/site/public/uninstall-host.sh" && -x "$RELEASE_ROOT/scripts/1helm-oci-runtime" ]] \
[[ -x "$RELEASE_ROOT/site/public/apply-linux-release.sh" && -x "$RELEASE_ROOT/site/public/install-oci-runtime.sh" && -x "$RELEASE_ROOT/site/public/install-linux-units.sh" && -x "$RELEASE_ROOT/site/public/uninstall-host.sh" && -x "$RELEASE_ROOT/scripts/1helm-oci-runtime" && -x "$RELEASE_ROOT/resources/cloudflared-linux-$CONNECTOR_ARCH" ]] \
|| fail "The current verified release is missing its OCI runtime contract."
write_status "installing" "$TARGET_VERSION" "The application is current; the host is applying its verified OCI contract."
APPLY_UNIT="1helm-host-contract-apply-${TARGET_VERSION//./-}-$$"
Expand Down Expand Up @@ -220,7 +225,7 @@ PACKAGE_VERSION="$("$NODE_LINK/bin/node" -p 'require(process.argv[1]).version' "
|| fail "The verified Linux artifact version does not match its release tag."
[[ -x "$STAGE/site/public/update-host.sh" ]] \
|| fail "The verified Linux artifact is missing its host updater."
[[ -x "$STAGE/site/public/apply-linux-release.sh" && -x "$STAGE/site/public/install-oci-runtime.sh" && -x "$STAGE/site/public/install-linux-units.sh" && -x "$STAGE/site/public/uninstall-host.sh" && -x "$STAGE/scripts/1helm-oci-runtime" && -r "$STAGE/deploy/1helm-oci-runtime-v1.conf" && -r "$STAGE/container/Containerfile.oci" ]] \
[[ -x "$STAGE/site/public/apply-linux-release.sh" && -x "$STAGE/site/public/install-oci-runtime.sh" && -x "$STAGE/site/public/install-linux-units.sh" && -x "$STAGE/site/public/uninstall-host.sh" && -x "$STAGE/scripts/1helm-oci-runtime" && -r "$STAGE/deploy/1helm-oci-runtime-v1.conf" && -r "$STAGE/container/Containerfile.oci" && -x "$STAGE/resources/cloudflared-linux-$CONNECTOR_ARCH" ]] \
|| fail "The verified Linux artifact is missing its OCI runtime contract."
chown -R "$SERVICE_USER:$SERVICE_USER" "$STAGE"

Expand Down
2 changes: 1 addition & 1 deletion src/server/channel-computers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const APPLE_RUNTIME_VERSION = "1.1.0";
export const APPLE_RUNTIME_PACKAGE = `container-${APPLE_RUNTIME_VERSION}-installer-signed.pkg`;
export const APPLE_RUNTIME_URL = `https://github.com/apple/container/releases/download/${APPLE_RUNTIME_VERSION}/${APPLE_RUNTIME_PACKAGE}`;
export const APPLE_RUNTIME_SHA256 = "0ca1c42a2269c2557efb1d82b1b38ac553e6a3a3da1b1179c439bcee1e7d6714";
export const DEFAULT_CHANNEL_IMAGE = process.env.HELM_CHANNEL_MACHINE_IMAGE || "local/1helm-channel-machine:0.0.32";
export const DEFAULT_CHANNEL_IMAGE = process.env.HELM_CHANNEL_MACHINE_IMAGE || "local/1helm-channel-machine:0.0.33";
const CONTAINER_CANDIDATES = [process.env.HELM_CONTAINER_CLI, "/usr/local/bin/container", "/opt/homebrew/bin/container", "container"].filter(Boolean) as string[];
const OCI_RUNTIME_VERSION = "1helm-oci-runtime-v1";
const OCI_HELPER_CANDIDATES = [
Expand Down
Loading
Loading