5.4 KiB
Software cartridge format 1
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.
Build and use a cartridge · Original cartridge classes
A software cartridge is a complete GPT disk image with 1 + m partitions,
where m is 1–32. Partition 1 describes every software package in all payload
partitions. All partitions use read-only EROFS. This format is the current
PROGRAM creation format; existing single-partition PROGRAM media remain readable.
SYSTEM, DATA and ENVIRONMENT layouts retain their existing meanings.
Disk and filesystem layout
GPT protective MBR + primary GPT
1 FDS_METADATA
FDS/CARTRIDGE.TOML
FDS/SOFTWARE.TOML
2 FDS_PAYLOAD02
bundles/demo.hello.tar.xz
bundles/demo.editor.tar.xz
3 FDS_PAYLOAD03
bundles/demo.report.tar.xz
...
backup GPT
Partition entries are consecutive, use Linux filesystem type GUIDs, unique nonzero partition UUIDs, no GPT attributes, and nonoverlapping MiB-aligned extents. Partition 1 starts at LBA 2048. Both GPT copies, their headers and CRCs must agree. A full-drive write to a larger USB device relocates the backup table and header. The existing on-target image parser and the workstation writer share these checks.
FDS/CARTRIDGE.TOML uses the existing format-1 identity with class = "program"
and media.writable = false. It contains no startup command. A sample catalogue:
format = 1
[[software]]
id = "demo.hello"
name = "AArch64 hello"
version = "1.0"
architecture = "aarch64"
partition = 2
archive_bytes = 2308
unpacked_bytes = 70504
entries = 2
sha256 = "REPLACE_WITH_THE_64_CHARACTER_LOWERCASE_SHA256"
[software.commands]
hello = "bin/hello"
Those lengths and the digest are illustrative. The builder generates actual
values. IDs and command names use the existing restricted FDS identifier syntax.
IDs are unique per cartridge. Every declared payload partition must contain
exactly the catalogue's bundles/<id>.tar.xz archive names, and every payload
partition must be represented. Multiple software entries may share a partition.
The catalogue contains 1–128 software entries and at most 64 KiB of TOML.
Archive contract
Bundles are deterministic USTAR archives compressed with single-threaded xz. Entries are sorted, timestamps/UID/GID are zero, and permissions normalize to 0755 for directories/executables or 0644 for ordinary data. Host source symlinks to regular files inside the root are flattened into regular files. Directory or escaping symlinks are rejected; the archive itself never contains links.
The reader rejects:
- Absolute, parent-traversing, repeated-separator, non-UTF-8 or control-character paths.
- Duplicate entries, file-as-parent conflicts, links, devices, sockets, FIFOs, privileged permissions, and GNU/PAX extension entries.
- Incorrect SHA-256, compressed length, unpacked length, entry count, or command paths that do not name executable regular files.
- ELF files that are not little-endian 64-bit AArch64, or any ELF in a bundle
declared
architecture = "any". - Invalid xz data, nonzero material after the tar end marker, and resource-limit violations. Each archive is at most 512 MiB compressed, 1 GiB unpacked and 65,536 entries. Xz decompression has a 256 MiB memory limit.
A source path must fit USTAR's path fields. Packaging reports paths that cannot be represented rather than emitting an unsupported extension header.
Guest lifecycle
On insertion the daemon validates the complete GPT against kernel partition
geometry and the current USB disk identity. It reads metadata without following
symlinks, mounts every payload read-only/noexec/nosuid/nodev, and checks the
archive inventory and lengths. fds bay BAY reports the full catalogue;
fds bays stays compact. No archive command runs automatically on insertion.
fds run BAY -- SOFTWARE-ID:COMMAND [ARGUMENT...] verifies and extracts that
package on first use. It creates a private temporary filesystem, applies the
archive contract, then makes the finished tree read-only and accessible for
execution. The runtime cache has a stricter 256 MiB per software tree limit,
including reserved inode overhead; a host-valid larger bundle may therefore
need splitting before running. Caches live under /run/fds/software/<bay>/ and
vanish after eject, unplug, restart cleanup, or shutdown.
Managed consumers run as UID/GID 1000 with no new privileges in the bay's cgroup.
FDS_APP, PATH, LD_LIBRARY_PATH and XDG_DATA_DIRS point to that software
root. DISPLAY/XAUTHORITY retain the established optional desktop integration.
The xz utility is already an explicit dependency of the mandatory fds-base
package; runtime extraction adds no target package.
Build recipes, compilers, installation hooks and privileged archive scripts are never part of this runtime path.
Safe eject stops consumers, rejects unexpected mount aliases, unmounts caches, payloads and metadata, then reports SAFE. Confirmed surprise removal permits lazy detachment after stopping consumers. Service restart removes stale mounts before rescanning. SYSTEM stays immutable throughout.
SHA-256 detects corruption and binds an archive to its catalogue; it does not establish a publisher's identity. Only run software you trust. Release signature verification remains the separate release workflow.