Skip to content
Open
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
191 changes: 149 additions & 42 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]
on: [workflow_call]

concurrency:
group: ci-${{ github.ref }}
Expand All @@ -15,13 +10,11 @@ jobs:
name: Lint & Typecheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- uses: pnpm/action-setup@v2
with:
version: 8
- uses: pnpm/action-setup@v5

- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'pnpm'
Expand Down Expand Up @@ -54,13 +47,11 @@ jobs:
artifact: gondolin-guest-arm64
zig-target: aarch64-linux-musl
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- uses: pnpm/action-setup@v2
with:
version: 8
- uses: pnpm/action-setup@v5

- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'pnpm'
Expand Down Expand Up @@ -108,13 +99,11 @@ jobs:
needs: [check]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- uses: pnpm/action-setup@v2
with:
version: 8
- uses: pnpm/action-setup@v5

- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'pnpm'
Expand Down Expand Up @@ -179,7 +168,7 @@ jobs:
needs: [check]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- uses: actions/setup-node@v4
with:
Expand Down Expand Up @@ -209,9 +198,9 @@ jobs:
needs: [check]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: '24'

Expand Down Expand Up @@ -253,19 +242,17 @@ jobs:
needs: [check, build-guest, krun-linux-build, krun-runner-package-smoke]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- uses: pnpm/action-setup@v2
with:
version: 8
- uses: pnpm/action-setup@v5

- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'pnpm'

- name: Download guest assets
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: gondolin-guest-x64
path: .
Expand Down Expand Up @@ -322,19 +309,17 @@ jobs:
needs: [check, build-guest]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- uses: pnpm/action-setup@v2
with:
version: 8
- uses: pnpm/action-setup@v5

- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'pnpm'

- name: Download guest assets
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: gondolin-guest-x64
path: .
Expand Down Expand Up @@ -373,13 +358,137 @@ jobs:
run: |
NODE_BIN="$(node -p 'process.execPath')"
make -C guest test NODE="$NODE_BIN"
make -C host test NODE="$NODE_BIN"
pnpm --dir host test
env:
GONDOLIN_GUEST_DIR: ${{ github.workspace }}/guest/image/out
WS_TIMEOUT: "120000"
# Enable VM/QEMU logs to help diagnose boot/hang issues in CI.
GONDOLIN_DEBUG: "net"

windows-host:
name: Windows host (QEMU)
needs: [check, build-guest]
runs-on: windows-latest
steps:
- uses: actions/checkout@v6

- uses: pnpm/action-setup@v5

- uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'pnpm'

- name: Install QEMU (Windows)
shell: pwsh
run: |
$url = 'https://qemu.weilnetz.de/w64/2026/qemu-w64-setup-20260415.exe'
$expected = '736e125e044149611c47510d5696bc877b8df741655f229a3e9348f31bd49bf2b0105c78717888f5259aac3708e51fdc9d2c45d919becec5ec9398a08c8d435a'
$installer = Join-Path $env:RUNNER_TEMP 'qemu-w64-setup-20260415.exe'
$installDir = Join-Path $env:ProgramFiles 'qemu'
$sevenZip = Get-Command 7z.exe -ErrorAction SilentlyContinue
if (-not $sevenZip) {
$sevenZip = Get-Command 7z -ErrorAction SilentlyContinue
}
if (-not $sevenZip) {
throw '7z is required to extract the QEMU installer on windows-latest'
}

Invoke-WebRequest -Uri $url -OutFile $installer
$actual = (Get-FileHash $installer -Algorithm SHA512).Hash.ToLowerInvariant()
if ($actual -ne $expected) {
throw "QEMU installer SHA512 mismatch: expected $expected got $actual"
}

if (Test-Path $installDir) {
Remove-Item -Recurse -Force $installDir
}
New-Item -ItemType Directory -Force $installDir | Out-Null
& $sevenZip.Source x '-y' "-o$installDir" $installer | Out-Host
if ($LASTEXITCODE -ne 0) {
throw 'Failed to extract the QEMU installer with 7z'
}

$qemuExe = Join-Path $installDir 'qemu-system-x86_64.exe'
$qemuGuiExe = Join-Path $installDir 'qemu-system-x86_64w.exe'
if (-not (Test-Path $qemuExe) -and -not (Test-Path $qemuGuiExe)) {
throw 'Extracted QEMU payload did not contain qemu-system-x86_64(.exe/.w.exe)'
}

Write-Host "Extracted QEMU into $installDir"

- name: Resolve QEMU path and accelerators
shell: pwsh
run: |
$candidates = @(
(Join-Path $env:ProgramFiles 'qemu\qemu-system-x86_64.exe'),
(Join-Path $env:ProgramFiles 'qemu\qemu-system-x86_64w.exe')
)
$qemu = $candidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if (-not $qemu) {
throw 'Failed to resolve qemu-system-x86_64(.exe/.w.exe) after extraction'
}

"WINDOWS_QEMU=$qemu" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
& $qemu -accel help

- name: Download guest assets
uses: actions/download-artifact@v8
with:
name: gondolin-guest-x64
path: .

- name: Extract guest assets
shell: pwsh
run: |
New-Item -ItemType Directory -Force guest/image/out | Out-Null
tar -xzf gondolin-guest-x64.tar.gz -C guest/image/out

- name: Install dependencies
run: pnpm install

- name: Build
run: pnpm run build

- name: Run host tests
timeout-minutes: 25
run: pnpm --dir host test
env:
GONDOLIN_GUEST_DIR: ${{ github.workspace }}\guest\image\out
WS_TIMEOUT: "120000"
GONDOLIN_DEBUG: "net"

- name: CLI smoke test (Windows QEMU)
timeout-minutes: 10
shell: pwsh
env:
GONDOLIN_GUEST_DIR: ${{ github.workspace }}\guest\image\out
run: |
$accelHelp = (& $env:WINDOWS_QEMU -accel help 2>&1 | Out-String)
$hasWhpx = $accelHelp -match '(^|\r?\n)\s*whpx\s*(\r?\n|$)'

$args = @('host/dist/bin/gondolin.js', 'exec', '--', '/bin/sh', '-lc', 'echo WINDOWS_CLI_SMOKE_OK')
$output = & node @args 2>&1
$text = ($output | ForEach-Object { $_.ToString() }) -join "`n"
Write-Host $text

if ($LASTEXITCODE -ne 0) {
throw 'Windows CLI smoke test failed'
}
if ($text -notmatch 'WINDOWS_CLI_SMOKE_OK') {
throw 'Windows CLI smoke test did not produce the success marker'
}

if ($hasWhpx) {
if ($text -match 'using QEMU TCG software emulation') {
throw 'Unexpected TCG warning while WHPX is available'
}
} else {
if ($text -notmatch 'using QEMU TCG software emulation') {
throw 'Expected the Windows CLI to warn when falling back to TCG'
}
}

# Optional: Publish preview package for PRs (uses trusted publishing)
# Uncomment to enable publishing test versions from PRs
# publish-preview:
Expand All @@ -391,13 +500,11 @@ jobs:
# contents: read
# id-token: write
# steps:
# - uses: actions/checkout@v4
# - uses: actions/checkout@v6
#
# - uses: pnpm/action-setup@v2
# with:
# version: 8
# - uses: pnpm/action-setup@v5
#
# - uses: actions/setup-node@v4
# - uses: actions/setup-node@v6
# with:
# node-version: '24'
# registry-url: 'https://registry.npmjs.org'
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
on: pull_request

jobs:
ci:
if: github.event.pull_request.head.repo.full_name != github.repository
uses: ./.github/workflows/ci.yml
5 changes: 5 additions & 0 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
on: push

jobs:
ci:
uses: ./.github/workflows/ci.yml
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ Gondolin uses `GONDOLIN_DEFAULT_IMAGE` (default: `alpine-base:latest`).

Requirements:

| macOS | Linux (Debian/Ubuntu) |
| ------------------------ | --------------------------------------------- |
| `brew install qemu node` | `sudo apt install qemu-system-arm nodejs npm` |
| macOS | Linux (Debian/Ubuntu) | Windows x64 |
| ------------------------ | --------------------------------------------- | ----------- |
| `brew install qemu node` | `sudo apt install qemu-system-arm nodejs npm` | Install Node.js 23.6+ and a QEMU build with WHPX support, then put `qemu-system-x86_64` or `qemu-system-x86_64w` on `PATH` |

Optional experimental libkrun backend setup:

Expand All @@ -85,7 +85,8 @@ make krun-runner
```

Published installs of `@earendil-works/gondolin` also include platform-specific
optional runner packages for supported targets.
optional runner packages for supported targets. `krun` is currently unsupported
on Windows hosts; use the default `qemu` backend there.

This stages `libkrun` under `.cache/` (no global install) and builds the local
runner helper at `host/krun-runner/zig-out/bin/gondolin-krun-runner`.
Expand All @@ -111,7 +112,8 @@ When `vmm=krun` is selected, Gondolin requires krun boot assets from the selecte
image manifest (`assets.krunKernel` and optional `assets.krunInitrd`).
For custom kernels/initrds, provide an explicit `sandbox.imagePath` asset object.

> Linux and macOS are supported. ARM64 is the most tested runtime path today.
> The QEMU backend is supported on macOS, Linux, and Windows x64.
> `krun` remains supported on macOS/Linux only. ARM64 is the most tested runtime path today.
> Linux x86_64 `make krun-runner` is covered by CI smoke builds.

## Feature Highlights
Expand All @@ -138,6 +140,7 @@ For custom kernels/initrds, provide an explicit `sandbox.imagePath` asset object
- [SSH](https://earendil-works.github.io/gondolin/ssh/)
- [Custom Images](https://earendil-works.github.io/gondolin/custom-images/)
- [Architecture Overview](https://earendil-works.github.io/gondolin/architecture/)
- [Windows QEMU showcase](docs/windows-showcase.md)
- [VM Backends (QEMU vs krun)](docs/backends.md)
- [Security Design](https://earendil-works.github.io/gondolin/security/)
- [Limitations](https://earendil-works.github.io/gondolin/limitations/)
Expand Down
1 change: 1 addition & 0 deletions docs/backends.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ This page is the authoritative backend-parity reference for SDK/CLI behavior.
### krun

- Guest architecture must match the **host** architecture
- Windows hosts are not supported; use `qemu` there
- Requires a **libkrunfw-compatible kernel**
- Gondolin requires image manifest krun boot assets:
- `assets.krunKernel`
Expand Down
9 changes: 5 additions & 4 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ want to be able to tightly control the file system, for convenience of the agent
and to control persistence.

Gondolin gives you that. Lightweight micro-VMs (QEMU by default, optional
libkrun backend) boot in under a second on your Mac or Linux machine. The
network stack and virtual filesystem are implemented entirely in JavaScript,
giving you complete programmatic control over what the sandbox can access and
what secrets it can use.
libkrun backend on macOS/Linux) boot in under a second on your Mac, Linux, or
Windows machine. The network stack and virtual filesystem are implemented
entirely in JavaScript, giving you complete programmatic control over what the
sandbox can access and what secrets it can use.

This documentation helps you get started with it. We also welcome your feedback
as this is an early project and we are eager to learn more about how you want
Expand Down Expand Up @@ -65,6 +65,7 @@ await vm.close();

- [Workloads](./workloads.md): typical workloads and lifecycles
- [CLI](./cli.md): run shells/commands, list sessions, and attach to running VMs
- [Windows QEMU showcase](./windows-showcase.md): end-to-end Git Bash walkthrough for Windows x64
- [Secrets Handling](./secrets.md): placeholder-based secret injection and host allowlists
- [Ingress](./ingress.md): expose guest HTTP servers on the host
- [SSH](./ssh.md): enable SSH access to the guest with safe defaults
Expand Down
9 changes: 7 additions & 2 deletions docs/limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ Notable gaps today:

See [VM Backends (QEMU vs krun)](./backends.md) for the maintained matrix.

## No Windows support
## Windows support notes

The host side of Gondolin is currently supported on macOS and Linux.
The QEMU backend is supported on Windows x64, where Gondolin uses loopback TCP
endpoints instead of Unix sockets and prefers WHPX when the installed
`qemu-system-x86_64` build advertises it.

The experimental `krun` backend remains unsupported on Windows; use
`vmm=qemu` there.
Loading