Skip to content

Promote qcow out of internal/: the consumer ADR-0021 §4 waited for exists - #5

Merged
aledbf merged 2 commits into
mainfrom
feat/promote-qcow-public
Sep 8, 2026
Merged

Promote qcow out of internal/: the consumer ADR-0021 §4 waited for exists#5
aledbf merged 2 commits into
mainfrom
feat/promote-qcow-public

Conversation

@aledbf

@aledbf aledbf commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

ADR-0021 §4 fixed the shape of the host-side surface and left its name to settle when a consumer existed. One does: spinbox is dropping containerd, and what replaces its snapshotter is this package.

internal/qcowqcow. The whole package, nothing else — every exported symbol was already exported, and everything under internal/ stays free to change.

Why this package and not a new abstraction beside it

The four properties that make it the right thing for spinbox are already here, and only the first is about qcow2:

  1. Declarative reconciliation, never commands. Manager.Apply(ctx, desired). Attach and detach are deliberately absent. containerd is the opposite: a command API over a daemon's database, where a lost command is a permanently divergent state.
  2. A two-path contract with the launcher, derived from a data directory and a volume id. It already fits spin-machine on the other side without anyone having coordinated it: machine.Spec.Args() emits exactly the -qmp unix:<path>,server=on,wait=off this expects, and has no way to turn off the qcow2 locking this depends on.
  3. Immutable layers in one host-wide directory, named by their own id, so a hundred chains descending from one layer share one file. That is a snapshotter whose sharing happens in the qcow2 backing header — read by the guest kernel, no union filesystem, no daemon, no mount namespace.
  4. The file is the truth. QEMU's answer outranks the pointer; chain shape is read back with qemu-img, "the one place that cannot disagree with itself", rather than tracked in a database that can drift from it.

chain.go and state.go depend only on internal/commit and internal/framed — the connect-rpc, crypto, lease and object-store weight is all in manager.go — so a consumer that wants a local chain and no control plane pays for one.

The package doc is rewritten

For a reader who is not in this repository. It named ActiveImage, which does not exist: the function is ActivePointer, and the difference is the whole point of it — rotation replaces the tip under a running VM, so the launcher must read the pointer and not compose a path. And it left the launcher's one prohibition buried beside the measurement that found it: file locking must stay on, or this package reads a live volume as an idle one and cannot detect that it did.

Verified

task ci, including the six demos against real QEMU. One stale entry dropped from hack/refs-allow.txt, which the refs gate caught.

🤖 Generated with Claude Code

https://claude.ai/code/session_019GHRRFvbnZi5q3KSTvyW5a

aledbf and others added 2 commits September 7, 2026 22:46
…ists

ADR-0021 §4 fixed the shape of the host-side surface — "a self-contained type
the agent loop *uses*, never a method on the loop, so spin's runner can take the
same type without the loop, its heartbeat, or its Control Plane client" — and
left the name to settle when a consumer existed. It does: spinbox is dropping
containerd, and what replaces its snapshotter is this package.

The move is the whole package and nothing else. `internal/qcow` is now `qcow`;
every exported symbol was already exported, and everything under internal/ stays
free to change.

Why this package and not a new abstraction beside it: the four properties spinbox
wants are already here and are not about qcow2. A host is told what it should be
serving and converges, with no attach and no detach. The contract with whoever
launches the VM is two paths derived from a data directory and a volume id.
Layers are immutable, named by their own id, and live in one directory for the
whole host, so a hundred chains descending from one layer share one file — a
snapshotter whose sharing happens in the qcow2 backing header, read by the guest
kernel, with no union filesystem and no daemon. And the file is the truth:
QEMU's answer outranks the pointer, and chain shape is read back with qemu-img
rather than tracked.

The package doc is rewritten for a reader who is not in this repository. It
named ActiveImage, which does not exist — the function is ActivePointer, and the
distinction is the point of it — and it left the launcher's one prohibition
buried next to the measurement that found it: file locking must stay on, or a
live volume reads as an idle one and nothing here can detect it.

Verified: task ci, including the six demos against real QEMU.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019GHRRFvbnZi5q3KSTvyW5a
The proto compatibility check compares against main through a git input, and
buf treats the path as a *remote*: it asks git for `refs/heads/main` inside it.
A developer's checkout has that branch. A pull request's does not —
actions/checkout leaves HEAD detached at the merge commit and creates no local
branch — so the check has failed on every PR it has ever run on, with

    Failure: could not clone file:///home/runner/work/storage/storage/.git:
    exit status 128
    fatal: couldn't find remote ref main

which reads like an infrastructure fault rather than like a check that was never
running. A push to main passed, because there HEAD *is* main: the lane was green
exactly when it had nothing to compare, and red exactly when it had something.

`ref=origin/main` instead, which is what "main is what spin will be built
against" meant anyway — the remote's main, and not whatever a local branch of
that name happens to point at.

Reproduced by cloning this repository, detaching HEAD and deleting the local
branch: `branch=main` fails there with the message above and `ref=origin/main`
exits 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019GHRRFvbnZi5q3KSTvyW5a
@aledbf
aledbf merged commit 7d8c266 into main Sep 8, 2026
6 of 9 checks passed
@aledbf
aledbf deleted the feat/promote-qcow-public branch September 8, 2026 10:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant