# FDS/OS MASTER IMPLEMENTATION PLAN This is the complete design specification, including future components and command interfaces. **M0–M12 software acceptance is complete, including Dasung base integration and the signed local 0.1.0 release.** Tests requiring physical Pi hardware are deferred; software and VM checks remain in scope. See the [implementation ledger](implementation-status.md). A command appearing here is not evidence that it exists in the checkout. Start with the [project overview](../../README.md) and [first-build walkthrough](getting-started.md) for current usage, or the [roadmap](roadmap.md) for implementation status. The 67 numbered sections below retain the full project requirements. ## 1. Project objective Implement an independent Linux distribution for Raspberry Pi 5: **FDS/OS — Felis Data Systems Operating System** FDS/OS targets a retro portable computer with these characteristics: - Raspberry Pi 5 - aarch64 - 12 USB cartridge bays - removable SYSTEM / DATA / PROGRAM / ENVIRONMENT cartridges - Dasung Paperlike 13K grayscale E-Ink display - removable battery - Very fast boot - Very fast shutdown - native s6 init - Void/XBPS package ecosystem - glibc as the system ABI - Rust + static musl for FDS-owned programs wherever practical Core design principle: > Preserve compatibility with modern Linux software while making FDS-owned software as independent of distribution runtime libraries as practical. --- # 2. ABI and libc strategy The system must have two distinct userspace domains. ## 2.1 General userspace Use: ```text glibc ``` Applies to: ```text GNU coreutils GNU binutils bash grep sed gawk findutils util-linux procps-ng Xorg WindowMaker XBPS packages Python GCC LLVM third-party software ``` The system package architecture is: ```text aarch64 ``` Do not use: ```text aarch64-musl ``` --- ## 2.2 FDS control plane FDS-maintained programs default to: ```text Rust + aarch64-unknown-linux-musl + static linking ``` Include at least: ```text fds-stage0 fds fds-cartridged fds-burn fds-inspect fds-eject fds-profile fds-power fds-boottrace ``` Principle: ```text FDS-owned runtime program ↓ static musl where practical ↓ Linux syscall ABI ``` These programs should not depend on the system's glibc version. The following checks must pass: ```bash file /usr/bin/fds ldd /usr/bin/fds ``` Expected output resembles: ```text statically linked not a dynamic executable ``` --- # 3. Do not convert the entire system to musl Do not force the following components to use musl: ```text Xorg WindowMaker general XBPS packages GNU toolchain graphics stack plugin-heavy applications ``` Do not attempt to maintain two complete repositories: ```text glibc repository musl repository ``` The purpose of musl is limited to: > Providing a stable, self-contained static linking environment for FDS-owned control-plane executables. --- # 4. Init architecture FDS/OS must be a native s6 system. Final architecture: ```text Linux ↓ /sbin/init ↓ s6-linux-init ↓ s6-svscan ↓ s6-rc ``` Prohibited: ```text systemd → s6 runit → s6 OpenRC → s6 custom shell PID1 → s6 ``` PID 1 must ultimately belong to the s6 architecture. Use: ```text skalibs execline s6 s6-linux-init s6-rc ``` The s6-rc source database resides at: ```text /etc/s6-rc/source ``` compiled database: ```text /etc/s6-rc/compiled ``` live database: ```text /run/s6-rc ``` **The compiled database must be generated during image construction.** Normal boot must never run: ```text s6-rc-compile ``` --- # 5. Package architecture Use Void Linux only for: ```text upstream package ecosystem XBPS xbps-src glibc packages GNU packages kernel packaging reference third-party packages ``` FDS defines its own: ```text fds-base ``` Do not directly depend on an all-inclusive Void base metapackage. Initial dependencies of `fds-base`: ```text glibc glibc-locales coreutils binutils findutils diffutils grep sed gawk bash tar gzip xz util-linux procps-ng shadow kbd e2fsprogs dosfstools erofs-utils eudev kmod iproute2 iputils pciutils usbutils ca-certificates tzdata xbps skalibs execline s6 s6-linux-init s6-rc fds-base-files fds-cartridged fds-cli fds-dasungd ``` FDS-owned static-musl binaries must not require: ```text musl runtime package ``` musl is already statically linked into each executable. --- # 6. Build host Primary build machine: ```text x86_64 Arch Linux ``` Target: ```text aarch64 Raspberry Pi 5 ``` Normal development **must not require an ARM build host**. The Pi 5 serves as: ```text deployment target hardware validation target performance target ``` It is not the primary build machine. Void's xbps-src supports foreign Linux hosts. Cross-build with: ```bash ./xbps-src -a aarch64 pkg PACKAGE ``` This performs a cross build. Rust FDS components use: ```text aarch64-unknown-linux-musl ``` This Rust target explicitly supports cross-compilation from other hosts. --- # 7. Repository layout Create: ```text fds-os/ │ ├── README.md ├── Makefile ├── Cargo.toml │ ├── docs/ │ ├── architecture.md │ ├── boot.md │ ├── cartridges.md │ ├── services.md │ ├── packages.md │ ├── performance.md │ └── recovery.md │ ├── vendor/ │ └── void-packages/ │ ├── packages/ │ ├── fds-base/ │ ├── fds-base-files/ │ ├── fds-kernel/ │ ├── fds-cartridged/ │ ├── fds-cli/ │ └── fds-eink/ │ ├── rust/ │ ├── fds-common/ │ ├── fds-stage0/ │ ├── fds-cli/ │ ├── fds-cartridged/ │ ├── fds-burn/ │ └── fds-boottrace/ │ ├── s6/ │ └── source/ │ ├── profiles/ │ ├── cli.toml │ ├── development.toml │ ├── windowmaker.toml │ └── recovery.toml │ ├── image/ │ ├── build-rootfs │ ├── build-system-cartridge │ ├── build-boot-volume │ ├── build-initramfs │ └── build-recovery │ ├── tools/ │ ├── bootstrap-host │ ├── build-package │ ├── qemu-test │ ├── burn │ └── configure-pi-eeprom │ ├── tests/ │ ├── unit/ │ ├── integration/ │ └── hardware/ │ └── out/ ``` --- # 8. Managing the Void upstream Do not maintain a long-lived fork with changes throughout void-packages. Use: ```text vendor/void-packages ``` Pin it to a specific commit. Record: ```text VOID_PACKAGES_COMMIT ``` Inject FDS packages through an overlay. Required relationship: ```text same FDS commit + same Void commit = same package input ``` Do not drift with daily changes to the Void rolling repository. --- # 9. Rust workspace Create the workspace: ```toml [workspace] members = [ "rust/fds-common", "rust/fds-stage0", "rust/fds-cli", "rust/fds-cartridged", "rust/fds-burn", "rust/fds-boottrace", ] ``` Default release profile: ```toml [profile.release] opt-level = "z" lto = true codegen-units = 1 panic = "abort" strip = true ``` Benchmark performance-critical binaries with: ```text opt-level = z vs opt-level = 2 vs opt-level = 3 ``` Do not assume that the smallest executable starts fastest. --- # 10. Rust dependency policy Use Clap for every Rust command-line interface, including test helpers. Declare typed commands, options, validation, and generated help; do not hand-parse the argument vector. The workspace shares the version already used by Dasung and pins it in `Cargo.lock`. Kernel command-line files and cartridge metadata remain separate domain formats with their own strict parsers. FDS core programs should avoid: ```text tokio openssl glibc-specific crates libudev-sys large asynchronous runtimes ``` Prefer: ```text std libc crate rustix nix serde toml small synchronous event loop netlink/sysfs directly where reasonable ``` Do not introduce a large runtime merely to listen for a few uevents. --- # 11. Boot architecture The machine's internal NVMe belongs to the: ```text machine firmware layer ``` The SYSTEM cartridge contains the: ```text operating system userspace ``` Boot sequence: ```text Raspberry Pi EEPROM ↓ internal NVMe ↓ kernel ↓ tiny FDS initramfs ↓ fds-stage0 ↓ locate SYSTEM cartridge ↓ mount EROFS ↓ switch_root ↓ s6 ↓ READY ``` Do not make Raspberry Pi firmware search all 12 USB cartridges for a boot device. --- # 12. Internal NVMe layout Recommended layout: ```text GPT p1 FDS_BOOT FAT32 512 MB p2 FDS_RECOVERY EROFS 1–2 GB p3 FDS_INTERNAL ext4 ``` ## FDS_BOOT Contains: ```text Pi firmware kernel DTBs config.txt cmdline.txt FDS initramfs ``` ## FDS_RECOVERY Contains a complete, independent recovery environment. ## FDS_INTERNAL Record: ```text hardware configuration Bay topology boot performance history cached cartridge metadata diagnostics ``` Do not store ordinary user files here. --- # 13. SYSTEM cartridge SYSTEM cartridge: ```text GPT + partition name FDS_SYSTEM + EROFS ``` For example: ```text /dev/??? └── GPT PARTNAME=FDS_SYSTEM ``` Never depend on: ```text /dev/sda /dev/sdb ``` With 12 USB devices, these names are completely unreliable identifiers. root: ```text read-only ``` --- # 14. fds-stage0 `fds-stage0` is a static-musl Rust executable. Target: ```text single executable initramfs control program ``` Avoid including the following wherever practical: ```text shell BusyBox glibc Python udev ``` Its responsibilities are limited to: ```text mount /proc mount /sys mount devtmpfs enumerate block devices find GPT PARTNAME FDS_SYSTEM mount EROFS root move /proc /sys /dev switch_root exec /sbin/init ``` Prefer direct use of: ```text mount(2) openat read ioctl netlink/sysfs pivot_root/switch_root equivalent ``` Do not invoke a shell or external commands. --- # 15. Missing SYSTEM cartridge For the selected Dasung monitor, early userspace must provide the display's control/keepalive and correct video mode before showing this prompt. The base SYSTEM daemon starts later and cannot satisfy missing-SYSTEM display requirements by itself. Implement and verify this handoff as part of Pi boot integration. When no SYSTEM cartridge is present: Display: ```text FELIS DATA SYSTEMS PORTABLE COMPUTER FP-85 BOOT ROM 0.1 SYSTEM MEDIA NOT PRESENT INSERT SYSTEM CARTRIDGE ``` Then: ```text continue listening for block device changes ``` When a SYSTEM cartridge is inserted: **Resume boot immediately.** A reboot must not be required. --- # 16. Multiple SYSTEM cartridges When multiple partitions are found with: ```text FDS_SYSTEM ``` Do not choose one arbitrarily. Display: ```text MULTIPLE SYSTEM CARTRIDGES BAY 02 FDS/OS 0.1 BAY 08 FDS/OS DEVELOPMENT SELECT SYSTEM: ``` Version 1 may enter a recovery/debug console. Add interactive selection in version 2. --- # 17. Kernel strategy Start the first version from Void's Raspberry Pi 5 kernel packaging. Do not maintain a large kernel fork from day one. Create: ```text fds-kernel ``` Maintain only: ```text config delta packaging version policy ``` Build boot-critical drivers into the kernel wherever practical: ```text BCM2712 RP1 PCIe USB host USB hub USB mass storage SCSI disk NVMe devtmpfs EROFS ext4 FAT HID keyboard console DRM/KMS ``` optional: ```text Wi-Fi Bluetooth USB Ethernet USB Serial audio SDR ``` These optional drivers may be modules. --- # 18. Initramfs Target: ```text As close as practical to: /init -> fds-stage0 ``` Test these formats: ```text uncompressed cpio lz4 zstd gzip ``` Benchmark on a real Pi 5. The following observation: ```text zstd image smaller ``` does not establish that boot will be faster. NVMe capacity is inexpensive. Boot milliseconds matter. --- # 19. Raspberry Pi firmware optimisation production EEPROM: Disable unnecessary network installation behavior. In particular: ```text NET_INSTALL_ENABLED=0 NET_INSTALL_AT_POWER_ON=0 ``` Raspberry Pi's official guidance states that USB controller initialization and device enumeration to detect a network-install keyboard add about one second to boot, making these features worth disabling on embedded devices that need fast startup. Provide: ```text tools/configure-pi-eeprom ``` Together with: ```text development.conf production.conf ``` All EEPROM changes must be: ```text documented reversible ``` --- # 20. Filesystem model SYSTEM: ```text EROFS read-only ``` runtime: ```text /run tmpfs /tmp tmpfs /var/tmp tmpfs /var/log tmpfs ``` Do not run root filesystem fsck during boot. Do not write logs to SYSTEM. Do not update SYSTEM in place. --- # 21. User data DATA cartridge: ```text GPT PARTNAME=FDS_DATA filesystem=ext4 ``` After insertion: ```text /data ``` Alternatively: ```text /home/fds ``` Mount this path from the DATA cartridge. When no DATA cartridge is present: ```text ephemeral tmpfs home ``` Therefore: > The machine itself does not own user data. --- # 22. Native s6 service graph Minimum boot bundle: ```text boot ├── runtime-fs ├── hostname ├── console ├── eudevd ├── udev-trigger ├── cartridged ├── getty └── dasungd ``` Explicitly excluded from the boot bundle: ```text network DHCP NTP sshd D-Bus Xorg WindowMaker Bluetooth Wi-Fi ``` --- # 23. Fast boot principle The interactive shell should depend only on: ```text root mounted console ready /dev minimally usable ``` It must not depend on: ```text all USB devices enumerated cartridge catalog scan network clock synchronization GUI ``` The intended sequence is: ```text power on ↓ kernel ↓ root ↓ s6 ↓ FDS> ``` Everything else proceeds in parallel afterward. --- # 24. eudev The normal system uses: ```text eudev ``` However, do not use: ```text udevadm settle ``` as a global boot barrier. Correct sequence: ```text start eudevd ↓ trigger existing devices ↓ continue boot ``` Services that need a particular device must handle their own readiness. --- # 25. cartridged Implement: ```text fds-cartridged ``` static-musl Rust. Responsibilities: ```text listen for USB/block events identify physical hub/port map device → BAY 01..12 inspect storage metadata mount/unmount cartridges activate FDS profiles communicate state to fds CLI ``` Do not depend on: ```text /dev/sdX ordering ``` Use: ```text sysfs USB topology ``` to identify physical bays. --- # 26. Bay topology Configuration: ```text /etc/fds/bays.toml ``` Example: ```toml [front] hub = "usb-topology-id" [front.ports] 1 = 1 2 = 2 3 = 3 4 = 4 5 = 5 6 = 6 [rear] hub = "usb-topology-id" [rear.ports] 1 = 7 2 = 8 3 = 9 4 = 10 5 = 11 6 = 12 ``` Users see: ```text BAY 01 ... BAY 12 ``` They must not see: ```text 1-2.4.1 ``` except in debug mode. --- # 27. Cartridge metadata At the root of a storage cartridge: ```text /FDS/CARTRIDGE.TOML ``` Format: ```toml format = 1 [cartridge] id = "fds.windowmaker" name = "WINDOW SYSTEM" class = "environment" version = "0.1" [media] writable = false [activation] profile = "windowmaker" ``` classes: ```text system data program environment hardware utility ``` --- # 28. Hardware cartridges USB hardware without a filesystem includes: For example: ```text USB Ethernet USB Serial USB Wi-Fi USB Audio ``` Identify it using: ```text VID PID serial USB class physical bay ``` Use these identifiers for recognition. Maintain: ```text /etc/fds/hardware-catalog.toml ``` Do not require every hardware cartridge to contain storage. --- # 29. ENVIRONMENT cartridge v1 Do not place the entire Xorg and WindowMaker runtime on cartridges in version 1. Implement the activation logic first: The SYSTEM image already contains: ```text Xorg WindowMaker fonts theme ``` The ENVIRONMENT cartridge initially provides only: ```text capability key + configuration ``` On insertion: ```text WINDOW SYSTEM ↓ cartridged ↓ fds-profile activate windowmaker ↓ s6-rc change +desktop ``` On removal: ```text desktop down ↓ return CLI ``` Move the GUI runtime onto the cartridge in version 2. --- # 30. WindowMaker stack Display managers are prohibited. Do not use: ```text GDM SDDM LightDM ``` Use: ```text s6 ↓ Xorg ↓ WindowMaker ``` A single-user machine may use: ```text autologin FDS console ``` Activating a GUI profile starts the user's graphical session directly. --- # 31. E-Ink profile Use the grayscale profile as the default WindowMaker appearance on every display. Install it globally and seed new user preferences without replacing existing customization. The native FDS Control panel follows the same style. The monitor controller is base hardware support, independent of the desktop profile. Include the existing Rust `dasungd` for the user's Paperlike 13K grayscale in every SYSTEM image and the native s6 boot bundle. Maintain the confirmed profile, static-musl target, reconnect handling, and SPI-driver protection. See [Dasung integration](dasung.md) for implementation, usage, and the remaining Pi and physical recovery checks. It must not require an ENVIRONMENT cartridge. Create: ```text fds-eink ``` Configuration: ```text no compositor no transparency no fade no desktop animation no smooth window animation outline move outline resize static wallpaper high contrast ``` WindowMaker should prioritize: ```text black white 2–4 grayscale levels ``` Fonts: ```text Terminus other high-contrast bitmap-friendly fonts ``` Do not update the clock every second. --- # 32. PROGRAM cartridge Create software cartridges on a generic Linux workstation using `fds-cartridge`. Each format-2 software recipe identifies a Void source package and its commands. The tool builds with `xbps-src`, installs the package and runtime dependencies into a private tree, and creates GPT images with metadata partition 1 plus one or more EROFS program partitions. Payloads contain `programs/SOFTWARE-ID/` installed roots, including executable files and libraries. Verify tree integrity and architecture on insertion; run new software directly from the read-only EROFS mount without guest extraction. Retain reading of legacy PROGRAM media and catalogue-format-1 archives. Publish commands through `/run/fds/bin` using a managed foreground launcher. Keep `fds run BAY -- SOFTWARE-ID:COMMAND` for managed background execution. Both paths run as the ordinary user and participate in safe eject and unplug cleanup. Resolve names by lowest bay, then lexical selector; expose qualified `bBAY:SOFTWARE-ID:COMMAND` aliases without overriding base system commands. Provide `fds-control`, a native static Rust X11 panel styled to match the default grayscale WindowMaker desktop. It displays bays and software, opens commands in a terminal, requests rescans and safe eject, and reports service errors. --- # 33. FDS CLI `fds` is a static-musl Rust binary. Implement at least: ```text fds info fds bays fds bay 3 fds cartridge 3 fds eject 3 fds profiles fds profile activate windowmaker fds poweroff fds reboot fds boot-profile ``` Output should be concise, fast, and suitable for E-Ink. For example: ```text FDS> fds bays 01 SYSTEM FDS/OS 0.1 ONLINE 02 DATA FELIS ONLINE 03 ENVIRONMENT WINDOW SYSTEM ONLINE 04 PROGRAM TEXT TOOLS ONLINE 05 EMPTY 06 EMPTY 07 ETHERNET EC-1 ONLINE 08 EMPTY 09 EMPTY 10 EMPTY 11 EMPTY 12 EMPTY ``` --- # 34. fds-burn The target media client remains static-musl Rust. New software creation and writing use native Linux workstation `fds-cartridge`: build Void source packages, construct and verify the complete metadata-first GPT image, preview the USB target, then write and verify that full image. Never assemble software partitions piecemeal on the destination drive. See [the workstation workflow](../workstation.md). Responsible for: ```text inspect target partition media write image verify write cartridge metadata ``` Typical usage: ```text fds burn system fds-system-cli.img BAY04 ``` Alternatively: ```text fds burn data BAY04 --label FELIS ``` Destructive operations require explicit confirmation. --- # 35. Package installation model A running SYSTEM cartridge must not use: ```text sudo xbps-install -Su ``` to modify itself in place. Use XBPS for: ```text building images resolving package dependencies resolving libraries for installed PROGRAM package trees development profile ``` SYSTEM update sequence: ```text build new SYSTEM image ↓ write new cartridge ↓ swap cartridge ``` rollback: ```text put old cartridge back ``` --- # 36. Image builder Implement: ```text make rootfs PROFILE=cli ``` Internal workflow: ```text create empty root ↓ xbps-install -r ROOT ... ↓ install FDS packages ↓ apply profile ↓ generate locales ↓ generate caches ↓ compile s6-rc database ↓ remove build junk ↓ make EROFS ``` All of the following must happen at build time: ```text locale generation font cache generation s6-rc compilation ldconfig package configuration ``` Do not perform them during boot. --- # 37. Build interface Eventually support: ```text make bootstrap make packages make rootfs PROFILE=cli make rootfs PROFILE=development make system-card PROFILE=cli make system-card PROFILE=development make initramfs make kernel make boot-volume make recovery make all ``` output: ```text out/ ├── fds-boot.img ├── fds-initramfs.img ├── fds-recovery.img ├── fds-system-cli.img ├── fds-system-development.img └── packages/ ``` --- # 38. Cross compilation policy All FDS Rust binaries must support building on x86_64 with: ```bash cargo build \ --release \ --target aarch64-unknown-linux-musl ``` All FDS XBPS packages should support the following wherever practical: ```bash ./xbps-src -a aarch64 pkg PACKAGE ``` For cross-built packages, xbps-src selects the corresponding cross environment and exposes variables such as `XBPS_CROSS_BASE` and `XBPS_RUST_TARGET`. If a package cannot be cross-built: Fallback priority: ```text 1. Use an official Void aarch64 binary 2. Fix the cross build 3. QEMU native-aarch64 build 4. Build natively on the Pi only as a last resort ``` The normal build pipeline must not require a Pi. --- # 39. Host-side QEMU testing Set up this workflow on the Framework: ```text x86_64 Arch ↓ cross build ↓ aarch64 image ↓ QEMU smoke test ↓ real Pi test ``` Use QEMU to test: ```text s6 graph shell package contents static-musl binaries config parsing filesystem layout shutdown logic cartridge metadata ``` Use a real Pi to test: ```text firmware RP1 USB topology 12 bays E-Ink power behaviour actual boot timing ``` --- # 40. Boot performance targets Define: ```text T0 power applied T1 kernel entered T2 fds-stage0 entered T3 switch_root T4 s6 running T5 CLI accepts input T6 WindowMaker accepts input ``` Target: ```text kernel → CLI: < 1.5 seconds target < 2.0 seconds maximum target s6 → CLI: < 400 ms target cold power → CLI: < 3 seconds stretch goal < 5 seconds hard goal CLI → WindowMaker: < 2 seconds ``` These are targets, not claimed performance results. Actual measurements are required. --- # 41. fds-boottrace Implement: ```text fds-boottrace ``` Record: ```text kernel timestamp stage0 start SYSTEM found root mounted switch_root s6 start console ready desktop ready ``` Output: ```text FDS BOOT PROFILE KERNEL .............. 0.713 s STAGE0 .............. 0.094 s SYSTEM DISCOVERY .... 0.082 s ROOT SWITCH ......... 0.031 s S6 .................. 0.047 s CONSOLE ............. 0.073 s KERNEL → READY ...... 1.040 s ``` Save benchmarks in: ```text docs/performance.md ``` --- # 42. Performance regression rule Any commit that causes: ```text boot regression > 100ms ``` must: ```text explain it or fix it ``` Do not add an artificial delay for a retro splash screen: ```text sleep 1 ``` If FDS can boot in 800 ms, let it boot in 800 ms. --- # 43. Fast boot blacklist Prohibited on the critical path: ```text DHCP DNS NTP sshd Wi-Fi setup Bluetooth setup package updates package verification font cache generation locale generation s6-rc compilation global udev settle GUI login manager Python filesystem fsck for read-only root persistent log flush ``` These activities must run: ```text build time on demand or parallel after READY ``` --- # 44. Networking network bundle: ```text network ``` By default, it is not part of: ```text boot ``` When an Ethernet cartridge is present or the user explicitly requests networking: ```text s6-rc change +network ``` DHCP must never delay the appearance of: ```text FDS> ``` The prompt must remain independent of DHCP. --- # 45. Logging Normal logging location: ```text /run/log ``` tmpfs. Persist only: ```text important errors explicit debug captures boot performance ``` Do not persist tens of megabytes of journal data as a server distribution might. FDS does not use a journal service. This is intentional. --- # 46. Shutdown Provide: ```text fds poweroff ``` Sequence: ```text freeze new cartridge operations ↓ stop GUI ↓ stop program services ↓ stop network ↓ sync DATA filesystems ↓ unmount DATA ↓ stop remaining services ↓ poweroff ``` SYSTEM root: ```text read-only ``` It therefore does not need to wait for journal writes to SYSTEM. --- # 47. Shutdown performance Target: ```text idle poweroff: < 1 second target normal writable DATA: < 1.5 seconds target ``` Do not use: ```text sleep 5 ``` to wait for daemons. s6 must base shutdown on actual process exit and readiness. --- # 48. Eject semantics Users cannot safely unplug writable DATA without ejecting it first. Command: ```text fds eject 02 ``` Behavior: ```text identify dependent programs ↓ stop them ↓ syncfs() ↓ unmount ↓ mark SAFE ``` Output: ```text BAY 02 DATA CARTRIDGE FELIS SAFE TO REMOVE ``` A physical LED or eject mechanism may be integrated later. --- # 49. Recovery environment FDS_RECOVERY must not depend on a SYSTEM cartridge. Include at least: ```text bash GNU coreutils util-linux filesystem tools XBPS tools fds fds-burn fds-inspect ``` FDS tools in recovery remain static-musl binaries. Recovery supports: ```text inspect SYSTEM burn SYSTEM repair DATA diagnose USB bays inspect boot log ``` --- # 50. Security Strictly prohibited: ```text /FDS/autorun.sh ``` executed automatically as root when a cartridge is inserted. Cartridge manifests must be declarative. For example: ```text profile = "windowmaker" ``` Instead of: ```text run = "/media/card/haha.sh" ``` PROGRAM cartridges run with ordinary user privileges by default. --- # 51. Initial boot UI Production boot should suppress Linux kernel console noise. Display: ```text FELIS DATA SYSTEMS PORTABLE COMPUTER FP-85 FDS BOOT ROM 0.1 MEMORY ........ READY SYSTEM ........ FDS/OS 0.1 CARTRIDGE BUS . READY READY. _ ``` However: **Do not delay startup to display these lines one at a time.** A single repaint is sufficient. A retro appearance does not require a four-second delay. --- # 52. Development SYSTEM cartridge The development profile may include a large toolset: ```text gcc clang binutils make cmake meson ninja pkg-config rust cargo git gdb strace perf vim/neovim ``` Provided that these tools: ```text do not start daemons ``` they should not significantly slow boot. Disk usage is not boot latency. --- # 53. Milestone M0 — Host build environment Implement: ```text repo skeleton pin void-packages install/use XBPS host tools xbps-src bootstrap aarch64 cross toolchain Rust musl target ``` Acceptance: On the x86_64 Framework, it must be possible to: ```text build aarch64 package build static-musl hello-world ``` Verify that the executable is static. --- # 54. M1 — FDS base rootfs Implement: ```text fds-base fds-base-files glibc system GNU userland XBPS ``` Acceptance: Generate: ```text out/rootfs-aarch64.tar ``` Verify: ```text glibc present GNU coreutils present GNU binutils present musl runtime package not required BusyBox absent systemd absent runit absent ``` --- # 55. M2 — Native s6 Implement the complete stack: ```text s6-linux-init s6-svscan s6-rc ``` Acceptance: In the booted rootfs: ```text PID1 belongs to s6 architecture ``` Also verify that: ```text s6-rc change ``` can control a test service. --- # 56. M3 — static-musl FDS tooling Implement: ```text fds-common fds CLI skeleton fds-stage0 skeleton ``` All components use: ```text Rust aarch64-unknown-linux-musl static ``` Acceptance: ```text readelf file ldd ``` must demonstrate the absence of a glibc runtime dependency. --- # 57. M4 — Pi boot Implement: ```text FDS_BOOT kernel initramfs fds-stage0 SYSTEM EROFS switch_root s6 ``` Acceptance: Pi: ```text power on ↓ internal NVMe kernel ↓ USB SYSTEM cartridge ↓ FDS> ``` --- # 58. M5 — Fast CLI Optimize: ```text firmware kernel initramfs stage0 s6 graph getty shell startup ``` Acceptance: ```text kernel → FDS> <2 sec ``` Create the first measured version of: ```text docs/performance.md ``` --- # 59. M6 — Cartridge daemon Implement: ```text udev/netlink events hub discovery 12 bay topology metadata mounting eject ``` Acceptance: With 12 USB devices inserted simultaneously: ```text every device maps to the correct physical BAY ``` --- # 60. M7 — DATA cartridge Implement: ```text mount home/data integration safe eject ``` Acceptance: After a sustained write test: ```text fds eject ``` must leave the filesystem clean. --- # 61. M8 — WindowMaker Implement: ```text Xorg WindowMaker E-Ink theme environment profile ``` Acceptance: On insertion of an ENVIRONMENT cartridge: ```text CLI → WindowMaker ``` On cartridge removal or profile deactivation: ```text WindowMaker → CLI ``` No reboot is required. --- # 62. M9 — Cartridge image tools Implement: ```text fds burn fds inspect fds format ``` Acceptance: The machine itself can create: ```text SYSTEM DATA PROGRAM ENVIRONMENT ``` cartridge. --- # 63. M10 — Shutdown optimisation Complete: ```text service stop graph DATA sync DATA unmount fast halt ``` Acceptance: ```text idle shutdown <1s target normal shutdown <1.5s target ``` Filesystem corruption is unacceptable. --- # 64. M11 — 12-device stress testing Hardware: ```text 12 USB devices ``` Test: ```text cold boot hot insert hot remove simultaneous enumeration simultaneous IO reboot shutdown ``` Check: ```text bay mapping stability USB resets kernel errors boot regression power issues ``` --- # 65. M12 — Production hardening Complete: ```text release signing image versioning package pinning reproducible build work recovery documentation production EEPROM config ``` Release: ```text FDS/OS 0.1 ``` --- # 66. Codex implementation rules Codex must follow these rules: 1. Implement only the current milestone at a time. 2. Do not implement later features ahead of schedule. 3. Keep the repository buildable at every milestone. 4. Default every custom FDS binary to static musl. 5. Keep general userspace on glibc. 6. Do not introduce systemd. 7. Do not introduce runit. 8. Do not use BusyBox to implement early userspace. 9. Do not use `sleep` to hide races. 10. Do not let networking block the console. 11. Do not generate caches during boot when they can be generated at build time. 12. Do not depend on `/dev/sdX` ordering. 13. Do not automatically execute arbitrary root scripts from cartridge contents. 14. Explain the necessity of each new dependency. 15. Support every boot optimization with benchmarks. 16. Support x86_64 to aarch64 cross builds for all FDS packages wherever practical. 17. Use native Pi builds only as a documented fallback. 18. Keep documentation synchronized with the implementation. --- # 67. First Codex task Implement only M0 now. Specific requirements: ```text A. Create the repository skeleton. B. Add vendor/void-packages, pinned to a specific commit. C. Write tools/bootstrap-host, supporting Arch Linux x86_64. D. Verify the static XBPS host tools. E. bootstrap xbps-src build environment. F. Verify: ./xbps-src -a aarch64 pkg G. Initialize the Rust workspace. H. Configure: aarch64-unknown-linux-musl I. Build: rust/fds-smoketest J. Verify: - architecture = AArch64 - static executable - no glibc dependency K. Provide: make bootstrap make smoke-test L. Create docs/build-host.md, accurately documenting all dependencies and commands. M. Add automated checks that exit nonzero on failure. N. Do not begin s6, kernel, GUI, or cartridge daemon implementation. On completion, report: 1. changed files 2. exact build commands 3. smoke-test output 4. known limitations 5. next milestone proposal ``` The original M0 gate above has been satisfied. The user explicitly authorized M1; its workflow and evidence are in [M1 rootfs](rootfs.md) and [M1 validation](m1-validation.md). The user subsequently authorized M2; see [Native init](init.md). The user subsequently authorized implementation through M12, with physical Pi testing deferred. The implementation ledger above records the current evidence and remaining work.