No description
  • Go 66.1%
  • Nix 18.2%
  • Python 8.5%
  • Shell 3.2%
  • C 1.8%
  • Other 2.2%
Find a file
2026-10-03 17:21:39 -04:00
.github Fix Pages deployment after optional CI skips (#108) 2026-10-03 17:21:39 -04:00
cmd Validate independently signed campaign witnesses (#100) 2026-10-02 16:44:02 -04:00
config protect nvme0n1 as protected on malak 2026-08-30 14:50:37 -04:00
deploy Record bounded Ace NVMe power-loss and offline tests (#95) 2026-10-01 12:18:26 -04:00
docs Record production provisioning and remote update plan (#105) 2026-10-03 16:43:30 -04:00
examples feat: prepare device-secret execution and recovery (#37) 2026-09-17 04:39:05 -04:00
internal/provisioning Validate independently signed campaign witnesses (#100) 2026-10-02 16:44:02 -04:00
keys add development USB SSH access 2026-09-05 21:55:53 -04:00
nix Retain vendor notices in Raspberry Pi firmware build outputs (#103) 2026-10-03 16:42:41 -04:00
policies rpi5 hardware configuration rework 2026-08-24 17:55:09 -04:00
profiles/device-classes promote to production state 2026-08-23 23:40:24 -04:00
releases/rpi5-v0.1.6 Include Raspberry Pi firmware notices with the public v0.1.6 release (#102) 2026-10-03 16:38:48 -04:00
reviewed-candidates docs: retain task 1 storage qualification and public evidence review (#104) 2026-10-03 16:43:06 -04:00
schemas feat: export authenticated provisioning records for fleet intake (#60) 2026-09-21 21:51:03 -04:00
scripts Validate independently signed campaign witnesses (#100) 2026-10-02 16:44:02 -04:00
signers/development-prototype clean up docs 2026-09-07 23:06:02 -04:00
tests Retain vendor notices in Raspberry Pi firmware build outputs (#103) 2026-10-03 16:42:41 -04:00
tools feat: integrate protected enrollment storage for malak CLI (#67) 2026-09-22 22:30:36 -04:00
web/pilot-reports docs: add public pilot enrollment reports (#74) 2026-09-26 19:42:02 -04:00
flake.lock feat: expose inventoried ARM64 checks to Hydra (#82) 2026-09-27 05:18:47 -04:00
flake.nix Validate independently signed campaign witnesses (#100) 2026-10-02 16:44:02 -04:00
go.mod Remove obsolete configuration and definitions 2026-08-13 19:15:08 -04:00
README.md Record bounded Ace NVMe power-loss and offline tests (#95) 2026-10-01 12:18:26 -04:00

Kaiba Provisioning

Kaiba Provisioning provides Go and Nix reference components and contracts for a fail-closed Raspberry Pi 5 secure-boot provisioning lane. It covers hardware qualification, approval-gated signing, deterministic release and media construction, audited execution, and operator-facing workflows.

Warning

This repository targets a sacrificial development cohort and explicitly reviewed hardware configurations. It is not a general-purpose Raspberry Pi imager or a turnkey production provisioning system. Several operations can affect EEPROM, OTP, boot media, or signing state; use only the configured Nix outputs and reviewed deployment procedures.

What is included

Area Purpose Primary entry points
Hardware qualification Probe a Pi without persisting changes, compare repeated observations, and emit redacted evidence kaiba-provision probe, kaiba-provision qualify
Control and audit Manage transactions, claims, approvals, quarantine, and an independent hash-chained audit log kaiba-provision-control, kaiba-provision-audit, kaiba-provision-authority-bridge
Lane execution Compile the fixed operation sequence, collect explicit acknowledgement, and execute one bound physical action at a time kaiba-provision-lane-workflow, kaiba-provision-lane-operator, kaiba-provision-lane-guard
Signing and releases Gate YubiKey-backed signing behind immutable approvals and verify complete signed releases offline kaiba-provision-signing-gate, kaiba-provision-sign-boot, kaiba-provision-sign-eeprom, kaiba-provision-finalize-release
Media construction Bind a release to an exact storage layout, write it through a configured device-specific package, and verify it independently kaiba-provision-media-device-stager, kaiba-provision-media-device-verifier, kaiba-provision-media-contract
Operator interfaces Observe one authenticated transaction through a read-only loopback UI, or run the separate in-memory simulation kaiba-provision-station, kaiba-provision-station-demo

Generic hardware-facing binaries and kaiba-provision-sign-boot are intentionally unconfigured and fail closed. The exported kaiba-provision-sign-eeprom is different: it is pinned to the local approval-gate socket and EEPROM release inputs, but has neither private-key nor direct hardware authority. Flake constructors bind complete deployments to reviewed inputs: lib.mkRpi5PhysicalLaneGuard, lib.mkDevelopmentYubiKeySigning, and lib.mkRpi5ProductionMedia.

Quick start

The supported development systems are x86_64-linux and aarch64-linux. Build natively for the selected architecture; see the native-build policy for remote builders and exceptions. Nix supplies a Go toolchain compatible with the module's Go 1.24 requirement and the other development tools:

nix develop
scripts/check.sh go ./internal/provisioning/campaignmedia

Build the non-persistent probe package:

nix build .#kaiba-provision

The resulting executable is result/bin/kaiba-provision; its pinned device profile, schemas, and RPIBOOT probe bundle are under result/share/kaiba/.

Run the station simulation

The demo is an in-memory simulation. It binds only to an explicit loopback address and has no hardware, signing, or persistence authority.

nix run .#kaiba-provision-station-demo -- --listen 127.0.0.1:8080

Open http://127.0.0.1:8080 in a browser. The static version deployed to GitHub Pages is built with:

nix build .#kaiba-provision-station-pages

Safety model

  • Plans, approvals, requests, and receipts are canonical and digest-bound.
  • Hardware selectors and execution-host bindings come from the versioned hardware catalog; callers cannot supply an arbitrary block device at runtime.
  • The physical lane guard uses a durable execute-once journal. Ambiguous outcomes enter reconciliation or quarantine and never become blind retries.
  • Signing keys and PINs are runtime-only. The repository contains public trust anchors and signed inputs, not private keys or credentials.
  • The live station observes one configured transaction without submitting control commands or performing enrollment. Its unconfigured foundation keeps a disabled backend. Neither mode falls back to the browser simulation, and the simulation never calls a live backend.
  • Raw device observations remain outside the repository. Only validated, whitelist-redacted qualification evidence belongs under tests/evidence/.

The relay-backed lane is designed around normally-off power and a fixed, reviewed USB topology. A development-only manual-power mode exists, but it does not provide automated fail-off guarantees and is not a production-lane qualification.

Repository layout

Path Contents
cmd/ CLI entry points
docs/ Security model, development runbooks, contract reference, and production roadmap
internal/provisioning/ Control, signing, media, lane, and station implementation packages
nix/ Packages, constructors, NixOS modules, and pinned patches
config/hardware/ Typed, host-bound hardware configurations
profiles/ and policies/ Device-class and development posture inputs
schemas/ Versioned JSON contracts
deploy/ Inert Ubuntu deployment bundles and preflight tooling
releases/ Public, signed release inputs
signers/ Public signer trust anchors and independent review records
tests/ Nix contracts, Go tests, deployment checks, fixtures, and UI tests

Documentation

The current delivery scope fixes the next milestone: apply the agreed hardware security configuration, enroll the verified device in a fleet, and guide the operator through a simple live UI.

The approved SPIFFE/SPIRE follow-on adds standalone, server, and agent identity roles through the existing kaiba-fleet service and shared kaiba-contracts. Provisioning's next work on that track is a separately qualified offline profile: evaluate native Pi monotonic state first, then a TPM add-on if required. The cross-project roadmap and hardware evidence matrix are tracked on the linked review branch. This follow-on preserves the current pilot procedure and does not mark any physical or production gate complete.

The Ace/Mako metadata collector now has scoped live observations. A separate temporary native SPIRE smoke on Ace observed issuance/rotation, restart without the consumed grant, and expiry denial with reported cleanup. It installed no persistent SPIRE service and closed no boot, rollback, time or admission gate.

The read-only reboot observer now supports before/after checks for the separately installed persistent identity pilot. It compares the intended generation, fresh unit-scoped identity, trust bundle, public enrollment status and existing services without rebooting or changing the host. The separate native warm-reboot receipt records generation 10 booting with unchanged identity/enrollment and automatic service startup. That historical observation alone did not qualify cold or offline boot.

The subsequent bounded spare-NVMe physical campaign passed the selected interrupted-write, backup and offline-refusal checks, then returned Ace to its original encrypted pilot disk. No further swaps are planned for LAN acceptance. These results do not qualify original-disk crash durability, autonomous offline operation, secure boot or hardware rollback prevention; full_qualification remains false.

Start with the documentation index. It defines the status language used throughout the guides so that implemented code, software tests, checked evidence, and proposed production controls are not conflated.

Superseded and deferred roadmaps are in the documentation archive. The existing online-verifier candidate is documented separately in the index; its server-required boot policy is not the selected offline fleet behavior.

Operator workflows cover hardware qualification, release signing, media staging, and the live development lane. The station interface and development target access guides make their current non-production boundaries explicit. The signing guides identify the configured flake outputs that still need to be restored or supplied by a reviewed consumer before starting a new live ceremony.

Deployment and evidence guides

The deployment bundles are inert by design: installation does not enable or start their services. Follow the linked preflight and operator steps before crossing a hardware or signing boundary.

Native-build policy

Avoid cross-compilation in Kaiba's Nix projects. Native builds are the default for application packages, kernels, NixOS images, and signing artifacts. Use x86_64-linux builders for x86 packages and aarch64-linux builders for ARM packages, locally and in CI. Apply the same default to downstream projects that consume this flake.

Nix supports cross-compilation, but cross-built derivations differ from native ones and have more limited upstream binary-cache coverage. A small platform override can therefore trigger builds of an entire compiler and userspace closure. Native builds let us reuse the pinned Nixpkgs and Raspberry Pi caches and share our own outputs through Cachix. See the Nix cross-compilation documentation.

  • Keep stdenv.buildPlatform and stdenv.hostPlatform the same for normal package builds. Do not introduce pkgsCross, crossSystem, or an x86 nixpkgs.buildPlatform for an ARM image as a convenience fallback.
  • From an x86 workstation, obtain ARM outputs from a trusted binary cache or configure a native ARM remote builder. Selecting an aarch64-linux flake attribute does not make an x86 machine able to build missing ARM outputs. The x86 signing workstation can verify and package existing ARM artifacts without compiling ARM programs.
  • Run checks that realize ARM programs or images on ARM runners. Keep x86 configuration checks evaluation-only: interpolating a target store path into a test script can pull in its complete cross-built dependency graph, even inside a shell conditional. Exclude such references during Nix evaluation instead.
  • Keep overlays scoped to the packages that need them so a target-specific change does not invalidate cached build tools on another architecture.

The existing exceptions are narrow: the Go evidencefile compile-only ARM portability test, same-CPU glibc-to-musl pkgsStatic tools such as initramfs BusyBox, and evaluation-only cross-platform fixtures that do not realize ARM outputs on x86. QEMU VM execution is emulation, not cross-compilation. These exceptions are not a precedent for cross-building full system images. Any new exception needs an explicit rationale in the change, a dependency/cache-cost assessment, and tests; prefer native coverage whenever it meets the need.

CI, Cachix, and GitHub Pages

The main CI workflow starts formatting and deployment checks alongside native Nix checks for both supported architectures. The x86 job runs the independent Go unit check first, and its later Nix checks reuse that result. Static-mode contract tests share a separate check and compiler cache. The formatting job does not repeat either suite.

Expensive ARM64 VM and image checks run in individually named native ARM jobs, with at most two of the VM/artifact jobs running concurrently. On PRs, CI compares each check's complete Nix derivation against the PR base and builds only changed checks. This includes transitive source, kernel, configuration and toolchain changes; it does not rely on a file-path allowlist. The selection job publishes both derivation identities and its decision for every check. All other ARM checks still run, as do the lightweight x86 verifier checks. The required aggregate accepts a skipped matrix only after a successful comparison reports unchanged inputs. Missing or failed comparison and any failed selected check block it. main pushes and manual runs build every check, and scripts/check.sh full continues to run the complete native suite.

After qualification, HYDRA_MAIN_ENABLED=true routes the ten heavy ARM64 checks on main pushes to Hydra on Ace. The aggregate then requires Hydra statuses for that commit and verifies every planned derivation. HYDRA_CI_ENABLED=true also delegates the ten checks on PR and manual CI runs through immutable jobsets for each run attempt. PRs test the merge commit; manual runs test the dispatched commit. The aggregate verifies the exact evaluation and planned derivations, and unchanged builds can be reused. Each flag can be set to false to restore the corresponding GitHub builders.

Pull requests consume binary caches but do not push to them. Successful main pushes upload to the kaiba-provisioning cache when CACHIX_AUTH_TOKEN grants write access; an explicit write-and-read-back probe makes a broken cache token or cache name fail the workflow. Raspberry Pi dependencies are pulled from nixos-raspberrypi. The dedicated verifier jobs also upload their built kernels on trusted main pushes. A running main workflow is allowed to finish its cache upload when a newer push arrives; obsolete PR runs are still cancelled.

The same workflow publishes the static station simulation from main, reusing the site already realized by the x86_64 Nix checks. Deployment waits for every CI job to pass. Enable Pages once under Settings > Pages by selecting GitHub Actions as the build and deployment source.

Development checks

Run focused tests while editing, then the software checks before pushing:

nix develop
scripts/check.sh go ./internal/provisioning/campaignmedia
scripts/check.sh fast

Run the complete Nix contract suite before merging changes that affect packages, schemas, release inputs, or deployment boundaries:

scripts/check.sh full

The development workflow describes focused, static-mode, contract, full-CI, and release-candidate validation.