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
40 changes: 39 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,43 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.0.29] - 2026-07-30

### Changed

- Linux and Windows channel computers now use one durable OCI container per
ordinary channel. Linux runs Podman natively; Windows hosts all channel
containers inside one installation-scoped managed WSL 2 runtime.
- OCI workspace, Files, home, and channel state are runtime-owned and
authoritative. Agent commands, terminals, Files, and Cowork see the same
storage directly without whole-workspace synchronization in the command
path. Containers retain files, tools, processes, network identity, and live
CPU/memory controls across stop/start.
- Channel archive creates a digest-qualified recovery backup. A missing OCI
world can be reconstructed only from an exact ownership-checked backup whose
SHA-256 matches; unsafe labels, mounts, paths, or deletion targets fail
closed.
- This runtime generation is a deliberate clean start. Desktop state lives in
`1Helm-OCI-v1`, Linux state lives in `/var/lib/1helm-oci-v1`, and retired LXC,
per-channel WSL, and application data are neither bridged, copied, imported,
converted, nor deleted.
- The embedded ReRouted engine advances to 0.5.10 so Google Antigravity Flash
and Pro streaming accepts CRLF-delimited Gemini SSE frames instead of
completing with empty output.
- The hardened Linux systemd service permits only Podman/netavark's ephemeral
runtime trees, while persistent network configuration and libpod scratch
stay inside 1Helm-owned roots. The installed service—not only direct root
diagnostics—can now verify and provision OCI channel computers.
- Repeat Linux installation preserves the root-owned OCI runtime and existing
channel storage permissions instead of recursively rewriting the new state
namespace as the application service user.
- Linux installs declare Podman's ephemeral runtime roots through systemd
tmpfiles, so the hardened service starts on a fresh host and after reboot
before any container has happened to create those directories.
- Windows packaging builds the app and Squirrel staging tree beneath one fresh
drive-root scratch directory, keeping deeply nested runtime dependencies
inside Squirrel's legacy 260-character path limit.

## [0.0.28] - 2026-07-29

### Fixed
Expand Down Expand Up @@ -826,7 +863,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.28...HEAD
[Unreleased]: https://github.com/gitcommit90/1Helm/compare/v0.0.29...HEAD
[0.0.29]: https://github.com/gitcommit90/1Helm/compare/v0.0.28...v0.0.29
[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
Expand Down
29 changes: 17 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,17 @@ On Apple Silicon:
4. Complete Captain → Providers → Workspace. If required, approve Apple's
signed container runtime once during workspace creation.

Application state lives under `~/Library/Application Support/1Helm` and every
This OCI generation starts in `~/Library/Application Support/1Helm-OCI-v1`;
the retired data directory is left untouched and is never imported. Every
update preserves it — credentials, databases, resident state, files, and
workspaces. Profile → Check for updates asks the Mac running 1Helm—not the
device displaying the web UI—to download and verify the signed update.

Windows 11 x64 gets a [Setup executable](https://1helm.com/download/windows)
that provisions one private WSL 2 world per channel. Linux hosts use a verified
installer that provisions a durable systemd service with an atomic,
that provisions one installation-scoped WSL 2 runtime and one durable OCI
container per ordinary channel. Linux hosts run the same channel containers
natively under Podman. The verified installer provisions a durable systemd
service with an atomic,
digest-verified, health-checked updater — see the
[Linux install guide](https://1helm.com/manual/install-linux). Whichever platform,
it works best on a dedicated machine: your crew works around the clock, and
Expand Down Expand Up @@ -278,8 +281,8 @@ and an audit trail. A prompt saying “use this service” is not a connector.
| Platform | Current contract |
|---|---|
| **Apple Silicon macOS 26** | Native desktop product and real isolated Linux computer per resident (Apple `container machine`, `home-mount=none`). |
| **Linux / CI** | Supported headless systemd host with one unprivileged LXC per resident (subordinate UID/GID mapping, exact ownership checks); CI may select an explicit test backend. |
| **Windows 11 x64** | Native desktop product with one private WSL 2 world per resident (Windows-drive mounts and interop disabled); the Setup executable ships with every release and its signature status is disclosed. |
| **Linux / CI** | Supported headless systemd host with one durable Podman OCI container per resident, runtime-owned storage, and exact ownership checks; CI may select an explicit test backend. |
| **Windows 11 x64** | Native desktop product with one installation-scoped WSL 2 OCI runtime and one durable container per resident; Windows-drive mounts and interop are disabled. |
| **iPhone and iPad** | App Store gateway to an already configured HTTPS 1Helm host; sessions live in the device-only iOS Keychain. |
| **Android 7+** | Directly distributed signed universal APK gateway; sessions are encrypted by a key held in Android Keystore. |

Expand All @@ -305,9 +308,9 @@ A fresh data directory opens first-run setup. The source runtime defaults to
| Environment variable | Default | Meaning |
|---|---|---|
| `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.28` | Versioned channel-machine image contract. |
| `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.29` | Versioned channel-machine image contract. |

### Agent-first JSON CLI

Expand Down Expand Up @@ -335,7 +338,7 @@ need an external database or a server transpilation step.
| Control plane | `node:http`, WebSocket, additive SQLite migrations. |
| Client | Vanilla TypeScript bundled with esbuild and Tailwind CSS. |
| Model routing | Embedded ReRouted headless engine, private internal gateway, account pools, retries, routes, quotas, and logs. |
| Computers | Defensive argv-only Apple `container machine`, narrow root-owned unprivileged LXC, and private WSL 2 backends; explicit `native`/`mock` test seams. |
| Computers | Defensive argv-only Apple `container machine`; native Podman OCI on Linux; one shared managed WSL 2/Podman runtime on Windows; explicit `native`/`mock` test seams. |
| Terminal | `node-pty`; ordinary terminals enter their channel VM while Skipper remains native. |
| Memory | Curated records with provenance plus an isolated Mnemosyne SQLite store per identity. |
| Scheduling | Durable obligations, wake reconciliation, lifecycle safety, repair, update, and pressure-aware sizing. |
Expand Down Expand Up @@ -373,10 +376,12 @@ the complete `npm test` contract.
## Security boundary

- Residents use separate Linux worlds: Apple machines with no Mac home mount,
unprivileged LXC with subordinate host IDs, or private WSL 2 distributions
with Windows-drive mounts and interop disabled.
or durable OCI containers on Linux and inside Windows' one shared managed
WSL runtime. Windows-drive mounts and interop are disabled.
- Skipper's host tools require Captain-authorized provenance.
- Workspace file mirrors are channel-scoped, size-bounded, and symlink-contained.
- OCI workspace storage is runtime-owned and authoritative; Files and Cowork
access it directly through a channel-scoped boundary. Apple mirrors remain
size-bounded and symlink-contained.
- Provider and connection credentials stay in host-owned storage.
- Registration, sessions, JSON bodies, uploads, collaboration access, and
gateway keys are bounded and independently controlled.
Expand Down
39 changes: 39 additions & 0 deletions container/Containerfile.oci
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
FROM docker.io/library/ubuntu:24.04@sha256:4fbb8e6a8395de5a7550b33509421a2bafbc0aab6c06ba2cef9ebffbc7092d90

ARG AGENT_UID=1000
ARG AGENT_GID=1000
ENV container=oci DEBIAN_FRONTEND=noninteractive

RUN test "${AGENT_UID}" -ge 1 && test "${AGENT_GID}" -ge 1 \
&& apt-get update && apt-get install -y --no-install-recommends \
bash build-essential ca-certificates coreutils cron curl dbus file findutils \
git gzip iproute2 iputils-ping jq less locales man-db nano openssh-client \
procps python3 python3-pip rsync sudo systemd systemd-sysv tar tzdata unzip \
vim-tiny wget xz-utils zip \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& existing_group="$(getent group "${AGENT_GID}" | cut -d: -f1 || true)" \
&& existing_user="$(getent passwd "${AGENT_UID}" | cut -d: -f1 || true)" \
&& if test -z "${existing_group}"; then groupadd --gid "${AGENT_GID}" agent; elif test "${existing_group}" != agent; then groupmod --new-name agent "${existing_group}"; fi \
&& if test -z "${existing_user}"; then useradd --uid "${AGENT_UID}" --gid "${AGENT_GID}" --create-home --shell /bin/bash agent; elif test "${existing_user}" != agent; then usermod --login agent --home /home/agent --move-home --gid "${AGENT_GID}" --shell /bin/bash "${existing_user}"; else usermod --home /home/agent --move-home --gid "${AGENT_GID}" --shell /bin/bash agent; fi \
&& mkdir -p /workspace/files /var/lib/1helm /etc/sudoers.d \
&& chown -R "${AGENT_UID}:${AGENT_GID}" /workspace /home/agent \
&& printf 'agent ALL=(ALL) NOPASSWD:ALL\n' >/etc/sudoers.d/agent \
&& chmod 0440 /etc/sudoers.d/agent \
&& printf '%s\n' '1helm-channel-oci-v1' >/var/lib/1helm/image-contract \
&& : >/etc/machine-id \
&& mkdir -p /var/lib/dbus \
&& : >/var/lib/dbus/machine-id \
&& systemctl set-default multi-user.target \
&& systemctl mask \
apt-daily.service apt-daily.timer apt-daily-upgrade.service apt-daily-upgrade.timer \
console-getty.service dev-hugepages.mount getty@.service man-db.timer systemd-logind.service \
dpkg-db-backup.timer e2scrub_all.timer e2scrub_reap.service fstrim.timer motd-news.timer \
systemd-ask-password-console.path systemd-ask-password-wall.path systemd-pstore.service \
systemd-sysext.service systemd-sysext.socket systemd-tmpfiles-clean.service \
systemd-tmpfiles-clean.timer systemd-tmpfiles-setup.service systemd-update-utmp.service \
sys-fs-fuse-connections.mount \
&& (systemctl disable cron.service || true)

STOPSIGNAL SIGRTMIN+3
CMD ["/sbin/init"]
12 changes: 0 additions & 12 deletions deploy/1helm-lxc-runtime-v2.conf

This file was deleted.

14 changes: 0 additions & 14 deletions deploy/1helm-lxc-unprivileged.conf

This file was deleted.

7 changes: 7 additions & 0 deletions deploy/1helm-oci-runtime-v1.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ONEHELM_OCI_RUNTIME_VERSION="1helm-oci-runtime-v1"
ONEHELM_OCI_STATE_ROOT="/var/lib/1helm-oci-v1/runtime/oci"
ONEHELM_OCI_RUN_ROOT="/run/1helm-oci"
ONEHELM_OCI_CONTAINERFILE="/usr/lib/1helm-oci/Containerfile.oci"
ONEHELM_OCI_SERVICE_USER="1helm"
ONEHELM_OCI_AGENT_UID="1000"
ONEHELM_OCI_AGENT_GID="1000"
14 changes: 9 additions & 5 deletions desktop/main.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ const { createNativeUpdateService } = require("./updater.cjs");
const { allowedRemoteUrl, desktopGatewayAction, isHostedWorkspaceOrigin, normalizeRemoteOrigin } = require("./workspace-target.cjs");

const LOOPBACK = "127.0.0.1";
// The OCI architecture is an intentional clean start. Keep the retired
// installation's Application Support/AppData tree untouched and never import
// it implicitly into this runtime generation.
const DATA_NAMESPACE = "1Helm-OCI-v1";
app.setPath("userData", path.join(app.getPath("appData"), DATA_NAMESPACE));
let mainWindow = null;
let authWindow = null;
let localOrigin = "";
Expand Down Expand Up @@ -45,8 +50,8 @@ function handleSquirrelEvent() {
if (event === "--squirrel-install" || event === "--squirrel-updated") {
spawnSync(updateExe, ["--createShortcut", exe], { stdio: "ignore", windowsHide: true });
} else if (event === "--squirrel-uninstall") {
const dataRoot = path.join(String(process.env.APPDATA || ""), "1Helm");
const wslRoot = path.join(path.dirname(dataRoot), "1Helm-WSL");
const dataRoot = app.getPath("userData");
const wslRoot = path.join(String(process.env.LOCALAPPDATA || ""), "1Helm-Runtime");
const cleanup = path.resolve(__dirname, "..", "scripts", "windows-removal.cjs");
spawnSync(process.execPath, [cleanup, dataRoot, wslRoot], { env: { ...process.env, ELECTRON_RUN_AS_NODE: "1" }, stdio: "ignore", windowsHide: true, timeout: 10 * 60_000 });
spawnSync(updateExe, ["--removeShortcut", exe], { stdio: "ignore", windowsHide: true });
Expand Down Expand Up @@ -144,9 +149,8 @@ function stopAutomaticServerStartup() {

function prepareWindowsWslDataRoot() {
if (process.platform !== "win32") return;
// Per-channel virtual disks stay outside the replaceable application
// directory and beside the durable Electron userData directory.
fs.mkdirSync(path.join(path.dirname(app.getPath("userData")), "1Helm-WSL"), { recursive: true });
// One installation-scoped virtual disk stays outside the replaceable app.
fs.mkdirSync(path.join(String(process.env.LOCALAPPDATA || ""), "1Helm-Runtime"), { recursive: true });
}

function allowedLocalUrl(raw) {
Expand Down
7 changes: 4 additions & 3 deletions docs/GOVERNANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ contract as the slice hardens.
summary or rely on generated commit notes as the user-facing release record.
9. Each supported desktop platform owns its native artifact and installed-app
verification lane. The retained Apple Silicon host owns macOS signing and
notarization; Linux owns the systemd/LXC artifact and updater acceptance;
Windows 11 x64 owns Squirrel and WSL acceptance. Authenticode is optional
notarization; Linux owns the systemd/OCI artifact and updater acceptance;
Windows 11 x64 owns Squirrel and shared-WSL OCI acceptance. Authenticode is optional
until 1Helm adopts a trusted Windows signing identity; unsigned artifacts
must be identified honestly, but their signature status is not a release
blocker.
Expand All @@ -82,7 +82,8 @@ contract as the slice hardens.
Application Support, and prove signature/ticket/Gatekeeper, launch, version,
loopback behavior, and retained state on the retained release host.
- Linux verification must use the digest-qualified release archive and prove a
real systemd update, health-failure rollback, and retained `/var/lib/1helm`.
real systemd update, health-failure rollback, and retained
`/var/lib/1helm-oci-v1`.
- Windows verification must prove the Setup/Squirrel signature status, clean
install, old-to-new update, loopback health, WSL lifecycle, and retained
application data on Windows 11 x64. Do not substitute a self-signed
Expand Down
21 changes: 12 additions & 9 deletions docs/USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,10 @@ scrollback. If the host confirms that the underlying terminal session itself no
longer exists, 1Helm opens a fresh one.
Ordinary residents cannot select or enter the Captain's native host. On
supported Apple Silicon Macs, each resident runs inside its own Apple
`container machine` with `home-mount=none`; Linux systemd hosts use one
unprivileged LXC per resident; the accepted Windows implementation uses one
private WSL 2 distribution per resident with Windows-drive mounts and interop
disabled. `native` and `mock` remain explicit source/CI test seams.
`container machine` with `home-mount=none`; Linux systemd hosts use one durable
Podman OCI container per resident; Windows uses one installation-scoped WSL 2
runtime hosting one container per resident, with Windows-drive mounts and
interop disabled. `native` and `mock` remain explicit source/CI test seams.

![The full-height channel Terminal in its persistent workspace](assets/guide/terminal.png)

Expand Down Expand Up @@ -371,12 +371,14 @@ Source/developer deployments report that their host operator owns updates.
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.
Comment on lines 371 to +381

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.


Before removing 1Helm, use its removal preparation flow. It is Captain-only,
requires typed confirmation, reports backend-owned resident machines, and
Expand Down Expand Up @@ -425,8 +427,9 @@ fallback.

## Security summary

- Per-resident Apple Linux VMs have no Mac home mount; Linux LXC uses
subordinate IDs; private WSL worlds disable Windows-drive mounts and interop.
- Per-resident Apple Linux VMs have no Mac home mount. OCI residents use exact
container labels and runtime-owned storage; the shared Windows runtime
disables Windows-drive mounts and interop.
- Credentials and connectors remain host-owned and minimally brokered.
- Membership scopes data and live events; private coworker channels are not
Captain-readable without invitation.
Expand Down
Loading
Loading