xoa-proxy
Rust HTTP/HTTPS proxy that streams an XOA image to XAPI.
Repository: Vagrantin/xoa-proxy · Language: Rust · License: AGPL-3.0
Table of contents
- Purpose
- Design
- Code structure
- Building
- Configuration
- GPG signing
- Integration with XO Lite CE
- Testing
- Contributing
Purpose
When XO Lite’s patched “Deploy XOA” button is clicked, XAPI needs to import a .xva VM archive. XAPI calls VM.import with a URL, it expects the server at that URL to serve the file over HTTP only and the file must be an XVA format.
With this limited support, was not statisfying for me, and originally even a blocker as I was relaying on Ronivay’s image which is a gz format that can be accessible over https only.
For that purpose, xoa-proxy is the interface in front of XAPI to support HTTP,HTTPS (including self-signed certificates) protocol and supporting as well on top of XVA, the gz format.
With this proxy it gives the flexibility to import images from different sources, even locally, and increase the security by introducing the HTTPS support.
Design
Technology choices
| Choice | Rationale |
|---|---|
| Rust | Memory safety, good for a long-lived server process in Dom0 |
Request flow
XO Lite (browser)
│ HTTP GET /image.xva
▼
xoa-proxy
│
│ HTTP/HTTPS interface for XAPI
│ Decompress Gzip format on the fly
│
▼
XAPI VM.import
│ Writes VDIs to local SR
▼
XOA VM created
HTTP vs HTTPS
xoa-proxy supports both HTTP and HTTPS (including self-signed certificates) when fetching the upstream XOA image. During download, gzip-compressed images are decompressed on the fly so that XAPI always receives a raw, uncompressed XVA stream.
The handoff to XAPI via VM.import is deliberately served over HTTP/1.0 — XAPI does not support chunked transfer encoding (an HTTP/1.1 feature), so using HTTP/1.1 framing would corrupt the import. The proxy handles this constraint internally; callers do not need to configure anything.
Code structure
xoa-proxy/
├── src/
│ └── main.rs ← HTTP server, request handler, streaming logic
├── tests/ ← Integration tests
├── .cargo/ ← Cargo config (cross-compile settings)
├── Cargo.toml ← Dependencies: hyper, tokio, tokio-util, ...
└── Cargo.lock
Core streaming pattern
The heart of the proxy is converting a tokio::fs::File into a hyper response body without loading the whole file into memory:
use tokio_util::io::ReaderStream;
use hyper::Body;
let file = tokio::fs::File::open("image.xva").await?;
let stream = ReaderStream::new(file);
let body = Body::wrap_stream(stream);
let response = Response::builder()
.header("Content-Type", "application/octet-stream")
.header("Content-Encoding", "gzip")
.body(body)?;
Building
Prerequisites
- Rust toolchain (stable) — install via rustup
- For cross-compilation to Dom0:
x86_64-unknown-linux-musltarget
# Native build (for testing)
cargo build
# Cross-compile for Dom0 (static musl binary)
rustup target add x86_64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-musl
The resulting binary at target/x86_64-unknown-linux-musl/release/xoa-proxy is a fully static executable with no shared library dependencies — suitable for embedding in the XCP-ng DOM0 environment.
Running locally for development or tests
# Place a test XVA at the expected path
cp /path/to/test.xva image.xva
# Start the proxy
./target/release/xoa-proxy
# Test streaming
curl -v http://127.0.0.1:3000/image.xva -o /dev/null
Configuration
In the current release the listen address and image path are hardcoded.
| Parameter | Current value |
|---|---|
| Listen address | 127.0.0.1:3000 |
| Proxy endpoint | image.xva |
GPG signing
The xoa-proxy RPM is signed with the RPM signing subkey of the XCP-ng Community Edition keypair. The same subkey is shared with xolite-ce — there is one subkey for both RPMs.
The public key (xcp-ng-ce-public.asc) is the same file distributed with every release. Importing it once is sufficient to verify any community RPM.
To verify the RPM locally:
# Option 1 — fetch from keyserver
gpg --keyserver keys.openpgp.org --recv-keys 2F591DB9D2C128C4C3D963F46DA00DCA5BBA215A
# Option 2 — import from the release page
gpg --import xcp-ng-ce-public.asc
# Check the RPM signature
rpm --checksig xoa-proxy-*.rpm
Integration with XO Lite CE
The XO Lite patch in xolite-ce sets the deploy URL to http://127.0.0.1:3000/image.xva. This is the address xoa-proxy listens on when started on a XCP-ng HL host.
Testing
# Run unit and integration tests
cargo test
# Integration tests are in tests/
# They spin up the proxy and verify streaming behaviour
Contributing
- Fork Vagrantin/xoa-proxy.
- Create a branch:
git checkout -b feature/my-change. - Run
cargo fmtandcargo clippybefore committing. - Open a pull request against
main.