karapace/docs/lock-spec.md

95 lines
3.6 KiB
Markdown
Raw Normal View History

# Karapace Lock File Specification (v2)
## Overview
The lock file (`karapace.lock`) captures the fully resolved state of an environment at build time. It ensures reproducible builds by pinning all dependency versions and the base image content digest.
The environment identity (`env_id`) is computed from the **locked** state — resolved package versions and content digest — not from unresolved manifest data. This guarantees: same lockfile → same env_id → same environment.
## Format
TOML with the following fields:
| Field | Type | Description |
|---|---|---|
| `lock_version` | `u32` | Lock format version. Must be `2`. |
| `env_id` | `string` | Full 64-character blake3 hex digest computed from locked state. |
| `short_id` | `string` | First 12 characters of `env_id`. |
| `base_image` | `string` | Base image identifier from manifest (e.g. `"rolling"`). |
| `base_image_digest` | `string` | Blake3 content digest of the actual base image rootfs. |
| `resolved_packages` | `array` | Sorted list of `{ name, version }` tables with pinned versions. |
| `resolved_apps` | `string[]` | Sorted, deduplicated list of resolved GUI apps. |
| `runtime_backend` | `string` | Normalized runtime backend name (lowercased). |
| `hardware_gpu` | `bool` | Whether GPU passthrough is requested. |
| `hardware_audio` | `bool` | Whether audio passthrough is requested. |
| `network_isolation` | `bool` | Whether network is isolated. |
| `mounts` | `array` | Sorted list of `{ label, host_path, container_path }` tables. |
| `cpu_shares` | `u64?` | CPU shares limit (optional). |
| `memory_limit_mb` | `u64?` | Memory limit in MB (optional). |
## Resolved Packages
Each entry in `resolved_packages` is a table:
```toml
[[resolved_packages]]
name = "git"
version = "2.44.0-1"
[[resolved_packages]]
name = "clang"
version = "17.0.6-1"
```
Versions are queried from the actual package manager inside the base image during the resolve phase. This ensures identity is based on real installed versions, not just package names.
## Invariants
- The lock file contains **content digests**, not tags or mutable references.
- `base_image_digest` is a blake3 hash of the actual rootfs content, not a hash of the image tag name.
- `env_id` is computed from all locked fields via `LockFile::compute_identity()`.
- Verification is split into two checks:
- **Integrity**: `verify_integrity()` — recomputes `env_id` and compares to the stored value.
- **Manifest intent**: `verify_manifest_intent()` — checks that the manifest hasn't drifted from what was locked.
## Generation
- Generated automatically by `karapace init` (with preliminary unresolved versions) and `karapace build` (with fully resolved versions).
- Written atomically (tempfile + rename) to the same directory as the manifest.
- Should be committed to version control for reproducible builds.
## Manual Override
- The lock file can only be regenerated by re-running `karapace build`.
- There is no `karapace lock --update` command; rebuilding is the only path.
- `karapace verify-lock` checks both integrity and manifest consistency.
## Example
```toml
lock_version = 2
env_id = "46e1d96fdd6fd988092fbcd19b1d89f2b080f3e74d0f4984b4ba45ca5b95e594"
short_id = "46e1d96fdd6f"
base_image = "rolling"
base_image_digest = "a1b2c3d4e5f6..."
runtime_backend = "namespace"
hardware_gpu = true
hardware_audio = false
network_isolation = false
[[resolved_packages]]
name = "clang"
version = "17.0.6-1"
[[resolved_packages]]
name = "git"
version = "2.44.0-1"
resolved_apps = ["debugger", "ide"]
[[mounts]]
label = "workspace"
host_path = "./"
container_path = "/workspace"
```