diff --git a/demo/README.md b/demo/README.md
new file mode 100644
index 00000000..e04128c4
--- /dev/null
+++ b/demo/README.md
@@ -0,0 +1,63 @@
+# demo/ -- StarshipOS in a browser
+
+A public "Try it" console: each visitor gets a StarshipOS machine of their own, logged in as a
+Zuse minted for the demo, resumed at its prompt in under a second, gone when they leave.
+Milestone *Public demo terminal* (#111-#117).
+
+```
+XWiki "Try it" page (iframe) -> Apache: TLS, websocket -> ttyd -> start-session.sh
+ -> QEMU amd64, resumed from the saved machine
+ <-> console.py <-> the visitor's terminal
+```
+
+## The pieces
+
+| File | What it does |
+|---|---|
+| `mint-zuse.sh` | Mints a Zuse of the demo's own -- never anyone's real one -- as a stick (#111, ruled 2026-11-06) |
+| `build-iso.sh` | Packs the amd64 ISO from a kernel build, as `make qemu` packs it |
+| `make-snapshot.sh` | Boots the machine once to `[zuse@Hera] ok>` and saves it (#113) |
+| `start-session.sh` | One visitor's session: resumes the saved machine, ends it on hang-up, close or time-out (#112) |
+| `console.py` | Joins the visitor's terminal to the machine only once it runs, discarding keys typed while it loads |
+| `qemu-args.sh` | The machine, shared by the two above: a resumed machine must match the saved one exactly |
+| `starshipos-demo.service` | ttyd as a systemd service, on localhost, at most 3 sessions (#114) |
+| `apache-demo.conf` | Example vhost: TLS, the websocket, framing allowed for the XWiki only (#115) |
+| `xwiki-try-it.xwiki` | The XWiki "Try it" page, with the console embedded (XWiki 2.1 syntax) |
+
+## What a visitor cannot do
+
+Each session is a copy: the firmware variables are copied, the image is read-only, and the Zuse
+stick's writes are discarded (`snapshot=on`). The guest has no network device. QEMU runs under its
+seccomp sandbox (no spawning, no privilege changes), as an unprivileged user, inside a systemd unit
+that fences memory, CPU and the file system. A session lasts at most `DEMO_SESSION_LIMIT` seconds
+(1800).
+
+## Setting it up
+
+From a checkout of the most recent green commit -- the latest that passed acceptance, `master`
+(or, once it is published, the nightly's `StarshipOS.iso`):
+
+```
+make ARCH=amd64 init INIT_DOCS=0
+demo/mint-zuse.sh /tmp/demo-zuse # once; keep the stick
+demo/build-iso.sh /tmp/StarshipOS.iso
+DEMO_PROMPT='[zuse@Hera]' demo/make-snapshot.sh /tmp/StarshipOS.iso /var/lib/starshipos-demo /tmp/demo-zuse/zusestick.img
+```
+
+Then install `starshipos-demo.service` and `apache-demo.conf` (their headers say how), and paste
+`xwiki-try-it.xwiki` into a page on the XWiki. The `{{html}}` macro there needs a page author
+with script rights.
+
+A new image means re-running `build-iso.sh` and `make-snapshot.sh`: the saved machine belongs to
+one image and one QEMU version. Sessions already running keep theirs.
+
+## Tested (2026-11-06, Ubuntu 24.04, QEMU 8.2.2, ttyd 1.7.4)
+
+- The Zuse minted: "genesis minted", then "identity confirmed" (`logs/20261106-192626`, `-192739`).
+- The saved machine: POST 538/538, "identity confirmed", `[zuse@Hera] ok>` after an 87 s boot.
+- A session, in a terminal and through ttyd's websocket: ready 0.2 s after it starts; `3 4 + .`
+ answers `7 ok`, `: SQUARE DUP * ; 7 SQUARE .` answers `49 ok`; keys typed before "Ready" are
+ discarded, not half-delivered.
+- QEMU is gone 0.1 s or less after a hang-up or a closed terminal, and on time at the session
+ limit.
+- Not tested here: Apache (none in the test container), several sessions at once (#116).
diff --git a/demo/apache-demo.conf b/demo/apache-demo.conf
new file mode 100644
index 00000000..7392b3b9
--- /dev/null
+++ b/demo/apache-demo.conf
@@ -0,0 +1,22 @@
+# apache-demo.conf -- the demo terminal on the public face (issue #115). An example: the host
+# name, the certificate paths and the XWiki allowed to embed it are Captain Bob's to set.
+# a2enmod proxy proxy_http proxy_wstunnel headers ssl rewrite
+
+ ServerName try.strshipos.com
+ SSLEngine on
+ SSLCertificateFile /etc/letsencrypt/live/try.strshipos.com/fullchain.pem
+ SSLCertificateKeyFile /etc/letsencrypt/live/try.strshipos.com/privkey.pem
+
+ # The terminal may be framed by the XWiki's "Try it" page, and by nothing else.
+ Header always unset X-Frame-Options
+ Header always set Content-Security-Policy "frame-ancestors https://www.starshipos-f18.org https://starshipos-f18.org"
+
+ ProxyPreserveHost On
+ RewriteEngine On
+ RewriteCond %{HTTP:Upgrade} =websocket [NC]
+ RewriteRule ^/ws$ ws://127.0.0.1:7681/ws [P,L]
+ ProxyPass / http://127.0.0.1:7681/
+ ProxyPassReverse / http://127.0.0.1:7681/
+ # A session lives up to 30 minutes; keep its websocket open that long.
+ ProxyTimeout 1900
+
diff --git a/demo/build-iso.sh b/demo/build-iso.sh
new file mode 100755
index 00000000..96a73c36
--- /dev/null
+++ b/demo/build-iso.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+# build-iso.sh -- the demo's ISO: the amd64 kernel's loader on an El Torito ISO, packed as
+# `make -f kernel/Makefile ARCH=amd64 qemu` packs it (kernel/Makefile, target qemu).
+#
+# demo/build-iso.sh [VAR=value ...]
+#
+# The VAR=value arguments go to the kernel build: EMERGENCY_CONSOLE_ENABLED=1 for a console with
+# no identity drive -- one of the two answers to issue #111, not yet ruled. A nightly's
+# StarshipOS.iso can be used in place of this, once the ruling says which image the demo serves.
+set -euo pipefail
+cd "$(dirname "$0")/.."
+out=$(realpath -m "${1:?usage: demo/build-iso.sh [VAR=value ...]}"); shift
+make --no-print-directory -f kernel/Makefile ARCH=amd64 clean
+make --no-print-directory -f kernel/Makefile ARCH=amd64 all "$@"
+tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT
+dd if=/dev/zero of="$tmp/efi.img" bs=512 count=8192 2>/dev/null
+mformat -i "$tmp/efi.img" ::
+mmd -i "$tmp/efi.img" ::/EFI ::/EFI/BOOT
+mcopy -i "$tmp/efi.img" build/amd64/kernel/starkernel_loader.efi ::/EFI/BOOT/BOOTX64.EFI
+xorriso -as mkisofs -r -J -e efi.img -no-emul-boot -o "$out" "$tmp" 2>/dev/null
+git checkout -q -- capsules/BLOCK_MAP.md v3/include/version.h 2>/dev/null || true # the build regenerates these
+echo "demo: $out"
diff --git a/demo/console.py b/demo/console.py
new file mode 100755
index 00000000..4d7a4ef5
--- /dev/null
+++ b/demo/console.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+"""console.py -- the visitor's terminal joined to the demo machine's serial console, once the
+machine is running (issue #112).
+
+ console.py
+
+A resumed machine takes a moment to load. Anything typed meanwhile would reach a serial port
+whose state is about to be replaced, and be lost half-way through a line ("3 4 + ." arriving as
+"4 + ."). So: wait until QEMU says the machine is running, throw away what was typed before,
+say so, and only then pass bytes both ways. Ends when either side closes.
+"""
+import json, os, select, socket, sys, termios, time, tty
+
+qmp_path, con_path = sys.argv[1], sys.argv[2]
+
+def connect(path, limit=30):
+ end = time.time() + limit
+ while True:
+ try:
+ s = socket.socket(socket.AF_UNIX); s.connect(path); return s
+ except OSError:
+ if time.time() > end: raise
+ time.sleep(0.1)
+
+qmp = connect(qmp_path).makefile("rw")
+qmp.readline()
+qmp.write(json.dumps({"execute": "qmp_capabilities"}) + "\n"); qmp.flush(); qmp.readline()
+end = time.time() + 60
+while True: # until the machine runs
+ qmp.write(json.dumps({"execute": "query-status"}) + "\n"); qmp.flush()
+ while True:
+ r = json.loads(qmp.readline())
+ if "return" in r or "error" in r: break # skip events
+ if r.get("return", {}).get("status") == "running": break
+ if time.time() > end: sys.exit("The machine did not start.")
+ time.sleep(0.1)
+
+con = connect(con_path)
+stdin, stdout = sys.stdin.fileno(), sys.stdout.fileno()
+saved = None
+if os.isatty(stdin):
+ termios.tcflush(stdin, termios.TCIFLUSH) # what was typed while the machine was loading
+ saved = termios.tcgetattr(stdin)
+ tty.setraw(stdin) # the machine echoes and edits the line itself
+os.write(stdout, b"Ready -- type a line and press Enter.\r\n")
+try:
+ while True:
+ r, _, _ = select.select([stdin, con], [], [])
+ if stdin in r:
+ data = os.read(stdin, 1024)
+ if not data: break
+ con.sendall(data)
+ if con in r:
+ data = con.recv(4096)
+ if not data: break
+ os.write(stdout, data)
+except OSError:
+ pass # the visitor's terminal went away
+finally:
+ if saved: termios.tcsetattr(stdin, termios.TCSADRAIN, saved)
diff --git a/demo/make-snapshot.sh b/demo/make-snapshot.sh
new file mode 100755
index 00000000..dc5adb5f
--- /dev/null
+++ b/demo/make-snapshot.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+# make-snapshot.sh -- boot the demo machine once, to its prompt, and save it: every session
+# resumes from here, at the prompt in seconds instead of after boot and POST (issue #113).
+#
+# demo/make-snapshot.sh [identity.img]
+#
+# gets the image, the identity drive if any, the firmware's variables as the boot
+# left them (vars.fd) and the machine's state (state). One QEMU, in the foreground.
+set -euo pipefail
+here=$(cd "$(dirname "$0")" && pwd)
+. "$here/qemu-args.sh"
+iso=${1:?usage: demo/make-snapshot.sh [identity.img]}
+dir=$(realpath -m "${2:?usage: demo/make-snapshot.sh [identity.img]}")
+ident=${3:-}
+prompt=${DEMO_PROMPT:-ok> } # what the console prints when it is ready
+boot_limit=${DEMO_BOOT_LIMIT:-600} # seconds a boot may take, POST included
+
+rm -rf "$dir"; mkdir -p "$dir"
+cp "$iso" "$dir/StarshipOS.iso"
+[ -z "$ident" ] || cp "$ident" "$dir/identity.img"
+cp /usr/share/OVMF/OVMF_VARS_4M.fd "$dir/vars.fd"
+demo_qemu_args "$dir" "$dir/vars.fd"
+
+log=$dir/boot.log qmp=$dir/qmp.sock
+qemu-system-x86_64 "${QEMU_ARGS[@]}" -serial "file:$log" -qmp "unix:$qmp,server=on,wait=off" &
+pid=$!
+trap 'kill $pid 2>/dev/null || true' EXIT
+qmp() { # one QMP command, after the greeting and capabilities; prints QEMU's replies
+ python3 - "$qmp" "$1" <<'PY'
+import json, socket, sys, time
+s = socket.socket(socket.AF_UNIX); s.connect(sys.argv[1]); f = s.makefile("rw")
+f.readline(); f.write(json.dumps({"execute": "qmp_capabilities"}) + "\n"); f.flush(); f.readline()
+f.write(sys.argv[2] + "\n"); f.flush(); print(f.readline().strip())
+PY
+}
+echo "demo: booting $iso to '$prompt' (at most ${boot_limit}s)"
+t0=$(date +%s)
+until [ -S "$qmp" ] && grep -aqF -- "$prompt" "$log" 2>/dev/null; do
+ kill -0 $pid 2>/dev/null || { echo "demo: QEMU exited during the boot" >&2; tail -5 "$log" >&2; exit 1; }
+ [ $(( $(date +%s) - t0 )) -lt "$boot_limit" ] || { echo "demo: no prompt in ${boot_limit}s" >&2; tail -5 "$log" >&2; exit 1; }
+ sleep 1
+done
+echo "demo: at the prompt after $(( $(date +%s) - t0 ))s; saving"
+sleep 2 # let the console finish the line
+# Saved running, not stopped first: QEMU saves the run state too, and a machine saved stopped
+# resumes stopped -- every session would be a frozen screen. Migration stops it at the end itself.
+qmp "{\"execute\":\"migrate\",\"arguments\":{\"uri\":\"file:$dir/state\"}}" >/dev/null
+until qmp '{"execute":"query-migrate"}' | grep -q '"status": "completed"'; do
+ qmp '{"execute":"query-migrate"}' | grep -q '"status": "failed"' && { echo "demo: saving failed" >&2; exit 1; }
+ sleep 1
+done
+qmp '{"execute":"quit"}' >/dev/null || true
+wait $pid 2>/dev/null || true
+trap - EXIT
+rm -f "$qmp"
+echo "demo: saved $(du -h "$dir/state" | cut -f1) to $dir/state"
diff --git a/demo/mint-zuse.sh b/demo/mint-zuse.sh
new file mode 100755
index 00000000..f4f6be5a
--- /dev/null
+++ b/demo/mint-zuse.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+# mint-zuse.sh -- a Zuse of the demo's own, as a stick (issue #111, ruled by Captain Bob,
+# 2026-11-06: "Mint Zuse for demo console").
+#
+# demo/mint-zuse.sh
+#
+# A fresh pair, never anyone's real Zuse: whatever a visitor does as this Zuse stays in a
+# throwaway machine, and the drive's writes are discarded (qemu-args.sh). Minted the way
+# disk/zuse-scratch was (disk/README.md): a blank 1 GiB Artemis disk and a blank 64 MiB identity
+# drive, booted with system=v3 on amd64 -- "Zuse: genesis minted onto attached thumbdrive" --
+# then booted again -- "Zuse: identity confirmed from attached thumbdrive" -- then laid out as a
+# StarshipOS stick (make zusestick), which is what the v4 demo machine attaches.
+#
+# gets artemis.img and zuse-thumb-ident.img (the pair) and zusestick.img (the stick).
+# Two QEMU boots, one at a time, in the foreground; their serial logs go to logs/ as any boot's.
+set -euo pipefail
+cd "$(dirname "$0")/.."
+out=$(realpath -m "${1:?usage: demo/mint-zuse.sh }")
+[ ! -e "$out/zuse-thumb-ident.img" ] || { echo "demo: $out already has a pair -- refusing to mint over it" >&2; exit 1; }
+mkdir -p "$out"
+truncate -s 1G "$out/artemis.img"
+truncate -s 64M "$out/zuse-thumb-ident.img"
+
+boot() { # boot : one system=v3 boot with the pair attached
+ local want=$1 bad=$2 mk log qmp t0
+ mk=$(mktemp)
+ make --no-print-directory -f kernel/Makefile ARCH=amd64 clean qemu QEMU_DISPLAY=none \
+ KERNEL_ARGS=system=v3 ARTDISK="$out/artemis.img" ZUSEDISK="$out/zuse-thumb-ident.img" > "$mk" 2>&1 &
+ local pid=$!
+ t0=$(date +%s)
+ until log=$(sed -n 's/^ Serial log: //p' "$mk") && [ -n "$log" ] && grep -aq -- "$want" "$log" 2>/dev/null; do
+ if [ -n "${log:-}" ] && grep -aq -- "$bad" "$log" 2>/dev/null; then echo "demo: '$bad'" >&2; kill $pid; exit 1; fi
+ kill -0 $pid 2>/dev/null || { echo "demo: the boot ended without '$want'" >&2; tail -20 "$mk" >&2; exit 1; }
+ [ $(( $(date +%s) - t0 )) -lt 900 ] || { echo "demo: no '$want' in 900s" >&2; kill $pid; exit 1; }
+ sleep 2
+ done
+ echo "demo: $want ($log, $(( $(date +%s) - t0 ))s)"
+ qmp=$(sed -n 's/^ QMP socket: //p' "$mk")
+ python3 - "$qmp" <<'PY'
+import json, socket, sys
+s = socket.socket(socket.AF_UNIX); s.connect(sys.argv[1]); f = s.makefile("rw")
+f.readline()
+for c in ({"execute": "qmp_capabilities"}, {"execute": "quit"}):
+ f.write(json.dumps(c) + "\n"); f.flush(); f.readline()
+PY
+ wait $pid || true
+ rm -f "$mk"
+}
+
+boot "Zuse: genesis minted onto attached thumbdrive" "fence marker write FAILED"
+boot "Zuse: identity confirmed from attached thumbdrive" "not recognized"
+make --no-print-directory -f kernel/Makefile ARCH=amd64 zusestick ZUSE_PAIR="$out"
+cp build/amd64/kernel/zusestick.img "$out/zusestick.img"
+git checkout -q -- capsules/BLOCK_MAP.md v3/include/version.h 2>/dev/null || true # the build regenerates these
+echo "demo: Zuse minted -- $out/zusestick.img"
diff --git a/demo/qemu-args.sh b/demo/qemu-args.sh
new file mode 100644
index 00000000..cedd1c0b
--- /dev/null
+++ b/demo/qemu-args.sh
@@ -0,0 +1,37 @@
+# qemu-args.sh -- the demo machine, sourced by make-snapshot.sh and start-session.sh.
+#
+# A session resumes a machine saved by make-snapshot.sh, and QEMU can resume a saved machine only
+# into the same machine: the same board, CPU, memory, firmware and devices. Both scripts build the
+# command line here, so they cannot drift apart. It is the machine `make -f kernel/Makefile
+# ARCH=amd64 qemu` boots (q35, TCG, 1 GiB, OVMF, the ISO as a CD), less what a visitor must not
+# have: no network device, no Artemis disk, no display, and QEMU's seccomp sandbox on.
+#
+# demo_qemu_args -> fills the array QEMU_ARGS
+#
+# /StarshipOS.iso is the image; /identity.img, if there, is the identity
+# drive every session gets (issue #111), attached with its writes discarded.
+
+OVMF_CODE=${OVMF_CODE:-/usr/share/OVMF/OVMF_CODE_4M.fd}
+
+demo_qemu_args() {
+ local dir=$1 vars=$2
+ QEMU_ARGS=(
+ -machine q35,accel=tcg
+ -cpu qemu64,+rdrand
+ -m 1024
+ -smp 1
+ -drive "if=pflash,format=raw,readonly=on,file=$OVMF_CODE"
+ -drive "if=pflash,format=raw,file=$vars"
+ -drive "if=none,id=cd,media=cdrom,readonly=on,file=$dir/StarshipOS.iso"
+ -device ide-cd,drive=cd,bootindex=0
+ -device qemu-xhci,id=xhci0,p2=16,p3=16 # kernel/Makefile XHCI_PORTS
+ -nic none
+ -display none
+ -no-reboot
+ -sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny
+ )
+ if [ -f "$dir/identity.img" ]; then
+ QEMU_ARGS+=(-drive "if=none,id=ident,format=raw,snapshot=on,file=$dir/identity.img"
+ -device usb-storage,bus=xhci0.0,port=1,drive=ident)
+ fi
+}
diff --git a/demo/starshipos-demo.service b/demo/starshipos-demo.service
new file mode 100644
index 00000000..2285a281
--- /dev/null
+++ b/demo/starshipos-demo.service
@@ -0,0 +1,33 @@
+# starshipos-demo.service -- the public demo's web terminal (issue #114): ttyd on localhost, one
+# throwaway StarshipOS machine per browser connection (start-session.sh). Apache in front
+# (apache-demo.conf) does TLS and the websocket.
+#
+# Install: the repository's demo/ at /opt/starshipos-demo/demo, the saved machine (make-snapshot.sh)
+# in /var/lib/starshipos-demo, a user starshipos-demo; then systemctl enable --now starshipos-demo.
+[Unit]
+Description=StarshipOS public demo terminal (ttyd)
+After=network.target
+
+[Service]
+User=starshipos-demo
+Group=starshipos-demo
+Environment=DEMO_STATE=/var/lib/starshipos-demo
+Environment=DEMO_SESSION_LIMIT=1800
+# -m: sessions at once -- each a QEMU of about 1 GiB and most of a core (issue #116 measures it)
+ExecStart=/usr/bin/ttyd --port 7681 --interface lo --writable --max-clients 3 \
+ --client-option titleFixed=StarshipOS --client-option disableLeaveAlert=true \
+ /opt/starshipos-demo/demo/start-session.sh
+Restart=on-failure
+# Each session is already a sandboxed QEMU with no network; this fences ttyd and its children too.
+NoNewPrivileges=yes
+ProtectSystem=strict
+ProtectHome=yes
+PrivateTmp=yes
+PrivateDevices=yes
+ReadOnlyPaths=/var/lib/starshipos-demo
+MemoryMax=4G
+CPUQuota=300%
+TasksMax=200
+
+[Install]
+WantedBy=multi-user.target
diff --git a/demo/start-session.sh b/demo/start-session.sh
new file mode 100755
index 00000000..09d4286f
--- /dev/null
+++ b/demo/start-session.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+# start-session.sh -- one visitor's session: the saved demo machine, resumed at its prompt, its
+# serial console on this terminal; gone when the visitor leaves or the time is up (issue #112).
+# ttyd runs this once per browser connection (issue #114).
+#
+# demo/start-session.sh [state-dir] (default: /var/lib/starshipos-demo)
+#
+# Nothing a visitor does survives the session: the firmware variables are a copy, the identity
+# drive's writes are discarded, the image is read-only, and the guest has no network.
+set -euo pipefail
+here=$(cd "$(dirname "$0")" && pwd)
+. "$here/qemu-args.sh"
+dir=${1:-${DEMO_STATE:-/var/lib/starshipos-demo}}
+limit=${DEMO_SESSION_LIMIT:-1800} # seconds a session may last
+
+[ -f "$dir/state" ] || { echo "The demo is not set up yet: no saved machine in $dir." >&2; exit 1; }
+tmp=$(mktemp -d "${TMPDIR:-/tmp}/starshipos-session.XXXXXX")
+trap 'rm -rf "$tmp"' EXIT
+cp "$dir/vars.fd" "$tmp/vars.fd"
+demo_qemu_args "$dir" "$tmp/vars.fd"
+
+printf '\r\nStarshipOS -- starting a throwaway machine, yours for up to %d minutes. Nothing you do is kept.\r\n' $((limit / 60))
+printf 'Try: 3 4 + . : SQUARE DUP * ; 7 SQUARE .\r\n\r\n'
+# QEMU is a tracked child, not exec'd, so that whatever ends the session ends the machine: ttyd
+# hangs up (it signals this script only), the service stops, or the time runs out. Its console is
+# a private socket, joined to this terminal by console.py only once the machine runs: keys typed
+# while it loads would be lost half-way through a line.
+qemu-system-x86_64 "${QEMU_ARGS[@]}" \
+ -chardev "socket,id=con,path=$tmp/con.sock,server=on,wait=off" -serial chardev:con \
+ -qmp "unix:$tmp/qmp.sock,server=on,wait=off" -monitor none \
+ -incoming "file:$dir/state" /dev/null ) &
+timer=$!
+# The bridge is a tracked child too: bash runs a trap only once its foreground command returns,
+# so a hang-up would otherwise wait on the bridge. `wait` returns to the trap at once.
+python3 -I "$here/console.py" "$tmp/qmp.sock" "$tmp/con.sock" 0<&0 &
+bridge=$!
+trap 'kill "$bridge" "$qemu" "$timer" 2>/dev/null; rm -rf "$tmp"' EXIT
+trap 'exit 0' HUP TERM INT
+wait "$bridge" || true
+printf '\r\n\r\nThe session has ended. Reload the page for a new one.\r\n'
diff --git a/demo/xwiki-try-it.xwiki b/demo/xwiki-try-it.xwiki
new file mode 100644
index 00000000..45f0c280
--- /dev/null
+++ b/demo/xwiki-try-it.xwiki
@@ -0,0 +1,28 @@
+= Try StarshipOS =
+
+This is StarshipOS, running. Below is the console of a real StarshipOS machine -- the amd64 build
+from the latest version that passed its acceptance tests -- started just for you. You are logged
+in as Zuse, the superuser, of a machine that exists only for your session: there is nothing to
+break, and nothing you do is kept. Close the page and it is gone.
+
+{{html clean="false"}}
+
+{{/html}}
+
+== Things to try ==
+
+StarshipOS speaks FORTH. Type a line and press Enter; ##ok## means it was done.
+
+|=You type|=It answers
+|##3 4 + .##|##7 ok## -- numbers go on a stack, ##+## adds the top two, ##.## prints the result
+|##: SQUARE DUP * ;##|##ok## -- you have just defined a new word
+|##7 SQUARE .##|##49 ok## -- and used it
+
+== What you are looking at ==
+
+* A machine of your own, booted from the same image the project publishes every night, resumed at
+ its prompt so you do not wait for it to boot.
+* It runs in an emulator on our server. It has no network, and it lasts at most 30 minutes.
+* If the console does not connect, the few machines we run are all in use -- try again in a few
+ minutes.
diff --git a/experiments/bare_metal/runs/doe-amd64-20261106-192626.csv b/experiments/bare_metal/runs/doe-amd64-20261106-192626.csv
new file mode 100644
index 00000000..e69de29b
diff --git a/experiments/bare_metal/runs/doe-amd64-20261106-192739.csv b/experiments/bare_metal/runs/doe-amd64-20261106-192739.csv
new file mode 100644
index 00000000..e69de29b
diff --git a/logs/20261106-192626/amd64/qemu-amd64-20261106-192626.log b/logs/20261106-192626/amd64/qemu-amd64-20261106-192626.log
new file mode 100644
index 00000000..1aeb40fd
--- /dev/null
+++ b/logs/20261106-192626/amd64/qemu-amd64-20261106-192626.log
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:60e20ed87bc0f9ea1a2e0ba5cfc04a899d33b26039ddfd3669b7f00a0fc6012c
+size 686495
diff --git a/logs/20261106-192739/amd64/qemu-amd64-20261106-192739.log b/logs/20261106-192739/amd64/qemu-amd64-20261106-192739.log
new file mode 100644
index 00000000..fb45133c
--- /dev/null
+++ b/logs/20261106-192739/amd64/qemu-amd64-20261106-192739.log
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a2345a6df917f05ab8973662b76aaa0490e06bbbf08f6dd6341e5ad87f431685
+size 686088