2
Developer Master Plan
Felis edited this page 2026-09-27 00:29:40 +08:00
This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Table of Contents
include_toc
include_toc
true

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. A command appearing here is not evidence that it exists in the checkout. Start with the project overview and first-build walkthrough for current usage, or the roadmap 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:

glibc

Applies to:

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:

aarch64

Do not use:

aarch64-musl

2.2 FDS control plane

FDS-maintained programs default to:

Rust
+
aarch64-unknown-linux-musl
+
static linking

Include at least:

fds-stage0
fds
fds-cartridged
fds-burn
fds-inspect
fds-eject
fds-profile
fds-power
fds-boottrace

Principle:

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:

file /usr/bin/fds
ldd /usr/bin/fds

Expected output resembles:

statically linked
not a dynamic executable

3. Do not convert the entire system to musl

Do not force the following components to use musl:

Xorg
WindowMaker
general XBPS packages
GNU toolchain
graphics stack
plugin-heavy applications

Do not attempt to maintain two complete repositories:

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:

Linux
  ↓
/sbin/init
  ↓
s6-linux-init
  ↓
s6-svscan
  ↓
s6-rc

Prohibited:

systemd → s6
runit → s6
OpenRC → s6
custom shell PID1 → s6

PID 1 must ultimately belong to the s6 architecture.

Use:

skalibs
execline
s6
s6-linux-init
s6-rc

The s6-rc source database resides at:

/etc/s6-rc/source

compiled database:

/etc/s6-rc/compiled

live database:

/run/s6-rc

The compiled database must be generated during image construction.

Normal boot must never run:

s6-rc-compile

5. Package architecture

Use Void Linux only for:

upstream package ecosystem
XBPS
xbps-src
glibc packages
GNU packages
kernel packaging reference
third-party packages

FDS defines its own:

fds-base

Do not directly depend on an all-inclusive Void base metapackage.

Initial dependencies of fds-base:

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:

musl runtime package

musl is already statically linked into each executable.


6. Build host

Primary build machine:

x86_64 Arch Linux

Target:

aarch64 Raspberry Pi 5

Normal development must not require an ARM build host.

The Pi 5 serves as:

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:

./xbps-src -a aarch64 pkg PACKAGE

This performs a cross build.

Rust FDS components use:

aarch64-unknown-linux-musl

This Rust target explicitly supports cross-compilation from other hosts.


7. Repository layout

Create:

fds-os/
│
├── README.md
├── Makefile
├── Cargo.toml
│
├── fds-os.wiki/                    # Authoritative Gitea wiki submodule
│   ├── Home.md
│   ├── Overview.md
│   ├── Hardware-Setup.md
│   ├── Developer-Guide.md
│   ├── Developer-Architecture.md
│   ├── Developer-Boot.md
│   └── Developer-Performance.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:

vendor/void-packages

Pin it to a specific commit.

Record:

VOID_PACKAGES_COMMIT

Keep this submodule clean. Prepare a writable checkout at .host/void-packages from the same pinned commit. Inject FDS packages from packages/ into that build checkout; keep generated sources, caches and build containers outside the submodule.

Required relationship:

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:

[workspace]
members = [
    "rust/fds-common",
    "rust/fds-stage0",
    "rust/fds-cli",
    "rust/fds-cartridged",
    "rust/fds-burn",
    "rust/fds-boottrace",
]

Default release profile:

[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
strip = true

Benchmark performance-critical binaries with:

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:

tokio
openssl
glibc-specific crates
libudev-sys
large asynchronous runtimes

Prefer:

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 native microSD (32 GB target) belongs to the:

machine firmware layer

The SYSTEM cartridge contains the:

operating system userspace

Boot sequence:

Raspberry Pi EEPROM
        ↓
native microSD
        ↓
kernel
        ↓
tiny FDS initramfs
        ↓
fds-stage0
        ↓
locate SYSTEM cartridge
        ↓
mount EROFS
        ↓
switch_root
        ↓
s6
        ↓
READY

Production uses SD-only EEPROM boot order. Development can fall back to a Raspberry Pi OS USB drive; the optional maintenance profile tries USB first so the FDS SD can remain inserted for repair. This deliberately adds firmware USB scanning only when requested. SYSTEM remains a separate USB cartridge.

This SD arrangement supersedes the original NVMe requirement at the user's request on 2026-09-26. Legacy PCIe NVMe settings remain supported, but native SD and NVMe must not both provide complete internal layouts during one boot. A USB reader is a flashing path, not accepted internal settings storage.


12. Internal storage layout

Recommended layout:

GPT

p1 FDS_BOOT
   FAT32
   512 MB

p2 FDS_RECOVERY
   EROFS
   1–2 GB

p3 FDS_INTERNAL
   ext4

FDS_BOOT

Contains:

Pi firmware
kernel
DTBs
config.txt
cmdline.txt
FDS initramfs

FDS_RECOVERY

Contains a complete, independent recovery environment.

FDS_INTERNAL

Record:

hardware configuration
Bay topology
boot performance history
cached cartridge metadata
diagnostics

Do not store ordinary user files here.


13. SYSTEM cartridge

SYSTEM cartridge:

GPT
+
partition name FDS_SYSTEM
+
EROFS

For example:

/dev/???
└── GPT PARTNAME=FDS_SYSTEM

Never depend on:

/dev/sda
/dev/sdb

With 12 USB devices, these names are completely unreliable identifiers.

root:

read-only

14. fds-stage0

fds-stage0 is a static-musl Rust executable.

Target:

single executable initramfs control program

Avoid including the following wherever practical:

shell
BusyBox
glibc
Python
udev

Its responsibilities are limited to:

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:

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:

FELIS DATA SYSTEMS
PORTABLE COMPUTER FP-85

BOOT ROM 0.1

SYSTEM MEDIA NOT PRESENT

INSERT SYSTEM CARTRIDGE

Then:

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:

FDS_SYSTEM

Do not choose one arbitrarily.

Display:

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:

fds-kernel

Maintain only:

config delta
packaging
version policy

Build boot-critical drivers into the kernel wherever practical:

BCM2712
RP1
PCIe
USB host
USB hub
USB mass storage
SCSI disk
MMC / native SD
NVMe (legacy alternative)
devtmpfs
EROFS
ext4
FAT
HID keyboard
console
DRM/KMS

optional:

Wi-Fi
Bluetooth
USB Ethernet
USB Serial
audio
SDR

These optional drivers may be modules.


18. Initramfs

Target:

As close as practical to:

/init -> fds-stage0

Test these formats:

uncompressed cpio
lz4
zstd
gzip

Benchmark on a real Pi 5.

The following observation:

zstd image smaller

does not establish that boot will be faster.

The 32 GB SD has ample capacity for the roughly 1.75 GiB internal image. Unused card capacity stays unallocated; SYSTEM remains on USB.

Boot milliseconds matter.


19. Raspberry Pi firmware optimisation

production EEPROM:

Disable unnecessary network installation behavior.

In particular:

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:

tools/configure-pi-eeprom

Together with:

development.conf
production.conf
maintenance.conf

All EEPROM changes must be:

documented
reversible

20. Filesystem model

SYSTEM:

EROFS
read-only

runtime:

/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:

GPT PARTNAME=FDS_DATA
filesystem=ext4

After insertion:

/data

Alternatively:

/home/fds

Mount this path from the DATA cartridge.

When no DATA cartridge is present:

ephemeral tmpfs home

Therefore:

The machine itself does not own user data.


22. Native s6 service graph

Minimum boot bundle:

boot
├── runtime-fs
├── hostname
├── console
├── eudevd
├── udev-trigger
├── cartridged
├── getty
└── dasungd

Explicitly excluded from the boot bundle:

network
DHCP
NTP
sshd
D-Bus
Xorg
WindowMaker
Bluetooth
Wi-Fi

23. Fast boot principle

The interactive shell should depend only on:

root mounted
console ready
/dev minimally usable

It must not depend on:

all USB devices enumerated
cartridge catalog scan
network
clock synchronization
GUI

The intended sequence is:

power on
   ↓
kernel
   ↓
root
   ↓
s6
   ↓
FDS>

Everything else proceeds in parallel afterward.


24. eudev

The normal system uses:

eudev

However, do not use:

udevadm settle

as a global boot barrier.

Correct sequence:

start eudevd
        ↓
trigger existing devices
        ↓
continue boot

Services that need a particular device must handle their own readiness.


25. cartridged

Implement:

fds-cartridged

static-musl Rust.

Responsibilities:

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:

/dev/sdX ordering

Use:

sysfs USB topology

to identify physical bays.


26. Bay topology

Configuration:

/etc/fds/bays.toml

Example:

[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:

BAY 01
...
BAY 12

They must not see:

1-2.4.1

except in debug mode.


27. Cartridge metadata

At the root of a storage cartridge:

/FDS/CARTRIDGE.TOML

Format:

format = 1

[cartridge]
id = "fds.windowmaker"
name = "WINDOW SYSTEM"
class = "environment"
version = "0.1"

[media]
writable = false

[activation]
profile = "windowmaker"

classes:

system
data
program
environment
hardware
utility

28. Hardware cartridges

USB hardware without a filesystem includes:

For example:

USB Ethernet
USB Serial
USB Wi-Fi
USB Audio

Identify it using:

VID
PID
serial
USB class
physical bay

Use these identifiers for recognition.

Maintain:

/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:

Xorg
WindowMaker
fonts
theme

The ENVIRONMENT cartridge initially provides only:

capability key
+
configuration

On insertion:

WINDOW SYSTEM
        ↓
cartridged
        ↓
fds-profile activate windowmaker
        ↓
s6-rc change +desktop

On removal:

desktop down
        ↓
return CLI

Move the GUI runtime onto the cartridge in version 2.


30. WindowMaker stack

Display managers are prohibited.

Do not use:

GDM
SDDM
LightDM

Use:

s6
 ↓
Xorg
 ↓
WindowMaker

A single-user machine may use:

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 for implementation, usage, and the remaining Pi and physical recovery checks. It must not require an ENVIRONMENT cartridge.

Create:

fds-eink

Configuration:

no compositor
no transparency
no fade
no desktop animation
no smooth window animation
outline move
outline resize
static wallpaper
high contrast

WindowMaker should prioritize:

black
white
2–4 grayscale levels

Fonts:

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:

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:

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.

Responsible for:

inspect target
partition media
write image
verify
write cartridge metadata

Typical usage:

fds burn system fds-system-cli.img BAY04

Alternatively:

fds burn data BAY04 --label FELIS

Destructive operations require explicit confirmation.


35. Package installation model

A running SYSTEM cartridge must not use:

sudo xbps-install -Su

to modify itself in place.

Use XBPS for:

building images
resolving package dependencies
resolving libraries for installed PROGRAM package trees
development profile

SYSTEM update sequence:

build new SYSTEM image
        ↓
write new cartridge
        ↓
swap cartridge

rollback:

put old cartridge back

36. Image builder

Implement:

make rootfs PROFILE=cli

Internal workflow:

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:

locale generation
font cache generation
s6-rc compilation
ldconfig
package configuration

Do not perform them during boot.


37. Build interface

Eventually support:

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:

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:

cargo build \
  --release \
  --target aarch64-unknown-linux-musl

All FDS XBPS packages should support the following wherever practical:

./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:

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:

x86_64 Arch
      ↓
cross build
      ↓
aarch64 image
      ↓
QEMU smoke test
      ↓
real Pi test

Use QEMU to test:

s6 graph
shell
package contents
static-musl binaries
config parsing
filesystem layout
shutdown logic
cartridge metadata

Use a real Pi to test:

firmware
RP1
USB topology
12 bays
E-Ink
power behaviour
actual boot timing

40. Boot performance targets

Define:

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:

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:

fds-boottrace

Record:

kernel timestamp
stage0 start
SYSTEM found
root mounted
switch_root
s6 start
console ready
desktop ready

Output:

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:

fds-os.wiki/Developer-Performance.md

42. Performance regression rule

Any commit that causes:

boot regression > 100ms

must:

explain it
or fix it

Do not add an artificial delay for a retro splash screen:

sleep 1

If FDS can boot in 800 ms, let it boot in 800 ms.


43. Fast boot blacklist

Prohibited on the critical path:

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:

build time
on demand
or parallel after READY

44. Networking

network bundle:

network

By default, it is not part of:

boot

When an Ethernet cartridge is present or the user explicitly requests networking:

s6-rc change +network

DHCP must never delay the appearance of:

FDS>

The prompt must remain independent of DHCP.


45. Logging

Normal logging location:

/run/log

tmpfs.

Persist only:

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:

fds poweroff

Sequence:

freeze new cartridge operations
        ↓
stop GUI
        ↓
stop program services
        ↓
stop network
        ↓
sync DATA filesystems
        ↓
unmount DATA
        ↓
stop remaining services
        ↓
poweroff

SYSTEM root:

read-only

It therefore does not need to wait for journal writes to SYSTEM.


47. Shutdown performance

Target:

idle poweroff:
< 1 second target

normal writable DATA:
< 1.5 seconds target

Do not use:

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:

fds eject 02

Behavior:

identify dependent programs
        ↓
stop them
        ↓
syncfs()
        ↓
unmount
        ↓
mark SAFE

Output:

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:

bash
GNU coreutils
util-linux
filesystem tools
XBPS tools

fds
fds-burn
fds-inspect

FDS tools in recovery remain static-musl binaries.

Recovery supports:

inspect SYSTEM
burn SYSTEM
repair DATA
diagnose USB bays
inspect boot log

50. Security

Strictly prohibited:

/FDS/autorun.sh

executed automatically as root when a cartridge is inserted.

Cartridge manifests must be declarative.

For example:

profile = "windowmaker"

Instead of:

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:

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:

gcc
clang
binutils
make
cmake
meson
ninja
pkg-config

rust
cargo

git
gdb
strace
perf

vim/neovim

Provided that these tools:

do not start daemons

they should not significantly slow boot.

Disk usage is not boot latency.


53. Milestone M0 — Host build environment

Implement:

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:

build aarch64 package
build static-musl hello-world

Verify that the executable is static.


54. M1 — FDS base rootfs

Implement:

fds-base
fds-base-files
glibc system
GNU userland
XBPS

Acceptance:

Generate:

out/rootfs-aarch64.tar

Verify:

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:

s6-linux-init
s6-svscan
s6-rc

Acceptance:

In the booted rootfs:

PID1 belongs to s6 architecture

Also verify that:

s6-rc change

can control a test service.


56. M3 — static-musl FDS tooling

Implement:

fds-common
fds CLI skeleton
fds-stage0 skeleton

All components use:

Rust
aarch64-unknown-linux-musl
static

Acceptance:

readelf
file
ldd

must demonstrate the absence of a glibc runtime dependency.


57. M4 — Pi boot

Implement:

FDS_BOOT
kernel
initramfs
fds-stage0
SYSTEM EROFS
switch_root
s6

Acceptance:

Pi:

power on
 ↓
native microSD kernel
 ↓
USB SYSTEM cartridge
 ↓
FDS>

58. M5 — Fast CLI

Optimize:

firmware
kernel
initramfs
stage0
s6 graph
getty
shell startup

Acceptance:

kernel → FDS> <2 sec

Create the first measured version of:

fds-os.wiki/Developer-Performance.md

59. M6 — Cartridge daemon

Implement:

udev/netlink events
hub discovery
12 bay topology
metadata
mounting
eject

Acceptance:

With 12 USB devices inserted simultaneously:

every device maps to the correct physical BAY

60. M7 — DATA cartridge

Implement:

mount
home/data integration
safe eject

Acceptance:

After a sustained write test:

fds eject

must leave the filesystem clean.


61. M8 — WindowMaker

Implement:

Xorg
WindowMaker
E-Ink theme
environment profile

Acceptance:

On insertion of an ENVIRONMENT cartridge:

CLI → WindowMaker

On cartridge removal or profile deactivation:

WindowMaker → CLI

No reboot is required.


62. M9 — Cartridge image tools

Implement:

fds burn
fds inspect
fds format

Acceptance:

The machine itself can create:

SYSTEM
DATA
PROGRAM
ENVIRONMENT

cartridge.


63. M10 — Shutdown optimisation

Complete:

service stop graph
DATA sync
DATA unmount
fast halt

Acceptance:

idle shutdown <1s target
normal shutdown <1.5s target

Filesystem corruption is unacceptable.


64. M11 — 12-device stress testing

Hardware:

12 USB devices

Test:

cold boot
hot insert
hot remove
simultaneous enumeration
simultaneous IO
reboot
shutdown

Check:

bay mapping stability
USB resets
kernel errors
boot regression
power issues

65. M12 — Production hardening

Complete:

release signing
image versioning
package pinning
reproducible build work
recovery
documentation
production EEPROM config

Release:

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:

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 <simple-package>

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 fds-os.wiki/Developer-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 and M1 validation. The user subsequently authorized M2; see Native init. The user subsequently authorized implementation through M12, with physical Pi testing deferred. The implementation ledger above records the current evidence and remaining work.