Repository navigation
Description
A feint volume is an API object with no bytes behind it. The measurement: the 0.12 runtime contract names no storage intent — no volume, no disk, in internal/core/machine/plan.go or machine.go; the only mention of storage in the whole layer is a tolerated "storage volume not found" error string in cli.go. Every volume the packs serve today is bookkeeping, and the instance's root disk belongs to the container, not to the model.
So "volume data survives a host reboot" is not a hardening task. It is the first extension of the runtime contract since 0.12, and everything else 0.19 promises depends on it — protection is meaningless on an object that holds nothing, and a quota on fiction measures fiction.
What is asked
- A storage intent in the contract, provider-neutral, expressed like the rest of the contract: what the pack wants (a volume of this size exists; it is attached to this machine), never how Incus does it. The fourth provider exists as code: a compilable fake pack drives machines, networks, addresses, firewall and balancer through the contract alone, never naming Incus, OVN or the Driver #517's fourth provider is where neutrality is proven: it must be able to drive the intent without naming Incus, OVN or the Driver.
- The Incus driver materializes it as a custom storage volume, owned the way everything else is owned: the name reaches a command, so it answers both questions — well formed, and ours. Every destructive path (
delete,detach) goes through the ownership gate; a volume name restored from the persistent store is an untrusted input like any other. - Deletion protection is the provider's own field, not a feint invention:
protected(Scaleway),DeletionProtection(Outscale),termination-protection(Exoscale). A protected volume or instance refuses deletion in the dialect the real cloud uses — that is more fidelity, not a divergence.
What proves it done
The recipe, manual like conformance:ssh because it reboots the operator's host — it does not join the CI suite, and that is deliberate:
- Through an official client: create a volume, attach it to a server.
- Over ssh: write a file into the attached volume.
- Reboot the host. Start the controller.
- Over ssh: the file is intact, byte for byte.
- Detach, reattach to another server: the bytes came along.
- With protection set, delete through the official client: refused, in the provider's error dialect. With protection cleared: deleted, and the bytes are really gone.
Step 6 is the witness pair: a gate that cannot show both the refusal and the acceptance is a guard that passes every attack test by breaking the product.
Gated on #196: the decision "feint manages a resource whose loss would matter" is written in docs/limits.md before this ships, and every guard it requires (a blast radius for feint clean among them) is filed as its own issue, before the fact.
Activity
stephrobert commentedon Nov 11, 2026
Refused: this is the resource whose loss would matter, which is the thing the decision names.
A volume holding bytes that survive a host reboot, with protection in each provider's vocabulary and quotas over real capacity, is the clearest possible example of what feint must not own. The sentence that makes feint safe to point at an operator's own station is it only touches what it made, and what it made is disposable — and the second half is what makes the first affordable. A sweep that could destroy something irreplaceable needs a confirmation, a backup story and a recovery path, and an emulator that grows those is one nobody dares aim at a real directory.
Object Storage is the one place where feint does hold bytes, and the difference is instructive rather than an exception: they are bounded by a byte budget, held under a directory feint owns, swept with everything else, and documented as lost with the lab. They are a fixture, not a user's data.
#196, which this issue carried as its gate, is answered in the same decision.
The refusal is of the work landing here, not of the work. A homelab that materialises durable resources is a legitimate product; it is a different one, and
docs/roadmap.mdhas drawn that boundary from the start, with #195 describing the HTTP contract a second backend would speak across.Decided in #879, written in
docs/limits.mdunder "The lab is disposable, and that is the boundary", which is where a reader meets it rather than in this thread.docs: the lab is disposable, and that is the boundary (#196, #733) (#879
stephrobert commentedon Nov 11, 2026
Reopened: the decision recorded in #879 was the wrong one, and the maintainer has reversed it.
#733 asked for the decision to be written, explicitly not for a particular outcome. What was written on 2026-11-11 answered "feint never manages a resource whose loss would matter" and refused this issue on that basis. The maintainer's answer is the other one: feint is to become the tool that builds a durable homelab — volumes holding real bytes, a reconciler, quotas.
So the roadmap sentence at
docs/roadmap.md:829is superseded in part rather than upheld, and the candidate wording #733 itself offered is the one that stands:Nothing about this issue's own content was wrong. What changed is the boundary it sits inside.
One consequence is named here rather than discovered later. The argument that makes feint safe to point at an operator's own station is "it only touches what it made, and what it made is disposable", and the second half is what makes a sweep affordable:
feint cleanremoves everything carrying the label, with no confirmation, no backup and no recovery path. Once feint holds a volume whose bytes somebody wants back, that sentence stops being true and every place invoking it has to be re-grounded. That work is now tracked, and it blocks nothing here — but it is the real cost of this direction, and it belongs in the open rather than in a commit message.stephrobert commentedon Nov 11, 2026
Reopened in error, and closed again. The decision stands as written in #879: the lab is disposable, and
docs/limits.mdcarries it.What happened is worth recording, because it is a measurement failure of my own and not a change of mind. The decision was taken cleanly, as a choice between three stated options, and the answer was "the lab stays disposable, and the record may persist". I then asked a follow-up in prose — "if you want feint to become the tool that builds a durable homelab, say so and I will do it" — which is a leading question with a yes-shaped answer, and I read the yes as a reversal of the decision rather than as a reply to a badly-posed question.
A decision taken against stated options was overturned by a sentence I wrote. That is the wrong direction for a change of this size, and the correction is to put it back rather than to leave the tracker reflecting it.
So: the boundary is a resource whose loss would matter, not persistence as such. #551 and #552 stay in scope — the emulator's own record may survive a restart, because it is explicitly rebuildable, never authoritative, and holds nobody's content. This issue is refused for the reason given in the closure above it, unchanged.