OXYGENHUB
# 92587de58cef oxygenflow / CHANGELOG.md
6.2 KiB · Markdown · last changed feat(runner): restarts cost no job (closes #49, closes #50) 23 days ago renderedsource blame history raw

Changelog

OxygenFlow has no release yet: the runner on Earth runs main. This file starts on 2026-10-01; earlier history is in oxy log.

Unreleased

  • Restarts cost no job (#49, #50, spec §11.5.1). A job's uploads and declines wait out a hub that is away for up to three minutes (a deploy restarting it), through refused connections as well as server errors, and log lines it could not send go with the next batch instead of being dropped. A runner told to stop drains: it claims nothing more, finishes and reports the jobs it holds, then exits; a second signal stops it at once. With KillMode=mixed and a TimeoutStopSec longer than the longest job in its unit, deploying a runner no longer has to wait for it to be idle.

  • A build cache (#28, spec §5.4). A disk mount may name a cache and the key files it depends on, such as Cargo.lock. The runner keeps passing jobs' disks and starts the next job of the same repository, job and bookmark from one, so builds carry their compiled dependencies forward; another bookmark copies it rather than taking it. Failing jobs' disks are never kept, the receipt names what a disk started from, and the pool stays under cache_gb (default 64 GiB).

  • Runners share images (#47, docs/jetstream.md §8.1). A runner with a [runner.swarm] section serves the image chunks it holds to other runners in its site, and fetches an image it lacks from them and the hub together, rarest chunks first. The hub is asked only for chunks no peer has or is fetching, so runners starting together split its work. Peers talk over TLS: each runner keeps its own certificate, the hub hands its fingerprint on with the peer, and a fetcher trusts no other. Peers admit each other with a ticket the hub signs, and every chunk is checked against its hash. Uploads are capped (upload_mib_s, default 50) at 16 connections. Without the section a runner fetches from the hub alone, as before.

  • A runner declines a job it cannot run (#36, spec §11.5). Before fetching anything it checks the tier, the architecture, that the image is there and verifies, and that the sandbox's memory is free. If any check fails it hands the job back to the hub with the reason instead of running and failing it.

  • The syscall tier (#36, docs/m0.md §3). runtime "syscall" jobs run without KVM, so boards such as the OrangePi RV2 can take them. The sandbox uses user, mount, PID, network, IPC and UTS namespaces on the image's unpacked tree, read-only. Landlock limits writes to /src and /tmp, and a seccomp filter refuses network sockets, tracing, mounts, BPF, keys, modules and io_uring. A delegated cgroup v2 group enforces the CPU and memory limits, and a job killed for memory is told so. A runner offers the tier only where all of this works. flow tiers shows which tiers a machine can run and why the others cannot, and flow run runs syscall jobs too.

  • A runner offers only the tiers it can run (#36). It checks that /dev/kvm opens before offering the MicroVM tier, says which tiers it serves when it starts, and refuses to start with none, naming why, rather than claiming jobs it would fail.

  • A runner tries a fetch again after a network hiccup (oxygenhub#111). Fetching or cloning the job's repository or a subrepo is retried on DNS, connection, timeout and 502/503/504 errors, waiting 2, 4, 8, 16 and 30 seconds; any other error still ends the job at once.

  • Architecture awareness (#36, spec §11.2). flow rootfs import records an image's architecture from its shell's ELF header and refuses an image for another architecture; a claim reports each image's architecture, and a sandbox may say arch "riscv64".

  • A job's identity (oxygenhub#103). identity "AUDIENCE" { mount-path "/run/secrets/…" } asks the hub for an OIDC token for that audience, mounted as a file like a secret.

  • Secrets as files (oxygenhub#107). A job's secret "name" { mount-path "/run/secrets/…" } names a secret the hub holds; it comes with the claim and flow-init writes it at that path, 0400, on a tmpfs, before the job starts. Never in the environment, the receipt or an artifact (spec §9). flow run --secret NAME=VALUE locally.

  • Variables from the hub (oxygenhub#102). A claim carries env, the variables the hub holds for the repository; they join the job's environment under FLOW_* and the pipeline's own env. flow run --var NAME=VALUE does the same locally.

  • A job says how long it may run (#23). timeout "90m" on a job, or on a sandbox for its jobs (hours, minutes, seconds; a minute to a day). The hub caps it at the site's maximum.

  • A pinned file is re-read from the disk before its hash is called wrong (#22).

  • A runner says which jobs it holds (oxygenhub#71). Each claim lists the jobs the runner is running or still has a result for, so the hub ends at once any job it lost by restarting, instead of an hour later at the time limit.

  • A runner says which images it holds (oxygenhub#73). Each claim lists the rootfs images in the runner's cas/ (read each time, so an image imported while it runs counts), and the hub offers only jobs whose image is among them. plex-1 had failed metalgear's jobs for want of one.

  • A job's result is kept until the hub has it (oxygenhub#71). The runner offered a finished job's result three times over seven seconds and then dropped it, so a hub that was briefly down (Earth's reset) left the job running forever. The result is now kept on disk (pending/ in the Flow home) and offered again, every 5 s doubling to a minute, for up to two hours, through any server error or lost connection; a runner that restarts sends what it kept. A refusal (the hub ended the job already) is final.

  • The Rust build image vendors mimalloc (#20). Oxygen's new allocator was missing from the image, so oxygen's runs #150 to #153 failed. The image was rebuilt with images/rust/build.sh, imported on Earth and pinned in the oxygen, oxygenhub and oxygenflow pipelines. Rebuild it whenever a Cargo.lock of those projects changes.

  • A hint for shell escapes in a pipeline. KDL 2.0 strings have no \033, \e or \x escapes; a pipeline that uses one (for ANSI colours, say) is still invalid, but flow check and the hub now name the line and say to write ESC as \u{1b}.