Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.0.28] - 2026-07-29

### Fixed

- Fresh Linux installs now create and use the same `/var/lib/1helm-lxc/machines`
runtime tree, so a newly created channel can provision, start, and enter its
private LXC computer instead of failing with a missing lifecycle path.

- Includes the durable installer payload and API-quota corrections from the
superseded Linux-only `0.0.27` prerelease.

## [0.0.27] - 2026-07-29

### Fixed
Expand Down Expand Up @@ -786,7 +797,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.27...HEAD
[Unreleased]: https://github.com/gitcommit90/1Helm/compare/v0.0.28...HEAD
[0.0.28]: https://github.com/gitcommit90/1Helm/compare/v0.0.27...v0.0.28
[0.0.27]: https://github.com/gitcommit90/1Helm/compare/v0.0.26...v0.0.27
[0.0.26]: https://github.com/gitcommit90/1Helm/compare/v0.0.23...v0.0.26
[0.0.25]: https://github.com/gitcommit90/1Helm/compare/v0.0.23...v0.0.25
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,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 narrow workspace mirrors. |
| `HELM_CHANNEL_COMPUTER_BACKEND` | `apple` on macOS, `lxc` on Linux, `wsl` on Windows | Host isolation backend; `native` and `mock` are explicit development/test overrides. |
| `HELM_CHANNEL_MACHINE_IMAGE` | `local/1helm-channel-machine:0.0.27` | Versioned channel-machine image contract. |
| `HELM_CHANNEL_MACHINE_IMAGE` | `local/1helm-channel-machine:0.0.28` | 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.27",
"version": "0.0.28",
"private": true,
"type": "module",
"license": "AGPL-3.0-only",
Expand Down
4 changes: 2 additions & 2 deletions site/public/install-lxc-runtime.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ APP_SOURCE="${1:-}"
INSTALL_ROOT="/opt/1helm"
RUNTIME_ROOT="$INSTALL_ROOT/runtime/lxc"
LXC_ROOT="/var/lib/1helm-lxc"
LXC_PATH="$LXC_ROOT/containers"
LXC_PATH="$LXC_ROOT/machines"
CACHE_BASE="/var/cache/1helm-lxc"
NETWORK_STATE="$LXC_ROOT/network"
HELPER_PATH="/usr/libexec/1helm-lxc-runtime"
Expand All @@ -20,7 +20,7 @@ IDMAP_PATH="/etc/1helm/lxc-idmap"
SUDOERS_PATH="/etc/sudoers.d/1helm-lxc-runtime"
SERVICE_USER="1helm"
IMAGE_BUILD="20260726_07:42"
IMAGE_RELEASE="0.0.27"
IMAGE_RELEASE="0.0.28"

# v0.0.11's updater unit made the exact destination files writable under
# ProtectSystem=strict. Atomic replacement still requires write access to each
Expand Down
2 changes: 1 addition & 1 deletion site/public/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ NODE_LINK="$INSTALL_ROOT/node-current"
STATE_ROOT="/var/lib/1helm"
SERVICE_USER="1helm"
NODE_VERSION="22.23.1"
RELEASE_VERSION="0.0.27"
RELEASE_VERSION="0.0.28"
HOST_CONTRACT_PATHS=(
/usr/libexec/1helm-lxc-runtime
/usr/libexec/1helm-lxc-net
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 @@ -67,7 +67,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.27";
export const DEFAULT_CHANNEL_IMAGE = process.env.HELM_CHANNEL_MACHINE_IMAGE || "local/1helm-channel-machine:0.0.28";
const CONTAINER_CANDIDATES = [process.env.HELM_CONTAINER_CLI, "/usr/local/bin/container", "/opt/homebrew/bin/container", "container"].filter(Boolean) as string[];
const LXC_RUNTIME_VERSION = "1helm-lxc-runtime-v1";
const LXC_HELPER_CANDIDATES = [
Expand Down
2 changes: 1 addition & 1 deletion src/server/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1009,7 +1009,7 @@ export function migrate(): void {
const platformBackend = process.platform === "darwin" ? "apple" : process.platform === "win32" ? "wsl" : "lxc";
const configuredBackend = String(process.env.HELM_CHANNEL_COMPUTER_BACKEND || platformBackend);
const backend = ["apple", "lxc", "wsl", "native", "mock"].includes(configuredBackend) ? configuredBackend : platformBackend;
const image = String(process.env.HELM_CHANNEL_MACHINE_IMAGE || "local/1helm-channel-machine:0.0.27");
const image = String(process.env.HELM_CHANNEL_MACHINE_IMAGE || "local/1helm-channel-machine:0.0.28");
// Earlier Linux/Windows releases persisted the compatibility `native`
// seam into every channel row. A production host update must actually
// move those rows onto the platform isolation backend; changing the unit's
Expand Down
2 changes: 1 addition & 1 deletion test/channel-computers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ test("Apple channel-computer contract preserves isolation, files, wakes, archive
test("runtime digest and packaged image recipe stay pinned", async () => {
assert.equal(computers.APPLE_RUNTIME_SHA256, "0ca1c42a2269c2557efb1d82b1b38ac553e6a3a3da1b1179c439bcee1e7d6714");
assert.match(computers.APPLE_RUNTIME_URL, /\/1\.1\.0\/container-1\.1\.0-installer-signed\.pkg$/);
assert.equal(computers.DEFAULT_CHANNEL_IMAGE, "local/1helm-channel-machine:0.0.27");
assert.equal(computers.DEFAULT_CHANNEL_IMAGE, "local/1helm-channel-machine:0.0.28");
const packaging = await readFile(join(root, "scripts", "package-mac-dmg.cjs"), "utf8");
assert.match(packaging, /container\(\?:\$\|\\\/\)/, "release packaging includes container/ image assets");
const image = await readFile(join(root, "container", "Containerfile"), "utf8");
Expand Down
5 changes: 4 additions & 1 deletion test/site.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ test("installer assets are explicit and syntax-valid", () => {
assert.match(installer, /snapshot_host_contract[\s\S]*rollback_host_contract[\s\S]*TRANSACTION_ACTIVE/, "fresh and repeat installs restore runtime files and unit state after any transactional failure");
assert.match(installer, /rollback_host_contract[\s\S]*1helm\.service\.active[\s\S]*api\/setup\/status[\s\S]*restored_healthy/, "installer rollback verifies the restored service before claiming recovery");
assert.match(installer, /NODE_VERSION="22\.23\.1"/);
assert.match(installer, /RELEASE_VERSION="0\.0\.27"/, "fresh installs use the deliberately published Linux release instead of a rate-limited API lookup");
assert.match(installer, /RELEASE_VERSION="0\.0\.28"/, "fresh installs use the deliberately published Linux release instead of a rate-limited API lookup");
assert.doesNotMatch(installer, /api\.github\.com/, "fresh installs do not depend on unauthenticated GitHub API quota");
assert.match(installer, /need=\([^\n]*flock[^\n]*make[^\n]*c\+\+[^\n]*python3[^\n]*\)/, "the host updater and native dependency toolchain are probed even when download prerequisites already exist");
assert.match(installer, /import ensurepip[\s\S]*python3-venv/, "the Linux host installs Python's venv support required by durable memory instead of accepting a python3 executable alone");
Expand Down Expand Up @@ -172,6 +172,9 @@ test("installer assets are explicit and syntax-valid", () => {
const lxcNetwork = readFileSync(`${root}/scripts/1helm-lxc-net`, "utf8");
const lxcConfig = readFileSync(`${root}/deploy/1helm-lxc-unprivileged.conf`, "utf8");
const uninstaller = readFileSync(`${root}/site/public/uninstall-host.sh`, "utf8");
assert.match(lxcInstaller, /LXC_PATH="\$LXC_ROOT\/machines"/, "installation and the runtime use the same LXC state directory");
assert.match(lxcHelper, /LXC_PATH="\$LXC_ROOT\/machines"/, "the runtime reads the installer-created LXC state directory");
assert.doesNotMatch(lxcInstaller, /LXC_ROOT\/containers/, "the obsolete mismatched LXC state directory is not installed");
Comment on lines +175 to +177

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

Also reject obsolete paths in the runtime helper.

The test verifies that lxcHelper defines the new LXC_PATH, but only checks for $LXC_ROOT/containers in lxcInstaller. A separate hardcoded obsolete reference in the helper could therefore pass this regression test.

Suggested assertion
  assert.doesNotMatch(lxcInstaller, /LXC_ROOT\/containers/, "the obsolete mismatched LXC state directory is not installed");
+ assert.doesNotMatch(lxcHelper, /LXC_ROOT\/containers/, "the runtime helper does not use the obsolete LXC state directory");
📝 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
assert.match(lxcInstaller, /LXC_PATH="\$LXC_ROOT\/machines"/, "installation and the runtime use the same LXC state directory");
assert.match(lxcHelper, /LXC_PATH="\$LXC_ROOT\/machines"/, "the runtime reads the installer-created LXC state directory");
assert.doesNotMatch(lxcInstaller, /LXC_ROOT\/containers/, "the obsolete mismatched LXC state directory is not installed");
assert.match(lxcInstaller, /LXC_PATH="\$LXC_ROOT\/machines"/, "installation and the runtime use the same LXC state directory");
assert.match(lxcHelper, /LXC_PATH="\$LXC_ROOT\/machines"/, "the runtime reads the installer-created LXC state directory");
assert.doesNotMatch(lxcInstaller, /LXC_ROOT\/containers/, "the obsolete mismatched LXC state directory is not installed");
assert.doesNotMatch(lxcHelper, /LXC_ROOT\/containers/, "the runtime helper does not use the obsolete LXC state directory");
🤖 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 `@test/site.mjs` around lines 175 - 177, Extend the regression assertions
around lxcHelper to also reject the obsolete $LXC_ROOT/containers path,
alongside the existing lxcInstaller check. Keep the current positive LXC_PATH
assertions unchanged and use the same does-not-match behavior and descriptive
message for both runtime and installer content.

assert.match(lxcInstaller, /20260726_07:42/);
assert.match(lxcInstaller, /9c23724d6d22b3a5adf5d0f79d7e3779ded16a6d45f928bce93e14c48113d955/);
assert.match(lxcInstaller, /d5351325dc23e344c4974d7ff546e5e0c91b8e47a9caeb26f39cdc60eaad19e8/);
Expand Down
Loading