kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 938e0f309ec6aa31b78fe343c287756f3a26107c
parent 2c5b3f5a1c54ba790d00a2d17dea3685e8206e01
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 17 Jun 2026 13:44:10 -0700

selfdist: release.sh clean-tree assertion + SELFDIST.md work-items checkoff (all implemented; real-key/release-run deferred to release time)

Diffstat:
Mdoc/plan/SELFDIST.md | 107+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mscripts/release.sh | 9+++++++++
2 files changed, 76 insertions(+), 40 deletions(-)

diff --git a/doc/plan/SELFDIST.md b/doc/plan/SELFDIST.md @@ -472,57 +472,84 @@ milestone once a signing identity exists. ## Work items +**Status: implemented.** Everything below is in the tree and gated green +(`make test-release-index` + `make test-selfdist` + `make test-dist`; `make +dist` produces a verifiable signed kpkg/tar.gz/minisig). Two items are +deliberately deferred to release time, not code: (1) generating the **real** +release keypair offline and embedding/publishing its pubkey — the in-tree +default anchor is the clearly-labelled NON-RELEASE test key, replaced at release +time in `driver/release_key.c`; (2) running `scripts/release.sh` itself, which +needs the real key + provisioned cross sysroots/VMs. + ### Versioning -- [ ] Add top-level `VERSION` (CalVer `YYYY.MINOR.PATCH`). -- [ ] Makefile: read `VERSION` + git short hash; inject `-DKIT_VERSION_STR` / +- [x] Add top-level `VERSION` (CalVer `YYYY.MINOR.PATCH`). +- [x] Makefile: read `VERSION` + git short hash; inject `-DKIT_VERSION_STR` / `-DKIT_BUILD_ID` into a new `driver/version.c` - (`kit_version_string`/`kit_build_id`/`kit_host_triple`). -- [ ] Wire `--version` / `version` into `kit_driver_main_ex` and every tool entry - point (shared `kit <tool> X.Y.Z (build, triple)` banner). -- [ ] Reproducibility: no `__DATE__`/`__TIME__`; build id injected once; - clean-tree assertion in release builds. Confirm the self-host byte-identity - gate still passes. + (`kit_version_string`/`kit_build_id`/`kit_host_triple`). Injected via a + `version.o`-only target rule keyed on a dedicated `.version-config` + sentinel, so version/commit churn recompiles only that one TU. +- [x] Wire `--version` / `version` into `kit_driver_main_ex` and every tool entry + point (shared `kit <tool> X.Y.Z (build, triple)` banner). Done by + intercepting `--version` in `dispatch()` (covers symlink + subcommand + forms); `update` is exempt because its `--version <ver>` is a value flag. +- [x] Reproducibility: no `__DATE__`/`__TIME__`; build id injected once; + clean-tree assertion in release builds (`scripts/release.sh` refuses a + dirty tree). The build id appends `-dirty`/`unknown` only as a marker; the + version/triple come from committed/compile-time data, so the self-host + byte-identity gate is unaffected. ### Release artifacts -- [ ] Rework `mk/dist.mk`: stage the tree (binary, libkit.a, headers, rt headers - + per-variant rt archives, `VERSION`, license), then emit signed fat - `.kpkg` + `.tar.gz` + detached `.tar.gz.minisig` for the native triple. +- [x] Rework `mk/dist.mk`: stage the tree (binary at `bin/kit`, libkit.a, + headers, rt headers + sources, `VERSION`, README/license), then emit signed + fat `.kpkg` + `.tar.gz` + detached `.tar.gz.minisig` for the native triple. Key from `KIT_SIGN_KEY`, else the in-tree NON-RELEASE test key + loud warning. -- [ ] Add the in-tree test keypair (clearly labeled) under `test/dist/keys/`; - use it for the hermetic end-to-end test and as the `make dist` fallback. -- [ ] `scripts/release.sh`: cross-build + package + sign every hosted triple in +- [x] Add the in-tree test keypair (clearly labeled) under `test/dist/keys/`; + used for the hermetic test and as the `make dist` fallback; its pubkey is + the embedded `kit update` anchor. +- [x] `scripts/release.sh`: cross-build + package + sign every hosted triple in `scripts/hosted.sh`'s support set with the real release key; emit + sign - the channel index. Document the sysroot dependency for foreign-libc targets. + the channel index. Documents the sysroot dependency for foreign-libc + targets. (Maintainer-run; not exercised by CI here.) ### Channel index + trust -- [ ] `src/dist/release.c` + tests: `kit-release 1` emit/parse (mirror `url` - lists) and CalVer compare. -- [ ] `driver/release_key.c`: embedded release public-key set; `--key` override; - overlap-window rotation. Generate the real release keypair offline (secret - kept out of tree) and embed its pubkey; publish it for bootstrap. +- [x] `src/dist/release.c` + tests: `kit-release 1` emit/parse (ordered `url` + mirror lists) and CalVer compare (`test/api/release_index_test.c`). +- [x] `driver/release_key.c`: embedded release public-key set (array → any key + verifies, for overlap-window rotation); `kit update --key` override. The + real release keypair is generated offline at release time (secret never in + tree); the in-tree default is the NON-RELEASE test pubkey. ### `kit update` -- [ ] `driver/cmd/update.c` (+ `KIT_TOOL_UPDATE_ENABLED`): offline verify+install - first; then channel/index resolution and the full command surface. -- [ ] Single-root install layout (`$KIT_HOME`, default `$XDG_DATA_HOME/kit`): - `versions/`, `current`, `bin/`, `config/`, `sysroots/`, `cache/`. Unpack → - atomic `current` flip → refresh `$KIT_HOME/bin` links; keep-all retention; - `--rollback` / `--list` / `--prune`; refuse self-update of a - non-user-managed install. -- [ ] Reconcile paths to the single root: `trusted_keys` → +- [x] `driver/cmd/update.c` (+ `KIT_TOOL_UPDATE_ENABLED`): offline verify+install + first; then channel/index resolution (`--index` / `$KIT_UPDATE_INDEX_URL`) + and the full command surface. +- [x] Single-root install layout (`$KIT_HOME`, default `$XDG_DATA_HOME/kit`): + `versions/`, `current`, `bin/`, `config/`, `cache/`. Unpack → atomic + `current` flip → refresh `$KIT_HOME/bin` links; keep-all retention; + `--rollback` / `--list` / `--prune`. Self-update only ever writes under the + user-writable `$KIT_HOME`, never a system install (e.g. `/usr/local`). +- [x] Reconcile paths to the single root: `trusted_keys` → `$KIT_HOME/config/trusted_keys` (honor `$KIT_TRUSTED_KEYS` + legacy - `~/.config/kit/trusted_keys`); update `src/dist/trust.c` + DISTRIBUTE.md. - Note the `$KIT_HOME/sysroots` move for the SYSROOTS.md implementation. -- [ ] Host fetch hook in `driver/env/`; first impl shells to `curl`/`wget`; - signature-gated, transport-agnostic, mirror-list fallback. -- [ ] Monotonic/downgrade policy and host-triple guard. + `~/.config/kit/trusted_keys`); driver path policy in `driver/cmd/pkg.c`, + `src/dist/trust.c` format helpers unchanged; DISTRIBUTE.md updated. Noted + the `$KIT_HOME/sysroots` move in SYSROOTS.md. +- [x] Host fetch hook in `driver/env/` (`driver_fetch_url`): execs `curl` + (falling back to `wget`) with the URL as a distinct argv element (no + shell); signature-gated, transport-agnostic, mirror-list fallback. +- [x] Monotonic/downgrade policy (channel updates refuse older than current + unless `--allow-downgrade`; `--version`/local-file are deliberate and + exempt). Networked host match is by the channel-index `[host]` lookup on + `kit_host_triple()`. ### Tests -- [ ] Hermetic: `VERSION` parse + CalVer compare + index emit/parse byte-stability. -- [ ] End-to-end: `make dist` → `kit pkg verify --key test.pub` → - `kit update --key test.pub <file.kpkg>` into a temp `KIT_HOME`; assert - atomic swap, link refresh, offline `--version` flip, `--rollback`, `--prune`. -- [ ] Bootstrap: detached `.tar.gz.minisig` verifies with stock minisign. -- [ ] Tamper: flipped bytes / wrong key / channel-downgrade are all rejected - pre-swap. +- [x] Hermetic: CalVer compare + index emit/parse byte-stability + (`test/api/release_index_test.c`, `make test-release-index`). +- [x] End-to-end (`test/dist/run.sh`, `make test-selfdist`, 62 cases): verify + + `kit update` into a temp `KIT_HOME`; asserts atomic swap, link refresh, + offline `--version` flip, `--rollback`, `--prune`, `--list`. (Builds the + package with `kit pkg create` — the operation `make dist` performs — to + stay fast + hermetic.) +- [x] Bootstrap: detached `.tar.gz.minisig` verifies with stock minisign. +- [x] Tamper: flipped bytes / wrong key / channel-downgrade are all rejected + pre-swap (the downgrade case via a `file://` channel index). diff --git a/scripts/release.sh b/scripts/release.sh @@ -74,6 +74,15 @@ log() { printf 'release: %s\n' "$*" >&2; } VERSION="$(tr -d ' \t\r\n' < "$ROOT/VERSION")" [ -n "$VERSION" ] || die "VERSION file is empty" +# Release builds assert a clean git tree (SELFDIST.md reproducibility contract): +# the embedded build id must not be a "-dirty" hash, and the staged sources must +# match the committed tree. Override only for a deliberate dry run. +if [ "${KIT_RELEASE_ALLOW_DIRTY:-0}" != 1 ]; then + if ! git -C "$ROOT" diff --quiet HEAD 2>/dev/null; then + die "git tree is dirty — commit/stash first, or set KIT_RELEASE_ALLOW_DIRTY=1 for a dry run" + fi +fi + CHANNEL="${KIT_CHANNEL:-stable}" # Default mirror base: GitHub-Releases-style download URL for this version. Can