update docs

This commit is contained in:
2026-09-22 13:23:34 +08:00
parent 99bc3d15c5
commit 8a4788fca8
126 changed files with 7198 additions and 2425 deletions
+410
View File
@@ -0,0 +1,410 @@
# M12 production preparation: software evidence
Development reference and historical context. For current operating instructions, use the [user manual](../README.md). Acceptance applies only to the source and artifacts identified in each record.
[Implementation ledger](implementation-status.md) · [EEPROM](eeprom.md) · [Recovery](recovery.md)
M12 **software acceptance is complete**, including the signed local FDS/OS
0.1.0 release. Physical Pi tests, EEPROM application, display behavior and
storage power-loss behavior are deferred. Earlier checkpoints below preserve
the development history; the final acceptance identifies the released bytes.
## Final local release acceptance
Completed on 2026-09-21. The local release is
[`out/fds-os-0.1.0/README.md`](../../out/fds-os-0.1.0/README.md).
It contains 38 signed manifest entries, versioned images and packages,
profile inventories, source and complete build-input archives, guides and
reproducibility evidence. It has not been published or installed on hardware.
- Frozen source SHA-256: `8947e57552f5db834fbb6bcce6d79623bf0605072b42b4ccf04d1e4fe7bb9f29`.
- Input-lock SHA-256: `6fa630446f91a7a0a2b85324b7a258ea6351978ff8f8a4cf9970f73f8d24fe2c`.
- Manifest SHA-256: `0c223b4996258efbe33db780ab0d05019c35e36be291499a9548c27371c451a4`.
- Signing public-key fingerprint (decoded 32 bytes): `d5c95e8c1a079ed523b859c0ee78b8e849bbf387b3719dcdae1661ae4f7eb267`.
- Signing public-key text-file SHA-256: `30d832747e95dca2c7ad3f5a117d2069a2d94e6d3eee2dd385caea688159e677`.
Two newly restored trees, `out/rebuild-m12-v5-a` and `out/rebuild-m12-v5-b`,
ran `tools/in-frozen-build make all` sequentially with no network access.
Both started without FDS build outputs. The complete 27-artifact comparison
passed, covering all five installable images, three rootfs tars, four initramfs
formats, kernel/DTB, four EEPROM preview files and all nine FDS packages.
Logs: `out/logs/m12-offline-v5-{a,b}.log`; comparison:
[`out/m12-reproducibility-v5.json`](../../out/m12-reproducibility-v5.json).
Every one of those artifacts also matches the normal build used by the complete
[Clap runtime acceptance](clap-validation.md). The identity proof is
[`out/m12-tested-to-release-v5.json`](../../out/m12-tested-to-release-v5.json),
and `out/m12-clap-acceptance-index.json` records the 21 acceptance logs and hashes.
This establishes exact-byte coverage of the released images without repeating
the same VM suite against identical images. Physical tests remain deferred.
The first restored tree additionally passed `make smoke-test`, `make check`
and `make signing-test` inside the isolated wrapper. A second full comparison
after those checks still passed. Logs are `out/logs/m12-v5-{smoke-test,check,signing-test}.log`;
the comparison is `out/m12-reproducibility-v5-after-checks.json`.
The optional direct host `qemu-aarch64` smoke run is reported as skipped when
absent; the separate ARM signing and OS runtime suites execute real ARM code.
The assembler verified source identity and rootfs build inputs before signing.
Verification against the separately retained public key checked every manifest
entry. OpenSSL independently verified the actual release signature. Both actual
release archives were extracted with mode-preserving flags; the complete input
lock and standalone source identity passed. Evidence:
[`out/m12-release-acceptance-v5.json`](../../out/m12-release-acceptance-v5.json),
with logs in `out/logs/m12-v5-*.log` and extracted files under
`out/m12-release-acceptance.mzkfsvev`.
The private signing key is outside the checkout, snapshot and release output.
Its corresponding trusted public key is
`/home/felis/.local/share/fds/signing-0.1.0-20260921/release-01.pub`.
On this workstation, from the checkout:
```sh
out/rebuild-m12-v5-a/target/x86_64-unknown-linux-gnu/release/fds-release verify \
out/fds-os-0.1.0 \
--key /home/felis/.local/share/fds/signing-0.1.0-20260921/release-01.pub
```
| Image | Bytes | SHA-256 |
| --- | ---: | --- |
| `fds-boot-0.1.0.img` | 536,870,912 | `1466bbac3f782115bce78b48376ab90a7364416379700e944c529280b7253703` |
| `fds-internal-0.1.0.img` | 1,881,145,344 | `5197cd5edbbb9f689f12d233371cf0079ac50b36a4d3ca3cfe4c38cd320fc468` |
| `fds-recovery-0.1.0.img` | 491,593,728 | `4b0c9dad4336535d5b90c35cacd1500528836008be3384bdcd22b078b8a25f5b` |
| `fds-system-cli-0.1.0.img` | 493,879,296 | `03233a1118471cd8550ef2813fa99d371ef00b23edde5a3ade161d371e36c177` |
| `fds-system-development-0.1.0.img` | 1,989,148,672 | `9110f966212b59186e60ae7f88b6500fb0909478e6cbbb17735eebc969092c1e` |
| `kernel_2712-0.1.0.img` | 26,751,488 | `9e811bc55aa8e0b6a7233c6c84978244f2affee7bc808f3913dcd2fab3cd8013` |
The [67-section ledger](implementation-status.md) records implementation,
evidence and physical deferrals for the entire master plan. Main-checkout status
documentation was updated after acceptance; the frozen trees and signed archives
retain the exact checkpoint documentation used for their source identity.
Consequently, their historical “verification in progress” wording predates this
record. Runtime code, configuration and image bytes were not changed by this
final documentation update. Use the preserved inputs to reproduce the exact
release images. The signed `RELEASE-NOTES.md` also corrects an archived guide's
fingerprint terminology: the tool hashes decoded key bytes, not the text file.
The standalone release verification guide includes that correction. The original
assembler output is retained at `out/fds-os-0.1.0-checkpoint/`; final packaging
adds these signed release notes and updated standalone guidance without changing
any image, package, source archive or input archive.
## EEPROM configuration and rollback
`make eeprom-test` passed against the actual pinned 2 MiB Pi 5 EEPROM image.
Evidence is in `out/m12-eeprom.5b97ph36/`; the public run is recorded in
`out/logs/m12-eeprom-final.log`. `make check` also passed afterward.
The tests cover both production and development settings, exact configuration
readback, unchanged firmware payloads, identical repeated outputs, preservation
of unrelated conditional settings, exact rollback configuration, existing-output
refusal, and rejection of invalid firmware/device/config inputs. The tool never
invokes an update/apply command. The default firmware is a preparation input,
not a backup of the user's Pi.
See the [EEPROM guide](eeprom.md) for the source commit, hashes, complete profile
settings, generated files, and the deferred physical application/rollback steps.
## Independent recovery
The recovery implementation has its own rootfs profile, EROFS builder, local
maintenance console, read-only cartridge defaults, and explicit DATA check/repair
commands. It retains native s6 and the base Dasung controller. No additional Rust
crate or target package was needed: repair uses the existing e2fsprogs and kernel
loop support while preserving the physical disk reservation.
`make recovery`, `make rootfs-test` and the complete `make recovery-test` passed.
The final runtime evidence is `out/m12-recovery.rlvmytrz/`, linked from
`out/m12-recovery-latest`. Logs are
`out/logs/m12-recovery-identity-build.log`,
`out/logs/m12-recovery-identity-rootfs.log`, and
`out/logs/m12-recovery-final-acceptance.log`.
| Artifact | Verified result |
| --- | --- |
| Recovery rootfs | `out/rootfs-build.D5TCGs/rootfs-aarch64.tar`, 499,609,600 bytes; 180 packages and 1,394 AArch64 ELF files |
| Rootfs SHA-256 | `0611c0f8f24d04a733401063f9f52c7bc08f6389cb707601df083390fe063ae1` |
| Recovery EROFS | `out/recovery-build.9ukiaknh/recovery.erofs`, 490,598,400 bytes |
| EROFS SHA-256 | `7a219ca803325e4d60c3acf1b4cffe8717e7a7ec4e65377f48c900c131910bcd` |
The tests use the packaged recovery image and Pi kernel. The only rootfs change
for virtual cartridge tests is the observed virtual bay map; no replacement
runtime binaries or simulated success responses are inserted.
Verified cases:
- Two independent EROFS constructions from the same tar produce identical bytes.
- Recovery boots with SYSTEM absent and with an unreadable SYSTEM. Its root is
read-only, PID 1 is native s6, the maintenance console is root, and Dasung is
supervised without needing the physical monitor.
- DATA remains read-only; ENVIRONMENT and Ethernet insertion do not automatically
start the desktop or DHCP. An ordinary user cannot invoke recovery repairs.
- A real ext4 inode reference-count error is detected without changing DATA.
Wrong confirmations and busy mounts are rejected. Confirmed repair fixes the
error, flushes/invalidate caches, and passes another read-only check. The
original test file survives, and a separate host e2fsck also passes.
- Failed checks survive daemon restart as quarantine. Confirmation from a previous
insertion is rejected. Clean checks preserve every byte of the DATA image.
- Recovery writes a complete known-good SYSTEM image to a disposable virtual USB
device, verifies it by readback, inspects it, and excludes it from DATA repair.
A separate VM boots that written image to the ordinary UID 1000 FDS console.
The source SYSTEM is the previously verified M10 CLI image; this proves the
recovery replacement workflow, not a new release-signing claim.
- Killing the daemon during a real checker kills the child and preserves DATA.
An explicit retry succeeds. Twenty partition-table rereads and a subsequent
service restart preserve the unmounted SAFE state; temporary loops are gone.
- The diagnostic collector completes all 17 commands and verifies its output
checksums. Successful cases power off through native s6.
The interruption/rescan case also passed three consecutive focused runs in
`out/m12-recovery-rescan.rlr9u2aq/`, `out/m12-recovery-rescan.cvzye8z0/`, and
`out/m12-recovery-rescan.wj0jlfkq/`. This caught and fixed a real bug: temporary
partition disappearance during a reread previously cleared the eject record.
The daemon now retains that record while the same physical disk insertion is
present; a new insertion still invalidates it.
The normal CLI rebuild and regression checks also passed: `make rootfs PROFILE=cli`,
`make rootfs-test`, `make system-card PROFILE=cli`, `make init-test`,
`make console-test`, `make data-test`, `make tooling-test`, and `make check`.
Evidence is in `out/logs/m12-cli-*.log`, `out/logs/m12-tooling.log`, and
`out/logs/m12-check.log`. The normal-image root check emitted
`FDS_M12_RECOVERY_BOUNDARY`, confirming that recovery repair remains unavailable
outside the recovery profile, even to root.
That CLI rootfs is `out/rootfs-build.HqBtpz/rootfs-aarch64.tar`
(SHA-256 `af132511a6784eced9e52e971f010df6717e3caed751d14017ca9e924196e8b3`),
and its SYSTEM image is `out/system-build.psymtwbr/system.img`
(SHA-256 `5bc69f31d74a4c07596b696a72138cd24327ff4bc467f428717a56ce44c6bd27`).
Native init passed in `out/m2-vm.GyJsNg`, console checks in `out/m5-vm.h5ygfe1j`,
and DATA checks in `out/m7-vm.nt_tsdfs`. Console readiness was 1,853.221941 ms,
32.182153 ms above the earlier M10 CLI baseline of 1,821.039788 ms; this is below
the 100 ms investigation threshold. These artifacts precede the internal-settings
implementation below. No physical timing or media-durability claim follows from
these VM measurements.
## Internal NVMe and machine settings
Implementation and current usage are described in [Internal storage](internal-storage.md).
`make internal-test` passed in `out/m12-internal.vqr74fcs/`, linked from
`out/m12-internal-latest`. The public log is
`out/logs/m12-internal-final-acceptance.log`; rootfs evidence is in
`out/logs/m12-machine-final-rootfs.log`. The test uses packaged CLI and recovery
images, the actual Pi kernel, and disposable virtual NVMe devices. No runtime
binary or configuration file is injected into the booted OS.
Verified cases:
- Two complete 1,881,145,344-byte internal GPT images are byte-identical, with
independent FAT, EROFS, ext4, GPT and payload checks. Their SHA-256 is
`0d464ba18b0988a79a75ea54b6d81c5fa01acfea567b3d0a79f9faf2a428dfc6`.
- `make internal-image` publishes the same hash at `out/internal-build.xxzvw_3l/internal.img`,
linked from `out/fds-internal.img`; its log is `out/logs/m12-internal-published.log`.
- The actual loader selects one correctly laid-out NVMe and leaves every byte
unchanged during read-only loading. The private mount is gone afterward.
- Root recovery installs validated settings atomically, preserves previous bytes,
flushes/unmounts storage, and keeps this boot's active mapping unchanged even
across a cartridge-daemon restart.
- A separate reboot loads the saved machine name, bay map and hardware catalog.
The real virtual keyboard is recognized in its assigned bay with its saved name.
- Boot diagnostics survive reboot and are retrieved byte-for-byte. Non-root
writes, path traversal, duplicates, existing retrieval outputs and oversized
diagnostic files are rejected. Independent post-write e2fsck passes.
- BOOT and RECOVERY payload hashes remain unchanged after settings/diagnostic
writes. Ordinary user data is not placed on internal storage.
- A complete USB lookalike cannot supply settings. Two eligible NVMes boot the
normal UID 1000 console with explicit ambiguity/fallback status.
- Invalid JSON, writable settings, symlinks and unclean ext4 leave the disk bytes
unchanged, record an error and retain a usable recovery console with image
defaults. No automatic filesystem repair or journal replay is performed.
An earlier test used unsupported PCI hotplug on QEMU's root PCI bus; the final
ambiguity fixture instead boots the ordinary SYSTEM with both NVMes present.
Earlier partial runs remain available, but are not counted as acceptance.
The settings trust checks exposed omitted `/` metadata in the rootfs archive.
EROFS had synthesized a host-owned mode-0777 root directory. The archive now
records root:root mode 0755 explicitly, both OS builders reject old archives,
and rootfs/VM tests check the result. `out/m12-root-metadata-repro-audit.json`
compares two otherwise-identical recovery builds; only the new `.` entry differs.
The build-specific auxiliary linker cache has been removed.
The tested recovery rootfs is `out/rootfs-build.P4WWwz/rootfs-aarch64.tar`
(SHA-256 `9d0e54533399da79616853e8233d2abb8efbc6b2d51772bd86f2669134c957b4`),
and its EROFS is `out/recovery-build.1ukd0xgl/recovery.erofs`
(SHA-256 `1c1930c86960f8e208408f4a0c244e4e5ae876263bab234b25307d4154ef3f91`).
The updated CLI rootfs is `out/rootfs-build.BP1Ylv/rootfs-aarch64.tar`
(SHA-256 `58eb153416972dca1c29a87d7b356638b8b1c08df56231470c18ee50fbad2d29`),
and SYSTEM is `out/system-build.o9d6affu/system.img`
(SHA-256 `cb544eb25f18b6572f78fc245818bd2cbfa18423b44d57b5af251069f2e6498e`).
The complete recovery regression also passed against these updated artifacts in
`out/m12-recovery.idrwxslu/` (`out/logs/m12-machine-recovery-regression.log`). It
repeats the missing/damaged SYSTEM, actual DATA repair, interrupted-checker,
partition-reread, full SYSTEM burn/readback, diagnostics and replacement-boot
checks described above. This time the source SYSTEM is the updated CLI image
listed here, including the settings loader and corrected filesystem-root metadata.
The updated normal CLI has already passed rootfs, SYSTEM construction, native
init, console independence and DATA regression checks. Logs are
`out/logs/m12-machine-cli-*.log`; native init evidence is `out/m2-vm.5FHPFv`,
console evidence is `out/m5-vm.ifuo9ei6`, and DATA evidence is
`out/m7-vm.6xcpnj97`. The measured VM console time was 1,865.948488 ms,
44.908700 ms above the earlier M10 CLI baseline, below the 100 ms review threshold.
## Build-host regression
After the host image-tool changes, `make bootstrap`, `make smoke-test`, and
`make check` all passed. Logs are `out/logs/m12-foundation-bootstrap.log`,
`out/logs/m12-foundation-smoke.log`, and `out/logs/m12-foundation-check.log`.
The smoke test cross-built and inspected GNU hello as aarch64 glibc and the Rust
smoketest as static aarch64-musl. Optional direct ARM execution was skipped because
the workstation lacks `qemu-aarch64`; the separate ARM VM/runtime suites above
still executed the actual OS tools. Upstream tracked Void files remain unchanged.
## Development profile
The native GNU C/C++ and Rust toolchains, build systems, Git, GDB, strace and Vim
are selected and documented in [Development](development.md). The expanded image
passed `make development-test` in `out/m12-development.r6mrgm0b/`.
The rootfs is `out/rootfs-build.p8DKvd/rootfs-aarch64.tar` (2,012,047,360 bytes;
245 packages and 1,731 AArch64 ELF files), and its SYSTEM is
`out/system-build.jnubdw1v/system.img`. Logs are
`out/logs/m12-development-clock-{build,rootfs,system,acceptance}.log`.
The ordinary UID 1000 console compiled and ran C/C++ with CMake/Ninja, C with
Meson/Ninja and Make, and a dependency-free Rust project with offline Cargo.
GDB stopped at `main`, reported a backtrace and continued; strace captured the
program's write; a local Git commit and headless Vim edit also succeeded.
SYSTEM remained read-only and unchanged, optional services stayed off, and
native shutdown completed. Desktop/network acceptance then passed in
`out/m8-vm.4c6_jj4h/`, and the complete shutdown/DATA error suite passed in
`out/m10-vm.q_ewzl4g/`; see `out/logs/m12-development-{desktop,power}.log`.
This test found a real startup issue: the VM's wall clock started at Unix second
1 while CMake's immutable inputs had the source timestamp 1,789,909,701.
Ninja consequently tried to regenerate its manifest repeatedly. Native startup
now advances only an older clock to the fixed image timestamp, without a time
server or waiting. The compiler acceptance verifies that path; a separate
`make clock-test` passed in `out/m12-clock.l39g3uhx/`, preserving a guest clock
set to 2030 before the actual startup code runs. Non-root clock changes are
rejected and boot measurements still use `Linux CLOCK_BOOTTIME`.
`out/logs/m12-clock-final.log` records that check. This is not physical RTC
validation and the floor does not supply the accurate current date.
The first corrected-clock run also exposed a test transport mistake: interactive
Bash consumed the literal tab in a generated Makefile. Test files now cross the
serial console as base64, preserving their exact bytes.
## Release signature tool
`make signing-test` passed in `out/m12-signing.md4r2kbo/`, recorded in
`out/logs/m12-signing-public.log`. It builds both x86_64 and static-musl ARM
executables, checks the RFC 8032 Ed25519 vector, and independently signs/verifies
the exact manifest domain with OpenSSL. Host and ARM signatures match OpenSSL's
bytes. Wrong and weak keys, altered signature/manifest/artifact bytes, wrong
sizes, missing files, symlinks, FIFOs, path traversal, duplicates, unknown fields,
oversized manifests, insecure private-key permissions and output overwrites are
rejected. These are disposable test keys, not a signed OS release.
The verifier is included in the base CLI package for all profiles. Its guide and
dependency rationale are in [Release signatures](releases.md), and the build now
collects license notices for all 68 resolved third-party ARM workspace crates,
including the vendored libusb notices. `make bootstrap`, `make smoke-test` and
`make check` passed after adding the signing dependencies, with logs under
`out/logs/m12-signing-{bootstrap,smoke,check}.log`. The later package-notice and
offline-build interface changes are being rebuilt and checked separately.
## Repeated console timing checkpoint
After the clock floor, packaged signer/notices and deterministic package epoch
changes, one console run measured 2,103.545197 ms. This exceeded the 100 ms
investigation threshold against the earlier single M10 sample. Five alternating
pairs then compared the preceding internal-settings CLI image with the updated
CLI image, using the same kernel and initramfs for every run. Evidence and image
hashes are in `out/m12-console-pairs.yuoz7unr/comparison.json`; all ten serial logs
are retained beside it. The execution log is `out/logs/m12-console-pairs.log`.
| Image | Five console-ready samples, ms | Median, ms |
| --- | --- | ---: |
| Before | 1884.195231, 1909.087577, 1960.273690, 2055.400846, 1908.411769 | 1909.087577 |
| Updated | 1938.522887, 1913.188789, 1935.906888, 1958.748315, 1943.363749 | 1938.522887 |
The median increased by 29.435310 ms, below the threshold. The initial updated
sample was outside the repeated updated range. This is workstation/QEMU evidence
including scheduling noise, not physical Pi performance; no artificial delay or
boot barrier was added. This checkpoint precedes Rust source-path normalization.
## Complete build and release interface checkpoint
`make packages` now uses the same complete base-package builder as rootfs
assembly. It passed in `out/logs/m12-packages-public.log`, including a fresh
cross-build of the Pi kernel. The preceding `make bootstrap`, `make smoke-test`
and `make check` passed in `out/logs/m12-offline-foundation-{bootstrap,smoke,check}.log`.
`make all` now sequences the complete image set. Its first complete run passed
in `out/logs/m12-all-first.log`. Subsequent rootfs and foundation checks passed,
as did the complete boot matrix (`out/m4-vm.kn9Hku/`), native development tools
(`out/m12-development.hpk59nul/`) and internal-storage acceptance
(`out/m12-internal.8z6nffpa/`). The corresponding logs are
`out/logs/m12-all-{recovery-rootfs,check,boot,development,internal}.log`.
These results precede source-path normalization in Rust release builds.
The frozen-input, restore, network-isolation, comparison and signed-assembly
interfaces are documented in [Offline rebuilds](reproducible-builds.md).
`make release-contract-test` passed in `out/m12-release-contracts.yg33ry5r/`
(`out/logs/m12-release-contracts.log`). Its small fixtures verify rejection of
changed input bytes/modes/paths/symlinks, invalid lock metadata, differing or
missing artifacts, comparison of one tree with itself, changed input locks,
existing output reports/directories and private-key symlinks. Snapshot creation
also refuses an active base-package build. These contract checks do not stand
in for two actual complete offline builds.
A settings-size regression test also passed: individually valid TOML comments
can expand during JSON escaping. The machine configuration now rejects an
encoded bundle exceeding its loader limit before installation writes any data.
All 15 shared-contract tests passed in `out/logs/m12-machine-encoding-tests.log`.
## Earlier release checkpoints
The user-requested [Clap migration](clap-validation.md) is complete: 48 Rust
tests, ARM CLI/alias execution, the complete image build, all runtime suites,
signing and Dasung checks, and repeated startup measurements passed. All Rust
command-line handling now uses the shared locked Clap dependency. The v4
offline candidate was deliberately stopped during kernel compilation to include
this change; its second tree was not built and it has no accepted comparison.
The accepted v5 pair uses this updated source checkpoint.
`make release-contract-test` also passed with the actual archive writer and the
documented extraction flags in `out/logs/m12-release-contracts-v4.log`: repeated
archives match, and extraction preserves ordinary executable modes, sticky
directories, symlinks and the complete frozen lock. Default tar extraction had
stripped those permissions in a small diagnostic fixture. The v3 candidate was
stopped during kernel compilation before acceptance so that the corrected
archive instructions and the completed user-guide audit could be frozen
together. It is not a failed runtime test or an accepted release build.
The first real snapshot exposed two restore defects, now corrected: shallow
Void bundles need their Git boundary file, and xbps-src's cross repository
configuration retains only custom filenames containing `local`. The dependency
that revealed the second issue was already captured; no network fallback was
added. `out/logs/m12-offline-v1-a.log` and `out/logs/m12-offline-v2-a.log` retain
the failed attempts. The resumed v2 tree is a diagnostic probe, not a clean
release acceptance run.
The fresh offline v2 compilation matched the normal build byte-for-byte for all
seven core FDS binaries, Dasung, the Pi kernel/DTB, and the kernel/Dasung packages.
Records are `out/m12-offline-v2-rust-comparison.json` and
`out/m12-offline-v2-native-comparison.json`. Personal absolute build paths are
absent from all eight release executables after path normalization; see
`out/m12-remap-binaries.json`. Bootstrap, smoke-test and checks passed in
`out/logs/m12-remap-{bootstrap,smoke,check}.log`.
The documented larger-disk GPT relocation was checked on a disposable regular
file in `out/m12-internal-relocation.j29f1rx6/`. After extending the image by
512 MiB and relocating its backup GPT, independent partition-table verification
passed and every partition's start, size and payload SHA-256 remained unchanged.
This verifies the image operation only; the physical write/readback and Pi boot
procedure in [Internal storage](internal-storage.md) remains deferred.
Those checkpoints preceded the final clean v5 pair. The acceptance record at
the top of this page closes the software release work with actual complete
builds, comparisons, signing, archive verification and the master-plan audit.