Developer Documentation

Everything you need to understand, build, and contribute to XCP-ng HL.

Table of contents

  1. Repository overview
  2. Tech stack
  3. Build pipeline — end to end
  4. Build orchestration
  5. Key design decisions
    1. Three-repo strategy
    2. Patch at source level
  6. GPG signing
    1. Key details
    2. Subkey roles
  7. Detailed component docs

Repository overview

XCP-ng HL is split across several functional repositories plus this documentation repo.

Vagrantin/xcp-hl          ← docs (this site)
      │
      ├── Vagrantin/xolite-ce       ← XO Lite patch + RPM build
      │         │ publishes signed RPM as GitHub Release artifact
      │         │ 
      ├─────────│───Vagrantin/xoa-proxy           ← Rust HTTP proxy + RPM build
      │         │        │  publishes signed RPM as GitHub Release artifact
      │         ▼        ▼
      ├── Vagrantin/xcp-ng-ce-iso   ← ISO assembly + ISO GitHub Releases
      │         │ downloads RPM from xolite-ce and xoa-proxy, assembles ISO
      │
      ├── Vagrantin/xoa-hl          ← XOA-HL: patched Xen Orchestra (RPM + container)
      │         ▼
      ├── Vagrantin/build-xoa-hl    ← Packer pipeline → XOA XVA image on XCP-ng
      │         │ publishes the XVA as a GitHub Release (xoa-image-* tags)
      │
      └── Vagrantin/buildorchestration ← Rust orchestrator: triggers/monitors all builds

Each repo has its own GitHub Actions pipeline. They are loosely coupled: xolite-ce and xoa-proxy publish versioned RPM artifacts that xcp-ng-ce-iso fetches by release tag. Neither repo needs to be checked out together for normal builds. xoa-hl builds the community-patched Xen Orchestra (XOA-HL), and build-xoa-hl packages it into an XVA image. buildorchestration sits on top and drives the whole pipeline on a daily schedule (see Build orchestration below).


Tech stack

Layer Technology
Hypervisor base XCP-ng 8.3 (Xen 4.17, Linux 4.19 Dom0)
XO Lite UI Vue 3 · TypeScript · Vite · Pinia (@xen-orchestra/lite)
XO Lite build Yarn (Corepack) · yarn build:xo-lite
RPM packaging rpmbuild, rpmsign, createrepo_c
ISO assembly create-install-image (XCP-ng toolchain, master branch)
ISO tooling mksquashfs, xorriso, isohybrid, implantisomd5
XOA-HL build Node.js 24 · Yarn workspaces · AlmaLinux 9 container
XVA image build Packer · ddelnano/xenserver plugin · Kickstart
Build environment Docker (xcp-ng-build-env:8.3)
Proxy server Rust · hyper · tokio · tokio_util::io::ReaderStream
CI/CD GitHub Actions
Signing GPG — offline master key + 2 signing subkeys (see below)

Build pipeline — end to end

1. xolite-ce CI (GitHub Actions)
   ├── Clone vatesfr/xen-orchestra at the tag pinned in UPSTREAM_TAG
   │   (currently xo-lite-v0.21.0 — bumped deliberately, not automatically)
   ├── Apply patches/community-xoa-deploy.patch
   ├── yarn build:xo-lite
   ├── rpmbuild → xo-lite-community-<VERSION>.rpm
   ├── rpmsign with RPM signing subkey (GPG_PRIVATE_KEY + GPG_PASSPHRASE)
   └── Publish signed RPM as GitHub Release asset (public key on
       keys.openpgp.org; import steps in the release notes)

2. xoa-proxy CI (GitHub Actions)
   ├── Install musl toolchain (musl-1.2.4, static libc)
   ├── Install Rust stable via rustup
   ├── Add x86_64-unknown-linux-musl target
   ├── cargo build --release --target x86_64-unknown-linux-musl
   ├── Prepare RPM sources (binary + systemd unit + logrotate config)
   ├── rpmbuild → xoa-proxy-<VERSION>.rpm
   ├── rpmsign with RPM signing subkey (GPG_PRIVATE_KEY + GPG_PASSPHRASE)
   └── Publish signed RPM as GitHub Release asset (public key on
       keys.openpgp.org; import steps in the release notes)

3. xcp-ng-ce-iso CI (GitHub Actions)
   ├── Download signed RPM from xolite-ce release
   ├── Download signed RPM from xoa-proxy release
   ├── Import GPG_PRIVATE_KEY (ISO signing subkey) into runner keyring
   ├── Export public key from runner keyring → inject into installer chroot
   ├── Set up community-repo/x86_64/ with createrepo_c
   ├── Run create-installimg.sh (root) — builds install.img (SquashFS)
   ├── Run create-iso.sh (non-root) — assembles ISO
   ├── isohybrid --uefi (hybrid MBR/GPT stamp)
   ├── implantisomd5
   ├── sha256sum → xcp-ng-8.3-ceN.iso.sha256
   ├── gpg --detach-sign  (ISO signing subkey via GPG_PRIVATE_KEY)
   └── Publish xcp-ng-8.3-ceN.iso + .iso.sha256 + .iso.sha256.asc as a
       Vagrantin/xcp-ng-ce-iso GitHub Release (public key on
       keys.openpgp.org; verification steps in the release notes)

4. xoa-hl CI (GitHub Actions)
   ├── Shallow-fetch vatesfr/xen-orchestra at the commit pinned in XO_COMMIT
   │   (currently 5.113.2 — the last XO 5.x release, bumped deliberately)
   ├── Apply patches/*.patch (menu-hide-items)
   ├── Write xoahl.config.toml + generate self-signed TLS certificate
   ├── yarn && yarn build (all workspaces), prune, strip devDependencies
   ├── tar → xoa-hl-<VERSION>.tar.gz
   ├── rpmbuild → xoa-hl-<VERSION>.noarch.rpm (thin: %post fetches the tarball)
   └── Publish tarball + RPM as a v<VERSION> GitHub Release

5. build-xoa-hl (Packer, on a real XCP-ng host)
   ├── Resolve AlmaLinux ISO checksum + latest xoa-hl RPM release URL
   ├── Generate inst.ks (Kickstart) and almalinux-build.json (Packer template)
   ├── packer build — install AlmaLinux 9 via Kickstart on the XCP-ng host
   ├── Provision: xe-guest-utilities, Node 24, xoa-hl RPM, first-boot units
   ├── Slim the image, blank /etc/machine-id
   └── Export XVA (xva_compressed) — the appliance XO Lite CE deploys

Build orchestration

The buildorchestration repo automates the pipeline above. Its xcp-orchestrator Rust workspace (orchestrator, iso-agent, xoa-vm-agent, shared crates) runs as a systemd service on a dedicated VM, triggered daily by a timer:

systemd timer (daily 05:00)
   ├── Trigger xolite-ce and xoa-proxy workflows via workflow_dispatch
   ├── Poll workflow runs until completion
   ├── Skip a component when its latest GitHub release already matches HEAD
   │   (release-based change detection — no rebuild-every-run)
   ├── On failure: pull job logs via the API and diagnose them with a local
   │   LLM (Ollama, qwen3-coder:30b) — writes an actionable fix suggestion
   ├── On success: trigger downstream xcp-ng-ce-iso and XOA XVA image builds
   └── Render a status dashboard (per-component status + log links)

Key design decisions

Three-repo strategy

Separating each RPM build from the ISO assembly keeps concerns clean: xolite-ce (UI patch, packaging) and xoa-proxy (Rust proxy, packaging) can each be iterated on independently without touching the ISO toolchain, and vice versa. Each publishes a versioned, signed RPM as a GitHub Release artifact. Those artifacts are then consumed to build the ISO.

Patch at source level

The XO Lite patch is applied to the Vue/TypeScript source of DeployXoaView.vue.


GPG signing

XCP-ng HL uses a single keypair following an offline master + subkeys model. The master key is kept offline and is never used for signing. Two signing subkeys are derived from it, one for both RPMs, one for the ISO.

Key details

Property Value
Master key fingerprint 2F59 1DB9 D2C1 28C4 C3D9 63F4 6DA0 0DCA 5BBA 215A
Published keys.openpgp.org
Email xcp-ng-ce.lid530@passmail.com
Public key file xcp-ng-ce-public.asc

Subkey roles

Subkey Used for
RPM signing subkey xo-lite-community-*.rpm and xoa-proxy-*.rpm
ISO signing subkey xcp-ng-8.3-ceN.iso.sha256.asc (detached signature over the ISO checksum file)

Detailed component docs

Page Description
xoa-proxy Rust HTTP/gzip proxy for XVA delivery
xolite-ce XO Lite patch, RPM spec, build pipeline
xcp-ng-ce-iso ISO assembly, toolchain, CI workflow
xoa-hl patched Xen Orchestra (XOA-HL) — tarball + thin RPM build
build-xoa-hl Packer pipeline building the XOA XVA image on XCP-ng
buildorchestration (GitHub) Rust build orchestrator + LLM build diagnostics

Table of contents


XCP-ng Community Edition — GPL-3.0 license. Not affiliated with Vates SAS or the XCP-ng project.

This site uses Just the Docs, a documentation theme for Jekyll.