Repository navigation
Releases
isothrum 2.1.7
Fixed
-
An operation cut before its end doesn't lose the run's anonymous volumes. The record a
Compose operation keeps of them was overwritten by the next operation, so the volumes an
attach orphaned before the app quit three seconds in (the rig's launch-ui spec, 2026-11-13)
stayed forever; the next operation carries a leftover record forward and removes what
dangles among its names. The runner logs what it records and removes. -
A Docker that answers no server version fails the start as a stopped one does. Docker 26
and older exit 0 with an empty line when the daemon doesn't answer, so the tools probe passed
and the start died later in the pull with a worse error. -
ESXi's
powered_onVM state reads as running. The plugin calls a VMpowered_onuntil it
has its SSH address andrunningafter; the first read as unknown, never ready, its address
skipped (a Windows guest before its tools report one).
isothrum 2.1.6
Fixed
- A Compose run's retire and detach remove the machine's anonymous volumes. The removal of a
machine taken out of the running run (compose rm -sf) and of a leftover container
(docker rm -f) left the anonymous volumes of the container's image behind: the attack box's
two, at every retire (the Cyber CTF rig's leftover audit, 2026-11-13, after 2.1.5 had covered
the stop and the update). Both removals take their volumes (-v), and a retire or detach
records the project's anonymous volumes before its steps and removes the recorded ones no
container holds after them, as an update does.
isothrum 2.1.5
Fixed
- A Compose run's
updateremoves the anonymous volumes its recreate orphaned. 2.1.4 removed
atdownthe anonymous volumes the containers still held; an update whose recreate makes new
anonymous volumes for the new container (the Cyber CTF fixture lab's: three left after three
updates, 2026-11-13) orphans the old ones at once, and no laterdownsees them attached. The
updateoperation now records the containers' anonymous volumes before its steps and removes,
after them, the recorded ones no container holds any more.
isothrum 2.1.4
Fixed
- A box for the
vmware_esxiprovider is downloaded as itsvmware_desktopbuild. The
vagrant-vmware-esxi plugin bootsvmware_desktopbuilds (no box on the registry carries a
vmware_esxione), so a run on ESXi asked the registry for a build that doesn't exist and its
download ended with "find an alternate box or use an alternate provider" (bento/debian-12,
2026-11-12). The download now asks for the build the plugin boots, as a run on vagrant-qemu asks
for thelibvirtbuild it boots. - A Compose run's
downremoves the anonymous volumes an update's recreate inherited. The
engine makes a volume for eachVOLUMEof a container's image (MySQL's data directory) and
removes it with the container ondown --volumesorrm --volumes, as long as that container
is the one it made it for. An update that recreates a service's container (a port pinned, an
image rebuilt) inherits those volumes into the new container by their ids, as Compose keeps a
service's data across a recreate, and the engine then counts them named: adown --volumes
after that removed the containers and left them, two a lab on every start, update and stop
(the Cyber CTF rig's leftover audit, 2026-11-13). Thedownoperation now records the
project's containers' anonymous volumes (the engine's label, or an older engine's unlabelled
id-named one) before its steps and removes what they left after them; a volume something else
still holds is said, not failed on. A VM's or a module'sdownrecords nothing.
isothrum 2.1.3
Fixed
- A published port's preferred number that the computer already holds moves to a free port.
WithISOLOOM_PUBLISH_FIXED(a lab's own ports), a published port took itspreferrednumber
whenever no other run of this runner had reserved it. A number some other process held (another
lab's VM already forwarding 8080, any program) then failed the startport_in_useat its
forwarded-port check, though Isoloom calls the number preferred, not fixed. The pick now passes
over a number the host holds and gives the port a free one, as an ephemeral port gets; the run's
outputs give that address. Compose and Vagrant pick the same way. Two starts in two processes
that pick the same free number at the same moment can still collide; that's #74's second part.
isothrum 2.1.2
Fixed
- The Windows binary starts on a Windows without the Visual C++ Redistributable. The release's
isothrum.exeimportedVCRUNTIME140.dll, so a Windows that never installed it (a fresh install,
a clean virtual machine) couldn't start it: exit0xC0000135, a DLL not found. GitHub's Windows
runners have the redistributable, so nothing in the release showed it. The release now links the
C runtime in (-C target-feature=+crt-static), and the build fails when the executable still
importsVCRUNTIME*.dllorMSVCP*.dll; CI checks the same on every push.
isothrum 2.1.1
Fixed
- A Vagrant run on QEMU downloads each box once. The box download before
vagrant upasked
for the box'sqemubuild, but vagrant-qemu boots libvirt-format boxes, sovagrant upfound
none and downloaded the box again (Box Provider: libvirt). The download now asks for the
format the provider boots (--provider libvirtfor QEMU), and a libvirt build already there
counts.
isothrum 2.1.0
Added
Error::ServerAuthFailed, codeserver_auth_failed: the server a run is on (Proxmox VE,
ESXi; theproxmoxandesxitargets, Vagrant onvmware_esxi) refused the run's credentials,
an API token revoked or a password changed. A step whose tool failed in the server's words for
a refused sign-in (an HTTP 401 or 403 from its API, Terraform's Proxmox provider's
authentication failure, ovftool'sincorrect user name or password, vagrant-vmware-esxi's
ESXi host accessandAuthentication failed for user) fails with it instead of
tool_failed, in an operation and in a status read, naming the server as the run's params do
(server: the API's host, the ESXi host), so an embedder can say which credentials to update.
Fixed
-
A Proxmox VM removed or powered off in Proxmox's web UI reads so. A Terraform run's status
read its local state alone, which changes only at the next apply or destroy: a VM removed on the
server read as running. The status of a run whose VMs have Proxmox's console now asks the
server's API where they stand (cluster/resources, with the run's connection: its token, else a
ticket for its password): a VM the server no longer has readsabsent(a resume refuses it as
gone, a start again rebuilds the run), one powered offstopped. A server that doesn't answer,
or a run whose connection gives no credentials, leaves the state's view; a server refusing the
credentials isserver_auth_failed. -
The tools a killed operation left running are ended by the next operation. Each tool runs
in a process group of its own (a job object on Windows), so a process killed outright (SIGKILL,
Task Manager, a crash) stopped none of them: adocker compose upran to its end seconds later,
avagrant upcould run an hour, and the next start waited on Vagrant's machine lock. An
operation's tools are now recorded in the run's state folder while they run (tools/: each
one's process id and start, under a lock the recording process holds while it lives), and
every operation, settling a killed one with the run locked, ends the tools a dead process left
first: its process group asked to stop (SIGINT), killed after 30 seconds; on Windows its tree
(taskkill /T /F). Only the very process recorded is ended: an id that now names another
process is left alone.ProcessSpechas atoolsfield (the folder a tool is recorded in). -
A download the network cut runs again. One
connection reset by peerin the middle of a
1.93 GB image layer, pulled in a VM, failed the whole start. A download (a step the manifest
marksdownload, a Compose start's pull, a Vagrant start'svagrant box add) that fails with a
network error that passes (a connection reset, refused or timed out, a TLS handshake timeout, an
unexpectedEOF, a name not resolved for now, a registry's gateway answering 502, 503 or 504,
curl's 18, 28, 35 or 56) now runs again, up to three more times, 5, 15 and 45 seconds apart, each
new try said in aretryingwarning with the step and the line that failed (as the runner's
other retries are); Docker keeps the layers it finished, so the next pull takes up where the last
one ended. A refusal (a sign-in,toomanyrequests, an image or a box the registry doesn't have)
still fails at once, as does a download that stalled, and an operation cut short stops waiting
with it. -
A status read's errors mask the connection's secrets. A status read emits nothing, so its
errors quoted what a tool printed of the connection's values as it was (a status step's failure,
and now a Proxmox API refusing the run's token or password): they are masked as an
operation's are.
isothrum 2.0.0
Highlights
2.0.0 is a major release: it removes and changes public API (every such entry below
starts with Breaking:). docs/migrating-to-2.0.md says what to
change.
- Breaking, for embedders: public API nothing used is gone (the
Hooksport, most of
isothrum-machine, test-double and builder leftovers), the process port lives at the crate root,
andpark,attachanddetachtake the operation's options. - Breaking, for the command line:
isothrum shellbecomesisothrum console <machine>,
--ssh-jumpis taken byuponly, andattachno longer stands up an ad-hoc box. - Breaking, for event readers: an event's
runandopcan be absent (on an event written
outside an operation), and thehook_failedwarning is gone. - Breaking, for every run: Isothrum reads run manifest version 1, the frozen contract
Isoloom 2.0 writes, and refuses the version 0 that 1.x read: regenerate the files first. - New operations:
update(bring a running run up to date with its regenerated manifest),
retireandconsole;isothrum completions. - Graphical consoles:
Running::graphical_consoleandisothrum screenreach a VM's screen
(VNC or RDP) on the loopback, opening an SSH tunnel where the manifest says, and a status says
each machine's console or why it has none. - CI-ready command line: Ctrl-C and SIGTERM stop the tools and exit 130/143 with a
cancelled
error; with--jsonevery failure is an event; a step's timeout counts time without progress. - Corporate environments: proxy and CA settings reach an elevated Hyper-V session and a
containerised Terraform; an owner-only home on Windows; the user'sTF_PLUGIN_CACHE_DIRis kept. - Releases you can verify: signed binaries with SBOM and provenance,
cargo binstall, CI on
every pull request, and documentation organised as tutorial, how-tos, reference and explanation. - Many fixes where unreadable tool output passed for success (Compose
configandps,
reap), and a sensitive output masked instatus.
Fixed
-
A step the run manifest marks a download runs with a download's rule. A Compose start in a
VM (docker-vm,cloud-docker) pulled its images inside the start's command, run through
vagrant sshorssh.rununder the start's limit, so a large image on a slow connection could
time the start out. Isoloom now writes that pull as a step of its own (pull-<step>) marked
"download": true: the runner lets it run while it makes progress, with no ceiling, stops it
once it shows none for itstimeout_swithdownload_stalled(its last line quoted), and
reports its failure asdownload_failed, as it does for a Compose start's pull on this
computer. -
A Vagrant start downloads its VMs' boxes before
vagrant up, with a download's rule. A box
downloaded byvagrant upcounted against theup's own limit. Before avagrant upthat
creates the run's VMs, the runner reads each machine's boxes from the manifest
(native.boxes, for the run's provider and this computer's architecture; the access machine's
left to its hot-add), skips those Vagrant has already (vagrant box list, the same provider,
version when exact and build), and adds the others in a step of their own,boxes-<step>:
vagrant box add <box> --provider <p> [--box-version <v>] [--architecture <a>], stopped with
download_stalledonce it shows no progress for 15 minutes,download_failedwith Vagrant's
reason when refused (Vagrant saying it has the box already is no failure). A manifest that
lists no boxes starts as before. -
A run whose operation's process died is failed, not ready. An operation killed midway
(SIGKILL, a crash, the computer switched off) never recorded how it ended: its record stayed
starting(orstopping,parking,resuming), so a run whose containers or VMs had come up
read asreadythough its set-up never ran, and the nextupwas refused as
already_running. Such a run now reads asfailed, and the next operation, once it holds the
run, records itfailed(itslastthe killed operation,cancelled), soupclears what
it left and starts afresh. -
A resume never brings back a run with a machine gone. A machine removed outside the runner
(docker rm -f,vagrant destroy, the hypervisor's Delete) while the run ran or was parked
was left out of a Compose resume, and a Vagrant resume made it again from its box,
unprovisioned (vagrant up --no-provision); either way the run was called ready.resume
now fails withnothing_to_resumenaming the machines (Error::PartlyGone) and records the
runfailed, and anupthat resumes parked runs (Occupied::ResumeParked) clears what is
left and starts it afresh instead. -
A Compose start pulls its images before its
up, with no time limit while they move. An
11 GB image pulled through a slow connection was still downloading when the start'supstep
ran into its limit, and the embedder could only say theuptook too long. Before a step that
runsdocker compose … up(and before the access machine's hot-add), the runner now pulls what
thatupstarts in a step of its own,pull-<step>:docker compose … pull --policy <the up's --pull, missing by default> --ignore-buildable [--include-deps] <its services>, with the
project, files, profiles and variables theuphas, its lines that step'slogevents. A pull
still moving has no ceiling; one that shows no progress for 15 minutes is stopped with the new
download_stalled(its last line quoted), and one that fails ends the start with the new
download_failedand Compose's reason, before anything starts. Theupthen runs under its own
limit. Anupthe runner doesn't read whole (an option it doesn't know), one with--pull never, and everyupon a Compose that refusespull --policypull in theupas before. -
A status read another process's action on a machine refuses is no failure. With no
operation of this runner holding the run (another process'svagrant upadding and provisioning
a VM, or one that began just after the read looked), eachstatuswas refused by Vagrant's
action lock, tried again for a minute, then failed with Vagrant's words (An action 'read_state' was attempted on the machine 'attackbox', … (Vagrant::Errors::MachineActionLockedError) (exited with code 1)), which an app polling the run showed as an error. A query now waits 10 s at most,
not again once an operation holds the run, and then reports the machines Vagrant named
busy(MachineState::Busy), the others as last read, the status not live. An operation's own
reads still wait a minute and fail with Vagrant's refusal. -
A graphical console is no longer asked for through a busy run.
graphical_consoleread the
run's Vagrant status even while an operation held the run, so a screen asked for while a VM was
being added waited a minute on Vagrant's locks, then failed with them. It reads the run as
statusdoes (as last read while an operation holds it) and refuses a machine another process
acts on at once, saying so. -
A graphical console that doesn't answer isn't handed out. A console on the loopback is
checked to accept a connection (2 s) beforegraphical_consolegives it, and refused otherwise
(its screen didn't answer at 127.0.0.1:5901). A vagrant-qemu VM started without a VNC display
(info vnc:None) is refused saying so and to restart it, not thatinfo vncnamed no server. -
An engine's sensitive output never fills a later template. A guest VM's read (
docker-vm)
and a Terraform run's guest login bound every engine output into${outputs.*}under the
engine's names, sensitive ones included, for the rest of the operation; they now bind as the
run's outputs are bound (outputs::bind: by the manifest's names, sensitive ones left out), and
only while they read. -
terraform.copy_modulecopies nothing a copy of a project never takes. It left out
.terraformand states only, so Terraform's crash logs (crash.log,crash.*.log, which can
hold a variable's secret),.gitand.vagrantwent into the run's state folder; it now
follows Isoloom'snever_copiedrule, as the project's archive does. -
A value given twice on the command line is refused (
--param A=…or--secret-from-env A
repeated: a usage error naming it), where the last one was kept without a word. -
A half-drawn VLAN is refused. A LAN with a
switchbut novlan, or the reverse, compiles to
Error::VlanIncomplete, and adeviceon a plain LAN toError::DeviceOffSwitch, where both
compiled as a plain LAN, the field dropped. -
A tool whose version can't be read is traced when the manifest sets its
min_version: it
still passes, with a warning naming it and what its probe printed. -
libvirt's KVM device is said as it is. A
/dev/kvmthat can't be opened for another reason
than permission (busy, no such device) read "add yourself to the kvm and libvirt groups". -
ISOTHRUM_LOG's value prefixes only the filter's error, not a badISOTHRUM_LOG_FORMAT's. -
An operation on a run that exists fills a param's default, refuses a required one with no
value, and leaves out one of another scope, as a start does. It took the recorded params and
the given ones only, so a param a run taken over wasn't given, or one a regenerated manifest
adds, had no value (${params.<name>}unbound, no Terraform variable), while a secret slot
fell back to its default; and a given param of another host or provider was kept. -
Runner::adoptkeeps the slot a deployment was applied in, or refuses it, and picks it under
the home's slots lock. A slot another run on the place held, or out of 1 to 99, was replaced
by a free one without a word, so a laterupdateordownapplied unde...
Isothrum 1.0.0
Isothrum 1.0 is the first stable release, paired with Isoloom 1.0 — the runner (crate + CLI) and model compiler that executes what Isoloom generates.
- Domain-neutral run API: the word "lab" is gone from the public API and internals (
Lab<'r>→Running,status::Run→Started,LabView→RunView, compilerLabFile→ProjectFile, CLILabArgs→TargetArgs). - Cloud + egress seal: drives the cloud Terraform targets and applies the optional
sealstep (drops the default route, limits egress to the environment's own CIDRs); the seal fails closed. - Stable
ErrorCodesurface: a#[non_exhaustive]enum with round-tripping snake_case names, compile-guarded so codes can't drift. ssh.runwith a bounded wait (wait_s, capped at 1800) for a booting cloud VM's first connection.
Published to crates.io (runner, compiler, WASM, CLI). Full detail: CHANGELOG.md.