Skip to content

Releases

isothrum 2.1.7

Choose a tag to compare

@github-actions github-actions released this 13 Nov 16:02

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_on VM state reads as running. The plugin calls a VM powered_on until it
    has its SSH address and running after; the first read as unknown, never ready, its address
    skipped (a Windows guest before its tools report one).

isothrum 2.1.6

Choose a tag to compare

@github-actions github-actions released this 13 Nov 13:57

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

Choose a tag to compare

@github-actions github-actions released this 13 Nov 11:44

Fixed

  • A Compose run's update removes the anonymous volumes its recreate orphaned. 2.1.4 removed
    at down the 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 later down sees them attached. The
    update operation 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

Choose a tag to compare

@github-actions github-actions released this 13 Nov 11:27

Fixed

  • A box for the vmware_esxi provider is downloaded as its vmware_desktop build. The
    vagrant-vmware-esxi plugin boots vmware_desktop builds (no box on the registry carries a
    vmware_esxi one), 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 the libvirt build it boots.
  • A Compose run's down removes the anonymous volumes an update's recreate inherited. The
    engine makes a volume for each VOLUME of a container's image (MySQL's data directory) and
    removes it with the container on down --volumes or rm --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: a down --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). The down operation 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's down records nothing.

isothrum 2.1.3

Choose a tag to compare

@github-actions github-actions released this 12 Nov 04:44

Fixed

  • A published port's preferred number that the computer already holds moves to a free port.
    With ISOLOOM_PUBLISH_FIXED (a lab's own ports), a published port took its preferred number
    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 start port_in_use at 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

Choose a tag to compare

@github-actions github-actions released this 12 Nov 02:36

Fixed

  • The Windows binary starts on a Windows without the Visual C++ Redistributable. The release's
    isothrum.exe imported VCRUNTIME140.dll, so a Windows that never installed it (a fresh install,
    a clean virtual machine) couldn't start it: exit 0xC0000135, 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
    imports VCRUNTIME*.dll or MSVCP*.dll; CI checks the same on every push.

isothrum 2.1.1

Choose a tag to compare

@github-actions github-actions released this 11 Nov 23:14

Fixed

  • A Vagrant run on QEMU downloads each box once. The box download before vagrant up asked
    for the box's qemu build, but vagrant-qemu boots libvirt-format boxes, so vagrant up found
    none and downloaded the box again (Box Provider: libvirt). The download now asks for the
    format the provider boots (--provider libvirt for QEMU), and a libvirt build already there
    counts.

isothrum 2.1.0

Choose a tag to compare

@github-actions github-actions released this 11 Nov 18:57

Added

  • Error::ServerAuthFailed, code server_auth_failed: the server a run is on (Proxmox VE,
    ESXi; the proxmox and esxi targets, Vagrant on vmware_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's incorrect user name or password, vagrant-vmware-esxi's
    ESXi host access and Authentication 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 reads absent (a resume refuses it as
    gone, a start again rebuilds the run), one powered off stopped. A server that doesn't answer,
    or a run whose connection gives no credentials, leaves the state's view; a server refusing the
    credentials is server_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: a docker compose up ran to its end seconds later,
    a vagrant up could 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. ProcessSpec has a tools field (the folder a tool is recorded in).

  • A download the network cut runs again. One connection reset by peer in the middle of a
    1.93 GB image layer, pulled in a VM, failed the whole start. A download (a step the manifest
    marks download, a Compose start's pull, a Vagrant start's vagrant box add) that fails with a
    network error that passes (a connection reset, refused or timed out, a TLS handshake timeout, an
    unexpected EOF, 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 a retrying warning 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

Choose a tag to compare

@github-actions github-actions released this 11 Nov 17:03

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 Hooks port, most of
    isothrum-machine, test-double and builder leftovers), the process port lives at the crate root,
    and park, attach and detach take the operation's options.
  • Breaking, for the command line: isothrum shell becomes isothrum console <machine>,
    --ssh-jump is taken by up only, and attach no longer stands up an ad-hoc box.
  • Breaking, for event readers: an event's run and op can be absent (on an event written
    outside an operation), and the hook_failed warning 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),
    retire and console; isothrum completions.
  • Graphical consoles: Running::graphical_console and isothrum screen reach 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 --json every 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's TF_PLUGIN_CACHE_DIR is 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 config and ps,
    reap), and a sensitive output masked in status.

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 ssh or ssh.run under 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 its timeout_s with download_stalled (its last line quoted), and
    reports its failure as download_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 by vagrant up counted against the up's own limit. Before a vagrant up that
    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_stalled once it shows no progress for 15 minutes, download_failed with 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 (or stopping, parking, resuming), so a run whose containers or VMs had come up
    read as ready though its set-up never ran, and the next up was refused as
    already_running. Such a run now reads as failed, and the next operation, once it holds the
    run, records it failed (its last the killed operation, cancelled), so up clears 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 with nothing_to_resume naming the machines (Error::PartlyGone) and records the
    run failed, and an up that 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's up step
    ran into its limit, and the embedder could only say the up took too long. Before a step that
    runs docker compose … up (and before the access machine's hot-add), the runner now pulls what
    that up starts 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 the up has, its lines that step's log events. 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_failed and Compose's reason, before anything starts. The up then runs under its own
    limit. An up the runner doesn't read whole (an option it doesn't know), one with --pull never, and every up on a Compose that refuses pull --policy pull in the up as 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's vagrant up adding and provisioning
    a VM, or one that began just after the read looked), each status was 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_console read 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
    status does (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) before graphical_console gives 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 that info vnc named 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_module copies nothing a copy of a project never takes. It left out
    .terraform and states only, so Terraform's crash logs (crash.log, crash.*.log, which can
    hold a variable's secret), .git and .vagrant went into the run's state folder; it now
    follows Isoloom's never_copied rule, 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 switch but no vlan, or the reverse, compiles to
    Error::VlanIncomplete, and a device on a plain LAN to Error::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/kvm that 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 bad ISOTHRUM_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::adopt keeps 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 later update or down applied unde...

Read more

Isothrum 1.0.0

Choose a tag to compare

@florianamette florianamette released this 18 Oct 11:58

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, compiler LabFile→ProjectFile, CLI LabArgs→TargetArgs).
  • Cloud + egress seal: drives the cloud Terraform targets and applies the optional seal step (drops the default route, limits egress to the environment's own CIDRs); the seal fails closed.
  • Stable ErrorCode surface: a #[non_exhaustive] enum with round-tripping snake_case names, compile-guarded so codes can't drift.
  • ssh.run with 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.