Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
05e8907
docs(adr): 0126 the lazy session disk (Proposed)
nikhilunni Nov 6, 2026
2162a0a
docs(adr): renumber the lazy session disk to 0127 and add what was le…
nikhilunni Nov 6, 2026
bb1b6ce
disk-source: a read-only ublk device whose bytes are one manifest
nikhilunni Nov 6, 2026
cd12445
disk-source: root-lane tests against the kernel ublk driver
nikhilunni Nov 6, 2026
35a6911
disk-source: test lost data and a stalled read separately
nikhilunni Nov 6, 2026
ed5e035
disk-source: keep in-flight memo cells and check base volume bytes
nikhilunni Nov 6, 2026
3ad201b
disk-source: move the read retry policy out of the ublk server
nikhilunni Nov 6, 2026
41525e2
disk-source: always answer a ublk request, and refuse a recovery of a…
nikhilunni Nov 6, 2026
579f87c
disk-source: root tests cannot leave a device or a process behind
nikhilunni Nov 6, 2026
36f3175
disk-thin: one path removes a device and one opens a new one
nikhilunni Nov 6, 2026
fd5e99c
disk: SessionDisk makes cloned drives (ADR 0127 step 2)
nikhilunni Nov 6, 2026
d177d95
disk-thin: root tests for cloned drives, run on the KVM lane
nikhilunni Nov 6, 2026
ecb3355
docs(adr): 0127 checkpoint view takes its snapshots under a suspend
nikhilunni Nov 6, 2026
586f199
disk-thin: a suspend runs to its resume, and open resumes what a deat…
nikhilunni Nov 6, 2026
25c1065
disk: a drive owns its volumes, and FileDisk refuses what ThinPoolDis…
nikhilunni Nov 6, 2026
8ce6077
disk-thin: ThinPoolDisk tells drives, views and half-made drives apart
nikhilunni Nov 6, 2026
b65828c
docs(adr): 0127 states why the checkpoint pair is consistent, and the…
nikhilunni Nov 6, 2026
5e63b48
docs(adr): 0127 the eager restore is bound by disk writes
nikhilunni Nov 6, 2026
f981e50
ci: report the runner kernel's block features, check targets directly
nikhilunni Nov 6, 2026
c4857f1
docs(adr): 0127 the shape of the lazy restore in the host-agent
nikhilunni Nov 6, 2026
8471b6c
ci: run the ublk disk-source tests on a lane of their own
nikhilunni Nov 6, 2026
5ac41c8
host-core: session volumes understand lazy role names
nikhilunni Nov 6, 2026
6ba8eb4
host-core: lazy provision and release of a session role
nikhilunni Nov 6, 2026
302ad94
host-core: checkpoint a session role while it hydrates
nikhilunni Nov 6, 2026
ad9870c
host-core: lazy checkpoint and teardown fixes from review
nikhilunni Nov 6, 2026
2cf8e08
docs(adr): 0127 the rollback limit and the discard dependency
nikhilunni Nov 6, 2026
eba6975
host-agent: run each lazy disk source as a process of its own
nikhilunni Nov 6, 2026
3e40c4d
ci: pin protoc on the ublk lane
nikhilunni Nov 6, 2026
b7db3a1
host-core: cut a hydrated session role over to its destination
nikhilunni Nov 6, 2026
502f934
host-agent: a sandbox owns the hydration of its disks
nikhilunni Nov 6, 2026
6a30135
host-agent: restore a session disk lazily when its bytes are not local
nikhilunni Nov 6, 2026
e51b1a5
host-agent: one eager budget for all of a restore's disks
nikhilunni Nov 6, 2026
4b1972a
host-agent: lazy disk sources survive a flag change and a cancel
nikhilunni Nov 6, 2026
8fd7258
disk: a lazy restore copies only the regions its destination lacks
nikhilunni Nov 6, 2026
7205255
test(disk-thin): stop the source after the cutover, as the host-agent…
nikhilunni Nov 6, 2026
18d1a75
docs(adr): 0127 hydration copies only what the destination lacks
nikhilunni Nov 6, 2026
9adff3e
host-agent: a move destination restores its disks eagerly
nikhilunni Nov 6, 2026
aa4bb48
host-core: the first checkpoint after a cutover reads only the chunks…
nikhilunni Nov 6, 2026
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
7 changes: 7 additions & 0 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,10 @@ slow-timeout = { period = "60s", terminate-after = 10 }
[[profile.default.overrides]]
filter = "package(engram-tui) & binary(perf)"
threads-required = "num-cpus"

# The ublk root tests each finish in seconds. A device that no process
# serves holds its I/O, so a test that wedges is ended at 2 minutes
# instead of the default 3.
[[profile.default.overrides]]
filter = "package(engram-disk-source) & binary(ublk_device)"
slow-timeout = { period = "30s", terminate-after = 4 }
27 changes: 22 additions & 5 deletions .github/scripts/detect-rebake-lanes.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@
from pathlib import Path

# Binaries that bake into each artifact.
FC_BINS = {"engram-host-agent", "engram-uffd-handler", "engram-egress-proxyd"}
FC_BINS = {"engram-host-agent", "engram-uffd-handler", "engram-egress-proxyd",
"engram-disk-source"}

# ADR 0098 R-CoSim (rung 1): the coordinator↔host BOUNDARY simulator.
# engram-dst-cosim deliberately spans BOTH closures — it pulls
Expand All @@ -111,12 +112,16 @@
# copies it to /usr/local/bin) and is spawned by path, not a cargo
# dependency, so it must be listed here or a daemon-only change
# deploys nowhere.
#
# engram-disk-source (ADR 0127): the same shape again — the host-agent
# container carries it and spawns it by path.
CONTAINER_BINS = {
"engram-coordinator",
"engram-host-agent",
"engram-host-operator",
"engram-uffd-handler",
"engram-egress-proxyd",
"engram-disk-source",
}
# The built-in claude harness binary. ADR 0062: it is NOT baked into any
# image — it rides the fleet `current_bundles` stamp (the
Expand Down Expand Up @@ -158,6 +163,11 @@
"engram-egress-proxyd",
}

# Crates whose root tests need the kernel's ublk driver (ADR 0127). They
# run on their own lane: the Firecracker runners' kernel has no ublk.
# engram-host-agent runs its disk sources as engram-disk-source processes.
UBLK_TEST_CRATES = {"engram-disk-source", "engram-host-agent"}

# Non-crate path prefixes per lane. `Cargo.lock`/root `Cargo.toml`/this
# script/the bake workflow conservatively trip the binary lanes.
BINARY_COMMON = ["Cargo.lock", "Cargo.toml",
Expand Down Expand Up @@ -440,6 +450,7 @@ def main():
agentd_closure = release_closure(meta, AGENTD_BINS)
e2e_closure = release_closure(meta, E2E_BINS)
cosim_closure = release_closure(meta, COSIM_BINS)
ublk_closure = release_closure(meta, UBLK_TEST_CRATES)
cli = release_closure(meta, CLI_BINS)

# ADR 0045 Phase B: a Firecracker-fork bump (submodule pointer) restages the
Expand Down Expand Up @@ -567,7 +578,12 @@ def main():
# fixtures and mount bundles via the sandbox crates already in the closure;
# tripping FC on a bundle-payload edit was a needless ~KVM lane. The
# e2e-STACK lane still gates on `e2e`, so bundle staging is validated there.
test_fc = ci_self or bool(cc & e2e_closure) or fc_fork or any_path(changed, FC_POOL_PATHS)
test_fc = (
ci_self
or bool(cc & e2e_closure)
or fc_fork
or any_path(changed, FC_POOL_PATHS)
)
# web / orchestrator: their own sources or the protos they codegen from.
# (The `engrams` CLI is a workspace crate: test_rust covers it.)
test_web = ci_self or proto or any_path(changed, WEB_PATHS)
Expand All @@ -584,6 +600,7 @@ def main():
# ADR 0098 R-CoSim: the coordinator↔host boundary sim — its own (spanning)
# release closure.
test_cosim = ci_self or bool(cc & cosim_closure)
test_ublk = ci_self or bool(cc & ublk_closure)

# ── per-image bake selectivity ─────────────────────────────────────
# Each image's lane evaluates against ITS OWN changed set: the registry
Expand All @@ -602,8 +619,7 @@ def main():
if baselines else ({}, {})

coord_closure = release_closure(meta, {"engram-coordinator"})
ha_closure = release_closure(
meta, {"engram-host-agent", "engram-uffd-handler", "engram-egress-proxyd"})
ha_closure = release_closure(meta, FC_BINS)
hop_closure = release_closure(meta, {"engram-host-operator"})
dirs = crate_dirs(meta, repo_root)

Expand Down Expand Up @@ -660,7 +676,7 @@ def image_flag(name):
print(f"-> test_rust={test_rust} test_cross={test_cross} test_fc={test_fc} "
f"test_web={test_web} test_orchestrator={test_orchestrator} "
f"test_buf={test_buf} ci_self={ci_self} proto={proto} "
f"test_cosim={test_cosim} "
f"test_cosim={test_cosim} test_ublk={test_ublk} "
f"test_integrations_cli={test_integrations_cli} "
f"test_deploy={test_deploy} test_site={test_site}",
file=sys.stderr)
Expand Down Expand Up @@ -695,6 +711,7 @@ def b(v):
f.write(f"test_orchestrator={b(test_orchestrator)}\n")
f.write(f"test_buf={b(test_buf)}\n")
f.write(f"test_cosim={b(test_cosim)}\n")
f.write(f"test_ublk={b(test_ublk)}\n")
f.write(f"test_integrations_cli={b(test_integrations_cli)}\n")
f.write(f"test_deploy={b(test_deploy)}\n")
f.write(f"test_site={b(test_site)}\n")
Expand Down
12 changes: 8 additions & 4 deletions .github/workflows/bake-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ jobs:
sudo cp -r /usr/include/linux /usr/include/asm-generic /opt/uapi/
sudo cp -r /usr/include/x86_64-linux-gnu/asm /opt/uapi/asm

- name: Build engram-host-agent + engram-uffd-handler + engram-egress-proxyd (musl-static)
- name: Build engram-host-agent + engram-uffd-handler + engram-egress-proxyd + engram-disk-source (musl-static)
env:
# Point the musl C compile (cc-rs) + bindgen/clang at the system
# kernel uapi headers so userfaultfd-sys finds <linux/types.h>.
Expand All @@ -269,12 +269,15 @@ jobs:
ENGRAM_EGRESS_PROXYD_FINGERPRINT="$FP" cargo build \
--target x86_64-unknown-linux-musl \
--release \
-p engram-host-agent -p engram-uffd-handler -p engram-egress-proxyd
-p engram-host-agent -p engram-uffd-handler -p engram-egress-proxyd \
-p engram-disk-source
mkdir -p dist
cp target/x86_64-unknown-linux-musl/release/engram-host-agent dist/
cp target/x86_64-unknown-linux-musl/release/engram-uffd-handler dist/
cp target/x86_64-unknown-linux-musl/release/engram-egress-proxyd dist/
file dist/engram-host-agent dist/engram-uffd-handler dist/engram-egress-proxyd
cp target/x86_64-unknown-linux-musl/release/engram-disk-source dist/
file dist/engram-host-agent dist/engram-uffd-handler dist/engram-egress-proxyd \
dist/engram-disk-source

- name: Log in to GHCR
uses: docker/login-action@v4
Expand Down Expand Up @@ -317,7 +320,8 @@ jobs:
--artifact-type application/vnd.engrams.fc-host-binaries.v1 \
engram-host-agent:application/octet-stream \
engram-uffd-handler:application/octet-stream \
engram-egress-proxyd:application/octet-stream
engram-egress-proxyd:application/octet-stream \
engram-disk-source:application/octet-stream
# Convenience tags. `oras tag` is a cheap registry-side re-point.
tag_with_retry "$REPO:$SHA" "$SHORT"
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == "refs/heads/main" ]]; then
Expand Down
92 changes: 90 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ jobs:
test_web: ${{ steps.lanes.outputs.test_web }}
test_orchestrator: ${{ steps.lanes.outputs.test_orchestrator }}
test_cosim: ${{ steps.lanes.outputs.test_cosim }}
test_ublk: ${{ steps.lanes.outputs.test_ublk }}
test_buf: ${{ steps.lanes.outputs.test_buf }}
test_integrations_cli: ${{ steps.lanes.outputs.test_integrations_cli }}
test_deploy: ${{ steps.lanes.outputs.test_deploy }}
Expand Down Expand Up @@ -1166,15 +1167,38 @@ jobs:
- name: Node-prep pool states (root)
run: sudo sh scripts/thin-pool-states-test.sh

# The disk over the pool, then the session disk under a real guest
# over the same pool (`session_disk` reads ENGRAM_TEST_THIN_POOL).
- name: kernel block features (report)
run: |
uname -r
sudo dmsetup targets
ls /dev/ublk-control 2>&1 || true
for f in /proc/config.gz "/boot/config-$(uname -r)"; do
[ -r "$f" ] && { zcat -f "$f" | grep -E 'CONFIG_(DM_CLONE|DM_DELAY|BLK_DEV_UBLK)=' || true; }
done
grep -E 'dm-clone|dm-delay|ublk' "/lib/modules/$(uname -r)/modules.builtin" 2>/dev/null || true

# The disk over the pool, its cloned drives over a loop-device
# source (dm-delay slows the copy where a test needs it), then the
# session disk under a real guest over the same pool (`session_disk`
# reads ENGRAM_TEST_THIN_POOL). A runner without the clone or delay
# target fails the lane, as a missing pool does.
- name: dm-thin session-disk tests (root)
env:
ENGRAM_TEST_THIN_POOL: engram-pool
run: |
# A target built into the kernel has no module, so the check is
# the kernel's target list, not modprobe.
for t in clone delay; do
sudo dmsetup targets | grep -q "^$t " || sudo modprobe "dm_$t" || true
done
for t in clone delay; do
sudo dmsetup targets | grep -q "^$t " \
|| { echo "FATAL: kernel $(uname -r) has no dm-$t target" >&2; exit 1; }
done
sudo -E env "PATH=$PATH" cargo nextest run \
-p engram-disk-thin \
--test thin_pool \
--test clone \
--run-ignored ignored-only \
--no-fail-fast \
--test-threads=1
Expand Down Expand Up @@ -1230,6 +1254,69 @@ jobs:
run: |
sudo chown -R "$USER":"$USER" target/ ~/.cargo ~/.docker 2>/dev/null || true

# ADR 0127: the disk source against the kernel's ublk driver. The
# Blacksmith kernel has no ublk, and the 6.17 kernel of the hosted
# 24.04 image fails on a device add, so this lane uses the hosted 22.04
# image (kernel 6.8, the driver in its extra modules). A runner that
# cannot load the driver fails the lane.
test-ublk:
name: tests (ublk disk source, root)
needs: detect
if: needs.detect.outputs.test_ublk == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 45
steps:
- uses: actions/checkout@v7
- name: Load the ublk driver
run: |
sudo apt-get update -qq
sudo modprobe ublk_drv || {
sudo apt-get install -y -qq --no-install-recommends "linux-modules-extra-$(uname -r)"
sudo modprobe ublk_drv
}
test -c /dev/ublk-control \
|| { echo "FATAL: kernel $(uname -r) has no ublk driver" >&2; exit 1; }
- uses: dtolnay/rust-toolchain@stable
# The 22.04 protoc is too old for proto3 optional fields; pin the
# release the bake uses.
- uses: arduino/setup-protoc@v3
with:
version: "25.9"
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install libclang
run: sudo apt-get install -y -qq --no-install-recommends libclang-dev
- uses: Swatinem/rust-cache@v2
with:
shared-key: ublk
- name: Install nextest
uses: taiki-e/install-action@nextest
- name: ublk disk-source tests (root)
env:
ENGRAM_TEST_UBLK: "1"
run: |
cargo nextest run -p engram-disk-source --no-run
sudo -E env "PATH=$PATH" cargo nextest run \
-p engram-disk-source \
--test ublk_device \
--run-ignored ignored-only \
--no-fail-fast \
--test-threads=1
# The host-agent runs each disk source as a process of its own.
- name: host-agent disk-source process tests (root)
env:
ENGRAM_TEST_UBLK: "1"
run: |
cargo build -p engram-disk-source --bin engram-disk-source
cargo nextest run -p engram-host-agent --test disk_source_ublk --no-run
sudo -E env "PATH=$PATH" \
"ENGRAM_DISK_SOURCE_BIN=$PWD/target/debug/engram-disk-source" \
cargo nextest run \
-p engram-host-agent \
--test disk_source_ublk \
--run-ignored ignored-only \
--no-fail-fast \
--test-threads=1

test-fc-e2e:
name: tests (firecracker true-e2e, Linux + KVM)
needs: detect
Expand Down Expand Up @@ -2417,6 +2504,7 @@ jobs:
- test-fc-unprivileged
- test-fc-pooled
- test-fc-root
- test-ublk
- test-fc-e2e
- vz
- test-macos
Expand Down
Loading
Loading