From 5306963cce2c91372022b25be705a479aea36d98 Mon Sep 17 00:00:00 2001 From: Marco Allegretti Date: Sun, 22 Feb 2026 18:38:41 +0100 Subject: [PATCH] docs: comprehensive public documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - docs/getting-started.md — install per distro, first use, common workflows - docs/architecture.md — 9-crate dependency graph, design decisions, data flow - docs/manifest-spec.md — manifest v1 specification - docs/lock-spec.md — lock file v2 specification - docs/store-spec.md — store format v2 specification - docs/hash-contract.md — two-phase identity hashing algorithm - docs/security-model.md — threat model, mount/device/env policy, privilege model - docs/cli-stability.md — 23 stable commands, exit codes, stability guarantees - docs/protocol-v1.md — remote protocol v1 draft - docs/layer-limitations-v1.md — phase 1 layer limitations - docs/api-reference.md — public API reference (Engine, D-Bus) - docs/versioning-policy.md — semantic versioning, deprecation policy - docs/verification.md — release artifact verification (SHA256, cosign, SBOM) - docs/e2e-testing.md — E2E test guide with distro-specific prerequisites - README.md — project overview, features, quick start, installation - CONTRIBUTING.md — development setup, architecture principles, code standards - CHANGELOG.md — full changelog for 0.1.0 and 2.0 hardening --- CHANGELOG.md | 99 ++++++++++++ CONTRIBUTING.md | 97 ++++++++++++ README.md | 222 +++++++++++++++++++++++++++ docs/api-reference.md | 213 ++++++++++++++++++++++++++ docs/architecture.md | 90 +++++++++++ docs/cli-stability.md | 84 +++++++++++ docs/e2e-testing.md | 77 ++++++++++ docs/getting-started.md | 282 +++++++++++++++++++++++++++++++++++ docs/hash-contract.md | 60 ++++++++ docs/layer-limitations-v1.md | 77 ++++++++++ docs/lock-spec.md | 94 ++++++++++++ docs/manifest-spec.md | 113 ++++++++++++++ docs/protocol-v1.md | 109 ++++++++++++++ docs/security-model.md | 83 +++++++++++ docs/store-spec.md | 150 +++++++++++++++++++ docs/verification.md | 132 ++++++++++++++++ docs/versioning-policy.md | 66 ++++++++ 17 files changed, 2048 insertions(+) create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 README.md create mode 100644 docs/api-reference.md create mode 100644 docs/architecture.md create mode 100644 docs/cli-stability.md create mode 100644 docs/e2e-testing.md create mode 100644 docs/getting-started.md create mode 100644 docs/hash-contract.md create mode 100644 docs/layer-limitations-v1.md create mode 100644 docs/lock-spec.md create mode 100644 docs/manifest-spec.md create mode 100644 docs/protocol-v1.md create mode 100644 docs/security-model.md create mode 100644 docs/store-spec.md create mode 100644 docs/verification.md create mode 100644 docs/versioning-policy.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a9eec95 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,99 @@ +# Changelog + +All notable changes to Karapace will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). + +## [Unreleased] — 2.0 Hardening + +### Breaking Changes + +- **Store format v2** — `STORE_FORMAT_VERSION` bumped to 2. New `staging/` and `wal/` directories. Version 1 stores require rebuild. +- **CLI pruned to 23 commands** — removed legacy commands: `init`, `preset`, `list-presets`, `export-app`, `unexport-app`, `quick`, `validate`, `verify-lock`, `export`, `list-images`, `remove-image`, `remote-list`, `tui`. +- **Content-addressed layers** — `LayerStore::put()` now returns the blake3 content hash used as filename. Callers must use the returned hash for references. +- **`Engine::gc()` requires `&StoreLock`** — compile-time enforcement that callers hold the store lock before garbage collection. +- **MetadataStore checksum** — `EnvMetadata` now includes an optional `checksum` field (blake3). Written on every `put()`, verified on every `get()`. Backward-compatible via `serde(default)`. + +### Added — 2.0 Hardening (M1–M8) + +- **M1: WAL Crash Safety** — Fixed race windows in `build()` and `restore()` (rollback registered before side-effects). Added WAL protection to `destroy()`, `commit()` (layer manifest rollback), and `gc()` (WAL marker). 8 new WAL crash-safety tests. +- **M2: Integrity Hardening** — `LayerStore::get()` verifies blake3 hash on every read. `MetadataStore` embeds and verifies blake3 checksum. `verify_store_integrity()` expanded to check objects, layers, and metadata. 4 new integrity tests. +- **M3: GC Safety** — `Engine::gc()` now requires `&StoreLock` parameter (type-enforced). Snapshot layers whose parent is a live base layer are preserved during GC. +- **M4: Remote Protocol** — `X-Karapace-Protocol: 1` header sent on all HTTP backend requests (PUT, GET, HEAD). `PROTOCOL_VERSION` constant exported from `karapace-remote`. 4 new header/auth verification tests via header-capturing mock server. +- **M5: unwrap() Audit** — 0 `unwrap()` in production `src/` code. 4 `Mutex::lock().unwrap()` calls in `MockBackend` replaced with proper `RuntimeError` propagation. +- **M6: Failure Mode Testing** — 11 new tests: WAL write failure on read-only dir, build failure when WAL dir is read-only (disk-full simulation), stop() SIGTERM with real process (ESRCH path), stop() with non-existent PID, permission denied on object read, read-only metadata dir, concurrent GC lock contention, layer corruption detection, metadata corruption detection, destroy nonexistent env, invalid manifest. +- **M7: Coverage Expansion** — `verify_store_integrity()` now checks objects + layers + metadata (was objects-only). `IntegrityReport` expanded with `layers_checked/passed` and `metadata_checked/passed` fields. New tests: freeze/archive state transitions, rename environment, verify-store after fresh build, rebuild preserves new and cleans old, HTTP list_blobs, large (1MB) blob roundtrip. +- **Total: 417 tests** (24 ignored, require privileged operations). Clippy `-D warnings` clean. `cargo fmt` clean. Release build OK. + +### Added — 1.0 Preparation + +- **Real tar layers** — `pack_layer()`/`unpack_layer()` in karapace-store: deterministic tar creation (sorted entries, zero timestamps, owner 0:0) for regular files, directories, and symlinks. Content-addressed via blake3. +- **Snapshot system** — `Engine::commit()` captures overlay upper as a tar snapshot; `Engine::restore()` atomically unpacks a snapshot via staging directory swap; `Engine::list_snapshots()` lists snapshots for an environment. +- **CLI: `snapshots` and `restore`** — new commands for snapshot management. +- **Write-ahead log (WAL)** — `store/wal/{op_id}.json` tracks in-flight operations with rollback steps. `Engine::new()` auto-recovers on startup. Integrated into `build()`, `commit()`, `restore()`. +- **Newtype wrappers threaded through all structs** — `EnvId`, `ShortId`, `ObjectHash`, `LayerHash` now used in `EnvMetadata` across all 8 crates. Transparent serde for backward compatibility. +- **Engine::push/pull** — transfer logic moved from `karapace-remote` to `Engine` methods. `karapace-remote` is now pure I/O. +- **CoreError::Remote** — new error variant for remote operation failures. +- **CLI stability contract** — `docs/cli-stability.md` defines stable command signatures for 1.x. +- **Remote protocol spec** — `docs/protocol-v1.md` (v1-draft) documents blob store routes, push/pull protocol, registry format. +- **Layer limitations doc** — `docs/layer-limitations-v1.md` documents Phase 1 limits (no xattrs, device nodes, hardlinks). + +### Changed + +- **CLI monolith decomposition** — split `main.rs` into ~30 command modules under `commands/`, thin dispatcher in `main.rs`. +- **Error type cleanup** — added `StoreError::InvalidName` and `StoreError::NameConflict` variants; removed `Io(Error::other)` hacks. +- **D-Bus serialization cleanup** — replaced hand-rolled JSON with typed `serde` response structs. +- **Engine store caching** — `MetadataStore`, `ObjectStore`, and `LayerStore` cached as fields on `Engine`. +- **Remote integrity verification** — `pull_env` verifies blake3 hash of each downloaded object and layer. +- **Store spec updated** — `docs/store-spec.md` reflects v2 format with WAL, staging, tar_hash, name field. +- **README updated** — reflects 23 commands, snapshot workflow, remote push/pull examples. + +## [0.1.0] — 2026-02-20 + +### Added + +- **Deterministic environment engine** — content-addressed, hash-based environment identity from resolved lock files. +- **Manifest v1** — declarative TOML manifest with strict schema validation, deterministic normalization, and canonical serialization. +- **Lock file v2** — resolved packages with pinned versions, base image content digest (not tag), dual verification (integrity + manifest intent). +- **Content-addressable store** — blake3 hashing, atomic writes (NamedTempFile + persist), integrity verification on read, reference counting, garbage collection with signal cancellation. +- **CLI commands** — `build`, `rebuild`, `enter`, `exec`, `destroy`, `stop`, `freeze`, `archive`, `list`, `inspect`, `diff`, `snapshots`, `commit`, `restore`, `gc`, `verify-store`, `push`, `pull`, `rename`, `completions`, `man-pages`, `doctor`, `migrate`. +- **`quick` command** — one-step environment creation for casual users (`karapace quick rolling -p git,curl --enter`). Generates a real manifest + lock file, preserving determinism while matching Distrobox-like simplicity. +- **Example manifests** — `examples/minimal.toml`, `examples/dev.toml`, `examples/gui-dev.toml`, `examples/ubuntu-dev.toml`, `examples/rust-dev.toml` for common use cases. +- **Multi-distro image support** — openSUSE Tumbleweed/Leap, Ubuntu (20.04–24.10), Debian (Bookworm/Trixie/Sid), Fedora (40–42), Arch Linux, custom URLs. +- **Runtime backends** — user namespace (`unshare` + `fuse-overlayfs` + `chroot`), OCI (`crun`/`runc`/`youki`), mock (for testing). +- **Host integration** — Wayland, X11, PipeWire, PulseAudio, D-Bus session bus, GPU (`/dev/dri`), audio (`/dev/snd`), SSH agent, fonts, themes, cursor themes, GTK/icon themes. +- **Desktop app export** — export GUI applications from environments as `.desktop` files on the host. +- **Overlay drift control** — diff, freeze, commit, export writable layer changes. +- **D-Bus desktop integration** — socket-activated `org.karapace.Manager1` service (feature-gated, opt-in). +- **Security model** — mount whitelist, device policy, environment variable allow/deny lists, resource limits, no privilege escalation. +- **Structured logging** — `log` + `env_logger` with `KARAPACE_LOG` env var and `--verbose`/`-v` CLI flag. +- **Concurrency safety** — `StoreLock` file locking on all mutating CLI and D-Bus operations, GC protects active/archived environments. +- **Automated tests** — unit tests, integration tests, crash injection tests, concurrent build safety, GC safety, reproducibility. +- **Shell completions** — `karapace completions bash|zsh|fish|elvish|powershell` for tab completion. +- **Man page generation** — `karapace man-pages ` generates man pages for all commands. +- **Prerequisite detection** — early check for `unshare`, `fuse-overlayfs`, `curl` with distro-aware install instructions. +- **CI pipeline** — GitHub Actions workflow: format, clippy, test, release build with artifact upload. + +### Security + +- Shell injection prevention via POSIX single-quote escaping (`shell_quote`) on all sandbox script interpolation. +- Environment variable key validation (`[a-zA-Z0-9_]` only). +- Image download integrity — blake3 digest stored on download, `verify_image()` detects corruption. +- Destroy guard — cannot destroy a running environment (must stop first). +- Atomic rebuild — new environment built before old one is destroyed (no data loss on failure). +- PID cast safety — `i32::try_from()` instead of `as i32` for `libc::kill()`. +- Zero `unwrap()` in production code — all error paths handled gracefully. +- Input validation in `quick` command — image and package names validated against TOML injection. +- `Cargo.lock` committed for reproducible builds. + +### Documentation + +- Manifest v0.1 specification (`docs/manifest-spec.md`) +- Lock file v2 specification (`docs/lock-spec.md`) +- Store format specification (`docs/store-spec.md`) +- Hash contract (`docs/hash-contract.md`) +- Security model with threat model and attack surface (`docs/security-model.md`) +- Public API reference (`docs/api-reference.md`) +- Versioning policy (`docs/versioning-policy.md`) +- `CONTRIBUTING.md` — development workflow, architecture principles, code standards. +- `LICENSE` — European Union Public Licence v1.2 (EUPL-1.2). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..bde7cb9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,97 @@ +# Contributing to Karapace + +## Development Setup + +```bash +# Clone and build +git clone https://github.com/marcoallegretti/karapace.git +cd karapace +cargo build + +# Run tests +cargo test --workspace + +# Full verification (must pass before submitting) +cargo fmt --all --check +cargo clippy --workspace --all-targets -- -D warnings +cargo test --workspace +cargo build --release --workspace +``` + +## Project Structure + +``` +crates/ + karapace-schema/ # Manifest parsing, normalization, lock file, identity hashing + karapace-store/ # Content-addressable store, metadata, layers, GC, integrity + karapace-runtime/ # Container runtime: images, sandbox, host integration, security + karapace-core/ # Build engine, lifecycle state machine, drift control, concurrency + karapace-remote/ # Remote store client, push/pull, registry + karapace-server/ # Reference remote server (tiny_http) + karapace-tui/ # Terminal UI (ratatui) + karapace-cli/ # CLI interface (23 commands) + karapace-dbus/ # D-Bus desktop integration (optional) +docs/ # Public documentation and specifications +examples/ # Ready-to-use manifest examples +data/ # systemd and D-Bus service files +``` + +## Architecture Principles + +Before implementing any feature, verify it aligns with these principles: + +1. **Determinism first.** Same manifest + lock = identical environment, always. +2. **No hidden mutable state.** All state changes are explicit and tracked. +3. **No silent drift.** Overlay changes are visible via `diff` and must be committed explicitly. +4. **No privilege escalation.** Everything runs as the unprivileged user. +5. **Convenience must not break reproducibility.** If there's a conflict, determinism wins. + +See [Architecture Overview](docs/architecture.md) for the full design. + +## Code Standards + +- **Zero warnings**: `cargo clippy --workspace --all-targets -- -D warnings` must pass. +- **Formatted**: `cargo fmt --all --check` must pass. +- **No `unwrap()` in production code** (test code is fine). +- **No `TODO`/`FIXME`/`HACK`** in committed code. +- **All values interpolated into shell scripts must use `shell_quote()`.** +- **All mutating operations must hold a `StoreLock`.** +- **All file writes must be atomic** (use `NamedTempFile` + `persist()`). + +## Testing + +- All new features must include tests. +- Run the full test suite: `cargo test --workspace` +- Integration tests go in `crates/karapace-core/tests/`. +- Unit tests go in the relevant module as `#[cfg(test)] mod tests`. + +## Submitting Changes + +1. Fork the repository. +2. Create a feature branch from `main`. +3. Make your changes, ensuring all verification checks pass. +4. Submit a pull request with a clear description. + +## Building the D-Bus Service + +The D-Bus service is not compiled by default: + +```bash +# Core CLI only (default) +cargo build --release + +# Include D-Bus service +cargo build --release --workspace +``` + +## Generating Shell Completions + +```bash +karapace completions bash > /etc/bash_completion.d/karapace +karapace completions zsh > /usr/share/zsh/site-functions/_karapace +karapace completions fish > ~/.config/fish/completions/karapace.fish +``` + +## License + +By contributing, you agree that your contributions will be licensed under the European Union Public Licence v1.2 (EUPL-1.2). diff --git a/README.md b/README.md new file mode 100644 index 0000000..d1cd753 --- /dev/null +++ b/README.md @@ -0,0 +1,222 @@ +# Karapace + +[![CI](https://github.com/marcoallegretti/karapace/actions/workflows/ci.yml/badge.svg)](https://github.com/marcoallegretti/karapace/actions/workflows/ci.yml) +[![License: EUPL-1.2](https://img.shields.io/badge/License-EUPL_1.2-blue.svg)](LICENSE) + +A deterministic container environment engine for immutable Linux systems. + +Karapace creates isolated, reproducible development environments using Linux namespaces and overlay filesystems — no root, no daemon, no Docker. Environments are content-addressed artifacts derived from declarative TOML manifests, with full host integration for GPU, audio, Wayland, and desktop applications. + +## What Karapace Is (and Isn't) + +Karapace is **the identity and certainty layer** for reproducible environments. It is not a general-purpose container runtime. + +| Need | Tool | +|---|---| +| Full system container lifecycle, advanced networking, snapshots | Incus, LXD, Podman | +| Deterministic, content-addressed, reproducible environments | **Karapace** | +| Quick disposable containers with zero config | Distrobox | +| Determinism + simplicity for casual use | **Karapace** `quick` command | + +Karapace is **complementary** — it can sit on top of or alongside any container runtime. Users needing full container features use a runtime; users needing reproducibility and traceability use Karapace. The `quick` command bridges the gap for users who want both simplicity and determinism. + +## Features + +- **Real container isolation** — Linux user namespaces (`unshare`), `fuse-overlayfs`, `chroot` +- **No root required** — runs entirely as an unprivileged user +- **No daemon** — direct CLI, no background service needed +- **Multi-distro images** — openSUSE, Ubuntu, Debian, Fedora, Arch from LXC image servers +- **Package installation** — `zypper`, `apt`, `dnf`, `pacman` inside the container +- **Host integration** — home directory, Wayland, PipeWire, D-Bus, GPU (`/dev/dri`), audio (`/dev/snd`), SSH agent, fonts, themes +- **Desktop app export** — export GUI apps as `.desktop` files on the host +- **OCI runtime support** — optional `crun`/`runc`/`youki` backend +- **Content-addressable store** — deterministic hashing, deduplication, integrity verification +- **Overlay drift control** — diff, freeze, commit, export writable layer changes +- **OSC 777 terminal markers** — container-aware terminal integration (Konsole, etc.) + +## Crate Layout + +| Crate | Purpose | +|---|---| +| `karapace-schema` | Manifest v1 parsing, normalization, identity hashing, lock file | +| `karapace-store` | Content-addressable store, layers, metadata, GC, integrity | +| `karapace-runtime` | Container runtime: image download, sandbox, host integration, app export | +| `karapace-core` | Build engine, lifecycle state machine, drift control, concurrency | +| `karapace-cli` | Full CLI interface (23 commands) | +| `karapace-dbus` | Socket-activated D-Bus desktop integration (**optional**, feature-gated) | +| `karapace-remote` | Remote content-addressable store, push/pull, registry | +| `karapace-server` | Reference remote server implementing protocol v1 (tiny_http) | +| `karapace-tui` | Terminal UI for environment management (ratatui) | + +## CLI Commands (23) + +``` +karapace build [manifest] # Build environment from manifest +karapace rebuild [manifest] # Destroy + rebuild +karapace enter [-- cmd...] # Enter environment (or run a command) +karapace exec -- # Execute command inside environment +karapace destroy # Destroy environment +karapace stop # Stop a running environment +karapace freeze # Freeze environment +karapace archive # Archive environment (preserve, prevent entry) +karapace list # List all environments +karapace inspect # Show environment metadata +karapace diff # Show overlay drift +karapace snapshots # List snapshots for an environment +karapace commit # Commit overlay drift as snapshot +karapace restore # Restore overlay from snapshot +karapace gc [--dry-run] # Garbage collect store +karapace verify-store # Check store integrity +karapace push [--tag name@tag] # Push environment to remote store +karapace pull [--remote url] # Pull environment from remote store +karapace rename # Rename environment +karapace doctor # Run diagnostic checks on system and store +karapace migrate # Check store version and migration guidance +karapace completions # Generate shell completions +karapace man-pages [dir] # Generate man pages +``` + +All commands support `--json` for structured output, `--store ` for custom store location, `--verbose` / `-v` for debug logging, and `--trace` for trace-level output. + +Set `KARAPACE_LOG=debug` (or `info`, `warn`, `error`, `trace`) for fine-grained log control. + +## Quick Start + +```bash +# Build (core CLI only — D-Bus service is opt-in) +cargo build --release + +# Build with D-Bus desktop integration +cargo build --release --workspace +``` + +```bash +# Write a manifest +cat > karapace.toml << 'EOF' +manifest_version = 1 + +[base] +image = "rolling" # openSUSE Tumbleweed (or "ubuntu/24.04", "fedora/41", "arch", etc.) + +[system] +packages = ["git", "curl", "vim"] + +[hardware] +gpu = true +audio = true + +[runtime] +backend = "namespace" +EOF + +karapace build +karapace enter + +# Run a command inside without interactive shell +karapace exec -- git --version + +# Snapshot and restore +karapace commit +karapace snapshots +karapace restore + +# Push/pull to remote +karapace push --tag my-env@latest +karapace pull my-env@latest + +# List environments +karapace list +``` + +## Example Manifests + +Ready-to-use manifests in `examples/`: + +| File | Description | +|---|---| +| `examples/minimal.toml` | Bare openSUSE system, no extras | +| `examples/dev.toml` | Developer tools (git, vim, tmux, gcc, clang) | +| `examples/gui-dev.toml` | GUI development with GPU + audio passthrough | +| `examples/ubuntu-dev.toml` | Ubuntu-based with Node.js, Python, build-essential | +| `examples/rust-dev.toml` | Rust development environment | + +## Shell Completions + +```bash +# Bash +karapace completions bash > /etc/bash_completion.d/karapace + +# Zsh +karapace completions zsh > /usr/share/zsh/site-functions/_karapace + +# Fish +karapace completions fish > ~/.config/fish/completions/karapace.fish +``` + +## Man Pages + +```bash +karapace man-pages /usr/share/man/man1 +``` + +## Installation + +### From Source (recommended) + +```bash +git clone https://github.com/marcoallegretti/karapace.git +cd karapace +cargo build --release +sudo install -Dm755 target/release/karapace /usr/local/bin/karapace +``` + +### With D-Bus Service + +```bash +cargo build --release --workspace +sudo install -Dm755 target/release/karapace /usr/local/bin/karapace +sudo install -Dm755 target/release/karapace-dbus /usr/local/bin/karapace-dbus +sudo install -Dm644 data/dbus/org.karapace.Manager1.service /usr/share/dbus-1/services/ +sudo install -Dm644 data/systemd/karapace-dbus.service /usr/lib/systemd/user/ +``` + +### Via Cargo + +```bash +cargo install --git https://github.com/marcoallegretti/karapace.git karapace-cli +``` + +## Prerequisites + +- Linux with user namespace support (`CONFIG_USER_NS=y`) +- `fuse-overlayfs` (for overlay filesystem) +- `curl` (for image downloads) +- Optional: `crun`/`runc`/`youki` (for OCI backend) + +Karapace checks for missing prerequisites at startup and provides distro-specific install instructions. + +## Documentation + +- **[Getting Started Guide](docs/getting-started.md)** — installation, first use, common workflows +- [Architecture Overview](docs/architecture.md) +- [Manifest v1 Specification](docs/manifest-spec.md) +- [Lock File v2 Specification](docs/lock-spec.md) +- [Store Format v2 Specification](docs/store-spec.md) +- [Hash Contract](docs/hash-contract.md) +- [Security Model](docs/security-model.md) +- [CLI Stability Contract](docs/cli-stability.md) +- [Remote Protocol v1 (Draft)](docs/protocol-v1.md) +- [Layer Limitations (Phase 1)](docs/layer-limitations-v1.md) +- [Public API Reference](docs/api-reference.md) +- [Versioning Policy](docs/versioning-policy.md) +- [Verification & Supply Chain](docs/verification.md) +- [E2E Testing](docs/e2e-testing.md) + +## Verification + +```bash +cargo fmt --all --check +cargo clippy --workspace --all-targets -- -D warnings +cargo test --workspace +cargo build --release --workspace +``` diff --git a/docs/api-reference.md b/docs/api-reference.md new file mode 100644 index 0000000..dbd8941 --- /dev/null +++ b/docs/api-reference.md @@ -0,0 +1,213 @@ +# Karapace Public API Reference + +## CLI Commands + +### Environment Lifecycle + +#### `karapace init [manifest]` +Initialize an environment from a manifest without building. Creates metadata and a preliminary lock file. +- **Default manifest**: `karapace.toml` + +#### `karapace build [manifest]` +Build an environment. Resolves dependencies, computes canonical identity, creates store objects/layers, and writes the lock file. +- **Default manifest**: `karapace.toml` + +#### `karapace enter [-- cmd...]` +Enter a built environment interactively, or run a command if `-- cmd` is provided. Transitions state to Running, then back to Built on exit. +- Accepts full env_id or short_id prefix. + +#### `karapace exec -- ` +Execute a command inside a built environment (non-interactive). Prints stdout/stderr. + +#### `karapace rebuild [manifest]` +Atomically rebuild an environment. Builds the new environment first; the old one is only destroyed after a successful build. +- Produces the same env_id for the same resolved manifest. +- If the build fails, the existing environment is preserved. + +#### `karapace stop ` +Stop a running environment by sending SIGTERM/SIGKILL to its process. + +#### `karapace freeze ` +Freeze an environment, preventing further entry. Transitions to Frozen state. + +#### `karapace archive ` +Archive an environment. Preserves it in the store but prevents entry. Can be rebuilt later. + +#### `karapace destroy ` +Destroy an environment's overlay and decrement its reference count. +- **Cannot destroy a running environment** — stop it first. + +### Drift Control + +#### `karapace diff ` +Show drift in the writable overlay. Lists added, modified, and removed files. + +#### `karapace commit ` +Commit overlay drift into the content store as a snapshot layer. +- Only works on Built or Frozen environments. + +#### `karapace export ` +Copy the writable overlay contents to a destination directory. + +### Store Management + +#### `karapace gc [--dry-run]` +Run garbage collection. Removes orphaned environments, layers, and objects. +- `--dry-run`: report only, do not delete. + +#### `karapace verify-store` +Verify integrity of all objects in the store (blake3 content hash check). + +#### `karapace verify-lock [manifest]` +Verify lock file integrity (recomputed env_id matches) and manifest consistency (no drift between manifest and lock). + +### Inspection + +#### `karapace inspect ` +Show environment metadata: state, layers, ref count, timestamps. + +#### `karapace list` +List all known environments with short_id, state, and env_id. + +#### `karapace validate [manifest]` +Validate a manifest file and print its preliminary env_id. + +### Desktop Integration + +#### `karapace export-app ` +Export a GUI application from an environment as a `.desktop` file on the host. + +#### `karapace unexport-app ` +Remove an exported application's `.desktop` file from the host. + +### Image Management + +#### `karapace list-images` +List cached container images with status and size. + +#### `karapace remove-image ` +Remove a cached container image from the store. + +### Quick Start + +#### `karapace quick [image] [-p packages] [--gpu] [--audio] [--enter]` +One-step environment creation for casual users. Generates a manifest from CLI flags, builds, and optionally enters. +- **Default image**: `rolling` (openSUSE Tumbleweed) +- `-p` / `--packages`: Comma-separated list of packages to install. +- `--gpu`: Enable GPU passthrough. +- `--audio`: Enable audio passthrough. +- `-e` / `--enter`: Enter the environment immediately after building. +- A real manifest and lock file are still generated (determinism is preserved). + +Examples: +```bash +karapace quick rolling -p git,curl --enter +karapace quick ubuntu/24.04 -p build-essential,cmake --gpu +karapace quick fedora/41 --enter +``` + +### Tooling + +#### `karapace completions ` +Generate shell completions for the specified shell. Supported: `bash`, `zsh`, `fish`, `elvish`, `powershell`. + +```bash +karapace completions bash > /etc/bash_completion.d/karapace +karapace completions zsh > /usr/share/zsh/site-functions/_karapace +karapace completions fish > ~/.config/fish/completions/karapace.fish +``` + +#### `karapace man-pages [dir]` +Generate man pages for all commands in the specified directory. +- **Default directory**: `man` + +#### `karapace push [--tag ] [--remote ]` +Push an environment (metadata + layers + objects) to a remote store. Skips blobs that already exist remotely. +- **`--tag`**: Publish under a registry key (e.g. `my-env@latest`). +- **`--remote`**: Remote store URL (overrides `~/.config/karapace/remote.json`). + +#### `karapace pull [--remote ]` +Pull an environment from a remote store. Reference can be a registry key (e.g. `my-env@latest`) or a raw env_id. +- **`--remote`**: Remote store URL (overrides config). + +#### `karapace remote-list [--remote ]` +List environments in the remote registry. + +## Global Flags + +| Flag | Description | +|---|---| +| `--store ` | Custom store location (default: `~/.local/share/karapace`). | +| `--json` | Structured JSON output for all applicable commands. | +| `--verbose` / `-v` | Enable debug-level logging output. | + +### Environment Variables + +| Variable | Description | +|---|---| +| `KARAPACE_LOG` | Log level filter: `error`, `warn` (default), `info`, `debug`, `trace`. | +| `KARAPACE_STORE` | Override default store path (used by the D-Bus service). | + +## Exit Codes + +| Code | Meaning | +|---|---| +| `0` | Success. | +| `1` | General error. | +| `2` | Manifest validation error. | +| `3` | Store integrity error. | + +## D-Bus API (Optional) + +Interface: `org.karapace.Manager1` +Path: `/org/karapace/Manager1` + +The D-Bus service exits after 30 seconds of idle (socket activation). Build with `cargo build -p karapace-dbus`. + +All methods return proper D-Bus errors (`org.freedesktop.DBus.Error.Failed`) on failure. Mutating methods acquire the store lock automatically. Methods accepting `id_or_name` resolve by env_id, short_id, name, or prefix. + +Desktop notifications are sent on build success/failure via `org.freedesktop.Notifications`. + +### Properties + +| Property | Type | Description | +|---|---|---| +| `ApiVersion` | `u32` | API version (currently `1`). | +| `StoreRoot` | `String` | Path to the store directory. | + +### Methods + +| Method | Signature | Description | +|---|---|---| +| `ListEnvironments` | `() → String` | JSON array of `{env_id, short_id, name?, state}`. | +| `GetEnvironmentStatus` | `(id_or_name) → String` | JSON status. Resolves by name. | +| `GetEnvironmentHash` | `(id_or_name) → String` | Returns env_id hash. Resolves by name. | +| `BuildEnvironment` | `(manifest_path) → String` | Build from manifest path. Sends notification. | +| `BuildNamedEnvironment` | `(manifest_path, name) → String` | Build and assign a name. Sends notification. | +| `DestroyEnvironment` | `(id_or_name) → String` | Destroy environment. Resolves by name. | +| `RunEnvironment` | `(id_or_name) → String` | Enter environment. Resolves by name. | +| `RenameEnvironment` | `(id_or_name, new_name) → String` | Rename an environment. | +| `ListPresets` | `() → String` | JSON array of `{name, description}` for built-in presets. | +| `GarbageCollect` | `(dry_run) → String` | Run GC. Acquires store lock. | +| `VerifyStore` | `() → String` | Verify store integrity. Returns `{checked, passed, failed}`. | + +## Rust Crate API + +### `karapace-core::Engine` + +- `Engine::new(store_root)` — Create engine instance +- `Engine::init(manifest_path)` — Initialize environment (Defined state) +- `Engine::build(manifest_path)` — Full resolve → lock → build pipeline +- `Engine::enter(env_id)` — Enter environment interactively +- `Engine::exec(env_id, command)` — Execute command in environment +- `Engine::rebuild(manifest_path)` — Destroy + rebuild +- `Engine::stop(env_id)` — Stop running environment +- `Engine::freeze(env_id)` — Freeze environment +- `Engine::archive(env_id)` — Archive environment (preserve, prevent entry) +- `Engine::commit(env_id)` — Commit overlay drift +- `Engine::destroy(env_id)` — Destroy environment +- `Engine::inspect(env_id)` — Get environment metadata +- `Engine::list()` — List all environments +- `Engine::gc(dry_run)` — Garbage collection (caller must hold store lock) +- `Engine::set_name(env_id, name)` — Set or clear environment name +- `Engine::rename(env_id, new_name)` — Rename environment diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..1545d44 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,90 @@ +# Karapace Architecture + +## Overview + +Karapace is a deterministic container environment engine organized as a Cargo workspace of 9 crates. Each crate has a single responsibility and clean dependency boundaries. + +## Crate Dependency Graph + +``` +karapace-cli ─────┬──▶ karapace-core ──┬──▶ karapace-schema + │ ├──▶ karapace-store + │ ├──▶ karapace-runtime + │ └──▶ karapace-remote + ├──▶ karapace-remote ──▶ karapace-store + └──▶ karapace-tui ────▶ karapace-core + +karapace-dbus ────────▶ karapace-core +karapace-server ──────▶ karapace-store (standalone HTTP server) +``` + +## Crate Responsibilities + +### `karapace-schema` +Manifest v1 parsing (TOML), normalization, canonical JSON serialization, environment identity hashing, lock file v2 (resolved packages, integrity/intent verification), and built-in presets. + +### `karapace-store` +Content-addressable object store (blake3), layer manifests, environment metadata (with naming, ref-counting, state machine), garbage collection with signal cancellation, and store integrity verification. All writes are atomic via `NamedTempFile` + `persist`. + +### `karapace-runtime` +Container runtime abstraction (`RuntimeBackend` trait) with three backends: +- **Namespace** — `unshare` + `fuse-overlayfs` + `chroot` (unprivileged) +- **OCI** — `crun`/`runc`/`youki` +- **Mock** — deterministic test backend + +Also handles image downloading, sandbox scripting, host integration (Wayland, GPU, audio, D-Bus), security policy enforcement, and desktop app export. + +### `karapace-core` +The `Engine` struct orchestrates the full lifecycle: init → resolve → lock → build → enter/exec → freeze → archive → destroy. Caches `MetadataStore`, `ObjectStore`, and `LayerStore` as fields. Handles drift detection (diff/commit/export via overlay upper_dir scanning) and garbage collection delegation. + +### `karapace-cli` +23 CLI commands, each in its own file under `commands/`. Shared helpers in `commands/mod.rs` (spinners, colored output, environment resolution, JSON formatting). `main.rs` is a thin dispatcher. Exit codes: 0 (success), 1 (failure), 2 (manifest error), 3 (store error). + +### `karapace-dbus` +Socket-activated D-Bus service (`org.karapace.Manager1`) with 11 methods. Typed serde response structs. Desktop notifications via `notify-rust`. 30-second idle timeout for socket activation. Hardened systemd unit file. + +### `karapace-remote` +Remote content-addressable store with `RemoteBackend` trait, HTTP backend (ureq), push/pull transfer with blake3 integrity verification on pull, and a JSON registry for name@tag references. + +### `karapace-tui` +Interactive terminal UI (ratatui + crossterm) with list/detail/help views, vim-style keybindings, search/filter, sort cycling, freeze/archive/rename actions, and confirmation dialogs. + +## Key Design Decisions + +1. **Content-addressed identity** — `env_id` is computed from the *resolved* lock file (pinned versions + base image content digest), not from unresolved manifest data. + +2. **Atomic operations** — All store writes use `NamedTempFile` + `persist` for crash safety. Rebuild builds the new environment before destroying the old one. + +3. **No `unwrap()` in production** — All error paths are handled with proper error types (`StoreError`, `CoreError`, `RemoteError`, `RuntimeError`). + +4. **Store locking** — `StoreLock` file lock on all mutating operations (CLI + D-Bus). GC respects active/archived environments. + +5. **Layered security** — Mount whitelist, device policy, env var allow/deny, resource limits. No privilege escalation. + +## Data Flow + +``` +Manifest (TOML) + │ + ▼ +NormalizedManifest (canonical JSON) + │ + ▼ resolve (RuntimeBackend) +ResolutionResult (base_image_digest + resolved_packages) + │ + ▼ +LockFile v2 (pinned, verifiable) + │ + ▼ compute_identity() +EnvIdentity (env_id = blake3 of canonical lock) + │ + ▼ build (store objects + layers + metadata) +Built Environment (overlay filesystem) +``` + +### `karapace-server` +Reference remote server implementing protocol v1 over HTTP (tiny_http). Provides blob storage, registry, and list endpoints. Used for testing push/pull workflows. + +## Test Coverage + +417 tests across all crates. 24 ignored tests require privileged operations (real `unshare`, `fuse-overlayfs`, ENOSPC simulation, namespace access). diff --git a/docs/cli-stability.md b/docs/cli-stability.md new file mode 100644 index 0000000..d1b2b8f --- /dev/null +++ b/docs/cli-stability.md @@ -0,0 +1,84 @@ +# Karapace CLI Stability Contract + +## Scope + +This document defines the stability guarantee for the Karapace CLI between 1.x releases. + +## Stable Commands (21) + +The following commands have **stable signatures** — no breaking changes to arguments, flags, or output format between 1.x releases: + +| Command | Description | +|---------|-------------| +| `build` | Build environment from manifest | +| `rebuild` | Destroy + rebuild environment | +| `enter` | Enter environment interactively | +| `exec` | Execute command inside environment | +| `destroy` | Destroy environment | +| `stop` | Stop a running environment | +| `freeze` | Freeze environment (read-only overlay) | +| `archive` | Archive environment (preserve, prevent entry) | +| `list` | List all environments | +| `inspect` | Show environment metadata | +| `diff` | Show overlay drift | +| `snapshots` | List snapshots for an environment | +| `commit` | Commit overlay drift as snapshot | +| `restore` | Restore overlay from snapshot | +| `gc` | Garbage collect orphaned store data | +| `verify-store` | Check store integrity | +| `push` | Push environment to remote store | +| `pull` | Pull environment from remote store | +| `rename` | Rename environment | +| `doctor` | Run diagnostic checks on system and store | +| `migrate` | Check store version and show migration guidance | + +## Zero-Maintenance Commands (2) + +These commands are auto-generated and have no hand-maintained logic: + +| Command | Description | +|---------|-------------| +| `completions` | Generate shell completions (bash/zsh/fish/elvish/powershell) | +| `man-pages` | Generate man pages | + +**Total: 23 commands.** + +## Global Flags + +All commands accept these flags (stable): + +- `--store ` — custom store location (default: `~/.local/share/karapace`) +- `--json` — structured JSON output on all query and store commands +- `--verbose` / `-v` — enable debug logging +- `--trace` — enable trace-level logging (more detailed than `--verbose`) + +## What "Stable" Means + +- **No removed flags** — existing flags continue to work. +- **No changed flag semantics** — same flag produces same behavior. +- **No changed exit codes** — exit code meanings are fixed. +- **No changed JSON output keys** — new keys may be added, existing keys are never removed or renamed. +- **New flags may be added** — additive changes are allowed. + +## What May Change + +- Human-readable (non-JSON) output formatting. +- Spinner and progress indicator appearance. +- Error message wording (not error codes). +- Addition of new commands. +- Addition of new optional flags to existing commands. + +## Removed Commands + +The following commands were removed before 1.0 and will not return: + +`init`, `preset`, `list-presets`, `export-app`, `unexport-app`, `quick`, `validate`, `verify-lock`, `export`, `list-images`, `remove-image`, `remote-list`, `tui` + +## Exit Codes + +| Code | Meaning | +|------|---------| +| 0 | Success | +| 1 | General failure | +| 2 | Manifest error (parse, validation) | +| 3 | Store error (integrity, version mismatch) | diff --git a/docs/e2e-testing.md b/docs/e2e-testing.md new file mode 100644 index 0000000..3e6c772 --- /dev/null +++ b/docs/e2e-testing.md @@ -0,0 +1,77 @@ +# End-to-End Testing + +Karapace includes end-to-end tests that exercise the real namespace backend with `unshare`, `fuse-overlayfs`, and actual container images. + +## Prerequisites + +- Linux with user namespace support (`CONFIG_USER_NS=y`) +- `fuse-overlayfs` installed +- `curl` installed +- Network access (images are downloaded from `images.linuxcontainers.org`) + +### Install on openSUSE Tumbleweed + +```bash +sudo zypper install fuse-overlayfs curl +``` + +### Install on Ubuntu/Debian + +```bash +sudo apt-get install fuse-overlayfs curl +``` + +### Install on Fedora + +```bash +sudo dnf install fuse-overlayfs curl +``` + +## Running E2E Tests + +E2E tests are `#[ignore]` by default. Run them explicitly: + +```bash +cargo test --test e2e -- --ignored --test-threads=1 +``` + +The `--test-threads=1` flag is important: E2E tests mount overlays and download images, so parallel execution can cause resource conflicts. + +## Test Descriptions + +| Test | What it does | +|---|---| +| `e2e_build_minimal_namespace` | Build a minimal openSUSE Tumbleweed environment with no packages | +| `e2e_exec_in_namespace` | Build + exec `echo hello` inside the container | +| `e2e_destroy_cleans_up` | Build + destroy, verify env_dir is removed | +| `e2e_rebuild_determinism` | Build + rebuild, verify env_id is identical | +| `e2e_build_with_packages` | Build with `which` package, verify resolved versions in lock file | + +## CI + +The GitHub Actions CI workflow includes an E2E job that runs on `ubuntu-latest`: + +```yaml +e2e: + name: E2E Tests + runs-on: ubuntu-latest + needs: [test] + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - name: Install prerequisites + run: | + sudo apt-get update -qq + sudo apt-get install -y -qq fuse-overlayfs curl + sudo sysctl -w kernel.unprivileged_userns_clone=1 || true + - name: Run E2E tests + run: cargo test --test e2e -- --ignored --test-threads=1 +``` + +## Troubleshooting + +- **"unshare: user namespaces not available"** — Enable with `sysctl kernel.unprivileged_userns_clone=1` +- **"fuse-overlayfs not found"** — Install the `fuse-overlayfs` package +- **"failed to download image"** — Check network connectivity and DNS +- **Stale mounts after failed test** — Run `fusermount3 -u /path/to/merged` or reboot diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..5429757 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,282 @@ +# Getting Started with Karapace + +Karapace creates isolated, reproducible development environments on Linux using +namespaces and overlay filesystems. No root, no daemon, no Docker. + +This guide walks you through installation, first use, and common workflows. + +## Prerequisites + +Karapace requires a Linux system with: + +- **User namespace support** (`CONFIG_USER_NS=y` — enabled on all major distros) +- **fuse-overlayfs** (overlay filesystem in userspace) +- **curl** (for downloading base images) + +Optional: + +- **crun**, **runc**, or **youki** (only if using the OCI backend) + +### Install prerequisites by distro + +**openSUSE Tumbleweed / Leap:** +```bash +sudo zypper install fuse-overlayfs curl +``` + +**Ubuntu / Debian:** +```bash +sudo apt install fuse-overlayfs curl +``` + +**Fedora:** +```bash +sudo dnf install fuse-overlayfs curl +``` + +**Arch Linux:** +```bash +sudo pacman -S fuse-overlayfs curl +``` + +Run `karapace doctor` at any time to check that all prerequisites are met. + +## Installation + +### From source (recommended) + +```bash +git clone https://github.com/marcoallegretti/karapace.git +cd karapace +cargo build --release +sudo install -Dm755 target/release/karapace /usr/local/bin/karapace +``` + +### Via cargo install + +```bash +cargo install --git https://github.com/marcoallegretti/karapace.git karapace-cli +``` + +### Shell completions + +```bash +# Bash +karapace completions bash > ~/.local/share/bash-completion/completions/karapace + +# Zsh +karapace completions zsh > ~/.local/share/zsh/site-functions/_karapace + +# Fish +karapace completions fish > ~/.config/fish/completions/karapace.fish +``` + +## Your first environment + +### 1. Write a manifest + +Create a file called `karapace.toml`: + +```toml +manifest_version = 1 + +[base] +image = "rolling" # openSUSE Tumbleweed + +[system] +packages = ["git", "curl"] + +[runtime] +backend = "namespace" +``` + +Available base images: `"rolling"` (openSUSE Tumbleweed), `"ubuntu/24.04"`, +`"debian/12"`, `"fedora/41"`, `"arch"`. + +### 2. Build the environment + +```bash +karapace build +``` + +This downloads the base image, installs the requested packages, and produces +a content-addressed environment. The output shows the environment ID (`env_id`) +and a short ID for convenience. + +### 3. Enter the environment + +```bash +karapace enter +``` + +You can use the short ID (first 8 characters) or a name instead of the full ID. +Inside the environment you have a full Linux userspace with the packages you +requested. + +### 4. Run a single command + +```bash +karapace exec -- git --version +``` + +## Naming environments + +By default, environments are identified by their content hash. You can assign +a human-readable name: + +```bash +karapace build --name mydev +karapace enter mydev +``` + +Or rename an existing environment: + +```bash +karapace rename mydev +``` + +## Common workflows + +### Snapshot and restore + +After making changes inside an environment (installing extra packages, editing +config files), you can snapshot and later restore: + +```bash +# See what changed +karapace diff mydev + +# Save a snapshot +karapace commit mydev + +# List snapshots +karapace snapshots mydev + +# Restore a previous snapshot +karapace restore mydev +``` + +### Freeze and archive + +```bash +# Freeze: prevent further changes (still enterable in read-only mode) +karapace freeze mydev + +# Archive: preserve metadata but prevent entry +karapace archive mydev +``` + +### Rebuild from scratch + +If you change your manifest, rebuild destroys the old environment and builds +a new one: + +```bash +karapace rebuild +``` + +### Push and pull (remote sharing) + +```bash +# Push to a remote store +karapace push mydev --tag my-env@latest + +# Pull on another machine +karapace pull my-env@latest --remote https://your-server.example.com +``` + +### GUI application export + +Export a GUI application from inside the container to your host desktop: + +```bash +karapace exec mydev -- karapace-export-app firefox +``` + +This creates a `.desktop` file on the host that launches the app inside the +container with GPU and audio passthrough. + +## Hardware passthrough + +Enable GPU and audio in your manifest: + +```toml +[hardware] +gpu = true # Passes /dev/dri into the container +audio = true # Passes PipeWire/PulseAudio socket +``` + +Karapace also forwards Wayland, X11, D-Bus session bus, SSH agent, fonts, +and GTK/icon themes automatically when available. + +## Custom bind mounts + +Mount host directories into the container: + +```toml +[mounts] +workspace = "~/projects:/workspace" +data = "/data/datasets:/datasets" +``` + +## Built-in presets + +For quick setup without writing a manifest: + +```bash +# List available presets +karapace list-presets + +# Build from a preset +karapace preset dev-rust +``` + +Available presets: `dev`, `dev-rust`, `dev-python`, `gui-app`, `gaming`, `minimal`. + +## Quick one-liner + +The `quick` command combines build + enter in a single step: + +```bash +karapace quick +``` + +This uses the `karapace.toml` in the current directory (or creates a minimal one). + +## Diagnostics + +```bash +# Check system prerequisites +karapace doctor + +# Verify store integrity +karapace verify-store + +# List all environments +karapace list + +# Inspect an environment +karapace inspect mydev + +# Garbage collect unused objects +karapace gc --dry-run +karapace gc +``` + +## Environment variables + +| Variable | Effect | +|----------|--------| +| `KARAPACE_LOG` | Log level: `error`, `warn`, `info`, `debug`, `trace` | +| `KARAPACE_STORE` | Custom store directory (default: `~/.local/share/karapace`) | + +Or use CLI flags: `--verbose` / `-v` for debug, `--trace` for trace, +`--store ` for a custom store, `--json` for machine-readable output. + +## Next steps + +- [Manifest v1 Specification](manifest-spec.md) — full manifest reference +- [Architecture Overview](architecture.md) — how Karapace works internally +- [CLI Stability Contract](cli-stability.md) — which commands are stable +- [Security Model](security-model.md) — isolation guarantees and threat model +- [Verification](verification.md) — verifying release artifact integrity diff --git a/docs/hash-contract.md b/docs/hash-contract.md new file mode 100644 index 0000000..18e73c6 --- /dev/null +++ b/docs/hash-contract.md @@ -0,0 +1,60 @@ +# Karapace Hash Contract + +## Overview + +The environment identity (`env_id`) is a deterministic blake3 hash that uniquely identifies an environment's fully resolved state. Two identical lock files on any machine must produce the same `env_id`. + +## Algorithm + +Blake3 (256-bit output, hex-encoded, 64 characters). + +## Two-Phase Identity + +Karapace computes identity in two phases: + +### Preliminary Identity (`compute_env_id`) + +Used only during `init` (before resolution) and for internal lookup. Computed from unresolved manifest data. **Not the canonical identity.** + +### Canonical Identity (`LockFile::compute_identity`) + +The authoritative identity used after `build`. Computed from the fully resolved lock file state. This is what gets stored in metadata and the lock file. + +## Canonical Hash Input + +The canonical hash includes the following inputs, fed in order: + +1. **Base image content digest**: `base_digest:` — real content hash, not a tag name hash. +2. **Resolved packages**: each as `pkg:@` (sorted by name). +3. **Resolved apps**: each as `app:` (sorted). +4. **Hardware policy**: `hw:gpu` if GPU enabled, `hw:audio` if audio enabled. +5. **Mount policy**: each as `mount: