diff --git a/README.md b/README.md
index b6b703c1..df398271 100644
--- a/README.md
+++ b/README.md
@@ -12,27 +12,51 @@ A from-scratch Linux distribution built on two commitments:
Kryptik is built from source via Linux From Scratch, with no upstream distro
base. Its binaries carry their own target triple:
-```
+```text
$ sysroot/usr/bin/bash --version
GNU bash, version 5.2.32(1)-release (x86_64-kryptik-linux-gnu)
```
## Status
-Pre-alpha. The build produces a hardened kernel with the linux-hardened
-patchset; install media (a USB image and an ISO) that boot by UEFI firmware
-alone; an installer; an installed system that verifies its root with
-dm-verity on every boot and keeps its state on an encrypted partition; A/B
-updates with a judged trial boot and recovery from the medium; and a zoned
-Wayland desktop. `make acceptance` runs every suite against the built images.
+A pre-release, v0.1.0, is on the repository's Releases page. The build
+produces a hardened kernel with the linux-hardened patchset; install media (a
+USB image and an ISO) that boot by UEFI firmware alone; an installer; an
+installed system that verifies its root with dm-verity on every boot and
+keeps its state on an encrypted partition; A/B updates with a judged trial
+boot and recovery from the medium; and a zoned Wayland desktop.
+`make acceptance` runs every suite against the built images, and a release
+is cut only from a run in which every suite passed.
What is tested, and what the last acceptance run proved, is in
[docs/status.md](docs/status.md). Everything so far has run under QEMU with
-OVMF firmware; nothing has run on physical hardware, and releases are signed
-by a key the build generates. Releases are on the repository's Releases
-page: what one is, how it is numbered and how to check a download is in
+OVMF firmware; nothing has run on physical hardware, and every release so far
+is signed by keys the build generated. What a release is, how it is numbered
+and how a production release will differ is in
[docs/releases.md](docs/releases.md).
+## Get it
+
+From the Releases page take the medium, its signed checksums and the anchor:
+`kryptik-VERSION-usb.img.zst` (or `kryptik-VERSION.iso.zst`),
+`kryptik-VERSION.SHA256SUMS` with its `.sig`, and `release-signers`. Check
+the download, then write it to a USB stick:
+
+```sh
+zstd -d kryptik-VERSION-usb.img.zst
+ssh-keygen -Y verify -f release-signers -I kryptik-release -n kryptik-media \
+ -s kryptik-VERSION.SHA256SUMS.sig < kryptik-VERSION.SHA256SUMS
+sha256sum -c --ignore-missing kryptik-VERSION.SHA256SUMS
+sudo dd if=kryptik-VERSION-usb.img of=/dev/sdX bs=4M status=progress oflag=sync
+```
+
+The anchor comes with the download, so this proves the files belong together,
+not who made them; a production release's anchor will be the one made
+offline. Boot with Secure Boot off, or enrol `kryptik-sb.der` from the same
+page in the firmware first. Installing, the first boot, daily use, updating
+and recovery are in the [user guide](docs/user-guide.md), which every release
+also ships as `INSTRUCTIONS.md`.
+
## Hardware
x86-64 with UEFI firmware. There is no initramfs, so the storage controller
@@ -67,12 +91,126 @@ A machine outside that list boots a kernel that cannot find its disk or its
network. Adding a driver is one line in the fragment, a firmware file one line
in the list.
+## How it fits together
+
+```mermaid
+flowchart TD
+
+subgraph group_zone_runtime["Zone runtime (kryptikd)"]
+ node_daemon["Zone daemon
[main.rs]"]
+ node_policy["Zone policy
[policy.rs]"]
+ node_registry["Zone registry
[registry.rs]"]
+ node_rootfs["Zone filesystem
[rootfs.rs]"]
+ node_files["Zone files
[files.rs]"]
+ node_cgroup["Resource limits
[cgroup.rs]"]
+ node_caps["Capabilities
[caps.rs]"]
+ node_landlock["Filesystem rules
[landlock.rs]"]
+ node_seccomp["Syscall filter
[seccomp.rs]"]
+ node_isolate["Namespace setup
[isolate.rs]"]
+ node_spawn["Zone process
[spawn.rs]"]
+ node_volume[("Encrypted volumes
[volume.rs]")]
+end
+
+subgraph group_desktop["Desktop isolation"]
+ node_launch["Desktop launcher
[kryptik-launch.c]"]
+ node_wlproxy["Wayland proxy
[kryptik-wlproxy]"]
+ node_session["Protocol session
[session.rs]"]
+ node_protocol["Protocol tables
[protocol.rs]"]
+ node_wire["Wire framing
[wire.rs]"]
+ node_identity["Zone colour identity
[identity.rs]"]
+ node_palette["Colour palette
[palette.rs]"]
+end
+
+subgraph group_services["Zone services"]
+ node_broker["Transfer broker
[broker.rs]"]
+ node_consent["Transfer consent
[consent.rs]"]
+ node_update["Update mediation
[update.rs]"]
+ node_fetch["Update fetcher
[update-fetch.py]"]
+ node_network["Network zones
[netzone.rs]"]
+ node_wifi["Wi-Fi control
[wifi.rs]"]
+end
+
+subgraph group_boot["Boot and updates"]
+ node_apply["Update apply
[kryptik-update]"]
+ node_efiboot["EFI boot entries
[kryptik-efiboot.c]"]
+end
+
+node_user(("User"))
+node_wayland_client(("Zone application"))
+node_compositor(("Wayland compositor"))
+node_channel(("Update channel"))
+
+node_user -->|"launches"| node_launch
+node_launch -->|"sends request"| node_daemon
+node_daemon -->|"loads policy"| node_policy
+node_daemon -->|"checks zone"| node_registry
+node_daemon -->|"prepares root"| node_rootfs
+node_daemon -->|"sets up files"| node_files
+node_daemon -->|"limits resources"| node_cgroup
+node_daemon -->|"sets capabilities"| node_caps
+node_daemon -->|"sets filesystem rules"| node_landlock
+node_daemon -->|"installs filter"| node_seccomp
+node_daemon -->|"creates namespaces"| node_isolate
+node_daemon -->|"manages storage"| node_volume
+node_daemon -->|"starts process"| node_spawn
+node_launch -->|"starts proxy"| node_wlproxy
+node_wayland_client -->|"connects"| node_wlproxy
+node_wlproxy -->|"creates session"| node_session
+node_session -->|"checks messages"| node_protocol
+node_session -->|"frames messages"| node_wire
+node_session -->|"forwards allowed traffic"| node_compositor
+node_compositor -->|"returns events"| node_session
+node_session -->|"forwards events"| node_wayland_client
+node_daemon -->|"serves broker"| node_broker
+node_broker -->|"requests approval"| node_consent
+node_launch -->|"requests clipboard move"| node_broker
+node_fetch -->|"fetches release data"| node_channel
+node_fetch -->|"submits update data"| node_broker
+node_broker -->|"dispatches update requests"| node_update
+node_update -->|"stages a checked release for"| node_apply
+node_apply -->|"arms the trial boot"| node_efiboot
+node_launch -->|"requests Wi-Fi action"| node_wifi
+node_wifi -->|"controls radio zone"| node_network
+node_identity -->|"uses colours"| node_palette
+
+classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
+classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
+classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
+classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
+classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
+class node_daemon,node_policy,node_registry,node_rootfs,node_files,node_cgroup,node_caps,node_landlock,node_seccomp,node_isolate,node_spawn,node_volume,node_user toneBlue
+class node_launch,node_wlproxy,node_session,node_protocol,node_wire,node_identity,node_palette toneAmber
+class node_broker,node_consent,node_update,node_fetch,node_network,node_wifi toneMint
+class node_apply,node_efiboot toneRose
+class node_wayland_client,node_compositor,node_channel toneIndigo
+```
+
+Each box names its file: the zone runtime and the zone services are
+`compartments/kryptikd/src/`, the proxy `compositor/wlproxy/src/`, the colour
+identity `compositor/zoneid/src/`, the launcher `tools/desktop/`, the fetcher
+`tools/net/`, the update tools `tools/update/` and `tools/efi/`.
+
+Zone 0 is the trusted base: PID 1, the services, kryptikd, the compositor and
+the desktop session, with no route out and no user applications. Every
+application runs in a zone that kryptikd creates as root from the zone's
+policy: its own namespaces and root, Landlock rules, a seccomp filter, cgroup
+limits, and a LUKS2 volume if the zone keeps state. A zone's windows reach
+the compositor only through the zone's own `kryptik-wlproxy`, which passes
+the protocol it knows and refuses the rest; the compositor draws the zone's
+colour, and the trusted chrome names it. Files and clipboards cross zones
+only through the broker, after the user answers its question. The net zone
+alone holds the wire and the radio: it fetches releases, and zone 0 stages
+what it hands over through that same broker before `kryptik update apply`
+writes the other slot and arms one trial boot. The whole model is in
+[docs/architecture.md](docs/architecture.md); the boundary of any one zone
+is what `kryptikd explain ` prints.
+
## What a zone does
`kryptik` is the command; `kryptikd` is what it calls. A zone has its own pid
namespace and hostname, and sees four processes where the host has 142:
-```
+```text
$ kryptik run untrusted -- /bin/sh -c \
'echo pid=$$; echo host=$(hostname); echo procs=$(ls /proc | grep -c "^[0-9]*$")'
kryptikd: zone "untrusted": ephemeral storage is a tmpfs freed on exit; its pages can reach swap, so this is not secure erasure
@@ -110,7 +248,7 @@ The tradeoff is spelled out in [docs/threat-model.md](docs/threat-model.md).
## Repository layout
-```
+```text
build/
stages/ Ordered build stages (00-host-check → 06-iso)
recipes/ One file per stage 04 step, sourced by 04-base-system.sh
@@ -147,7 +285,9 @@ docs/ Architecture, threat model, decisions, status
The `Distro` workflow (`.github/workflows/distro.yml`) builds and tests
everything on GitHub's runners: stages 01–02, then stages 04–06, then
`make acceptance` under KVM, uploading the acceptance report and the tested
-images. To build locally, on Linux:
+images. A tag `v` builds that version from nothing and, for a
+development release, drafts it on the Releases page. To build locally, on
+Linux:
```sh
make check # what the host is missing
@@ -166,6 +306,8 @@ make acceptance EXPORT=DIR # every suite on the newest media; root and KVM
Host setup: [docs/building.md](docs/building.md). Booting, installing,
updating and recovering a release: [docs/user-guide.md](docs/user-guide.md).
+A production release, signed with keys made offline:
+[docs/release-keys.md](docs/release-keys.md).
## Documentation
@@ -176,6 +318,7 @@ updating and recovering a release: [docs/user-guide.md](docs/user-guide.md).
- [Supply chain](docs/supply-chain.md): source integrity and its gaps
- [Status](docs/status.md): what is tested and what the last run proved
- [Roadmap](docs/roadmap.md): what remains for 1.0 and 2.0
+- [Releases](docs/releases.md): what a release is, how it is numbered, how to check a download
- [Building](docs/building.md) and the [user guide](docs/user-guide.md)
- [Release keys](docs/release-keys.md): making, keeping, using and replacing the keys that sign releases