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
68 changes: 68 additions & 0 deletions scripts/factory-edge-demo.md
Original file line number Diff line number Diff line change
@@ -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.
56 changes: 56 additions & 0 deletions scripts/factory-edge-demo.sh
Original file line number Diff line number Diff line change
@@ -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 <name>` 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