jera
The shared infra runtime — the non-UI job/infra engine lifted out of nornir so both nornir and korp depend on one lean crate instead of pulling each other's dependency trees (no dep-hell).
jera is runtime only. The job-list UI stays in facett-jobview; jera is the engine underneath it.
What's in it
-
jera::jobs— the durable job ledger / engine. One redb list (jobs.redb) of every long-running operation (running → done|failed+ timing), orthogonal to whatever result the op produced. Fat-client (writes the localjobs.redb) or thin-client (submits each record over aJobSink), an exclusive serial-scheduler gate, and opportunistic retention. Public API:JobStore,JobHandle,JobSink,JobRecord,JobSelector, thekind/statusmodules. -
jera::container— a no-shell container runtime over the podman/Docker REST API (bollard). Bring aContainerSpecup as a detached container, stream its logs, and tear it down (BollardBootRunner/BollardEngine), or run one to completion as a tracked job (run_container). There is nostd::process::Commandand no CLI shell-out anywhere on this path — the constellation's zero-shell charter. (Featurebollard, on by default.) -
jera::vm— a no-shell KVM/VM boot seam. Map a self-containedBootSpeconto the hypervisor boot primitive and drive it as a job.StubVmBootRunneris the honest not-wired default;TunnrVmBootRunner(featurevm-tunnr) drives tunnr's realtunnr_vm::boot_testKVM primitive. -
jera::boot— the engine-agnostic boot lifecycle (BootStatus,BootProc,BootRunner) shared by the container / VM / exe runners, and the job-boundBootJobsmanager: the reusable "start X as a tracked job" engine, with the bidirectional lifecycle wired (kill the job → kill the boot; the boot dies → the job terminates).
Start a container as a job
use jera::jobs::JobSink;
use jera::boot::{BootJobs, BootTarget};
use jera::container::ContainerSpec;
let mut jobs = BootJobs::new(JobSink::noop(), "my-workspace");
let id = jobs.boot(BootTarget::Container(ContainerSpec {
image: "docker.io/library/nginx:alpine".into(),
ports: vec![8080],
..Default::default()
}));
jobs.poll(); // drives the lifecycle both ways
jobs.kill(&id); // stop + remove over the REST API
Or run one to completion and capture its outcome (feature bollard):
let outcome = jera::container::run_container(
JobSink::noop(),
&ContainerSpec::image("docker.io/library/alpine:3"),
"alpine", "my-workspace",
)?;
assert_eq!(outcome.exit_code, Some(0));
A ContainerSpec also carries airgap isolation and bind mounts — additive knobs (default = unchanged, existing specs byte-identical):
use jera::container::{ContainerSpec, NetMode, BindMount};
let spec = ContainerSpec {
net: NetMode::None, // --network none: no egress (airgap)
mounts: vec![BindMount::ro("/etc/certs", "/certs")], // -v /etc/certs:/certs:ro
..ContainerSpec::image("packer:1")
};
assert_eq!(spec.network_arg().as_deref(), Some("--network none"));
assert_eq!(spec.bind_strings(), vec!["/etc/certs:/certs:ro".to_string()]);
Mounts are threaded into draupnir's mount-aware run (run_to_completion_with_binds); net is the rendered contract routers gate on (the OCI engine hook is a draupnir follow-up). See .nornir/container-spec.md.
Features
| feature | default | what it adds |
bollard |
✅ | the real podman/Docker REST engine + run_container |
vm-tunnr |
— | the real KVM backend over tunnr_vm::boot_test (dep pending the tunnr repo) |
A lean consumer sets default-features = false (nornir does, then forwards its own test-boot-container-bollard feature to jera/bollard).
Status
Extracted from nornir; consumed by nornir (via the nornir-jobs shim + the test-boot runtime) and destined for korp. This standalone sibling directory will move into a shared repo later.
License
MIT OR Apache-2.0.