Skip to content

RFC: slotted extension storage (builds on #789) - #838

Draft
Paco Huelsz (frhuelsz) wants to merge 8 commits into
mainfrom
user/frhuelsz/rfc-slotted-sysexts
Draft

Paco Huelsz (frhuelsz) wants to merge 8 commits into
mainfrom
user/frhuelsz/rfc-slotted-sysexts

Conversation

@frhuelsz

@frhuelsz Paco Huelsz (frhuelsz) commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Builds directly on #789.

#789 carries extension images in the COSI so an OS-coupled extension ships and rolls out with the OS. It leaves one question open, and names this RFC three times while doing so: a bundled extension still has to land somewhere that follows the A/B slot, and today the only way to satisfy that is a dedicated A/B partition pair (the root-verity reference config spends four partitions and 2.4 GB on exactly this).

This RFC answers #789's open question "how does a COSI entry request the policy in RFC 0838?" with an explicit scope: shared|slotted, spelled identically on the COSI ExtensionImage and the Host Configuration Extension, and mutually exclusive with path so placement never changes implicitly.

A bundled extension is already tied to the OS image it shipped with, so slotted placement is its natural destination. The two features are expected to be used together in the common case; an independently hosted OCI extension placed per slot is the unusual combination.

Worth reviewer attention:

  • Selection is recomputed every boot, never persisted. Unsupervised rollback runs no Trident code, so anything written while staging would be stale with nothing to correct it.
  • Both halves run in the initrd, because /etc layers have no choice and /run is bind-moved across switch-root. Resolving the slot once means the sysext set and the /etc layer cannot disagree.
  • /etc payloads are confext-format images mounted as extra lowerdirs, so /etc stays mutable on systemd 255. They must declare ID=_any or they will silently stop merging when a platform moves to native confext mounting (AzL 4.0 ships systemd 258.4).
  • Why content matching alone is insufficient is argued explicitly, since it is the obvious alternative.

Includes a change inventory across the new component, Trident and ACL with T-shirt sizing for design and implementation.

Nothing #789 delivers is restated: the deployed inventory, effective set, resolved-placement validation, atomic staging and SELinux check relocation are referenced as dependencies.

Draft: open questions on per-slot initrd, selection granularity and retention are unresolved.

Paco Huelsz (frhuelsz) and others added 2 commits October 8, 2026 11:53
Adds a 'slotted' scope for sysexts/confexts, stored per A/B volume in a
configurable store (default /var/trident/sysexts) and activated via an
/etc/extensions symlink written during servicing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The A/B rollback path invokes no subsystems: automatic rollback is
firmware-driven and validate_boot only reconciles Host Status, while
manual_rollback::stage_rollback touches only the pcrlock policy. A symlink
written during servicing would therefore survive a rollback pointing at the
wrong slot.

Activation now resolves at boot from the booted volume, via a single
<store>/current flip ordered before systemd-sysext.service.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
1 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Paco Huelsz (frhuelsz) and others added 2 commits October 8, 2026 12:05
The Rust types are an implementation detail; the public surface is the Host
Configuration document.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The original premise was wrong. Trident does not place extensions on
non-slotted storage: validate_extension_images_locations already rejects any
extension directory not on an A/B volume. The real cost is that satisfying it
requires a dedicated A/B volume pair, which the reference config pays for with
four partitions and 2.4 GB, and which a layout whose only A/B volume is a
verity-sealed /usr cannot pay at all.

The proposal is now to relax that check: slot-scoping as a property of the
extension rather than of the partition table, satisfied by an A/B volume as
today or by a Trident-owned per-slot store.

Activation no longer needs a boot-time unit. The extensions subsystem gains
ManualRollbackAb in runs_on, and automatic rollback is corrected at commit via
systemd-sysext refresh; version matching keeps the intervening window absent
rather than wrong.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@frhuelsz Paco Huelsz (frhuelsz) changed the title RFC: slotted system extensions RFC: slotted extension storage Oct 9, 2026
Replaces the previous mechanism. Symlinks are no longer written by the
extensions subsystem into /var/lib/extensions during servicing; selection is
recomputed every boot into /run/extensions from the slot that actually booted.
That removes the ManualRollbackAb runs_on change and the commit-time repair,
since nothing persists a decision that a rollback could invalidate.

Corrections and additions from review:

- Before=systemd-sysext.service is the ordering edge. ensure-sysext.service
  carries BindsTo= and After=systemd-sysext.service, so it runs after the
  merge and is the wrong anchor.
- WantedBy= plus Before= does not fail closed; the merge unit needs a
  Requires= drop-in.
- Per-file symlinking cannot commit a set atomically.
- A store manifest replaces globbing, so absence is distinguishable from loss.
- Two-phase rollout, since a rollback slot may predate the loader.
- Documents why content matching alone is insufficient, and scopes slotted
  extensions to /usr and /opt because reordering the merge ahead of an /etc
  overlay would exhaust the kernel filesystem stacking budget.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Both halves resolve the booted slot and expose that slot's content before a
consumer reads it, and both must act before the /etc overlay is mounted in the
initrd. /run is bind-moved across switch-root, so the sysext symlinks written
there remain valid for the later merge in the real root. Resolving the slot
once means the two halves cannot disagree about which slot they came from, and
it removes the unit-ordering, condition and fail-closed problems a real-root
systemd unit carries.

/etc payloads are confext-format images mounted as additional lowerdirs on the
existing overlay, so /etc stays mutable on systemd 255. Keeping the confext
format rather than inventing a tarball means the artefact is already what
native systemd-confext consumes on 256 or later; Azure Linux 4.0 ships 258.4.
Payloads must declare ID=_any, because native mounting enforces
extension-release matching that the custom mount bypasses, and a concrete ID
would silently stop merging at the transition.

Adds a change inventory across the new component, Trident and ACL with
T-shirt estimates for design and implementation, plus suggested sequencing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
0789 was rewritten and now names this RFC in three places: the optional path
discussion, destination validation, and an open question asking how a bundled
extension requests slotted placement. This RFC now answers that question rather
than sitting beside it.

The common case is a COSI-bundled extension that is slot-scoped, since an
extension shipped with the OS image is already tied to it. An independently
hosted extension placed per slot is the unusual combination.

Removes what 0789 delivers: the invented manifest.json is replaced by 0789's
Deployed Extension Inventory, the store is keyed by (kind, id) instead of
filenames, and the effective set, resolved-placement validation, atomic staging
and SELinux check relocation are referenced rather than restated.

Adds scope: shared|slotted on both the COSI ExtensionImage and the Host
Configuration Extension, spelled identically so one concept has one name, and
mutually exclusive with path so placement never changes implicitly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@frhuelsz Paco Huelsz (frhuelsz) changed the title RFC: slotted extension storage RFC: slotted extension storage (builds on #789) Oct 14, 2026
'commit' already names the post-boot servicing operation in Trident, so using
it for the loader publishing its symlink set is a confusing collision. The
loader has no relationship to that operation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant