Skip to content

Replace Linux and Windows channel VMs with durable OCI containers - #40

Merged
gitcommit90 merged 8 commits into
mainfrom
feat/durable-oci-channel-computers
Jul 30, 2026
Merged

Replace Linux and Windows channel VMs with durable OCI containers#40
gitcommit90 merged 8 commits into
mainfrom
feat/durable-oci-channel-computers

Conversation

@gitcommit90

@gitcommit90 gitcommit90 commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Outcome

Ships the deliberate clean-start 0.0.29 desktop architecture: native Podman OCI channel computers on Linux, one managed WSL 2 OCI runtime on Windows, and the existing isolated Apple container backend on macOS. Includes the ReRouted 0.5.10 Antigravity CRLF streaming fix.

Acceptance ledger

  1. macOS Apple Silicon candidate was built from f09132556c56df59c4c4c210da8d0d8fb65bc4df, Developer ID signed, Apple notarized and stapled, accepted by Gatekeeper, installed on macstudiojoseph, and approved by the owner.
  2. Linux package was built from the same commit and digest, installed on the retained systemd host, and passed OCI creation, shared /workspace, Files, persistence, resize, stop/start network identity, digest-qualified backup rejection/restore, repeat install, and host reboot preservation.
  3. Windows Setup, full nupkg, and literal RELEASES were built on Windows x64 from the same source archive. Squirrel size/SHA-1 metadata, runtime payloads, x64 node-pty, and absence of host-local instructions were verified. Authenticode status is honestly NotSigned; no self-signing is used.
  4. This is intentionally a fresh runtime generation. No old LXC, per-channel WSL, desktop, or application state is bridged, copied, imported, or converted into the new namespaces.
  5. Embedded ReRouted 0.5.10 preserves Google Antigravity Gemini responses whose streaming SSE uses CRLF delimiters, with a focused regression test.

Verification

  • npm run typecheck
  • npm run build
  • npm test — 125 integration assertions plus 111 Node tests passed; 2 browser-dependent tests skipped because Chrome is absent
  • npm run test:onboarding-browser — 20/20 passed
  • git diff --check
  • Complete local six-file desktop matrix SHA-256 verified

Summary by CodeRabbit

  • New Features

    • Introduced durable OCI containers for channel computers, with runtime-owned storage and persistence across stop/start.
    • Added digest-verified channel backup and recovery.
    • Added shared managed WSL 2/OCI runtime support on Windows and native OCI hosting on Linux.
    • Added runtime resizing and improved setup, readiness, removal, and update workflows.
  • Bug Fixes

    • Improved compatibility with CRLF-delimited Gemini streaming responses.
    • Improved Windows packaging reliability for long installation paths.
  • Documentation

    • Updated installation, architecture, security, platform support, and recovery guidance for the OCI runtime model.
    • Released version 0.0.29.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This change replaces LXC/WSL channel isolation with durable OCI containers, runtime-owned channel storage, Podman-based Linux hosting, shared WSL2 provisioning on Windows, OCI installation/update flows, digest-qualified recovery, updated client/server contracts, and corresponding tests and documentation.

Changes

OCI runtime migration

Layer / File(s) Summary
OCI contracts and storage routing
container/Containerfile.oci, deploy/1helm-oci-runtime-v1.conf, src/server/channel-storage.ts, src/server/db.ts, src/client/api.ts, package.json
Defines OCI backend contracts, runtime identity and storage roots, database defaults, container image configuration, and version metadata.
OCI container lifecycle and recovery
scripts/1helm-oci-runtime
Adds Podman network/container provisioning, verification, resource updates, command execution, backups, digest validation, safe restore, and deletion.
Host installation and packaging
site/public/*, scripts/install-wsl-runtime.ps1, scripts/windows-removal.cjs, scripts/package-windows.cjs, desktop/main.cjs
Switches Linux installation and systemd services to OCI, provisions the shared Windows WSL runtime, updates removal handling, and uses shorter Windows packaging paths with artifact validation.
Server, client, and authoritative file integration
src/server/channel-computers.ts, src/server/channel-storage.ts, src/server/agents.ts, src/server/bots.ts, src/server/index.ts, src/client/*
Routes OCI channels through runtime-owned storage and lifecycle operations, removes guest synchronization for OCI, adds readiness and resize handling, and updates runtime setup messaging.
Lifecycle, packaging, and runtime validation
test/*, scripts/run-test-suite.mjs
Replaces legacy backend tests with OCI lifecycle, persistence, recovery, ownership, packaging, installer, and ReRouted CRLF-stream tests.
Release and platform documentation
README.md, docs/*, site/*, CHANGELOG.md
Updates platform architecture, installation paths, security boundaries, release verification, and the 0.0.29 changelog entry.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The description is detailed, but it does not follow the required template and omits Summary, Type of change, and Release Notes sections. Add the missing template sections: Summary, Type of change, Release Notes with changelog bullets, and the required checklist items.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed It clearly summarizes the main change: replacing Linux and Windows channel VMs with durable OCI containers.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/durable-oci-channel-computers

Comment @coderabbitai help to get the list of available commands.

@gitcommit90 gitcommit90 reopened this Jul 30, 2026
@gitcommit90
gitcommit90 merged commit f091325 into main Jul 30, 2026
5 of 7 checks passed
@gitcommit90
gitcommit90 deleted the feat/durable-oci-channel-computers branch July 30, 2026 07:59
@gitcommit90

Copy link
Copy Markdown
Owner Author

The exact artifact-producing commit f091325 passed PR CI and CodeQL and was fast-forwarded to main to preserve the already-approved Mac signature/notarization and all cross-platform artifact provenance. Closing this now-fully-contained PR without creating a different squash SHA.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 10

🧹 Nitpick comments (7)
src/server/channel-storage.ts (2)

43-53: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Each path resolution costs up to three SQLite queries.

channelWorkspacePath/channelFilesPath both re-enter channelFilesystemRoot, which queries channels, channel_computers, and workspace. These are called per-file in workspace listing/mirroring loops. A small per-channel memo (invalidated on computer row changes) would remove the repeated lookups.

🤖 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 `@src/server/channel-storage.ts` around lines 43 - 53, Add a per-channel memo
for the resolved root used by channelFilesystemRoot, and have
channelWorkspacePath and channelFilesPath reuse that memoized value instead of
repeating the SQLite lookups for each file. Invalidate the memo whenever the
relevant channel computer/workspace rows change, while preserving current path
resolution behavior for both OCI and host backends.

30-36: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Windows state root duplicates the installed manifest value.

The \var\lib\1helm-oci-v1\runtime\oci suffix hardcodes ONEHELM_OCI_STATE_ROOT from deploy/1helm-oci-runtime-v1.conf. If the manifest root is ever bumped (e.g. -v2), Windows silently resolves every channel path under a non-existent directory while Linux keeps working. Derive it from a single shared constant (or read it back from the runtime via the helper) so both sides move together.

Also worth noting: HELM_OCI_HOST_STATE_ROOT/HELM_OCI_STATE_ROOT are accepted unvalidated here, but the helper rejects anything not matching ^/.+/runtime/oci$ (scripts/1helm-oci-runtime Line 20), so a typo surfaces as an opaque "unsafe runtime state root" failure rather than a config error.

🤖 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 `@src/server/channel-storage.ts` around lines 30 - 36, Update ociHostStateRoot
and the Windows path construction to derive the runtime state-root suffix from
the shared manifest/runtime source instead of hardcoding “1helm-oci-v1”.
Preserve the existing environment-variable precedence and non-Windows fallback,
and validate HELM_OCI_HOST_STATE_ROOT/HELM_OCI_STATE_ROOT against the same
/.../runtime/oci format before returning them so invalid configuration produces
the established configuration error.
src/server/db.ts (1)

936-942: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Unvalidated backend env value diverges from channel-storage.ts.

Here HELM_CHANNEL_COMPUTER_BACKEND is validated against the allowed list and falls back to platformBackend; effectiveBackend() in src/server/channel-storage.ts (Lines 25-27) accepts the raw value. With a typo, the DB row says oci while path routing returns the host tree until the row is read. Share one validated resolver between the two.

🤖 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 `@src/server/db.ts` around lines 936 - 942, The backend selection logic in the
database initialization flow must match channel-storage routing for invalid
HELM_CHANNEL_COMPUTER_BACKEND values. Extract or reuse a shared validated
resolver from the logic around effectiveBackend(), then update the
platformBackend/configuredBackend/backend setup in the database flow to use it,
preserving the allowed backends and platform-specific fallback.
src/server/channel-computers.ts (1)

609-609: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider a dedicated predicate instead of isolatedBackend(c) && c.backend !== "oci".

OCI is an isolated backend but never participates in host↔guest mirroring, so six call sites now repeat the same exclusion. A mirrorsWorkspace(computer) helper (computer.backend === "apple") would make the invariant explicit and prevent a future backend from silently opting into mirroring.

Also applies to: 670-670, 698-698, 739-739, 756-756, 825-825

🤖 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 `@src/server/channel-computers.ts` at line 609, Introduce a dedicated
mirrorsWorkspace(computer) predicate that returns true only for the Apple
backend, then replace the repeated isolatedBackend(computer) plus OCI exclusion
checks at all listed call sites with this predicate. Preserve the existing
early-return behavior while centralizing the host↔guest mirroring eligibility
rule.
test/channel-computers-backend-child.mjs (2)

49-50: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace the fixed 2.2 s sleep with a bounded poll.

A single fixed wait for an async in-container write is a flake source under CI load and always burns the full delay. Poll for the file with a deadline instead.

♻️ Proposed refactor
-  await new Promise((resolveWait) => setTimeout(resolveWait, 2200));
-  assert.equal(readFileSync(join(authoritativeRoot, "workspace", "background.txt"), "utf8"), "background");
+  const backgroundPath = join(authoritativeRoot, "workspace", "background.txt");
+  const deadline = Date.now() + 20_000;
+  while (!existsSync(backgroundPath) && Date.now() < deadline) await new Promise((resolveWait) => setTimeout(resolveWait, 100));
+  assert.equal(readFileSync(backgroundPath, "utf8"), "background", "background in-container write reaches authoritative storage");
🤖 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/channel-computers-backend-child.mjs` around lines 49 - 50, Replace the
fixed 2.2-second setTimeout before the background.txt assertion with a bounded
polling loop that repeatedly checks for the expected file/content until it
matches or a deadline is reached, using a short interval and preserving a clear
timeout failure. Keep the existing readFileSync assertion outcome for successful
completion.

89-90: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Narrow the call-log negative match to helper operations.

oci-calls.log contains full argv, including the shell command text sent through runChannelCommand. An unanchored /sync/i will fail on any future test command containing rsync/fsync/"resync" rather than on a real workspace-copy regression. Match the operation token instead, e.g. per-line JSON.parse and assert args[0] is never a sync/import/export operation.

🤖 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/channel-computers-backend-child.mjs` around lines 89 - 90, Replace the
broad raw-text assertion on oci-calls.log with per-line JSON parsing, then
inspect each parsed call’s args[0] operation token and assert it is never a
workspace sync, import, or export operation. Keep the assertion focused on
helper operations so command text containing words like rsync, fsync, or resync
does not trigger it.
test/fake-oci-runtime.mjs (1)

139-149: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Derive the backup digest from content so digest verification is actually exercised.

A constant "a".repeat(64) makes restore's digest check tautological — a tampered or swapped backup payload still verifies, so the recovery path the child test asserts never proves integrity. createHash is already imported.

♻️ Proposed refactor
   cpSync(channelDir(name), join(destination, "channel"), { recursive: true, preserveTimestamps: true });
   cpSync(configPath(name), join(destination, "config.json"));
-  const digest = "a".repeat(64);
+  const hash = createHash("sha256");
+  for (const entry of readdirSync(join(destination, "channel"), { recursive: true, withFileTypes: true }).sort((a, b) => (a.name < b.name ? -1 : 1))) {
+    if (!entry.isFile()) continue;
+    hash.update(entry.name).update(readFileSync(join(entry.parentPath ?? entry.path, entry.name)));
+  }
+  const digest = hash.update(readFileSync(join(destination, "config.json"))).digest("hex");
   writeFileSync(join(destination, "digest"), digest);
🤖 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/fake-oci-runtime.mjs` around lines 139 - 149, Update the backup branch
in the fake OCI runtime to derive the digest written to destination/digest from
the backed-up payload using the existing createHash import, rather than a
constant value. Hash the same channel/config content that restore verifies and
continue returning that computed digest in the JSON response, so restore
integrity checks detect tampering or swapped backups.
🤖 Prompt for all review comments with 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.

Inline comments:
In `@docs/USER_GUIDE.md`:
- Around line 371-381: Update the host-update preservation guidance in
USER_GUIDE.md to document both Windows durable state roots:
%APPDATA%\1Helm-OCI-v1 and %LOCALAPPDATA%\1Helm-Runtime. Keep the existing macOS
and Linux paths and replacement/deletion guidance unchanged.

In `@scripts/1helm-oci-runtime`:
- Around line 167-184: Update ensure_network so its network exists/create
sequence is serialized with the same installation identity lock used by
ensure_network_identity, or explicitly tolerate a concurrent podman network
create failure when the network now exists. Preserve the ownership-label
validation after either path, ensuring concurrent provisioning for the same
installation does not abort under set -e.
- Around line 368-380: Extend the readiness wait in the container startup loop
around verify_container beyond the current 10-second budget, using a longer
deadline appropriate for systemd initialization and create’s existing 30-minute
allowance. Replace the fixed 0.1-second polling with bounded backoff while
continuing to run the ownership and storage health probe until it succeeds or
the new deadline expires, then retain the existing failure path.
- Around line 568-584: Update the create branch to use an EXIT/ERR cleanup trap,
following the pattern in restore_container, so failures from
create_container—including die during verification—remove the newly created
container and storage when storage_existed is false. Invoke create_container
unconditionally rather than in an if condition, preserving set -e behavior for
ensure_network, ensure_network_identity, and podman create failures. Clear or
disable the trap after successful creation before starting the container.

In `@scripts/install-wsl-runtime.ps1`:
- Around line 20-23: Normalize wsl.exe output by removing embedded null
characters before matching or comparing it. Update Test-PinnedWslRuntime and the
distro-name list handling near the related call site so both version detection
and distro matching use the cleaned output, consistent with the existing
windows-removal.cjs behavior.

In `@site/public/uninstall-host.sh`:
- Line 34: Update the uninstall cleanup command in uninstall-host.sh to also
remove /etc/tmpfiles.d/1helm-oci.conf, preserving the existing forced removal
behavior for all OCI host artifacts.

In `@src/server/agents.ts`:
- Around line 48-63: Update src/server/agents.ts lines 48-63 in
ensureChannelWorkspace to explicitly signal when runtime-owned storage is
expected but the workspace is absent, using a domain error or equivalent flag
before downstream filesystem access. Also update src/server/agents.ts lines
947-950 to guard all three realpathSync calls with existsSync, or consistently
rely on the new ensureChannelWorkspace domain error, so callers receive an
actionable failure instead of a raw filesystem exception.

In `@src/server/channel-computers.ts`:
- Around line 1345-1377: Update runtimeReadiness() to avoid synchronous
spawnSync calls blocking the event loop during the version and readiness probes.
Use asynchronous process execution with awaited results, or reuse a short-lived
cached readiness result refreshed in the background, while preserving the
existing platform-specific parsing and returned readiness fields.
- Around line 558-561: The channel provisioning flow around recordObserved must
stop creating agent-owned runtime directories in the host workspace. Remove or
relocate the mkdirSync call for notes, whiteboards, code, docs, and
presentations so OCI runtime directory creation and ownership remain handled by
the supported container setup, while preserving the subsequent workspace-change
cleanup and status update.

In `@src/server/db.ts`:
- Line 744: Update the channel_computers schema initialization and migration
flow so existing databases replace or widen the old backend CHECK constraint to
include 'oci' before the channel-computer insert loop runs. Preserve existing
rows and ensure both fresh and upgraded databases accept the oci seeding path.

---

Nitpick comments:
In `@src/server/channel-computers.ts`:
- Line 609: Introduce a dedicated mirrorsWorkspace(computer) predicate that
returns true only for the Apple backend, then replace the repeated
isolatedBackend(computer) plus OCI exclusion checks at all listed call sites
with this predicate. Preserve the existing early-return behavior while
centralizing the host↔guest mirroring eligibility rule.

In `@src/server/channel-storage.ts`:
- Around line 43-53: Add a per-channel memo for the resolved root used by
channelFilesystemRoot, and have channelWorkspacePath and channelFilesPath reuse
that memoized value instead of repeating the SQLite lookups for each file.
Invalidate the memo whenever the relevant channel computer/workspace rows
change, while preserving current path resolution behavior for both OCI and host
backends.
- Around line 30-36: Update ociHostStateRoot and the Windows path construction
to derive the runtime state-root suffix from the shared manifest/runtime source
instead of hardcoding “1helm-oci-v1”. Preserve the existing environment-variable
precedence and non-Windows fallback, and validate
HELM_OCI_HOST_STATE_ROOT/HELM_OCI_STATE_ROOT against the same /.../runtime/oci
format before returning them so invalid configuration produces the established
configuration error.

In `@src/server/db.ts`:
- Around line 936-942: The backend selection logic in the database
initialization flow must match channel-storage routing for invalid
HELM_CHANNEL_COMPUTER_BACKEND values. Extract or reuse a shared validated
resolver from the logic around effectiveBackend(), then update the
platformBackend/configuredBackend/backend setup in the database flow to use it,
preserving the allowed backends and platform-specific fallback.

In `@test/channel-computers-backend-child.mjs`:
- Around line 49-50: Replace the fixed 2.2-second setTimeout before the
background.txt assertion with a bounded polling loop that repeatedly checks for
the expected file/content until it matches or a deadline is reached, using a
short interval and preserving a clear timeout failure. Keep the existing
readFileSync assertion outcome for successful completion.
- Around line 89-90: Replace the broad raw-text assertion on oci-calls.log with
per-line JSON parsing, then inspect each parsed call’s args[0] operation token
and assert it is never a workspace sync, import, or export operation. Keep the
assertion focused on helper operations so command text containing words like
rsync, fsync, or resync does not trigger it.

In `@test/fake-oci-runtime.mjs`:
- Around line 139-149: Update the backup branch in the fake OCI runtime to
derive the digest written to destination/digest from the backed-up payload using
the existing createHash import, rather than a constant value. Hash the same
channel/config content that restore verifies and continue returning that
computed digest in the JSON response, so restore integrity checks detect
tampering or swapped backups.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c94c2e46-fe9b-43d9-9cbc-58fba8ed0579

📥 Commits

Reviewing files that changed from the base of the PR and between f75725c and f091325.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (57)
  • CHANGELOG.md
  • README.md
  • container/Containerfile.oci
  • deploy/1helm-lxc-runtime-v2.conf
  • deploy/1helm-lxc-unprivileged.conf
  • deploy/1helm-oci-runtime-v1.conf
  • desktop/main.cjs
  • docs/GOVERNANCE.md
  • docs/USER_GUIDE.md
  • docs/VISION.md
  • docs/release-checklist.md
  • docs/release-lifecycle.md
  • docs/release-notes-template.md
  • package.json
  • public/index.html
  • scripts/1helm-lxc-net
  • scripts/1helm-lxc-runtime
  • scripts/1helm-oci-runtime
  • scripts/install-wsl-runtime.ps1
  • scripts/package-mac-dmg.cjs
  • scripts/package-windows.cjs
  • scripts/run-test-suite.mjs
  • scripts/windows-removal.cjs
  • site/content.mjs
  • site/manual.html
  • site/public/apply-linux-release.sh
  • site/public/install-linux-units.sh
  • site/public/install-lxc-runtime.sh
  • site/public/install-oci-runtime.sh
  • site/public/install.sh
  • site/public/migrate-linux-host-contract.sh
  • site/public/uninstall-host.sh
  • site/public/update-host.sh
  • src/client/api.ts
  • src/client/channel.ts
  • src/client/onboarding.ts
  • src/client/settings.ts
  • src/server/agents.ts
  • src/server/bots.ts
  • src/server/channel-computers.ts
  • src/server/channel-storage.ts
  • src/server/db.ts
  • src/server/index.ts
  • src/server/updates.ts
  • test/channel-computers-backend-child.mjs
  • test/channel-computers-backend-migration.mjs
  • test/channel-computers-isolated-backends.mjs
  • test/channel-computers.mjs
  • test/desktop.mjs
  • test/fake-lxc-runtime.mjs
  • test/fake-oci-runtime.mjs
  • test/fake-wsl.mjs
  • test/native-world.mjs
  • test/production-browser.mjs
  • test/routing-antigravity.mjs
  • test/site.mjs
  • test/update-service.mjs
💤 Files with no reviewable changes (11)
  • deploy/1helm-lxc-unprivileged.conf
  • scripts/1helm-lxc-net
  • deploy/1helm-lxc-runtime-v2.conf
  • site/public/migrate-linux-host-contract.sh
  • test/fake-lxc-runtime.mjs
  • site/public/install-lxc-runtime.sh
  • test/fake-wsl.mjs
  • scripts/1helm-lxc-runtime
  • test/channel-computers-backend-migration.mjs
  • test/update-service.mjs
  • src/server/updates.ts

Comment thread docs/USER_GUIDE.md
Comment on lines 371 to +381
Every host update preserves:

```text
~/Library/Application Support/1Helm
~/Library/Application Support/1Helm-OCI-v1
```

On macOS that directory contains databases, credentials, workspaces, resident
state, and narrow mirrors. Linux preserves the equivalent host state under
`/var/lib/1helm`. Do not delete either data root during replacement.
state, and Apple mirrors. Linux preserves the equivalent OCI-generation state
under `/var/lib/1helm-oci-v1`. The retired data roots remain untouched and are
not imported by this generation. Do not delete either current data root during
replacement.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

ast-grep outline desktop/main.cjs --items all
rg -n -C2 'DATA_NAMESPACE|setPath\("userData"|1Helm-Runtime' \
  desktop/main.cjs scripts/windows-removal.cjs docs/USER_GUIDE.md

Repository: gitcommit90/1Helm

Length of output: 3024


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Show the guide section with line numbers
sed -n '350,395p' docs/USER_GUIDE.md | cat -n

# Look for any other Windows-specific retention guidance in the guide
rg -n -C2 'Windows|LOCALAPPDATA|1Helm-Runtime|1Helm-OCI-v1|userData|retained' docs/USER_GUIDE.md

Repository: gitcommit90/1Helm

Length of output: 5074


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Inspect the Windows cleanup / data-root handling in the app code
sed -n '1,170p' desktop/main.cjs | cat -n

# Inspect the Windows removal helper
sed -n '1,220p' scripts/windows-removal.cjs | cat -n

Repository: gitcommit90/1Helm

Length of output: 13400


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Search for any docs that mention the Windows runtime root by name
rg -n -C2 '1Helm-Runtime|1Helm-OCI-v1|LOCALAPPDATA' .

Repository: gitcommit90/1Helm

Length of output: 14683


Document the Windows state roots. Add %APPDATA%\1Helm-OCI-v1 and %LOCALAPPDATA%\1Helm-Runtime here so the recovery guidance covers both durable Windows paths.

🤖 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 `@docs/USER_GUIDE.md` around lines 371 - 381, Update the host-update
preservation guidance in USER_GUIDE.md to document both Windows durable state
roots: %APPDATA%\1Helm-OCI-v1 and %LOCALAPPDATA%\1Helm-Runtime. Keep the
existing macOS and Linux paths and replacement/deletion guidance unchanged.

Comment thread scripts/1helm-oci-runtime
Comment on lines +167 to +184
ensure_network() {
local owner="$1" installation network labels
installation="$(owner_installation "$owner")"
network="1helm-$installation"
if "${PODMAN[@]}" network exists "$network"; then
labels="$("${PODMAN[@]}" network inspect --format '{{json .Labels}}' "$network")"
python3 - "$labels" "$installation" <<'PY'
import json, sys
labels=json.loads(sys.argv[1] or "{}")
if labels.get("com.1helm.managed") != "true" or labels.get("com.1helm.installation") != sys.argv[2]:
raise SystemExit("network ownership labels do not match")
PY
else
"${PODMAN[@]}" network create --disable-dns=false \
--label com.1helm.managed=true --label "com.1helm.installation=$installation" "$network" >/dev/null
fi
printf '%s' "$network"
}

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

ensure_network check-then-create runs outside the identity lock.

network exists followed by network create is a TOCTOU: two concurrent create invocations for channels of the same installation both miss, and the loser's podman network create fails, aborting provisioning under set -e. The flock in ensure_network_identity (Line 194) is acquired later and does not cover this. Either take the same lock here or treat an "already exists" create failure as success.

🤖 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/1helm-oci-runtime` around lines 167 - 184, Update ensure_network so
its network exists/create sequence is serialized with the same installation
identity lock used by ensure_network_identity, or explicitly tolerate a
concurrent podman network create failure when the network now exists. Preserve
the ownership-label validation after either path, ensuring concurrent
provisioning for the same installation does not abort under set -e.

Comment thread scripts/1helm-oci-runtime
Comment on lines +368 to +380
for _ in {1..100}; do
# Podman can briefly fail name-based --user lookup while it reconstructs
# its ephemeral runroot after a host reboot even though the persisted
# image passwd database is intact. These identities are pinned by the
# installed runtime manifest and image contract, so use their numeric form
# for the readiness probe and every subsequent exec boundary.
if "${PODMAN[@]}" exec --user 0:0 "$name" /bin/sh -c 'test "$(cat /var/lib/1helm/owner)" = "$1" && test -d /workspace && test -d /home/agent' 1helm-start "$owner" >/dev/null 2>&1; then
verify_container "$name" "$owner"
return
fi
sleep 0.1
done
die "container did not pass its ownership and storage health check"

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

10s readiness budget is tight for a systemd container's first boot.

100 × 0.1s covers systemd reaching a usable state; on a loaded host or first boot after an image build this can expire and die, which then trips the create-cleanup path even though the container was healthy moments later. The caller already allows 30 minutes for create. Consider a longer overall deadline with a backoff instead of a fixed 10s.

🤖 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/1helm-oci-runtime` around lines 368 - 380, Extend the readiness wait
in the container startup loop around verify_container beyond the current
10-second budget, using a longer deadline appropriate for systemd initialization
and create’s existing 30-minute allowance. Replace the fixed 0.1-second polling
with bounded backoff while continuing to run the ownership and storage health
probe until it succeeds or the new deadline expires, then retain the existing
failure path.

Comment thread scripts/1helm-oci-runtime
Comment on lines +568 to +584
create)
(($# == 5)) || die "create requires name, owner, CPUs, memory MiB, and image"
! container_exists "$1" || die "container already exists"
storage_existed=0
[[ -e "$(channel_root "$1")" ]] && storage_existed=1
prepare_storage "$1" "$2"
if ! create_container "$1" "$2" "$3" "$4" "$5"; then
container_exists "$1" && "${PODMAN[@]}" rm -f "$1" >/dev/null 2>&1 || true
if [[ "$storage_existed" -eq 0 ]]; then
failed_root="$(channel_root "$1")"
[[ "$failed_root" == "$CHANNELS_ROOT/$1" ]] || die "unsafe failed-create cleanup target"
rm -rf -- "$failed_root"
fi
exit 1
fi
start_container "$1" "$2"
;;

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 | 🏗️ Heavy lift

The failed-create cleanup path is unreachable for the most likely failures.

create_container reports errors through die, which exit 1s the whole script (Line 18). So when verify_container at Line 360 rejects the new container, the if ! create_container body never runs: the container and its storage are left behind, and every subsequent create dies at Line 570 with "container already exists" — provisioning is wedged until an operator intervenes manually.

Secondly, calling create_container in a condition context suppresses set -e inside it, so a failure in ensure_network/ensure_network_identity no longer stops the function and podman create proceeds with empty --network/--ip values.

Use an EXIT/ERR trap for cleanup (as restore_container does at Line 497) and call create_container unconditionally.

🤖 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/1helm-oci-runtime` around lines 568 - 584, Update the create branch
to use an EXIT/ERR cleanup trap, following the pattern in restore_container, so
failures from create_container—including die during verification—remove the
newly created container and storage when storage_existed is false. Invoke
create_container unconditionally rather than in an if condition, preserving set
-e behavior for ensure_network, ensure_network_identity, and podman create
failures. Clear or disable the trap after successful creation before starting
the container.

Comment on lines 20 to 23
function Test-PinnedWslRuntime {
$versionOutput = (& "$env:SystemRoot\System32\wsl.exe" --version 2>&1 | Out-String)
return $LASTEXITCODE -eq 0 -and $versionOutput -match [regex]::Escape($wslVersion)
$output = (& $wsl --version 2>&1 | Out-String)
return $LASTEXITCODE -eq 0 -and $output -match [regex]::Escape($wslVersion)
}

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 | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Does wsl.exe --list --quiet still emit UTF-16 output with embedded null characters when captured in PowerShell 5.1 or PowerShell 7 as of 2026?

💡 Result:

Yes, as of July 2026, wsl.exe continues to emit UTF-16LE (Little-Endian) encoded output by default, which manifests as embedded null characters when captured in PowerShell 5.1 or 7 [1][2][3]. While this remains the default behavior for compatibility reasons [4][5], you can resolve this and force wsl.exe to output standard UTF-8 by setting the environment variable WSL_UTF8 to 1 [1][3][5]. To apply this fix in your PowerShell session: $env:WSL_UTF8 = 1 Once this variable is set, wsl.exe commands (including wsl.exe --list --quiet) will produce UTF-8 output, allowing PowerShell to parse the text correctly without needing to strip null characters or adjust console encodings [1][5]. This workaround is widely documented and is the standard approach for ensuring compatibility with PowerShell, scripting environments, and third-party tools [1][4][3].

Citations:


🏁 Script executed:

set -euo pipefail

printf '%s\n' '--- scripts/install-wsl-runtime.ps1 (relevant lines) ---'
nl -ba scripts/install-wsl-runtime.ps1 | sed -n '1,140p'

printf '\n%s\n' '--- scripts/windows-removal.cjs references to wsl output ---'
rg -n --context 2 'replaceAll\("\\0"|wsl --list|wsl --version|wsl.exe|output\(' scripts/windows-removal.cjs scripts/install-wsl-runtime.ps1

Repository: gitcommit90/1Helm

Length of output: 252


🏁 Script executed:

set -euo pipefail

printf '%s\n' '--- scripts/install-wsl-runtime.ps1 (lines 1-140) ---'
cat -n scripts/install-wsl-runtime.ps1 | sed -n '1,140p'

printf '\n%s\n' '--- scripts/windows-removal.cjs matches ---'
rg -n -C 2 'replaceAll\("\\0"|wsl --list|wsl --version|wsl\.exe|output\(' scripts/windows-removal.cjs scripts/install-wsl-runtime.ps1

Repository: gitcommit90/1Helm

Length of output: 13159


Strip \0 from wsl.exe output before comparing. scripts/install-wsl-runtime.ps1:20-23,97wsl.exe can still return UTF-16LE text with embedded nulls in PowerShell, so both the version check and the distro-name list can miss valid matches and trigger unnecessary reinstall/import paths. scripts/windows-removal.cjs already removes \0 for the same reason.

🤖 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 20 - 23, Normalize wsl.exe
output by removing embedded null characters before matching or comparing it.
Update Test-PinnedWslRuntime and the distro-name list handling near the related
call site so both version detection and distro matching use the cleaned output,
consistent with the existing windows-removal.cjs behavior.

rm -f -- /etc/sudoers.d/1helm-lxc-runtime /etc/1helm/lxc-runtime-v2.conf /usr/libexec/1helm-lxc-runtime /usr/libexec/1helm-lxc-net
systemctl disable --now 1helm-update.path 2>/dev/null || true
rm -f -- /etc/systemd/system/1helm.service /etc/systemd/system/1helm-update.service /etc/systemd/system/1helm-update.path
rm -f -- /etc/sudoers.d/1helm-oci-runtime /etc/1helm/oci-runtime-v1.conf /usr/libexec/1helm-oci-runtime /usr/lib/1helm-oci/Containerfile.oci

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

Remove the OCI tmpfiles policy too.

/etc/tmpfiles.d/1helm-oci.conf is part of the OCI host contract but is left behind. Remove it on uninstall so systemd-tmpfiles cannot recreate stale runtime paths after the host is removed.

Proposed fix
-rm -f -- /etc/sudoers.d/1helm-oci-runtime /etc/1helm/oci-runtime-v1.conf /usr/libexec/1helm-oci-runtime /usr/lib/1helm-oci/Containerfile.oci
+rm -f -- /etc/sudoers.d/1helm-oci-runtime /etc/1helm/oci-runtime-v1.conf /etc/tmpfiles.d/1helm-oci.conf /usr/libexec/1helm-oci-runtime /usr/lib/1helm-oci/Containerfile.oci
📝 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
rm -f -- /etc/sudoers.d/1helm-oci-runtime /etc/1helm/oci-runtime-v1.conf /usr/libexec/1helm-oci-runtime /usr/lib/1helm-oci/Containerfile.oci
rm -f -- /etc/sudoers.d/1helm-oci-runtime /etc/1helm/oci-runtime-v1.conf /etc/tmpfiles.d/1helm-oci.conf /usr/libexec/1helm-oci-runtime /usr/lib/1helm-oci/Containerfile.oci
🤖 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 `@site/public/uninstall-host.sh` at line 34, Update the uninstall cleanup
command in uninstall-host.sh to also remove /etc/tmpfiles.d/1helm-oci.conf,
preserving the existing forced removal behavior for all OCI host artifacts.

Comment thread src/server/agents.ts
Comment on lines 48 to 63
export function ensureChannelWorkspace(channelId: number): string {
const channel = q1("SELECT id,name,personal_main_owner_id FROM channels WHERE id=? AND status<>'deleted'", channelId);
if (!channel) throw new Error("Channel workspace not found.");
const root = channelRoot(channelId);
for (const dir of WORLD_DIRS) mkdirSync(join(root, dir), { recursive: true });
for (const dir of COWORK_DIRS) {
const path = join(root, "workspace", dir);
if (!existsSync(path)) {
mkdirSync(path, { recursive: true });
markWorkspaceDirty(channelId, `workspace/${dir}`, "upsert");
for (const dir of WORLD_DIRS.filter((entry) => entry !== "workspace" && entry !== "files")) mkdirSync(join(root, dir), { recursive: true });
if (!channelUsesRuntimeStorage(channelId) || existsSync(channelWorkspace(channelId))) {
mkdirSync(channelWorkspace(channelId), { recursive: true });
mkdirSync(channelFiles(channelId), { recursive: true });
for (const dir of COWORK_DIRS) {
const path = join(channelWorkspace(channelId), dir);
if (!existsSync(path)) {
mkdirSync(path, { recursive: true });
markWorkspaceDirty(channelId, `workspace/${dir}`, "upsert");
}
}
}

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

ensureChannelWorkspace no longer guarantees the workspace/files roots for OCI channels, but callers still assume it does. Runtime-owned storage means those directories appear only after the container is provisioned, so every caller that stats or resolves them can now throw a raw filesystem error instead of a domain error.

  • src/server/agents.ts#L48-L63: signal the unready state explicitly (throw a domain error, or return a flag) when runtime storage is expected but absent, so downstream surfaces render an actionable message.
  • src/server/agents.ts#L947-L950: guard the three realpathSync calls with existsSync (as line 923 already does) or rely on the new domain error from ensureChannelWorkspace.
📍 Affects 1 file
  • src/server/agents.ts#L48-L63 (this comment)
  • src/server/agents.ts#L947-L950
🤖 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 `@src/server/agents.ts` around lines 48 - 63, Update src/server/agents.ts lines
48-63 in ensureChannelWorkspace to explicitly signal when runtime-owned storage
is expected but the workspace is absent, using a domain error or equivalent flag
before downstream filesystem access. Also update src/server/agents.ts lines
947-950 to guard all three realpathSync calls with existsSync, or consistently
rely on the new ensureChannelWorkspace domain error, so callers receive an
actionable failure instead of a raw filesystem exception.

Comment on lines 558 to +561
recordObserved(computer, inspection);
run("UPDATE channel_computers SET provision_status='ready',desired_state='auto',last_update=?,last_update_attempt=?,last_error='',updated=? WHERE channel_id=?", now(), now(), now(), computer.channel_id);
for (const directory of ["notes", "whiteboards", "code", "docs", "presentations"]) mkdirSync(join(hostWorkspace(computer.channel_id), directory), { recursive: true });
run("DELETE FROM channel_workspace_changes WHERE channel_id=?", computer.channel_id);
run("UPDATE channel_computers SET provision_status='ready',desired_state='auto',synced_host_revision=host_revision,last_update=?,last_update_attempt=?,last_error='',updated=? WHERE channel_id=?", now(), now(), now(), computer.channel_id);

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

🏁 Script executed:

#!/bin/bash
rg -n 'notes|whiteboards|presentations|chown' scripts/1helm-oci-runtime

Repository: gitcommit90/1Helm

Length of output: 423


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== scripts/1helm-oci-runtime around ownership setup ==\n'
sed -n '300,560p' scripts/1helm-oci-runtime

printf '\n== src/server/channel-computers.ts around the provision path ==\n'
sed -n '520,575p' src/server/channel-computers.ts

Repository: gitcommit90/1Helm

Length of output: 17093


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "hostWorkspace|ociHostStateRoot|mkdirSync\\(" src scripts -g '!**/node_modules/**'

Repository: gitcommit90/1Helm

Length of output: 4826


mkdirSync here is still creating host-owned runtime directories in the OCI storage tree. scripts/1helm-oci-runtime only fixes ownership for workspace/, files/, and home/; it does not apply any agent ownership to the notes/, whiteboards/, code/, docs/, or presentations directories created on the host here, so a permission mismatch or post-recordObserved failure still looks possible.

🤖 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 `@src/server/channel-computers.ts` around lines 558 - 561, The channel
provisioning flow around recordObserved must stop creating agent-owned runtime
directories in the host workspace. Remove or relocate the mkdirSync call for
notes, whiteboards, code, docs, and presentations so OCI runtime directory
creation and ownership remain handled by the supported container setup, while
preserving the subsequent workspace-change cleanup and status update.

Comment on lines +1345 to 1377
if (backend === "oci") {
let helper = "", version = "", system: unknown = null, error = "";
try {
helper = resolveLxcHelper();
const prefix = process.env.HELM_LXC_HELPER_USE_SUDO === "0" || process.getuid?.() === 0 ? [] : ["-n", helper];
const executable = prefix.length ? "sudo" : helper;
const versionResult = spawnSync(executable, [...prefix, "version"], { encoding: "utf8", timeout: 10_000 });
version = versionResult.status === 0 ? String(versionResult.stdout || "").trim() : "";
const readyResult = spawnSync(executable, [...prefix, "ready"], { encoding: "utf8", timeout: 15_000 });
if (readyResult.status === 0) {
try { system = JSON.parse(String(readyResult.stdout || "")); } catch { system = String(readyResult.stdout || "").trim(); }
} else error = String(readyResult.stderr || readyResult.stdout || "LXC runtime readiness check failed.").trim();
if (platform() === "win32") {
helper = `${installationScopedRuntimeName()}:/usr/libexec/1helm-oci-runtime`;
const invocation = ociInvocation(["version"]);
const versionResult = spawnSync(invocation.command, invocation.args, { encoding: "buffer", timeout: 15_000, env: invocation.env });
version = versionResult.status === 0 ? windowsLines(versionResult.stdout as Buffer).join("").trim() : "";
const readyInvocation = ociInvocation(["ready"]);
const readyResult = spawnSync(readyInvocation.command, readyInvocation.args, { encoding: "buffer", timeout: 30_000, env: readyInvocation.env });
if (readyResult.status === 0) {
try { system = JSON.parse(Buffer.from(readyResult.stdout as Buffer).toString("utf8")); } catch { system = windowsLines(readyResult.stdout as Buffer); }
} else error = windowsLines(Buffer.concat([readyResult.stderr as Buffer || Buffer.alloc(0), readyResult.stdout as Buffer || Buffer.alloc(0)])).join(" ") || "OCI runtime readiness check failed.";
} else {
const versionInvocation = ociInvocation(["version"]);
helper = versionInvocation.command;
const versionResult = spawnSync(versionInvocation.command, versionInvocation.args, { encoding: "utf8", timeout: 15_000, env: versionInvocation.env });
version = versionResult.status === 0 ? String(versionResult.stdout || "").trim() : "";
const readyInvocation = ociInvocation(["ready"]);
const readyResult = spawnSync(readyInvocation.command, readyInvocation.args, { encoding: "utf8", timeout: 30_000, env: readyInvocation.env });
if (readyResult.status === 0) {
try { system = JSON.parse(String(readyResult.stdout || "")); } catch { system = String(readyResult.stdout || "").trim(); }
} else error = String(readyResult.stderr || readyResult.stdout || "OCI runtime readiness check failed.").trim();
}
} catch (failure) { error = (failure as Error).message; }
const supported = linux && supportedArchitecture;
const supported = (linux || windows) && supportedArchitecture;
return {
backend, supported, ready: Boolean(supported && helper && version === LXC_RUNTIME_VERSION && system && !error),
backend, supported, ready: Boolean(supported && helper && version === OCI_RUNTIME_VERSION && system && !error),
platform: platform(), architecture: process.arch, cli: helper || null, version: version || null, system,
runtime_version: LXC_RUNTIME_VERSION, status: error ? "error" : system ? "running" : "missing", error: error || null,
};
}
if (backend === "wsl") {
let cli = "", version: unknown = null, system: unknown = null, error = "";
try {
cli = resolveWslCli();
const versionResult = spawnSync(cli, ["--version"], { encoding: "buffer", timeout: 10_000 });
if (versionResult.status === 0) version = windowsLines(versionResult.stdout as Buffer);
const statusResult = spawnSync(cli, ["--status"], { encoding: "buffer", timeout: 15_000 });
if (statusResult.status === 0) system = windowsLines(statusResult.stdout as Buffer);
else error = windowsLines(Buffer.concat([statusResult.stderr as Buffer || Buffer.alloc(0), statusResult.stdout as Buffer || Buffer.alloc(0)])).join(" ") || "WSL 2 readiness check failed.";
} catch (failure) { error = (failure as Error).message; }
const supported = windows && supportedArchitecture;
const artifact = supportedArchitecture ? WSL_ROOTFS_ARTIFACTS[arm64 ? "arm64" : "amd64"] : null;
return {
backend, supported, ready: Boolean(supported && cli && system && !error),
platform: platform(), architecture: process.arch, cli: cli || null, version, system,
runtime_version: WSL_RUNTIME_VERSION, rootfs_release: WSL_ROOTFS_RELEASE,
rootfs_name: artifact?.name || null, rootfs_sha256: artifact?.sha256 || null,
runtime_version: OCI_RUNTIME_VERSION, shared_runtime: windows ? installationScopedRuntimeName() : null,
storage_authority: windows ? `\\\\wsl.localhost\\${installationScopedRuntimeName()}\\var\\lib\\1helm-oci-v1\\runtime\\oci` : ociHostStateRoot(),
status: error ? "error" : system ? "running" : "missing", error: error || null,
};

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 | 🏗️ Heavy lift

runtimeReadiness() blocks the event loop for up to ~45 s per call.

Both branches use spawnSync with 15 s + 30 s timeouts, and on Windows each call crosses into WSL (cold-start of the distribution can consume most of that budget). This function is called synchronously from several request paths — /api/computers, /api/channel-computers/runtime, /api/channels/:id/computer, /api/setup/complete, and twice more in runtime/install / runtime/start — so a stalled or missing runtime freezes the whole server, including unrelated requests and WebSocket traffic.

Consider making the probe async (spawn + await) or caching the result for a few seconds and refreshing it in the background.

🤖 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 `@src/server/channel-computers.ts` around lines 1345 - 1377, Update
runtimeReadiness() to avoid synchronous spawnSync calls blocking the event loop
during the version and readiness probes. Use asynchronous process execution with
awaited results, or reuse a short-lived cached readiness result refreshed in the
background, while preserving the existing platform-specific parsing and returned
readiness fields.

Comment thread src/server/db.ts
CREATE TABLE IF NOT EXISTS channel_computers (
channel_id INTEGER PRIMARY KEY REFERENCES channels(id) ON DELETE CASCADE,
backend TEXT NOT NULL CHECK (backend IN ('apple','lxc','wsl','native','mock')),
backend TEXT NOT NULL CHECK (backend IN ('apple','oci','native','mock')),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm nothing else rebuilds channel_computers or rewrites legacy backends.
rg -nP --type=ts -C3 "channel_computers" src/server/db.ts | rg -nP -C3 "DROP|RENAME|backend|lxc|wsl"
rg -nP -C3 "'lxc'|\"lxc\"|'wsl'|\"wsl\"" src test scripts

Repository: gitcommit90/1Helm

Length of output: 1058


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant sections in src/server/db.ts.
sed -n '700,980p' src/server/db.ts | cat -n

# Find any other channel_computers rebuild/drop logic and legacy backend rewrites.
rg -n "DROP TABLE|ALTER TABLE|CREATE TABLE IF NOT EXISTS channel_computers|backend IN \(|lxc|wsl|oci" src/server/db.ts src test scripts

Repository: gitcommit90/1Helm

Length of output: 48789


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the backend-reading path and any fallback behavior.
sed -n '1,140p' src/server/channel-storage.ts | cat -n
sed -n '1,220p' src/server/channel-computers.ts | cat -n

# Look for any schema migration helpers applied to channel_computers elsewhere.
rg -n "channel_computers|effectiveBackend|backend\s*===" src/server src/client test scripts

Repository: gitcommit90/1Helm

Length of output: 36312


Migrate channel_computers on upgrade

CREATE TABLE IF NOT EXISTS leaves the old CHECK (backend IN ('apple','lxc','wsl','native','mock')) in place on upgraded databases, so the new backend='oci' seeding path can’t populate the table. Rebuild or widen this schema before the insert loop runs, or upgraded channels won’t get control-plane rows.

🤖 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 `@src/server/db.ts` at line 744, Update the channel_computers schema
initialization and migration flow so existing databases replace or widen the old
backend CHECK constraint to include 'oci' before the channel-computer insert
loop runs. Preserve existing rows and ensure both fresh and upgraded databases
accept the oci seeding path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant