Files
fds-os/docs/developer/software-format.md
T
2026-09-22 13:23:34 +08:00

5.4 KiB
Raw Blame History

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.