Merge pull request #129 from dan-claude-bot/feat/mint-stamp

feat: a minted box records how it was minted, and box info reads it back
This commit is contained in:
Daniel Marin 2026-07-21 12:24:40 +01:00 committed by GitHub
commit 8f1a281c20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 624 additions and 5 deletions

View file

@ -5,6 +5,75 @@ which records not just what changed but what each drill run proved.
## Unreleased
### Added
- **A minted box records how it was minted, and `box info` reads it back**
(#103) — `cmd_new` knew a great deal at the moment it launched and wrote
three `user.*` keys, dropping the rest on the floor: the box version that
minted it, the base image (an *unpinned alias on a moving remote* — two
boxes minted a month apart from "the same template" are not the same box,
and nothing on either said so), the rig role, which rig repo and ref
converged it, the mint time, and whether a container was chosen or fallen
back into for want of `/dev/kvm`. There is no host-side per-box store —
the Incus instance config *is* the database — so every one of those facts
was simply gone the moment the mint returned. The same single write point
now carries them as `user.box.*`, plus `user.box.schema=1` naming the
stamp's *shape* (an integer, not the box version: it moves only when a key
is removed or repurposed, never when one is added). The alias's resolved
fingerprint is pinned in a second call after the launch, read from
`volatile.base_image` — best-effort by construction, because a box that
exists and boots must never be failed over a provenance field. Deliberately
not stamped: cpu/memory (`limits.*` hold them, and a duplicate drifts the
first time someone edits a limit by hand), disk (a VM's *is* the root
device size and a container's does not exist — its root rides the pool),
and the tier, which `box_tier()` derives from whoever is *asking*.
- **A clone re-stamps its own provenance instead of inheriting a lie**
(#103) — `incus copy` carries every `user.*` key forward (drill audit item
B2), which is what makes a clone know its template and user for free and is
exactly why the mint stamp could not ride along untouched: an inherited
stamp does not go stale, it goes **false**, claiming a mint time the clone
was not present for and a box version that never saw it. `box new --from`
now re-stamps the four keys that describe *this* instance's coming into
being — schema, version, created, `origin=clone` with `origin.from=<src>`
on the copied instance *before* it is started, so a clone is never
observable wearing its source's provenance. The lineage keys (template,
user, image, role, rig pin) are left alone on purpose: the clone's disk
genuinely did come from them, and re-deriving them from the cloning
process's own template lookup would be the actual lie. `origin.from`
records **one hop** — a clone of a clone names its parent and forgets its
grandparent, because the alternative is an unbounded chain in a config
value and the parent is the box an operator can go look at.
One key sits in **neither** column and is therefore *cleared*:
`user.box.mode.asked`. It records whether a container was asked for or
fallen back into, which makes it a mint-*event* fact — and the asker was
the **source's** operator. A clone refuses `--vm`/`--container` outright,
so nobody was asked anything about this instance, and there is no true
value to re-stamp it with; inheriting it made `box info` print
`MODE vm (asked: auto)` on a clone, describing a demand never made of it.
The clear lands with the re-stamp, before the start, and the read side
needs no special case: the `MODE` line is gated on `asked`, so absence
renders as silence while `TYPE` still reports VM or CT off the preserved
instance type.
- **`box info` grew a provenance block** (#103) — it printed
`NAME / STATE / TYPE / IPV4`, exposures and snapshots, and surfaced *none*
of the `user.box.*` keys, including the two that already existed. A stamp
nothing can read is not a stamp. Every key is read with absence tolerated:
`incus config get` on an unset key prints empty and exits 0 (audit item
B4), so "no stamp" and "the daemon said no" arrive identically, and both
must render as a box with blanks. **Boxes minted before this stamp existed
keep working under every verb** — the README's standing promise, and the
same one `user.claudebox` carries — showing `MINTED (not recorded — this
box predates the mint stamp)` rather than an invented time or an error; a
pre-rename box still reads as the claude template. A schema this box does
not recognise is treated as *newer than me*: it shows what it understands
and says so, because a box outlives the release that minted it and refusing
to describe one a later release minted perfectly well is the wrong answer.
`box info --json` needed no code — the keys ride `incus list --format json`
in `config`. `test/cli.sh` drives both halves against a fake incus that
logs the arguments box builds, including the absence assertions that keep
cpu/memory/disk/tier out of the namespace and the lineage keys out of the
clone's re-stamp.
### Changed
- **`state:needs-human` no longer waits on the cron to become true** (#141)

View file

@ -390,7 +390,7 @@ the door is per-port, punched and removable at runtime.
box new --name <box> [--template <t>] [--from <src>[/<snap>]] [--cpu <n>] [--memory <size>] [--disk <size>] [--vm|--container]
box templates # list the templates this install can mint
box list # list your boxes
box info <box> # one box: state, IP, exposures, snapshot labels
box info <box> # one box: state, IP, exposures, provenance, snapshots
box shell <box> # enter as the template's user
box exec <box> -- <cmd...> # run a command in the box
box tmux <box> [session] # attach/create a tmux session — survives disconnects
@ -427,6 +427,49 @@ clones an existing box or snapshot. VM mode (`--vm`, the default where
`security.nesting=true`) is for hosts without nested virt — weaker isolation,
dev/test only.
## What minted this box: `box info`
A box outlives the release that minted it, the template that shaped it and the
image build it came from — and until
[#103](https://github.com/heavy-duty/box/issues/103) it recorded none of them.
There is no host-side per-box store; the Incus instance config _is_ the
database, so a fact not written at mint time is simply gone. `box new` now
stamps what it knew, and `box info` reads it back:
```
NAME work
STATE RUNNING
TYPE VM
IPV4 10.x.x.x
MINTED 2026-07-19T14:22:07Z by box 0.8.1
TEMPLATE claude (user claude, role claude)
IMAGE images:debian/13/cloud @ 8a2f1c9d4e5b…
MODE vm (asked: auto)
RIG heavy-duty/rig@main
ORIGIN mint
```
The image line carries both halves on purpose: the template names an
_unpinned alias on a moving remote_, so what it resolved to at that mint is the
only reproducible fact. `box info --json` carries every key verbatim — they
ride `incus list --format json` in `config`.
**A clone re-stamps.** `incus copy` preserves `user.*` keys, so a clone inherits
its source's template and user for free — but inheriting the mint stamp would
not make it stale, it would make it **false**: the clone was not present at that
mint. `box new --from` therefore re-stamps the four keys that describe _this_
instance's coming into being (`ORIGIN clone of work/authed`, a fresh time, the
box version that cloned it) and leaves the lineage keys alone, because the
clone's disk genuinely did come from that image, template and role. `origin.from`
records one hop: a clone of a clone names its parent, not its grandparent.
**Boxes minted before this stamp existed keep working**, under this verb and
every other — they render as a box with blanks and say `MINTED (not recorded)`
rather than erroring. `user.box.schema` names the stamp's _shape_ (an integer,
not the box version) so a box minted by a later release reads back on an older
box as "here is what I understand, and there is more I don't".
## Boxes are just Incus instances
A box is an ordinary Incus instance tagged `user.box=1` (pre-0.4.0 boxes

188
bin/box
View file

@ -12,7 +12,18 @@ inst="" # the resolved Incus instance, set by the 'box' precondition
die() { echo "box: $*" >&2; exit 1; } # 1 = it went wrong
usage_error() { echo "box: $*" >&2; echo "try 'box help'." >&2; exit 2; } # 2 = you asked wrong
version() { echo "box $(cat "$root/VERSION" 2>/dev/null || echo unknown) ($root)"; }
# The tree's own version, read in ONE place. 'box --version' says it out loud;
# the mint stamp (#103) writes it onto every instance box creates, so a box can
# still name the release that made it long after that release is history.
box_version() { cat "$root/VERSION" 2>/dev/null || echo unknown; }
version() { echo "box $(box_version) ($root)"; }
# The SHAPE of the mint stamp, not the box version — an integer that changes
# only when a key is removed or repurposed, never when one is added (a reader
# that does not know a key simply does not print it). Absent means pre-stamp:
# every box minted before #103 has no schema key at all, and must keep working
# under every verb, which is the same promise 'user.claudebox' carries.
BOX_STAMP_SCHEMA=1
# Which tier is THIS PROCESS? Decided from live credentials (argless 'id -nG':
# what the kernel will present when incus opens the socket), never from the
@ -73,7 +84,7 @@ CMDS=(
"new^--name <box> [--template <t>] [--from <src>[/<snap>]] [--cpu <n>] [--memory <size>] [--disk <size>] [--vm|--container]^^Mint a box from a template (default: blank), or --from an existing box/snapshot^fn:cmd_new^"
"templates^^^List the templates this install can mint^fn:cmd_templates^"
"list^[--json]^^List your boxes^fn:cmd_list^"
"info^<box> [--json]^box^One box: state, type, IP, and its snapshot labels^fn:cmd_info^"
"info^<box> [--json]^box^One box: state, type, IP, what minted it, snapshot labels^fn:cmd_info^"
"shell^<box>^box^Open a shell in a box, as its template's user^fn:cmd_shell^"
"exec^<box> -- <cmd...>^box^Run a command inside a box^fn:cmd_exec^"
"tmux^<box> [<session>]^box^Attach or create a tmux session in a box — survives disconnects^fn:cmd_tmux^"
@ -1002,8 +1013,16 @@ load_template() {
# environment must die on the host, never execute in the guest. bash's =~
# anchors to the whole string — a multi-line value cannot sneak one clean
# line past it the way a line-oriented grep would.
# The rig pin, resolved from the mint environment, in ONE place: render_userdata
# substitutes it into the seed, and the mint stamp (#103) records it onto the
# instance. Two spellings of the same default would eventually disagree, and a
# stamp that disagrees with the seed is worse than no stamp at all.
rig_repo() { printf '%s\n' "${RIG_REPO:-heavy-duty/rig}"; }
rig_ref() { printf '%s\n' "${RIG_REF:-main}"; }
render_userdata() {
local f="$1" repo="${RIG_REPO:-heavy-duty/rig}" ref="${RIG_REF:-main}" data
local f="$1" repo data ref
repo="$(rig_repo)"; ref="$(rig_ref)"
[[ "$repo" =~ ^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$ ]] \
|| die "RIG_REPO must look like owner/repo: $repo"
[[ "$ref" =~ ^[A-Za-z0-9._/-]+$ ]] \
@ -1026,6 +1045,13 @@ cmd_templates() {
echo "mint one: box new --name <box> --template <template>"
}
# When this instance came into being. A timestamp in a CONVERGENT file would be
# churn — the same run writing a different byte every time — but a mint is not
# convergent: it happens exactly once, to exactly one instance, and is never
# re-run against it. UTC and ISO 8601 so it sorts as a string and means the same
# thing on every host that reads it back.
mint_time() { date -u +%Y-%m-%dT%H:%M:%SZ; }
cmd_new() {
[ -n "$name" ] || usage_error "usage: $(synopsis_of new)"
require_stack
@ -1037,6 +1063,47 @@ cmd_new() {
case "$from" in */*) snap="${from#*/}" ;; esac
srcref="$(iname_of "$src")"; [ -n "$snap" ] && srcref="$srcref/$snap"
incus copy "$srcref" "$instance"
# 'incus copy' carries every user.* key forward (audit B2) — which is what
# makes a clone know its template and user for free, and is also why the
# mint stamp (#103) cannot simply ride along. A clone that inherited the
# stamp verbatim would claim to have been minted at the source's mint time,
# by the box version that minted the SOURCE, in a mint that never touched
# this instance. That is not a stale field, it is a false one.
#
# So re-stamp exactly the keys that describe THIS instance's coming into
# being, and leave the rest alone:
# · version / created / schema — the clone was made HERE, NOW, by THIS box
# · origin=clone, origin.from=<srcref> — how, and from what
# Deliberately NOT re-stamped, because they are lineage and stay true: the
# clone's disk really did come from that image, that template, that user and
# that rig role — reading them off the source is the whole point of a clone.
# ('incus copy' preserves the instance type too, so mode stays true as well.)
#
# 'mode.asked' is the one key that sits in NEITHER column, and so it is
# CLEARED rather than re-stamped or inherited. It is a mint-event fact —
# only the mint knew whether a container was asked for or fallen back into
# for want of /dev/kvm — and the asker was the SOURCE's operator. A clone
# refuses --vm/--container outright (nobody was asked anything here), so an
# inherited 'asked' makes 'box info' print a demand that was never made of
# this instance. There is no true value to re-stamp it with: the honest
# answer is absence, and absence is already how the whole block renders
# what it does not know — the MODE line simply does not print, while TYPE
# above still says VM or CT off the preserved instance type.
#
# origin.from records ONE hop. A clone of a clone names its parent and
# forgets its grandparent: the alternative is an unbounded chain in a config
# value, and the parent is the box an operator can actually go look at.
incus config set "$instance" \
user.box.schema="$BOX_STAMP_SCHEMA" \
user.box.version="$(box_version)" \
user.box.created="$(mint_time)" \
user.box.origin=clone \
user.box.origin.from="$srcref"
# Cleared, not set-to-empty: an empty value is still a key, and a reader
# that greps the config would find it. Tolerated failure because the source
# may predate the stamp and never have carried the key at all — a clone
# must not die over a key that was already absent.
incus config unset "$instance" user.box.mode.asked >/dev/null 2>&1 || true
incus start "$instance"
wait_agent "$instance"
reset_identity "$instance"
@ -1077,6 +1144,47 @@ cmd_new() {
# which user. 'incus copy' preserves user.* keys (audit B2), so a clone
# knows what it is without ever consulting the template again.
#
# And the rest of what this line knows and used to drop on the floor (#103).
# There is no host-side per-box store — the Incus instance config IS the
# database — so a fact not written here is simply gone the moment the mint
# returns. The stamp describes the MINT, not the outcome: it lands before
# cloud-init and before rig, and nothing later edits it.
#
# schema the stamp's shape, so a future reader knows what it is holding
# version the box that minted it — 'box --version' is a fact about the
# binary in front of you, never about the box you are looking at
# image the alias asked for. It is an UNPINNED alias on a moving
# remote: two boxes minted a month apart from "the same
# template" are not the same box, and the alias alone cannot
# say so. What it resolved to is pinned after the launch below.
# mode what it minted as, and what was ASKED — a container that fell
# back for want of /dev/kvm and one the operator asked for read
# identically afterwards, and only the mint knew which
# role the rig role box auto-runs at the hook below
# rig.* WHICH rig converged it, stamped only for a seed that actually
# installs rig from the pin ('blank' seeds none, so it gets none)
# created when. See mint_time() for why a timestamp belongs here.
# origin mint. A clone re-stamps it (see the --from branch above).
#
# NOT stamped, on purpose: cpu/memory (limits.* already hold them, and a
# duplicate drifts the first time someone edits the limit by hand); disk
# (a VM's is the root device size, and a container's does not exist — its
# root rides the pool, so a stamped value would be fiction); and tier,
# which box_tier() derives from whoever is ASKING, not from the box.
local stamp=(
--config user.box.schema="$BOX_STAMP_SCHEMA"
--config user.box.version="$(box_version)"
--config user.box.image="$T_IMAGE"
--config user.box.mode="$m"
--config user.box.mode.asked="$mode"
--config user.box.created="$(mint_time)"
--config user.box.origin=mint
)
[ -z "$T_BOOTSTRAP_ROLE" ] || stamp+=(--config user.box.role="$T_BOOTSTRAP_ROLE")
if grep -q '@RIG_REPO@' "$root/templates/$t/user-data.yaml" 2>/dev/null; then
stamp+=(--config user.box.rig.repo="$(rig_repo)" --config user.box.rig.ref="$(rig_ref)")
fi
#
# The launch is narrated and TIME-BOXED (#93). Twice in the 2026-07-19
# release drill the child 'incus launch' wedged before the create was
# even accepted — 'incus operation list' empty, the instance never
@ -1095,6 +1203,7 @@ cmd_new() {
--config user.box=1 \
--config user.box.template="$t" \
--config user.box.user="$T_USER" \
"${stamp[@]}" \
--config limits.cpu="$T_CPU" \
--config limits.memory="$T_MEMORY" \
--config cloud-init.user-data="$(render_userdata "$root/templates/$t/user-data.yaml")" \
@ -1132,6 +1241,20 @@ cmd_new() {
# Not a wedge: incus refused and said why on stderr, right above.
die "incus launch failed (exit $rc)"
fi
# The one field the launch line could not know: 'user.box.image' above is
# the ALIAS, and an alias on a moving remote is not a reproducible fact.
# Incus resolves it during the launch and records what it landed on in
# volatile.base_image — read it back and pin it into the stamp, so an
# incident six months from now can ask "was this box built on the image
# that broke?" and get an answer instead of a template name.
#
# Best-effort BY CONSTRUCTION, and that is the whole design of this line:
# it runs only after a launch that already succeeded, and a box that exists
# and boots must never be failed over a provenance field. Every failure
# here is silent and leaves the alias standing as the honest partial answer
# — a stamp with no fingerprint, which is exactly how cmd_info renders it.
local fp; fp="$(incus config get "$instance" volatile.base_image 2>/dev/null || true)"
[ -z "$fp" ] || incus config set "$instance" user.box.image.fingerprint="$fp" >/dev/null 2>&1 || true
wait_agent "$instance"
echo "box: waiting for phase-1 (cloud-init)..."
echo "box: (its full narration, live: incus exec $name -- tail -f /var/log/cloud-init-output.log)"
@ -1284,6 +1407,59 @@ cmd_list() {
fi
}
# One instance config key. 'incus config get' on an UNSET key prints empty and
# exits 0 (audit B4) — so the '|| true' here covers the daemon refusing, not the
# key being absent, and every caller below reads absence as an empty string.
box_cfg() { incus config get "$1" "$2" 2>/dev/null || true; }
# The mint stamp (#103), read back for 'box info'. Every key is optional and so
# is the whole block: a box minted before the stamp existed carries none of it
# and must render as a box with blanks — never as an error, and never as a box
# wearing a mint time it does not have. Legacy boxes are not a transitional
# case: a box outlives the release that minted it, which is exactly what the
# legacy 'user.claudebox' tag already says out loud at resolve_box.
box_provenance() {
local i="$1" schema created ver img fp m asked tpl u role rrepo rref origin from
schema="$(box_cfg "$i" user.box.schema)"
created="$(box_cfg "$i" user.box.created)"; ver="$(box_cfg "$i" user.box.version)"
img="$(box_cfg "$i" user.box.image)"; fp="$(box_cfg "$i" user.box.image.fingerprint)"
m="$(box_cfg "$i" user.box.mode)"; asked="$(box_cfg "$i" user.box.mode.asked)"
tpl="$(box_cfg "$i" user.box.template)"; u="$(box_cfg "$i" user.box.user)"
role="$(box_cfg "$i" user.box.role)"
rrepo="$(box_cfg "$i" user.box.rig.repo)"; rref="$(box_cfg "$i" user.box.rig.ref)"
origin="$(box_cfg "$i" user.box.origin)"; from="$(box_cfg "$i" user.box.origin.from)"
# A pre-rename box has no metadata at all but is always a Claude box — the
# same mapping box_user() makes, for the same reason.
[ -n "$tpl" ] || [ "$(box_cfg "$i" user.claudebox)" != 1 ] || { tpl=claude; u="${u:-claude}"; }
echo
if [ -n "$created" ] || [ -n "$ver" ]; then
printf '%-11s%s\n' MINTED "${created:-(time not recorded)} by box ${ver:-unknown}"
else
printf '%-11s%s\n' MINTED "(not recorded — this box predates the mint stamp)"
fi
if [ -n "$tpl" ]; then
local paren=""
[ -z "$u" ] || paren="user $u"
[ -z "$role" ] || paren="${paren:+$paren, }role $role"
printf '%-11s%s\n' TEMPLATE "$tpl${paren:+ ($paren)}"
fi
[ -z "$img" ] || printf '%-11s%s\n' IMAGE "$img${fp:+ @ ${fp:0:12}…}"
# The mode is only worth a line alongside what was ASKED: TYPE above already
# says VM or CT, but only the mint knew whether a container was chosen or
# fallen back into for want of /dev/kvm.
[ -z "$asked" ] || printf '%-11s%s\n' MODE "${m:-?} (asked: $asked)"
[ -z "$rrepo" ] || printf '%-11s%s\n' RIG "$rrepo@${rref:-?}"
[ -z "$origin" ] || printf '%-11s%s\n' ORIGIN "$origin${from:+ of $from}"
# A schema box does not recognise is NEWER than box, not broken: show what is
# understood and say so, rather than refusing to describe a box that a later
# release minted perfectly well. (A non-integer lands here too, which is the
# right side to fail on.)
if [ -n "$schema" ] && { ! [ "$schema" -eq "$schema" ] 2>/dev/null || [ "$schema" -gt "$BOX_STAMP_SCHEMA" ]; }; then
printf '%-11s%s\n' NOTE "stamp schema '$schema' is newer than this box ($(box_version)) reads ($BOX_STAMP_SCHEMA) — showing what it understands"
fi
}
cmd_info() {
local box="${args[0]}" row
if [ "$json" -eq 1 ]; then incus list "$inst" --format json; return; fi
@ -1305,6 +1481,12 @@ cmd_info() {
printf '%-11s%s → port %s\n' EXPOSED "${listen#tcp:}" "${d#expose-}"
done < <(incus config device list "$inst" 2>/dev/null)
# What built this box, from what, when, with which box (#103). Nothing else
# on the host records it — the instance config IS the store — so a stamp
# nothing surfaces is a stamp nobody has. 'box info --json' carries the keys
# for free: 'incus list --format json' includes config verbatim.
box_provenance "$inst"
echo
case "${snaps:-0}" in
''|0)

View file

@ -205,8 +205,15 @@ rm -rf "$EVILROOT"
# grep -q failure mode).
# ---------------------------------------------------------------------------
RUFN="$(mktemp)"
awk '/^render_userdata\(\) \{/,/^\}/' "$ROOT/bin/box" > "$RUFN"
# The pin's DEFAULTS live in rig_repo/rig_ref, one definition shared with the
# mint stamp (#103) — extract them alongside the function that reads them, or
# the extracted copy silently renders an empty repo and every assertion below
# goes green against nothing.
{ grep -cE '^rig_(repo|ref)\(\)' "$ROOT/bin/box" | grep -qx 2 || echo 'die "the rig pin helpers moved — this extraction is stale"'
grep -E '^rig_(repo|ref)\(\)' "$ROOT/bin/box"
awk '/^render_userdata\(\) \{/,/^\}/' "$ROOT/bin/box"; } > "$RUFN"
check "render_userdata: extracted from bin/box (guards the awk)" 0 "RIG_REPO" cat "$RUFN"
check "render_userdata: the pin's defaults came with it (guards the grep)" 0 "heavy-duty/rig" cat "$RUFN"
check "render_userdata: the extracted function is valid bash" 0 "" bash -n "$RUFN"
SEED="$(mktemp)"
@ -1068,6 +1075,324 @@ check "import: reset_identity follows the start" 0 "" bash -c '
check "grant: allows backups (the export workflow)" 0 "" \
grep -qF 'restricted.backups allow' "$ROOT/host/grant-user.sh"
# ---------------------------------------------------------------------------
# The mint stamp (#103) — DRIVEN on both halves, write and read.
#
# There is no host-side per-box store: the Incus instance config IS the
# database, so the only proof that a fact survives the mint is the argument
# list box hands 'incus launch'. A fake incus logs every call verbatim and
# answers just enough for cmd_new and cmd_info to run to completion with no
# daemon anywhere — the same trick the confirm-gate drive uses above.
#
# The read half matters as much as the write half, and legacy boxes most of
# all: every box minted before this stamp existed carries none of these keys,
# and a box outlives the release that minted it. 'incus config get' on an
# unset key prints EMPTY and exits 0 (audit B4), so "no stamp" and "daemon
# said no" arrive identically — 'box info' must render both as a box with
# blanks, never as an error.
# ---------------------------------------------------------------------------
MSHIM="$(mktemp -d)"; MWORK="$(mktemp -d)"
cat > "$MSHIM/incus" <<'SHIM'
#!/usr/bin/env bash
# Fake incus for the mint-stamp drive. Knobs, all optional:
# FAKE_BASE_IMAGE what 'config get <i> volatile.base_image' resolves to;
# empty = incus does not know it (the degraded mint)
# FAKE_CFG a file of "<key> <value>" lines answering 'config get'
# FAKE_ROW the csv row 'list --columns nstS' returns
# The launch carries a whole cloud-init seed, so the call is logged with its
# newlines flattened — an assertion about "the launch line" must see one line.
printf 'incus %s\n' "$*" | tr '\n' ' ' >> "$FAKE_INCUS_LOG"
printf '\n' >> "$FAKE_INCUS_LOG"
case "$*" in
*volatile.base_image) printf '%s\n' "${FAKE_BASE_IMAGE-}" ;;
"config get "*)
[ -n "${FAKE_CFG:-}" ] || exit 0
key="$*"; key="${key##* }"
awk -v k="$key" '$1 == k { $1 = ""; sub(/^ /, ""); print }' "$FAKE_CFG" ;;
*"--columns nstS") printf '%s\n' "${FAKE_ROW-}" ;;
*"--columns 4") echo '10.1.2.3 (enp5s0)' ;;
esac
exit 0
SHIM
chmod +x "$MSHIM/incus"
export FAKE_BASE_IMAGE=deadbeefcafe0123456789 # what the alias resolves to
mintbox() { # mintbox <logfile> <args...> — the real box, shimmed, no TTY
local log="$1"; shift
: > "$log"
env FAKE_INCUS_LOG="$log" PATH="$MSHIM:$PATH" "$BOX" "$@" </dev/null >"$log.out" 2>&1
local rc=$?
cat "$log.out"
return "$rc"
}
# The launch line, isolated: every assertion below is about ONE incus call, and
# grepping the whole log would let a key stamped by some other call pass.
launchline() { grep -m1 '^incus launch ' "$1"; }
# launch_has/restamp_has <log> <ere> — a matcher per surface, so the ABSENCE
# assertions (a key that must not be stamped) are a plain non-zero exit rather
# than a nest of quoting.
launch_has() { launchline "$1" | grep -qE "$2"; }
restamp_has() { grep -F 'config set' "$1" | grep -qE "$2"; }
# --- the write half: a fresh mint ------------------------------------------
MLOG="$MWORK/mint.log"
check "mint: a shimmed 'box new' runs to completion" 0 "ready" \
mintbox "$MLOG" new --name w1 --template claude-box --container
# Each key on its own check: a single grep for the whole block would go green
# on a partial stamp, and "which fact was dropped" is the useful failure.
check "mint: stamps the schema — the stamp's SHAPE, not the box version (#103)" \
0 "user.box.schema=1" launchline "$MLOG"
check "mint: stamps the box version that minted it (#103)" \
0 "user.box.version=$(cat "$ROOT/VERSION")" launchline "$MLOG"
check "mint: stamps the base image ALIAS asked for (#103)" \
0 "user.box.image=images:debian/13/cloud" launchline "$MLOG"
check "mint: stamps the mode it minted as (#103)" \
0 "user.box.mode=container" launchline "$MLOG"
# The demand, not just the outcome: TYPE already says CT afterwards, but only
# the mint knew whether a container was ASKED for or fallen back into.
check "mint: stamps the mode that was ASKED, not only the outcome (#103)" \
0 "user.box.mode.asked=container" launchline "$MLOG"
check "mint: stamps the rig role box will auto-run (#103)" \
0 "user.box.role=claude-box" launchline "$MLOG"
check "mint: stamps which rig converged it — repo (#103)" \
0 "user.box.rig.repo=heavy-duty/rig" launchline "$MLOG"
check "mint: stamps which rig converged it — ref (#103)" \
0 "user.box.rig.ref=main" launchline "$MLOG"
check "mint: stamps the origin (#103)" 0 "user.box.origin=mint" launchline "$MLOG"
# The timestamp's SHAPE, so a local-time or seconds-since-epoch spelling fails
# here: UTC ISO 8601, which is the only form that sorts and travels.
check "mint: stamps the mint time as UTC ISO 8601 (#103)" 0 "" \
launch_has "$MLOG" 'user\.box\.created=[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z'
# The existing three keys are UNTOUCHED — the stamp extends the namespace, it
# does not rewrite it, and box_user()/the login hint read two of them.
check "mint: the pre-existing boundary tag still rides the same line" \
0 "user.box=1" launchline "$MLOG"
check "mint: the pre-existing template stamp is untouched" \
0 "user.box.template=claude-box" launchline "$MLOG"
check "mint: the pre-existing user stamp is untouched" \
0 "user.box.user=claude" launchline "$MLOG"
# Deliberately NOT stamped. limits.* already hold cpu/memory and a duplicate
# drifts the first time someone edits a limit by hand; a container's disk does
# not exist (its root rides the pool); and the tier is a fact about whoever is
# ASKING. Absence assertions, so a well-meant addition has to argue here first.
check "mint: does NOT duplicate cpu into the user.box namespace (#103)" 1 "" \
launch_has "$MLOG" 'user\.box\.cpu'
check "mint: does NOT duplicate memory into the user.box namespace (#103)" 1 "" \
launch_has "$MLOG" 'user\.box\.memory'
check "mint: does NOT stamp a disk — a container's does not exist (#103)" 1 "" \
launch_has "$MLOG" 'user\.box\.disk'
check "mint: does NOT stamp the tier — it describes the asker, not the box (#103)" 1 "" \
launch_has "$MLOG" 'user\.box\.tier'
# --- the resolved fingerprint: the alias is not a reproducible fact ---------
# $T_IMAGE is an unpinned alias on a moving remote. Incus resolves it during
# the launch and records what it landed on; box reads that back and pins it.
check "mint: pins the RESOLVED image fingerprint after the launch (#103)" 0 "" \
grep -qF "config set w1 user.box.image.fingerprint=$FAKE_BASE_IMAGE" "$MLOG"
check "mint: ...and it is a SECOND call, not something the launch could know" 1 "" \
launch_has "$MLOG" 'image\.fingerprint'
# The load-bearing half of that design: a box that exists and boots must never
# be failed over a provenance field. With no fingerprint to be had, the mint
# still succeeds and the alias stands alone as the honest partial answer.
NOFP="$MWORK/nofp.log"
FAKE_BASE_IMAGE="" # exported above; incus does not know what the alias resolved to
check "mint: an unknowable fingerprint does not fail the mint (#103)" 0 "ready" \
mintbox "$NOFP" new --name w4 --template blank --container
check "mint: ...and it stamped no empty fingerprint key either (#103)" 1 "" \
grep -q 'image.fingerprint' "$NOFP"
FAKE_BASE_IMAGE=deadbeefcafe0123456789
# --- a seed that installs no rig is stamped no rig pin ----------------------
# 'blank' carries no @RIG_REPO@ token, so there is no rig to name. A stamp
# that named one anyway would be fiction, which is worse than a missing key.
check "mint: the 'blank' seed installs no rig, so no rig pin is stamped (#103)" 1 "" \
launch_has "$NOFP" 'user\.box\.rig\.'
check "mint: ...and no role either — blank names none (#103)" 1 "" \
launch_has "$NOFP" 'user\.box\.role'
# --- the rig pin has ONE definition, and both readers get the same answer ---
# The seed substitutes it and the stamp records it. Two spellings of the same
# default would eventually disagree, and a stamp that disagrees with the seed
# it shipped alongside is worse than no stamp. Driven through the environment
# override, so the two are compared on a value neither can have hardcoded.
RIGLOG="$MWORK/rig.log"
export RIG_REPO=someone/rig RIG_REF=probe-ref
mintbox "$RIGLOG" new --name w5 --template claude-box --container >/dev/null 2>&1
unset RIG_REPO RIG_REF
check "mint: the rig pin override reaches the STAMP (#103)" 0 "" \
launch_has "$RIGLOG" 'user\.box\.rig\.repo=someone/rig'
check "mint: ...both halves of it (#103)" 0 "" \
launch_has "$RIGLOG" 'user\.box\.rig\.ref=probe-ref'
check "mint: ...and the SEED it shipped with carries the same pin (#103)" 0 "" \
launch_has "$RIGLOG" 'someone/rig/probe-ref'
# --- the clone: the sharpest edge of the whole stamp ------------------------
# 'incus copy' carries every user.* key forward (audit B2) — which is what
# makes a clone know its template for free, and is exactly why the stamp
# cannot ride along untouched. An inherited stamp does not go stale, it goes
# FALSE: the clone would claim a mint time it was not present for, by a box
# version that never saw it.
CLONELOG="$MWORK/clone.log"
check "clone: a shimmed 'box new --from' runs to completion" 0 "cloned" \
mintbox "$CLONELOG" new --name w2 --from work/authed
check "clone: re-stamps the origin as a clone, not a mint (#103)" 0 "" \
grep -qF 'user.box.origin=clone' "$CLONELOG"
check "clone: names the source it was taken from — one hop (#103)" 0 "" \
grep -qF 'user.box.origin.from=work/authed' "$CLONELOG"
check "clone: re-stamps the box version that made THIS instance (#103)" 0 "" \
grep -qF "user.box.version=$(cat "$ROOT/VERSION")" "$CLONELOG"
check "clone: re-stamps a fresh created time (#103)" 0 "" \
grep -qE 'user\.box\.created=[0-9]{4}-[0-9]{2}-[0-9]{2}T' "$CLONELOG"
# The other half of the decision, and the reason it is a decision at all: the
# LINEAGE keys are left alone on purpose. The clone's disk genuinely came from
# that image, that template, that user, that role — re-stamping them from the
# cloning process's own template lookup would be the actual lie, and would
# break the login hint that reads user.box.template off the instance.
check "clone: does NOT re-stamp the template — it is inherited lineage (#103)" 1 "" \
restamp_has "$CLONELOG" 'user\.box\.template'
check "clone: does NOT re-stamp the user — box_user() reads the source's (#103)" 1 "" \
restamp_has "$CLONELOG" 'user\.box\.user'
check "clone: does NOT re-stamp the image — the disk really came from it (#103)" 1 "" \
restamp_has "$CLONELOG" 'user\.box\.image'
check "clone: does NOT re-stamp the role — the source's rig converged it (#103)" 1 "" \
restamp_has "$CLONELOG" 'user\.box\.role'
# The third column, and the one review found: 'mode.asked' is neither lineage
# nor re-stampable. It is a mint-event fact whose asker was the SOURCE's
# operator, and a clone refuses --vm/--container, so nobody was asked anything
# here. It is CLEARED — there is no true value to give it.
check "clone: clears the inherited mode.asked — nobody asked THIS box (#103)" 0 "" \
grep -qF 'config unset w2 user.box.mode.asked' "$CLONELOG"
check "clone: ...and does not re-stamp it with a fabricated answer (#103)" 1 "" \
restamp_has "$CLONELOG" 'user\.box\.mode\.asked'
# Cleared, never set-to-empty: an empty value is still a key on the instance.
check "clone: clears it rather than setting it empty (#103)" 1 "" \
grep -qE 'config set .*user\.box\.mode\.asked=($|[[:space:]])' "$CLONELOG"
# Order: the re-stamp lands on the copied instance BEFORE it is started, so a
# clone is never observable wearing its source's provenance. Fail-closed — an
# absent line makes the arithmetic fail, not pass.
restamp_precedes_start() {
local set start
set="$(grep -n 'config set .* user.box.origin=clone' "$1" | head -1 | cut -d: -f1)"
start="$(grep -n '^incus start ' "$1" | head -1 | cut -d: -f1)"
[ -n "$set" ] && [ -n "$start" ] && [ "$set" -lt "$start" ]
}
check "clone: the re-stamp precedes the start (#103)" 0 "" \
restamp_precedes_start "$CLONELOG"
# The clear rides the same rule for the same reason: a clone must never be
# observable — not for one moment, not to 'box info' — wearing an 'asked' its
# operator never gave. Fail-closed the same way.
clear_precedes_start() {
local unset_ln start
unset_ln="$(grep -n 'config unset .* user.box.mode.asked' "$1" | head -1 | cut -d: -f1)"
start="$(grep -n '^incus start ' "$1" | head -1 | cut -d: -f1)"
[ -n "$unset_ln" ] && [ -n "$start" ] && [ "$unset_ln" -lt "$start" ]
}
check "clone: the mode.asked clear precedes the start too (#103)" 0 "" \
clear_precedes_start "$CLONELOG"
# --- the read half: 'box info' surfaces it ---------------------------------
# A stamp nothing can read is not done. cmd_info printed NAME/STATE/TYPE/IPV4
# and surfaced none of the keys that already existed.
STAMPED="$MWORK/stamped.cfg"
cat > "$STAMPED" <<'CFG'
user.box 1
user.box.schema 1
user.box.created 2026-07-19T14:22:07Z
user.box.version 0.8.0
user.box.image images:debian/13/cloud
user.box.image.fingerprint 8a2f1c9d4e5b6a7c8d9e
user.box.mode vm
user.box.mode.asked auto
user.box.template claude
user.box.user claude
user.box.role claude
user.box.rig.repo heavy-duty/rig
user.box.rig.ref main
user.box.origin mint
CFG
infobox() { # infobox <cfg-file> — 'box info work' against a canned config
env FAKE_INCUS_LOG=/dev/null FAKE_CFG="$1" FAKE_ROW='work,RUNNING,VIRTUAL-MACHINE,0' \
PATH="$MSHIM:$PATH" "$BOX" info work </dev/null 2>&1
}
check "info: surfaces the mint time and the box that minted it (#103)" \
0 "MINTED 2026-07-19T14:22:07Z by box 0.8.0" infobox "$STAMPED"
check "info: surfaces the image alias AND what it resolved to (#103)" \
0 "IMAGE images:debian/13/cloud @ 8a2f1c9d4e5b" infobox "$STAMPED"
check "info: surfaces the template with its user and role (#103)" \
0 "TEMPLATE claude (user claude, role claude)" infobox "$STAMPED"
check "info: surfaces which rig converged it (#103)" \
0 "RIG heavy-duty/rig@main" infobox "$STAMPED"
check "info: surfaces the origin (#103)" 0 "ORIGIN mint" infobox "$STAMPED"
# Still the box it always was: the new block is additive, above the snapshots.
check "info: still prints the state block it always did" 0 "IPV4 10.1.2.3" infobox "$STAMPED"
# A clone reads back as a clone, naming its source. Modelled on what the
# --from branch actually leaves behind: origin re-stamped, mode.asked cleared.
CLONECFG="$MWORK/clone.cfg"
{ grep -v -e '^user.box.origin ' -e '^user.box.mode.asked ' "$STAMPED"
echo 'user.box.origin clone'
echo 'user.box.origin.from work/authed'; } > "$CLONECFG"
check "info: a clone says so, and names the box it came from (#103)" \
0 "ORIGIN clone of work/authed" infobox "$CLONECFG"
# ...and stays silent about a demand nobody made of it. The MODE line is gated
# on 'asked' precisely so absence renders as silence rather than as a guess;
# TYPE above still reports VM off the instance type, so nothing is lost.
info_has_mode() { infobox "$1" | grep -q '^MODE'; }
check "info: a clone prints no MODE line — nobody asked IT anything (#103)" 1 "" \
info_has_mode "$CLONECFG"
check "info: ...while TYPE still reports what it actually is (#103)" \
0 "TYPE VM" infobox "$CLONECFG"
# The mint keeps its MODE line — there, the operator really was asked.
check "info: a MINT still surfaces what was asked for (#103)" \
0 "MODE vm (asked: auto)" infobox "$STAMPED"
# --- legacy boxes: the promise that they keep working under every verb ------
# A box carrying the boundary tag and NOTHING else — every box minted before
# this stamp existed. It must still render, exit 0, and say plainly that the
# mint was not recorded rather than inventing one or erroring out.
LEGACY="$MWORK/legacy.cfg"; printf 'user.box 1\n' > "$LEGACY"
check "info: a box with NO stamp at all still renders, exit 0 (#103)" \
0 "NAME work" infobox "$LEGACY"
check "info: ...and says the mint was not recorded, rather than inventing one" \
0 "predates the mint stamp" infobox "$LEGACY"
info_has() { infobox "$1" | grep -qE "$2"; }
check "info: ...and prints no half-empty IMAGE/ORIGIN lines for keys it lacks" 1 "" \
info_has "$LEGACY" '^(IMAGE|ORIGIN|RIG|MODE) '
# A pre-rename box carries user.claudebox=1 and no metadata at all, and is
# always a Claude box — the same mapping box_user() makes, honored forever.
PRERENAME="$MWORK/prerename.cfg"; printf 'user.claudebox 1\n' > "$PRERENAME"
check "info: a pre-rename box still reads as the claude template (#103)" \
0 "TEMPLATE claude" infobox "$PRERENAME"
# --- a schema from the future is not a broken box --------------------------
# A box outlives the release that minted it, so an OLDER box will one day read
# a NEWER box's stamp. It shows what it understands and says so; refusing to
# describe a box a later release minted perfectly well is the wrong answer.
FUTURE="$MWORK/future.cfg"
{ grep -v '^user.box.schema ' "$STAMPED"; echo 'user.box.schema 99'; } > "$FUTURE"
check "info: an unrecognised (newer) schema is noted, not refused (#103)" \
0 "NOTE" infobox "$FUTURE"
check "info: ...and it still shows every key it DOES understand (#103)" \
0 "MINTED 2026-07-19T14:22:07Z" infobox "$FUTURE"
check "info: ...and it still exits 0 — a future box is not a broken box (#103)" \
0 "ORIGIN" infobox "$FUTURE"
# A non-integer schema lands on the same side: noted, never fatal under set -e.
GARBAGE="$MWORK/garbage.cfg"
{ grep -v '^user.box.schema ' "$STAMPED"; echo 'user.box.schema not-a-number'; } > "$GARBAGE"
check "info: a non-integer schema is noted, not fatal (#103)" 0 "NOTE" infobox "$GARBAGE"
# VERSION has ONE reader in bin/box — box_version() — and both 'box --version'
# and the mint stamp go through it. A second 'cat $root/VERSION' is how the two
# would eventually disagree about what minted a box.
# shellcheck disable=SC2016 # '$root' is bin/box's variable, matched literally
one_version_reader() {
[ "$(grep -cF 'cat "$root/VERSION"' "$ROOT/bin/box")" -eq 1 ]
}
check "the tree's VERSION has a single reader, box_version() (#103)" 0 "" \
one_version_reader
rm -rf "$MSHIM" "$MWORK"
# The rehearsal itself stays runnable: syntax-checked here, run on real hosts.
check "multiuser.sh is valid bash" 0 "" bash -n "$ROOT/drill/multiuser.sh"
check "multiuser.sh refuses without the env gate" 2 "opt in" \