A minted box records nothing about how it was minted #103

Closed
opened 2026-07-19 17:33:23 +00:00 by dan-claude-bot · 0 comments
dan-claude-bot commented 2026-07-19 17:33:23 +00:00 (Migrated from github.com)

A minted box records nothing about how it was minted

What the mint throws away

cmd_new() knows a great deal at the moment it launches. bin/box:1038-1045:

timeout -k 5 "$budget" incus launch "$T_IMAGE" "$instance" --profile box-net \
  --config user.box=1 \
  --config user.box.template="$t" \
  --config user.box.user="$T_USER" \
  --config limits.cpu="$T_CPU" \
  --config limits.memory="$T_MEMORY" \
  --config cloud-init.user-data="$(render_userdata "$root/templates/$t/user-data.yaml")" \
  "${extra[@]}" </dev/null || rc=$?

Three user.* keys. In scope at that same line, and dropped on the floor:

  • the box version that minted it$root/VERSION is read in exactly one
    place, version() at bin/box:15, and never in the mint path
  • the base image$T_IMAGE is images:debian/13/cloud in all five
    templates (templates/*/box.env). 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 says so.
  • the rig role$T_BOOTSTRAP_ROLE (bin/box:910) is passed to
    incus exec … rig bootstrap "$T_BOOTSTRAP_ROLE" at bin/box:1121 and then
    forgotten
  • rig repo and refRIG_REPO / RIG_REF (bin/box:950), substituted
    into the seed at :956-957. Which rig converged this box is unrecoverable.
  • mint time — box writes no timestamp anywhere except a snapshot label
    (bin/box:1296) and an export filename (:1324)
  • modepick_mode() (bin/box:778) chose vm or container; recoverable
    from the instance type, but the demand ($T_REQUIRE_VM, :908) is not

cmd_info() (bin/box:1222-1260) prints NAME / STATE / TYPE / IPV4,
exposures, snapshots. It surfaces none of the three keys that already
exist. There is no host-side per-box store — the Incus instance config is
the database — so what is not stamped is simply gone.

Proposed stamp

Extend the existing user.box.* namespace at the same single write point.
That namespace is already load-bearing and already proven: drill audit item
B2 (drill/RUNS.md:26, drill/drill.sh:454-464) establishes that
incus copy preserves user.*, which is why clones inherit template and
user with no extra code.

user.box=1                                  # existing — the tag
user.box.schema=1                           # new — this stamp's shape
user.box.template=claude                    # existing
user.box.user=claude                        # existing
user.box.version=0.7.0                      # new — box that minted it
user.box.image=images:debian/13/cloud       # new — the alias asked for
user.box.image.fingerprint=8a2f1c…          # new — what it resolved to
user.box.mode=vm                            # new
user.box.role=claude                        # new — rig role box requested
user.box.rig.repo=heavy-duty/rig            # new
user.box.rig.ref=main                       # new
user.box.created=2026-07-19T14:22:07Z       # new — UTC, ISO 8601
user.box.origin=mint                        # new — mint | clone | import
user.box.origin.from=cold/authed            # new — clone/import only

Deliberately not stamped:

  • cpu / memorylimits.cpu and limits.memory already hold them
    (bin/box:1042-1043). Duplicating invites drift when someone edits the
    limit by hand. Disk is the exception: it lands as a root device size on
    VMs only and is recorded nowhere for containers (bin/box:1011, note at
    :1019). Either stamp user.box.disk or accept the gap knowingly.
  • tierbox_tier() (bin/box:26-33) derives admin/restricted/none from
    live id -nG, never the group DB. It is a property of whoever is asking,
    not of the box. Stamping it records who happened to mint and then rots.

user.box.image.fingerprint is the one field that needs work rather than a
flag: $T_IMAGE is an alias, so the fingerprint is only known after the
launch resolves it. Read it back from volatile.base_image and set it in a
follow-up incus config set, or accept alias-only and lose reproducibility.

Where it is written, and when

One place: the --config block at bin/box:1038-1045, plus at most one
incus config set after the launch for the resolved fingerprint. No new file,
no new directory, no host-side index. The stamp lands before cloud-init
and before rig — it describes the mint, not the outcome.

Three existing paths need an explicit decision, and all three are places
where a naive stamp becomes a lie:

  1. Clonebin/box:977-988. incus copy carries every user.* key
    forward, so a clone inherits the source's version, created-at and origin
    verbatim. reset_identity() (bin/box:986, def :846-865) is the
    existing "refresh identity on clone" hook and the natural home for a
    re-stamp: origin=clone, origin.from=<srcref>, fresh created, current
    version. Note --cpu/--memory/--disk are already refused for clones
    (:979), so limits.* carry over stale too — same class of problem.
  2. Importbin/box:1388-1391 re-stamps user.box=1 and nothing else,
    under a comment (:1354-1358) that already draws the right line between
    "artifact truth" and "this host's truth". Mint facts are the originating
    host's truth and should survive; origin=import and an import timestamp
    are this host's.
  3. Legacy backfillhost/migrate-host.sh:78 fabricates
    user.box.template=claude user.box.user=claude for pre-tag boxes. New
    keys need a defensible default there, or an explicit unknown.

How it is read back

cmd_info() grows a metadata block above the existing output. Every read uses
${var:-} and tolerates absence — drill audit item B4 (drill/RUNS.md:28):
incus config get on an unset key returns empty with exit 0, so an
old box must render as a box with blanks, never as an error.

NAME       claude-1
STATE      RUNNING
TYPE       vm
IPV4       10.x.x.x

MINTED     2026-07-19T14:22:07Z by box 0.7.0
TEMPLATE   claude (user claude, role claude)
IMAGE      images:debian/13/cloud @ 8a2f1c…
ORIGIN     mint

box info --json already exists and passes incus list --format json
straight through (bin/box:1224) — the keys ride along in config for free,
which is the audit surface the issue actually asks for.

Schema versioning

user.box.schema is an integer, not the box version. It changes only when the
shape changes — a key removed or repurposed. Readers treat a schema they do
not recognise as "newer than me, show what I understand, say so" rather than
refusing; a box outlives the release that minted it, which bin/box:716-718
already says out loud about legacy tags. Absent schema means pre-manifest,
which is exactly the boxes migrate-host backfills.

Open questions

  • Fingerprint or not? A second incus config set after every launch, for
    a field nobody reads until an incident. Cheap, but it is one more failure
    point in the mint path's hottest section.
  • Does the clone re-stamp lose the ancestry? origin.from records one
    hop. A clone of a clone either chains (unbounded) or forgets its grandparent.
    One hop seems right; worth stating rather than defaulting into.
  • Should the stamp be re-derivable rather than stored? cloud-init.user-data
    is already readable back via incus config get, and rig repo/ref are inside
    it. Arguably the seed is the record and only version + created + image
    fingerprint are genuinely new information.
  • box list columns. boxes_csv() (bin/box:1153-1158) queries
    --columns nstS. Incus can filter and column on config keys — is a
    TEMPLATE or AGE column in box list worth the width?

Note on scope

This issue is deliberately only about the VM as infrastructure — what box
built, from what, when, with which version. What was provisioned into the
box is rig's half, and the two must not collide: box writes host-side Incus
config, rig writes guest-side files under /etc/rig. Neither can reach the
other's store by accident — rig runs inside the guest as root with no Incus
socket (no incus exec, no ssh, anywhere in rig's tree). The composition
point is box info, which runs on the host and can read the guest's half
when the box is running. The other half is heavy-duty/rig#61.

Supersedes #100, which asked for this in one sentence.

# A minted box records nothing about how it was minted ## What the mint throws away `cmd_new()` knows a great deal at the moment it launches. `bin/box:1038-1045`: ```sh timeout -k 5 "$budget" incus launch "$T_IMAGE" "$instance" --profile box-net \ --config user.box=1 \ --config user.box.template="$t" \ --config user.box.user="$T_USER" \ --config limits.cpu="$T_CPU" \ --config limits.memory="$T_MEMORY" \ --config cloud-init.user-data="$(render_userdata "$root/templates/$t/user-data.yaml")" \ "${extra[@]}" </dev/null || rc=$? ``` Three `user.*` keys. In scope at that same line, and dropped on the floor: - **the box version that minted it** — `$root/VERSION` is read in exactly one place, `version()` at `bin/box:15`, and never in the mint path - **the base image** — `$T_IMAGE` is `images:debian/13/cloud` in all five templates (`templates/*/box.env`). 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 says so. - **the rig role** — `$T_BOOTSTRAP_ROLE` (`bin/box:910`) is passed to `incus exec … rig bootstrap "$T_BOOTSTRAP_ROLE"` at `bin/box:1121` and then forgotten - **rig repo and ref** — `RIG_REPO` / `RIG_REF` (`bin/box:950`), substituted into the seed at `:956-957`. Which rig converged this box is unrecoverable. - **mint time** — box writes no timestamp anywhere except a snapshot label (`bin/box:1296`) and an export filename (`:1324`) - **mode** — `pick_mode()` (`bin/box:778`) chose vm or container; recoverable from the instance type, but the *demand* (`$T_REQUIRE_VM`, `:908`) is not `cmd_info()` (`bin/box:1222-1260`) prints `NAME / STATE / TYPE / IPV4`, exposures, snapshots. It surfaces **none** of the three keys that already exist. There is no host-side per-box store — the Incus instance config *is* the database — so what is not stamped is simply gone. ## Proposed stamp Extend the existing `user.box.*` namespace at the same single write point. That namespace is already load-bearing and already proven: drill audit item B2 (`drill/RUNS.md:26`, `drill/drill.sh:454-464`) establishes that `incus copy` preserves `user.*`, which is why clones inherit template and user with no extra code. ``` user.box=1 # existing — the tag user.box.schema=1 # new — this stamp's shape user.box.template=claude # existing user.box.user=claude # existing user.box.version=0.7.0 # new — box that minted it user.box.image=images:debian/13/cloud # new — the alias asked for user.box.image.fingerprint=8a2f1c… # new — what it resolved to user.box.mode=vm # new user.box.role=claude # new — rig role box requested user.box.rig.repo=heavy-duty/rig # new user.box.rig.ref=main # new user.box.created=2026-07-19T14:22:07Z # new — UTC, ISO 8601 user.box.origin=mint # new — mint | clone | import user.box.origin.from=cold/authed # new — clone/import only ``` **Deliberately not stamped:** - **cpu / memory** — `limits.cpu` and `limits.memory` already hold them (`bin/box:1042-1043`). Duplicating invites drift when someone edits the limit by hand. *Disk is the exception*: it lands as a root device size on VMs only and is recorded nowhere for containers (`bin/box:1011`, note at `:1019`). Either stamp `user.box.disk` or accept the gap knowingly. - **tier** — `box_tier()` (`bin/box:26-33`) derives admin/restricted/none from live `id -nG`, never the group DB. It is a property of *whoever is asking*, not of the box. Stamping it records who happened to mint and then rots. `user.box.image.fingerprint` is the one field that needs work rather than a flag: `$T_IMAGE` is an alias, so the fingerprint is only known after the launch resolves it. Read it back from `volatile.base_image` and set it in a follow-up `incus config set`, or accept alias-only and lose reproducibility. ## Where it is written, and when One place: the `--config` block at `bin/box:1038-1045`, plus at most one `incus config set` after the launch for the resolved fingerprint. No new file, no new directory, no host-side index. The stamp lands **before** cloud-init and before rig — it describes the mint, not the outcome. Three existing paths need an explicit decision, and all three are places where a naive stamp becomes a lie: 1. **Clone** — `bin/box:977-988`. `incus copy` carries every `user.*` key forward, so a clone inherits the source's version, created-at and origin verbatim. `reset_identity()` (`bin/box:986`, def `:846-865`) is the existing "refresh identity on clone" hook and the natural home for a re-stamp: `origin=clone`, `origin.from=<srcref>`, fresh `created`, current `version`. Note `--cpu/--memory/--disk` are already refused for clones (`:979`), so `limits.*` carry over stale too — same class of problem. 2. **Import** — `bin/box:1388-1391` re-stamps `user.box=1` and nothing else, under a comment (`:1354-1358`) that already draws the right line between "artifact truth" and "this host's truth". Mint facts are the *originating* host's truth and should survive; `origin=import` and an import timestamp are this host's. 3. **Legacy backfill** — `host/migrate-host.sh:78` fabricates `user.box.template=claude user.box.user=claude` for pre-tag boxes. New keys need a defensible default there, or an explicit `unknown`. ## How it is read back `cmd_info()` grows a metadata block above the existing output. Every read uses `${var:-}` and tolerates absence — drill audit item B4 (`drill/RUNS.md:28`): `incus config get` on an unset key returns empty with exit 0, so an old box must render as a box with blanks, never as an error. ``` NAME claude-1 STATE RUNNING TYPE vm IPV4 10.x.x.x MINTED 2026-07-19T14:22:07Z by box 0.7.0 TEMPLATE claude (user claude, role claude) IMAGE images:debian/13/cloud @ 8a2f1c… ORIGIN mint ``` `box info --json` already exists and passes `incus list --format json` straight through (`bin/box:1224`) — the keys ride along in `config` for free, which is the audit surface the issue actually asks for. ## Schema versioning `user.box.schema` is an integer, not the box version. It changes only when the *shape* changes — a key removed or repurposed. Readers treat a schema they do not recognise as "newer than me, show what I understand, say so" rather than refusing; a box outlives the release that minted it, which `bin/box:716-718` already says out loud about legacy tags. Absent schema means pre-manifest, which is exactly the boxes migrate-host backfills. ## Open questions - **Fingerprint or not?** A second `incus config set` after every launch, for a field nobody reads until an incident. Cheap, but it is one more failure point in the mint path's hottest section. - **Does the clone re-stamp lose the ancestry?** `origin.from` records one hop. A clone of a clone either chains (unbounded) or forgets its grandparent. One hop seems right; worth stating rather than defaulting into. - **Should the stamp be re-derivable rather than stored?** `cloud-init.user-data` is already readable back via `incus config get`, and rig repo/ref are inside it. Arguably the seed *is* the record and only version + created + image fingerprint are genuinely new information. - **`box list` columns.** `boxes_csv()` (`bin/box:1153-1158`) queries `--columns nstS`. Incus can filter and column on config keys — is a `TEMPLATE` or `AGE` column in `box list` worth the width? ## Note on scope This issue is deliberately **only about the VM as infrastructure** — what box built, from what, when, with which version. What was *provisioned into* the box is rig's half, and the two must not collide: box writes host-side Incus config, rig writes guest-side files under `/etc/rig`. Neither can reach the other's store by accident — rig runs inside the guest as root with no Incus socket (no `incus exec`, no ssh, anywhere in rig's tree). The composition point is `box info`, which runs on the host and can read the guest's half when the box is running. The other half is heavy-duty/rig#61. Supersedes #100, which asked for this in one sentence.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: heavy-duty/box#103
No description provided.