1
Developer Cartridges
Felis edited this page 2026-09-24 13:51:28 +08:00
Table of Contents
include_toc
include_toc
true

Using cartridges and configuring bays

Development reference and historical context. For current operating instructions, use the user manual. Acceptance applies only to the source and artifacts identified in each record.

Documentation index · Boot images · Services

M6 adds the base fds-cartridged service, twelve bay states, USB hardware recognition, strict metadata inspection, read-only storage mounts and eject. Its ARM VM acceptance checks passed. Writable DATA integration has passed M7 software acceptance; desktop/program activation passed M8 and media creation tools passed M9 software acceptance. M10 ordered shutdown has passed software acceptance.

Inspect the current machine

At the FDS console:

fds bays
fds bay 3
fds cartridge 3
fds --json bays

The ordinary fds user can use these commands. bays shows all twelve bays, including empty and unconfigured ones. cartridge includes validated metadata and the current mount path. --json exposes a versioned machine-readable report. fds rescan explicitly refreshes inventory; normal insert/remove events trigger refresh automatically. The console does not wait for the inventory scan.

State Meaning and next action
UNCONFIGURED No measured controller/port mapping exists for this bay
EMPTY Configured bay with no detected USB device
HARDWARE USB device without storage; catalog name or VID/PID is shown
UNRECOGNIZED STORAGE Storage exists but no named FDS partition is available
MOUNTED READ ONLY Manifest validated; filesystem available at the displayed path
MOUNTED READ WRITE Active DATA is writable at /data; eject before removal
PROTECTED The active SYSTEM root; eject is refused
AMBIGUOUS Multiple devices or named partitions match; nothing is selected arbitrarily
ERROR Inspection, mounting or metadata validation failed; read the diagnostic
SAFE The requested unmount succeeded; remove the cartridge

An unrecognized storage state may briefly appear while the kernel discovers its partitions. It is not permission to remove a device being used elsewhere. Errors and ambiguous media never produce SAFE. An additional mount of the same filesystem also blocks managed eject until it is unmounted.

Calibrate the physical bay map

The shipped /etc/fds/bays.toml is deliberately empty: no Pi bay wiring has yet been measured. This produces UNCONFIGURED states, not invented assignments.

  1. On the assembled machine, insert one known USB device into one physical bay.
  2. Run fds topology. This diagnostic explicitly shows controller/port paths; ordinary bay commands omit those implementation details.
  3. Record that path and repeat for each bay. Test both USB 2 and USB 3 devices, because their companion root hubs can have different logical port paths.
  4. Put the measured map in your machine configuration directory and build the internal image, or use fds machine install DIRECTORY from recovery and reboot. Machine settings persist independently of SYSTEM. The packaged /etc/fds/ files are fallback defaults for missing/invalid internal storage; fds machine status shows which source is active.
  5. Verify all twelve devices together, in different insertion orders and after reboot. That physical acceptance remains deferred.

A syntax example only, using identities that must be replaced by observations:

[front]
hub = "platform/example-controller:usb2/1"
[front.ports]
1 = 1
2 = 2
3 = 3
4 = 4
5 = 5
6 = 6

[front_superspeed]
hub = "platform/example-controller:usb3/1"
[front_superspeed.ports]
1 = 1
2 = 2
3 = 3
4 = 4
5 = 5
6 = 6

hub names the controller and parent port chain. Its numbered ports map the next downstream port to a bay number from 1 through 12. Additional groups can map the rear hub. Explicit USB 2/3 aliases may refer to the same bay; duplicate ports, overlapping parent/child mappings, and duplicate bays within one hub are rejected. If two devices simultaneously match aliases for one bay, it becomes AMBIGUOUS. A hub inserted into a cartridge bay with multiple downstream devices also needs an explicit future composite-device policy; M6 refuses to guess.

The identity excludes the Linux USB bus number and /dev/sdX enumeration order. It includes the controller's sysfs path, USB protocol generation and port chain. Moving a hub to another controller or changing wiring requires recalibration.

Storage formats and metadata

Class GPT partition name Filesystem and current behavior
SYSTEM FDS_SYSTEM EROFS; current root is protected, additional media read-only
DATA FDS_DATA ext4; inspected read-only first, then activated at /data under the M7 policy
PROGRAM FDS_METADATA + FDS_PAYLOAD02… Metadata and xz bundles in 1+m EROFS partitions; see software format
Legacy PROGRAM FDS_PROGRAM Existing single EROFS application tree; still readable
ENVIRONMENT FDS_ENVIRONMENT EROFS; declarative selection of a trusted built-in profile
UTILITY FDS_UTILITY EROFS; inspection only, no automatic actions
HARDWARE None required VID/PID, optional serial, device/interface class and bay

Storage contains /FDS/CARTRIDGE.TOML. For example:

format = 1
[cartridge]
id = "fds.windowmaker"
name = "WINDOW SYSTEM"
class = "environment"
version = "0.1"
[media]
writable = false
[activation]
profile = "windowmaker"

The manifest class must match the GPT partition name. SYSTEM, PROGRAM and ENVIRONMENT are read-only; DATA declares writable media during the initial read-only inspection. Unknown keys, root commands, unsafe identifiers, control characters, symlink metadata and nonregular metadata files are rejected. The parser accepts at most 64 KiB. No /FDS/autorun.sh is run.

Inspect a standalone manifest with fds inspect /path/to/CARTRIDGE.TOML. For mounted storage, fds cartridge N reports the daemon's validated copy. Media is inspected in a root-only staging directory, then published at /run/fds/media/NN after validation, with nodev, nosuid and noexec. Device major/minor and kernel disk sequence are checked before mounting an opened block-device descriptor, preventing stale enumeration names from selecting a replacement disk.

Eject a mounted cartridge

fds eject 2

Leave any shell working directory inside that cartridge and close files first. SAFE is emitted only after an ordinary unmount succeeds. Busy mounts return an error; forced or lazy unmount is never used to declare safe removal. Once SAFE, the daemon keeps that insertion unmounted until removal and reinsertion, including across a service restart. Its root-owned volatile marker is tied to the kernel disk sequence, so a replacement disk is not mistaken for ejected media. The active SYSTEM cannot be ejected. Swap SYSTEM only after shutting down.

Pulling a mounted cartridge without eject is surprise removal. The daemon clears its inventory and detaches a vanished read-only mount where needed; that cleanup is not a successful eject. M7 DATA handling extends ejection to writable DATA, consumer tracking and syncfs. The temporary /home/fds remains usable without DATA and is lost at power-off.

Hardware recognition

Edit packages/fds-cartridged/files/hardware-catalog.toml, then rebuild SYSTEM. Entries name devices, never executable actions:

[[device]]
name = "MY SERIAL ADAPTER"
vendor = "1234"
product = "5678"
# Optional exact restrictions:
serial = "UNIT-1"
class = "02"

Replace these example IDs with observed values. Device or interface class may match class. Multiple matching catalog entries produce an error; unknown hardware remains identified by VID/PID without being mistaken for empty media. Dasung control remains in the independent base monitor service.

Implementation and diagnostics

The daemon subscribes to kernel USB/block events before taking its first snapshot. A receive-buffer overflow causes a fresh snapshot. IPC uses a bounded Unix socket at /run/fds/control.sock, mode 0660 and group fds, with peer UID checks for root and the ordinary FDS user. Slow clients have individual deadlines and do not block other clients. No asynchronous runtime or new external Rust dependency is introduced: the existing std, libc, serde, serde_json and toml components suffice.

Root diagnostics: s6-svstat /run/service/cartridged and cat /run/log/cartridged/current. Logs are bounded and volatile. Restart through s6-rc -l /run/s6-rc -d change cartridged, then the corresponding -u command. Close users of cartridge mounts first; restart cleanup refuses busy leftovers.

On the build host, make cartridge-test runs fixtures and full virtual USB tests. These exercise actual kernel events and mounts but cannot establish physical wiring, USB power stability, or Pi port behavior. M11 expands to twelve-device stress, and physical calibration must be recorded separately.