121 lines
5.4 KiB
Markdown
121 lines
5.4 KiB
Markdown
# Software cartridge format 1
|
||
|
||
Development reference and historical context. For current operating instructions, use the [user manual](../README.md). Acceptance applies only to the source and artifacts identified in each record.
|
||
|
||
|
||
[Build and use a cartridge](workstation.md) · [Original cartridge classes](cartridges.md)
|
||
|
||
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
|
||
|
||
```text
|
||
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:
|
||
|
||
```toml
|
||
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](releases.md).
|