From 1707948312f8da6495cf2aae58e7b030fee2224b Mon Sep 17 00:00:00 2001 From: Ari Date: Sat, 14 Nov 2026 17:15:56 -0800 Subject: [PATCH] demo: a raw factory-edge demo in one short script Three workloads run side by side, each allowed one destination. The maintenance workload asks for one it was not given; the host refuses and records it, mvmctl explain shows it, and the two inspection workloads carry on. This is beats 1 to 4 and 6 of the proposed factory-edge presentation, with no assertions, staging or teardown machinery: about 25 lines of logic that print the demo. The workloads are started one after another, because starting them together can fail with 'another local volume lifecycle operation is already in progress'. The guide is half a page and maps each step to the mvm-client call the Studio UI would use. That table is from reading the library, not from running it, and it records one gap found on the way: the library cannot report what a machine is allowed to reach (#5521). --- scripts/factory-edge-demo.md | 68 ++++++++++++++++++++++++++++++++++++ scripts/factory-edge-demo.sh | 56 +++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 scripts/factory-edge-demo.md create mode 100755 scripts/factory-edge-demo.sh diff --git a/scripts/factory-edge-demo.md b/scripts/factory-edge-demo.md new file mode 100644 index 0000000000..e78256988c --- /dev/null +++ b/scripts/factory-edge-demo.md @@ -0,0 +1,68 @@ +# The factory-edge demo + +Three small programs run side by side, each allowed to reach only its own +destination. Two do inspection work. The third, the maintenance program, asks +for a destination it was not given. The host refuses and records that, and the +other two carry on. + +This is the demo described in "Proposed factory-edge presentation" in +`specs/contracts/host-interface-distributed-execution.md`. Everything is +synthetic traffic. + +## Run it + +```sh +scripts/factory-edge-demo.sh +``` + +About 15 seconds. It starts the three programs and prints: + +1. All three are running. +2. What each is allowed to reach. +3. What the host refused for the maintenance program: `DNS lookup of example.com`, + not in its allow-list. +4. That the two inspection programs have nothing refused. + +Every `mvmctl explain` ends with `audit chain verifies clean`, which is the +evidence line. `scripts/factory-edge-demo.sh --down` removes the programs again. + +## The same thing from the library + +The Studio UI does these same four things through the `mvm-client` library, not +the command line: + +| Demo step | Command line | Library (`mvm-client`) | +| --- | --- | --- | +| Start a program with one allowed destination | `mvmctl run -d --name N --allow-host H:443 -- CMD` | `LaunchRequest::builder(LifecycleMode::Persistent, image).name(N).command(CMD).allow_egress(H, 443).build()?`, then `LocalBackend::new().launch(request)` | +| List them | `mvmctl machine ls` | `inventory::list_local_inventory` | +| What a program is allowed | `mvmctl machine inspect N` | not available: `inspect_machine` returns status, backend, memory and ports but not the allowed destinations ([#5521](https://github.com/tinylabscom/mvm/issues/5521)); the UI can show what it asked for at launch | +| What the host refused, and the audit result | `mvmctl explain N` | `explain::explain_local_run("local", N)`, which returns the events, `egress_denials` and `chain_verified` | + +This table comes from reading the library's code and has not been run, so check +it against the library before building on it. Use a new name for each run, +because `explain` by name is ambiguous when a name has been used before. + +## Set up a new Mac, once + +From a checkout of this repository: + +```sh +MVM_VERSION=v0.28.0-rc.2 MVM_SKIP_BOOTSTRAP=1 sh install.sh +export MVM_HOME="$HOME/.mvm-demo" +mvmctl image pull curlimages/curl:8.21.0 +mvmctl run --image curlimages/curl:8.21.0 -- true +``` + +The last command builds the runtime the first time, which takes a few minutes. +It needs an Apple Silicon Mac on macOS 26 or later, and working internet and +DNS, because each allowed destination is looked up when its program starts. + +## Known limits + +- The three programs are started one after another. Starting them together can + fail with `another local volume lifecycle operation is already in progress`. +- A launch takes about 4 seconds on this release, so the demo takes about 15. + The cause and the fix are written up in the factory-edge section of the + contract document. +- Not shown: Studio's own view, the real maintenance and inspection tasks, and + stopping and replacing a program. diff --git a/scripts/factory-edge-demo.sh b/scripts/factory-edge-demo.sh new file mode 100755 index 0000000000..ce613eb27c --- /dev/null +++ b/scripts/factory-edge-demo.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# The factory-edge demo, raw. +# +# Three small workloads run side by side. Two do inspection work and each may +# reach only its own destination. The third, the maintenance workload, asks for a +# destination it was not given. The host refuses that and records it, `mvmctl +# explain` shows it, and the other two carry on. +# +# scripts/factory-edge-demo.sh start the three workloads and show the result +# scripts/factory-edge-demo.sh --down remove them again +# +# It assumes an installed mvmctl and a prepared home (see factory-edge-demo.md). +# MVMCTL, MVM_HOME and IMAGE can be overridden. +set -euo pipefail + +mvm="${MVMCTL:-mvmctl}" +image="${IMAGE:-curlimages/curl:8.21.0}" +export MVM_HOME="${MVM_HOME:-$HOME/.mvm-demo}" MVM_RESIDENCY=cold + +if [ "${1:-}" = --down ]; then + for m in $($mvm machine ls | awk '$1 ~ /^(line-a|line-b|maintenance)-/ { print $1 }'); do + $mvm machine stop "$m" --yes >/dev/null 2>&1 || true + $mvm machine rm "$m" --yes >/dev/null 2>&1 || true + done + echo "removed"; exit 0 +fi + +id="$(date +%H%M%S)" # names are unique per run, so `explain ` is never ambiguous +a="line-a-$id"; b="line-b-$id"; m="maintenance-$id" + +# One workload: a name, the one destination it is allowed, and what it does. +launch() { $mvm run -d --image "$image" --name "$1" --allow-host "$2" -- sh -c "$3" >/dev/null; } +work() { echo "while true; do curl -sS -m 10 -o /dev/null https://$1/; sleep 2; done"; } + +echo "Starting three workloads, each allowed one destination" +launch "$a" pypi.org:443 "$(work pypi.org)" +launch "$b" files.pythonhosted.org:443 "$(work files.pythonhosted.org)" +launch "$m" api.github.com:443 "while true; do curl -sS -m 10 https://example.com/ >/dev/null 2>&1; sleep 3; done" + +echo; echo "== 1. All three are running" +$mvm machine ls | grep -E "^NAME|-$id " + +echo; echo "== 2. What each is allowed to reach" +for n in "$a" "$b" "$m"; do $mvm machine inspect "$n" | grep -E "^(name|allow-host):"; done + +echo; echo "== 3. The maintenance workload asks for example.com, which it was not given" +for _ in $(seq 1 30); do + $mvm explain "$m" 2>/dev/null | grep -q "egress denied" && break + sleep 1 +done +$mvm explain "$m" | sed -n '/^outcome:/,$p' + +echo; echo "== 4. The inspection workloads are unaffected" +for n in "$a" "$b"; do + echo "-- $n"; $mvm explain "$n" | sed -n '/^outcome:/,$p' +done