Commit Graph
12 Commits
Author SHA1 Message Date
vhaudiquet 593ce20f4f feat(error): type the public library API with ecr::Error
anyhow at the public boundary left library consumers unable to match
on failure modes: every mistake was an opaque error, and the ones
worth branching on (host prerequisites, unsupported architectures)
could only be recognized by string matching.

Public functions now return Result<T, ecr::Error> (thiserror) with
semantic variants for those conditions: user namespaces unavailable,
binfmt_misc unregistered, architecture not served by the registry,
missing subuid/subgid entry.  anyhow stays the internal error type;
everything else converts to Error::Other, which transparently wraps
the full context chain.  Errors raised inside the namespace child
cross the process boundary as formatted chains and also surface as
Other; the error pipe wraps them in anyhow before formatting so the
chain still survives.  The CLI maps the error back into anyhow, so
its output is unchanged.
2026-10-04 21:20:51 +02:00
vhaudiquet 2225c33ef2 feat(download): verify content against registry and CDN digests
Downloaded content was hashed nowhere: OCI layer digests were only
used to build blob URLs and distro tarballs were trusted as served,
so a truncated or tampered rootfs went straight into the cache and
from there into execution.

Hash sha256 and sha512 while streaming and verify before the atomic
rename, so a bad download never enters the cache:

- OCI architecture manifests requested by digest are checked against
  that digest, since the layer digests read from them are trusted
  next; layer blobs are checked against the digest the manifest names.
- Direct distro tarballs are checked against the checksums published
  next to them (Alpine: per-file .sha256; Ubuntu: SHA256SUMS in the
  release directory).  A missing or unfetchable checksum artifact
  degrades to an unverified download with a warning, mirroring the
  freshness-check policy; a mismatch always fails.
- Digest algorithms other than sha256/sha512 cannot be checked: warn
  and accept.
2026-10-04 21:20:24 +02:00
vhaudiquet 22f1999470 feat(cli): start the command in the mapped current directory
The default --bind overlay mounted the current directory at
/root/<basename>, but the command still started in /root: the
library's working-directory fallback only considers read-write
binds.  Pass the mapped target as the explicit working_dir so
relative paths keep meaning what they meant on the host.

Bind sources are canonicalized when the targets are built
instead of at mount time.  This fixes --bind . and other
basename-less relative paths, which cli_mount_target rejected,
and lets the --bind/--bind-rw dedup match relative and absolute
spellings of the same directory.
2026-10-03 00:53:58 +02:00
vhaudiquet fe0b99ee6b refactor(verbose): replace the global flag and veprintln! with log
Diagnostics now go through the log facade: the library no longer
decides where output goes, and consumers plug in their own sink (the
CLI installs a stderr logger printing warnings always and ecr debug
diagnostics under -v; dependency records stay filtered out).  The
kernel 'quiet' flag moves from the global verbosity flag to an
explicit QemuConfig field.
2026-10-03 00:13:34 +02:00
vhaudiquet 031c6ba113 deps: drop the unused indicatif dependency
The library stopped drawing progress UI; rendering moved to ecr-cli.
2026-10-03 00:06:32 +02:00
vhaudiquet b3ad0ab3e0 feat(cli): render library progress with indicatif
The CLI owns the terminal now: TermProgress turns the library's
Progress events into bars and spinners on stderr (hidden by indicatif
when stderr is not a terminal).  Named transfers (OCI layers, kernel,
tarball) render as byte bars, extraction and initramfs as file-count
spinners.
2026-10-03 00:06:22 +02:00
vhaudiquet b6ddd85525 feat(rootfs): cache-aware rootfs preparation with persist hook
Add ecr::rootfs with the full rootfs lifecycle behind the library:

- RootfsCache::prepare resolves an image reference, downloads through
  the tarball cache (with the OCI :latest digest freshness check) and
  extracts into a scratch directory tracked by PreparedRootfs.
- PreparedRootfs::persist packs the current rootfs back into its cache
  entry (compressed to match the entry's extension, symlinks and
  permissions preserved) and marks it with a .provisioned sidecar.
- RootfsCache::prepare_provisioned composes both into the hot-cell
  flow: provision once, and every later call sharing the cache skips
  the download and the provisioning step.

extract: an "oci-" cache entry without a layers.manifest is a
persisted provisioned rootfs; extract it as a plain archive.

The CLI now drives prepare and drops its inline cache/orchestration
code and the dirs/tempfile dependencies.  The binfmt check moves ahead
of the download so foreign-arch runs fail before pulling an image.
2026-09-21 00:06:20 +02:00
vhaudiquet b6e5b4f006 refactor: split ecr into library and cli crates
The root package becomes a virtual workspace: `crates/ecr` holds the
library (package name `ecr`) and `crates/ecr-cli` the command line
front-end, which keeps installing the `ecr` binary. No behavior change.

The library must not depend on CLI types, so mount::setup_mounts now
takes the `no_bind` flag instead of a `&Args`.
2026-09-20 23:29:03 +02:00
vhaudiquet 240a66c532 fix: initramfs hard links, streaming archive, verbatim argv transport
- hard links in the rootfs were written as zero-size entries with no
  inode set (all zero), so the kernel could link them to the wrong file;
  assign a synthetic inode per (device, inode) group.
- build the cpio archive by streaming entries to disk instead of holding
  the whole archive in memory.
- the VM command was joined into a single string and shell-expanded in
  the guest, mangling quotes and nested commands; pass the argv
  base64-encoded (ECR_ARGV) and exec it verbatim.
- parse ecr parameters from /proc/cmdline in the init script, set the
  hostname via procfs (arch has no hostname binary), and give the serial
  console a moment to drain before poweroff.
2026-09-20 22:37:33 +02:00
vhaudiquet 931a6dcfd5 feat: use cpio crate for initramfs creation
Add the `cpio` crate as dependency, removing e2fsprogs external dependency.
2026-06-16 20:31:25 +02:00
vhaudiquet 4f44af4449 feat: add --kernel flag for QEMU system emulation mode
Add --kernel <PATH> option to boot extracted rootfs in a QEMU virtual
machine instead of namespace/chroot mode. The rootfs is converted to an
ext4 disk image using mke2fs and booted with the provided kernel.
2026-06-16 19:16:34 +02:00
vhaudiquet 04ed1f25ae feat: basic functionality 2026-06-09 19:09:57 +02:00