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
69 changes: 69 additions & 0 deletions guix/NIX_BASELINE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Nix baseline — the Guix migration parity target

This document copiously inventories the **current Nix setup** so the Guix
implementation can reproduce **the same standards**. It is the parity checklist:
the migration is not "done" for a row until its Guix equivalent builds/boots/passes
to the same bar. Keep it updated as `flake.nix` evolves — it is the source of
truth for what Guix must match, the same way `board-spec.yaml` is for the boards.
Comment on lines +6 to +7

Status legend: ☐ not started · ◐ spiked · ☑ parity proven on a Linux runner.

## 1. Packages (`flake.nix` `packages`)
| Nix output | Source | Guix equivalent | Status |
|---|---|---|---|
| `meshd`, `meshd-linkd`, `meshd-exitd` | `packages/mesh/*.nix` | Guix package definitions (`guix/packages/mesh.scm`) | ☐ |
| `bearbrowser` | `packages/browser/bearbrowser.nix` | Guix package (LibreWolf-derived; nonguix for nonfree bits) | ☐ |
| `lampstand` | `packages/search/lampstand.nix` | Guix package | ☐ |
| `sourceos-syncd` | `packages/sourceos-syncd/default.nix` | Guix package | ☐ |
| `sourceos-boot` | `packages/sourceos-boot/default.nix` | Guix package | ☐ |

## 2. Images (`flake.nix`, via `nixos-generators`)
| Nix image | Def | Guix equivalent (`guix system image -t …`) | Status |
|---|---|---|---|
| `sourceos-installer-iso` (x86_64 / aarch64) | `images/iso-x86_64.nix`, `images/iso-aarch64.nix` | `guix system image -t iso9660` from a Guix installer profile | ☐ |
| `sourceos-image-qcow2-desktop` | desktop generator | `guix/system/desktop.scm` → `-t qcow2` | ◐ (this PR) |
| `sourceos-image-qcow2-canary` | `images/canary-x86_64.nix` | Guix canary profile → qcow2 | ☐ |
| `sourceos-image-qcow2-stable` | `images/release-x86_64.nix` | Guix stable/release profile → qcow2 | ☐ |
| (workstation baseline) | — | `guix/system/workstation.scm` | ◐ (spike) |

## 3. NixOS modules → Guix services
| Nix module | Path | Guix equivalent (service type) | Status |
|---|---|---|---|
| mesh | `modules/nixos/mesh` | `guix/services/mesh.scm` | ☐ |
| sourceos-shell | `modules/nixos/sourceos-shell` | `guix/services/sourceos-shell.scm` | ☐ |
| sourceos-syncd | `modules/nixos/sourceos-syncd` | `guix/services/sourceos-syncd.scm` | ☐ |

## 4. Checks / tests (`tests/`, `flake.nix` `checks`)
The primary gate is Layer-1 deterministic boot; the contract/smoke suite asserts
each edition + component. Guix's analog is **`guix system test`** (marionette).
| Nix check | Guix equivalent | Status |
|---|---|---|
| edition boot: `tests/{desktop,server,edge}-boot.nix` | `guix system test` per profile (graphical.target / sshd+firewall / zram) | ☐ |
| `*-smoke.nix` / `*-contract.nix` (mesh, sourceos-shell, sourceos-syncd, boot, canary, stable, exit, builder, workstation) | Guix system tests + package tests, one per contract | ☐ |
| Agent-S Layer-2 GUI test (`tests/agent-s/`) | **Already wired**: `tests/agent-s/run-guix.sh` builds `guix/system/desktop.scm` and drives it (local/cloud) | ◐ (this PR) |

## 5. CI workflows
| Nix workflow | Purpose | Guix equivalent | Status |
|---|---|---|---|
| `nix-ci.yml` | flake check / fmt | `guix` lint + `guix system build` of each profile | ☐ |
| `nix-build-images.yml` | build images | build qcow2/iso via `guix system image` | ☐ |
| `image-tests.yml` | Layer-1 boot + Agent-S | `guix system test` + `run-guix.sh` | ☐ |
| `build-custom.yml`, `release-images.yml` | custom/release builds | Guix release build + publish | ☐ |

## 6. Substitutes (cache) + signing
| Nix | Doc | Guix equivalent | Status |
|---|---|---|---|
| Binary cache | `docs/NIX_CACHE_SETUP.md` | `guix publish` server + client `--substitute-urls`; authorize the key with `guix archive --authorize` | ☐ |
| Image signing | `docs/SIGNING_SETUP.md` | Guix signs substitutes with its own key; map the estate signing/WIF flow onto `guix publish` keys | ☐ |

## 7. Dev shells
| Nix | Guix equivalent | Status |
|---|---|---|
| `devShells.default` (`git jq nixpkgs-fmt`) | `guix shell git jq` (+ a `guix/manifests/dev.scm`) | ☐ |
| lampstand dev shell | `guix shell -m guix/manifests/lampstand.scm` | ☐ |

## Parity acceptance
A row flips to ☑ only when its Guix artifact **builds and (for images) boots**
on a Linux runner and passes the same assertion its Nix counterpart does — proved,
not asserted. Cutover (flip the default from Nix to Guix) happens per-profile
once its whole column is ☑, with Nix kept until then.
67 changes: 67 additions & 0 deletions guix/system/desktop.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
;;; SourceOS Guix desktop image (x86_64) — the Agent-S GUI test target.
;;;
;;; A GNOME desktop built from Guix + nonguix (real kernel/firmware/microcode),
;;; produced as a bootable qcow2 that Agent-S drives (open Activities, launch
;;; Files, see a window). This is the Guix equivalent of the Nix
;;; `sourceos-image-qcow2-desktop` the Agent-S Layer-2 harness consumes today.
;;;
;;; Build the image on a Linux runner (NOT macOS):
;;; guix time-machine -C guix/channels.scm -- \
;;; system image -t qcow2 guix/system/desktop.scm # -> a bootable .qcow2
;;; then feed it to tests/agent-s (local or cloud) via tests/agent-s/run-guix.sh.
;;;
;;; NOTE for Agent-S: the login manager must AUTOLOGIN so the agent lands on a
;;; live desktop over VNC. gdm autologin is a small modify-services tweak added
;;; when this profile is first realised on a runner (kept out here until it can
;;; be build-validated — a wrong gdm-configuration would silently break the test).

(use-modules (gnu)
(nongnu packages linux)
(nongnu system linux-initrd))
(use-service-modules desktop ssh)
(use-package-modules certs ssh)

(define %keyboard-layout (keyboard-layout "us"))

(operating-system
(host-name "sourceos-desktop")
(timezone "UTC")
(locale "en_US.utf8")
(keyboard-layout %keyboard-layout)

;; nonguix: real Linux + firmware + microcode (the allowUnfree equivalent).
(kernel linux)
(firmware (list linux-firmware))
(initrd microcode-initrd)

(bootloader (bootloader-configuration
(bootloader grub-efi-bootloader)
(targets '("/boot/efi"))
(keyboard-layout %keyboard-layout)))

(file-systems (append
(list (file-system
(mount-point "/")
(device (file-system-label "SOURCEOS_ROOT"))
(type "ext4"))
(file-system
(mount-point "/boot/efi")
(device (file-system-label "SOURCEOS_EFI"))
(type "vfat")))
%base-file-systems))

(users (cons (user-account
(name "sourceos")
(comment "SourceOS operator")
(group "users")
(supplementary-groups '("wheel" "netdev" "audio" "video")))
%base-user-accounts))

(packages (append (list nss-certs) %base-packages))

;; Full GNOME on top of the desktop stack (%desktop-services provides gdm +
;; NetworkManager); openssh for headless control from the harness.
(services (cons* (service gnome-desktop-service-type)
(service openssh-service-type
(openssh-configuration (openssh openssh-sans-x)))
%desktop-services)))
Comment on lines +62 to +67
80 changes: 80 additions & 0 deletions tests/agent-s/run-guix.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/env bash
# Build the SourceOS *Guix* desktop image and run the Agent-S GUI test against
# it, LOCAL or CLOUD, with a LOCAL main model by default.
#
# This wires the Guix build (guix/system/desktop.scm) to the existing Agent-S
# harness (tests/agent-s/harness.sh) — the harness is image-source-agnostic
# (it just needs IMG=*.qcow2 + a grounder + a main model), so a Guix-built
# image drops straight in alongside the Nix one.
#
# Modes:
# --mode local build + boot on this Linux/KVM host (default)
# --mode cloud build + run on a GCP GPU VM (grounder on the GPU)
# Main model (Agent-S generation model) — LOCAL by default, per the estate's
# local-agent-default posture:
# --main-model local use AS_MAIN_URL (a local vLLM/TGI endpoint) [default]
# --main-model anthropic use ANTHROPIC_API_KEY
# --main-model openai use OPENAI_API_KEY
#
# Requires (Linux runner): guix, qemu (+ /dev/kvm for local), and a reachable
# grounding-model endpoint (UI-TARS on a GPU). NOT runnable from macOS.
set -euo pipefail

HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO="$(cd "$HERE/../.." && pwd)"

MODE="local"
MAIN_MODEL="local"
while [ $# -gt 0 ]; do
case "$1" in
--mode) MODE="$2"; shift 2 ;;
--main-model) MAIN_MODEL="$2"; shift 2 ;;
*) echo "unknown arg: $1" >&2; exit 2 ;;
esac
done
Comment on lines +28 to +34

# Local-first main model: default to a local endpoint; only reach for an external
# provider when explicitly requested (and only then require its key).
case "$MAIN_MODEL" in
local)
: "${AS_MAIN_URL:?set AS_MAIN_URL to a local main-model endpoint (e.g. http://localhost:8081/v1)}"
export AS_PROVIDER="local" AS_MAIN_URL
;;
anthropic) : "${ANTHROPIC_API_KEY:?set ANTHROPIC_API_KEY}"; export AS_PROVIDER="anthropic" ;;
openai) : "${OPENAI_API_KEY:?set OPENAI_API_KEY}"; export AS_PROVIDER="openai" ;;
*) echo "unknown --main-model: $MAIN_MODEL" >&2; exit 2 ;;
Comment on lines +36 to +45
esac

# Grounder (UI-TARS): local endpoint by default; overridable for cloud/GPU host.
export AS_GROUND_URL="${AS_GROUND_URL:-http://localhost:8080/v1}"

build_guix_image() {
command -v guix >/dev/null 2>&1 || { echo "missing guix (Linux runner only)" >&2; exit 1; }
echo "building Guix desktop image (pinned channels)…" >&2
local out
out="$(guix time-machine -C "$REPO/guix/channels.scm" -- \
system image -t qcow2 "$REPO/guix/system/desktop.scm")"
echo "$out"
}

case "$MODE" in
local)
[ -e /dev/kvm ] || echo "warn: no /dev/kvm — boot will be slow (TCG)" >&2
IMG="$(build_guix_image)"
echo "running Agent-S locally against $IMG (provider=$AS_PROVIDER)" >&2
IMG="$IMG" bash "$HERE/harness.sh"
;;
cloud)
# Run the whole flow ON a GCP GPU VM so the UI-TARS grounder is local to the
# GPU: sync the repo up, then invoke --mode local there. The VM itself
# (provisioned with a GPU + guix + qemu/kvm) is supplied via GCP_HOST; how it
# is spun up (gcloud/Terraform) is out of scope for this runner.
: "${GCP_HOST:?set GCP_HOST=user@vm — a reachable GCP GPU VM with guix + kvm}"
echo "cloud mode: sync repo to $GCP_HOST and run --mode local there (grounder on the GPU)." >&2
rsync -a --delete --exclude '.git' "$REPO/" "$GCP_HOST:sourceos/"
# AS_GROUND_URL defaults to the VM's local grounder; forward the model choice.
exec ssh "$GCP_HOST" \
"cd sourceos && AS_GROUND_URL='${AS_GROUND_URL}' bash tests/agent-s/run-guix.sh --mode local --main-model '${MAIN_MODEL}'"
Comment on lines +74 to +77
;;
*) echo "unknown --mode: $MODE" >&2; exit 2 ;;
esac
Loading