Cozystack Linstor
Linstor distributed storage management for Cozystack — ZFS pool creation, storage classes, node and pool status
| Argument | Type | Description |
|---|---|---|
| node | string | Node name to create the pool on |
| device | string | Block device path (e.g. /dev/vdb) |
| poolName | string | ZFS pool name |
| Argument | Type | Description |
|---|---|---|
| node | string | Node name |
| poolName | string | ZFS pool name |
Resources
2026.08.01.1
Fixes the HIGH device-reuse fail-open guard in createZfsPool
(cozystack-linstor-fail-open-guards, local model). The idempotency decision
was previously (node, storagePool NAME) only and never considered device:
- Direction A (device wipe): reusing an already-provisioned device under a
DIFFERENT storage-pool name fell through to the destructive
physical-storage create-device-poolcall — a device wipe. Now REFUSED (success:false). - Direction B (silent no-op): the SAME storage-pool name with a DIFFERENT
requested device was silently suppressed as "already exists" (
success:true) without ever provisioning the requested device. Now REFUSED (success:false) instead of silently discarding the request.
Fix is verify-before-destructive (CLAUDE.md Rule 5): createZfsPool now reads
LINSTOR's live physical-storage list inventory to establish
device-availability BEFORE any create decision, and folds it together with the
existing (node, storagePool) name match into a 4-way branch:
nameMatch && !deviceFree-> idempotent no-op (unchanged,success:true)nameMatch && deviceFree-> Direction B, REFUSED (success:false)!nameMatch && !deviceFree-> Direction A, REFUSED (success:false)!nameMatch && deviceFree-> legit create (unchanged,create-device-poolargv stays byte-identical)
A device absent from the live inventory is conservatively treated as "in use" (fail-closed), per the documented conservative bias in the plan.
extensions/models/cozystack_linstor.ts: added the physical-storage inventory read + 4-way branch increateZfsPool.execute;model.versionbumped to2026.08.01.1.fixtures/physical-storage-list.json: new synthetic fixture (doc-derived from LINBIT'slinstor-api-pyPhysicalDevice/NodeStorageEntryresponse classes, no live capture) plus afixtures/PROVENANCE.mdentry.cozystack_linstor_adversarial_test.ts: the two fail-open characterization pins are flipped to assert refusal (nocreate-device-poolcall,success:false);fixtures-secret-scanFIXTURES map extended with the new fixture.cozystack_linstor_methods_test.ts/cozystack_linstor_property_test.ts/cozystack_linstor_coverage_test.ts: harness updates for the inserted physical-storage-list read and shifted invocation indices; the legit-create and genuine-idempotent-no-op paths stay green.
Also included in this release (previously unreleased): the test backfill to the
STANDARD.md five-suite quality bar (wave-1 child of the extension-quality
backfill program, ext-quality-test-backfill) — no behavior change of its own,
the model version stayed 2026.07.16.2 until this release's guard fix above.
- Added
extensions/models/cozystack_linstor_test.ts(contract-fixture),cozystack_linstor_methods_test.ts(methods),cozystack_linstor_adversarial_test.ts(adversarial),cozystack_linstor_coverage_test.ts(coverage),cozystack_linstor_property_test.ts(property-invariant-flow) — 0 tests before this change, 87 after. - Added
fixtures/— pure doc-derived, synthetic LINSTOR machine-readable (-m) and KubernetesDeploymentwire-shape fixtures (node-list,storage-pool-list,deploy-ready,deploy-notready) plusPROVENANCE.md. No live call was made against the real Cozystack cluster or its kubeconfig; every value is synthetic (worker-0..2,/dev/vdb, RFC 5737 addresses). - Boundary is
Deno.Command(notfetch) — a reassignable fake stub captures every{command, args}invocation and returns a queued{success, stdout, stderr}, with@std/testingFakeTimecollapsing the model's one-at-a-time 5s retry delays for the retry-exhaustion tests. - The two live pre-flight checks (
cluster-reachable,linstor-controller-ready) are exercised directly viamodel.checks[<name>].execute(), not just the 6 methods — closing a round-1 review HIGH finding that the destructive-op guards themselves were untested. createZfsPool's idempotency-key guard (node+storagePoolNAME only, neverdevice) was pinned in both fail-open directions at the time of this test backfill: a different storagePool name on the same device still issued the destructive create-device-pool call (wipe), and — per a review fold-in — the same storagePool name with a different requested device was silently suppressed as "already exists" without ever provisioning the requested device. Both directions are FIXED by this same release (see above,cozystack-linstor-fail-open-guards).- The
linstor-controller-readycheck'sparseInt(stdout) < 1comparison is still pinned as fail-open on a non-numericstdout(parseInt("abc")isNaN, andNaN < 1isfalse) — a review fold-in, tracked separately and NOT fixed by this release (out of scope for `cozystack-linstor-fa
Release 2026.07.16.2 — align model versions with manifests
Maintenance release across the @magistr extensions. For most packages this
carries no functional change: the only edit is the model's version: field,
brought back in line with its manifest version so the published model type
version and the package version no longer drift.
Functional changes in this release are limited to:
anime-cron: normalizeTitle now strips a ": subtitle" suffix and a trailing parenthesized year before comparison, fixing dedup false-misses where the torrent title carries a subtitle or year that the AniList romaji does not.
arckit: first publish. Standalone ArcKit port — a 12-phase architecture governance state machine with 65 bundled templates, driven by a bundled skill.
Also tracks three extensions (kaiten, observability-agent, music-library) that previously existed only as untracked working-tree directories, recovered from stashes.
Maintenance version bump. No functional changes.
Added 1, removed 1 models
Merge pull request #4 from umag/extensions/magistr-grade-a-workspace
extensions: stage 15 @magistr extensions as Grade A workspace dirs + wire CI
- Has README or module doc2/2earned
- README has a code example1/1earned
- README is substantive1/1earned
- Most symbols documented1/1earned
- No slow types (deprecated)1/1earned
- Dependencies pass trust audit2/2earned
- Has description1/1earned
- Platform support declared (or universal)2/2earned
- License declared1/1earned
- Verified public repository2/2earned