Skip to content
Closed
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
81 changes: 81 additions & 0 deletions .github/workflows/platform-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,84 @@ jobs:
- run: npm run check -w run-spaceapp
- run: npm run pack:dry-run -w run-spaceapp
- run: node packages/run-spaceapp/bin/spaceapp.mjs --version

# Issue #63 acceptance: a real rootless Podman install of the small profile on Linux — install,
# wait for /readyz, back up, purge, and leave nothing running.
podman-linux:
name: Launcher / ubuntu-latest / Podman rootless
runs-on: ubuntu-latest
timeout-minutes: 40
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: "22.23.0"
cache: npm

# ubuntu-latest ships Podman 4.9; the launcher supports Podman >= 5, so install it from the
# libcontainers (Kubic) repository, the upstream packagers' channel for current Podman on Ubuntu.
- name: Install Podman 5
run: |
. /etc/os-release
echo "deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${VERSION_ID}/ /" | sudo tee /etc/apt/sources.list.d/devel-kubic-libcontainers-unstable.list
curl -fsSL "https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${VERSION_ID}/Release.key" | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/devel-kubic-libcontainers-unstable.gpg > /dev/null
sudo apt-get update
sudo apt-get install -y podman
podman --version

# The supported Compose provider is Docker Compose v2. The runner image carries the Docker CLI
# plugin; expose it where Podman's provider lookup finds it, then prove `podman compose` works.
- name: Provide the Docker Compose v2 provider for podman compose
run: |
if ! podman compose version >/dev/null 2>&1; then
plugin="$(find /usr/libexec/docker /usr/lib/docker /usr/local/lib/docker -name docker-compose -type f 2>/dev/null | head -n 1)"
test -n "$plugin"
sudo ln -sf "$plugin" /usr/local/bin/docker-compose
fi
podman compose version

# Rootless prerequisites: subordinate id ranges and the API socket the Compose provider dials.
- name: Prepare rootless Podman
run: |
grep -q "^runner:" /etc/subuid || sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 runner
mkdir -p "/run/user/$(id -u)/podman"
nohup podman system service --time=0 "unix:///run/user/$(id -u)/podman/podman.sock" >/tmp/podman-service.log 2>&1 &
podman info --format 'rootless={{.Host.Security.Rootless}} cgroup={{.Host.CgroupVersion}} version={{.Version.Version}}'

- name: Install workspace dependencies
run: npm ci
env:
ELECTRON_SKIP_BINARY_DOWNLOAD: "1"

- name: Install SpaceApp small on rootless Podman
run: |
node packages/run-spaceapp/bin/spaceapp.mjs install --engine podman --profile small --non-interactive \
--answers '{"confirm":true,"profile":"small","access":"isolated","engine":"podman","companions":false,"telemetry":false,"open":false}'
env:
SPACEAPP_HOME: ${{ runner.temp }}/spaceapp-home

- name: Wait for the application to answer on 127.0.0.1:4911
run: |
for attempt in $(seq 1 60); do
if curl -fsS http://127.0.0.1:4911/readyz; then exit 0; fi
sleep 5
done
echo "SpaceApp did not become ready" >&2
exit 1

- name: Back up the Podman installation
run: node packages/run-spaceapp/bin/spaceapp.mjs backup
env:
SPACEAPP_HOME: ${{ runner.temp }}/spaceapp-home

- name: Uninstall with data purge and confirm nothing is left running
run: |
node packages/run-spaceapp/bin/spaceapp.mjs uninstall --purge-data --non-interactive \
--answers '{"purgeConfirm":"DELETE"}'
test -z "$(podman ps -q)"
test -z "$(podman ps -aq)"
env:
SPACEAPP_HOME: ${{ runner.temp }}/spaceapp-home
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ Coordinate multi-step tasks conversationally through the Room Agent or speak nat
- Minimum (`small` profile): 4 CPU cores, 8 GB-class RAM (at least 7 GiB reported by the operating system), 6.5 GiB free disk.
- `large` profile (adds the managed browser): 8 CPU cores, 15 GiB RAM, 11 GiB free disk.
- **Docker**: If Docker is not installed, the installer automatically installs official Docker Desktop (Windows/macOS) or Docker Engine (Linux).
- **Podman (alternative, Linux/macOS)**: `install --engine podman` runs the same stack on rootless Podman >= 5 (cgroup v2) through the Docker Compose v2 provider (`podman compose`). Podman is never installed by the launcher; see the run-spaceapp README's "Container engines" section for requirements, the macOS machine sizing check, and the backup/restore migration used instead of in-place engine switching.

### Run the Installation Command

Expand Down
34 changes: 34 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,40 @@ its code signature, installs it, starts it, and waits until it is ready. Docker
Desktop uses its managed lightweight Linux environment. Apple Silicon uses the
native `linux/arm64` images and Intel Macs use `linux/amd64`.

## Podman instead of Docker (Linux and macOS)

The same stack runs on rootless Podman. Install Podman yourself first (the
launcher never installs it), then:

```bash
npx --yes run-spaceapp@latest install --engine podman
```

You need Podman >= 5 (on Linux, on cgroup v2) and its Compose provider:
Docker Compose v2, so that `podman compose version` succeeds — the stack
relies on Compose v2 semantics (profiles, file secrets, health conditions,
`--wait`), and podman-compose is not a supported provider. On Linux, start
the rootless user socket the provider connects to:

```bash
systemctl --user enable --now podman.socket
```

On macOS, create and start a Podman machine (`podman machine init &&
podman machine start`); `spaceapp doctor` compares the machine's CPUs,
memory, and disk with your profile and prints the `podman machine set`
resize command when it is too small.

The engine is stored in the configuration and used by every later command.
If Docker is missing but Podman is ready, a plain `install` picks Podman
automatically and says so. Switching engines later is refused — volumes are
engine-private — and the installer prints the supported migration instead:
`backup`, `uninstall --purge-data`, `install --engine <other>`, `restore`.
`--access host-root` stays Docker-only, and Podman on Windows is not
supported. `spaceapp doctor` reports the engine version, rootless state,
cgroup version, and whether memory/CPU limits can be delegated to your user
cgroup; when they cannot, it says so up front rather than failing late.

## Windows 11 — Command Prompt

Enable hardware virtualization and install Node.js 20.11 or newer. Open
Expand Down
57 changes: 57 additions & 0 deletions packages/run-spaceapp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,63 @@ before pausing writers. Existing data is kept in place; the launcher never delet
images, backups or volumes to free space. Separate Docker disk capacity is checked
when measurable and its availability is reported.

### Container engines: Docker or rootless Podman

Docker is the default engine. On Linux and macOS the same stack can run on
rootless Podman instead:

```bash
npx --yes run-spaceapp@latest install --engine podman
```

Requirements for the Podman engine:

- Podman >= 5 on cgroup v2 (Linux). The launcher never installs Podman; when
it is missing or too old it prints the install instructions for your
platform and stops.
- The Compose provider is Docker Compose v2, reached through `podman compose`
(the standalone `docker-compose` binary, or the Docker CLI with its compose
plugin). podman-compose is not a supported provider: the stack relies on
Compose v2 semantics (profiles, file-based secrets, `depends_on` health
conditions including `required: false`, `--wait`). Confirm with
`podman compose version`.
- Rootless Podman needs its user socket for the provider:
`systemctl --user enable --now podman.socket`.
- On macOS, a Podman machine: `podman machine init && podman machine start`.
The launcher checks the machine's CPUs, memory, and disk against the
selected profile in `doctor` and explains how to resize it
(`podman machine set --cpus N --memory MIB --disk-size GIB`).

The chosen engine is saved in the installation configuration and used by
every later command (`up`, `down`, `status`, `logs`, `update`, `backup`,
`restore`, `doctor`, `uninstall`, the provider installer, and the rest). If
Docker is absent but a supported Podman is ready, a plain `install` picks
Podman automatically and says so; `--engine docker` insists on Docker.

Engines cannot be switched on an existing installation: volumes are private
to each engine, so the data would be stranded. The installer refuses with the
supported migration — `backup` on the current engine,
`uninstall --purge-data`, `install --engine <other>`, then `restore`.

Rootless notes:

- The published images start as root inside the container and drop to
`10001:10001` themselves; the launcher deliberately does **not** set
`userns_mode: keep-id`, which breaks that startup. The default subordinate
UID mapping is correct, and backup/restore one-off containers run as `0:0`
(the mapped root) so they can read every file.
- Workspace bind mounts are rendered with the SELinux `z` relabel option
under Podman, so they work on SELinux-enforcing hosts and keep their host
ownership. Read-only workspaces stay read-only.
- Profile memory/CPU limits (`mem_limit`/`cpus`) need cgroup v2 controller
delegation to your user cgroup. `doctor` reports the engine, version,
rootless state, cgroup version, and delegation; when cpu/memory are not
delegated it says so plainly (the stack runs, limits unenforced) instead of
failing late. The support bundle carries the same facts, never secrets.
- `--access host-root` is refused with the Podman engine (rootless-only
support); use isolated access, or Docker for host-root. Podman on Windows
is not supported.

### Telemetry

Telemetry is off unless you turn it on. The install wizard lists what is sent
Expand Down
2 changes: 1 addition & 1 deletion packages/run-spaceapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
],
"scripts": {
"test": "node --test tests/*.test.mjs",
"check": "node --check bin/spaceapp.mjs && node --check src/package-info.mjs && node --check src/index.mjs && node --check src/cli.mjs && node --check src/storage.mjs && node --check src/telemetry.mjs && node --check src/prerequisites.mjs",
"check": "node --check bin/spaceapp.mjs && node --check src/package-info.mjs && node --check src/index.mjs && node --check src/cli.mjs && node --check src/storage.mjs && node --check src/telemetry.mjs && node --check src/prerequisites.mjs && node --check src/engine.mjs",
"pack:dry-run": "npm pack --dry-run"
},
"repository": {
Expand Down
Loading