Tufaceous is the library and tooling for Oxide update repositories. It is a layer of Oxide-specific tooling on top of tough, a Rust library that implements TUF.
This README is intended to provide a high-level overview of the library and instructions for adding to it. More context can be found in RFD 621.
We selected ZIP archives as the primary format for our repositories, despite ZIP's many shortcomings. Because Oxide customers are receiving update repositories and uploading them to their systems, we wanted them to be readable for most users so that they could see what is inside.
The ZIP archive has a top-level repo directory. Inside this directory is a metadata directory, containing the signed TUF metadata, and a targets directory, containing all the targets described by that metadata.
"Artifacts" are our jargon for a file that the Oxide control plane needs in order to perform updates. (All artifacts are "targets", TUF jargon for a file described by the repository metadata, but not all targets are artifacts.)
Artifacts in the repository are described by the artifacts-v2.json. Each artifact has a set of tags that is used by the control plane to decide what to do with the artifact. When creating and using artifacts, these tags are strongly-typed (see KnownArtifactTags in artifact/src/tags.rs), but they are encoded as an arbitrary key-value mapping. This allows us to potentially release a new type of artifact and use it in the same release, once Nexus has been updated.
As a general rule it is not possible to use this tooling to create a repository with arbitrary blobs and artifacts in it. The library is very opinionated about how artifacts are named to keep things usable for the CLI and for users of these repositories.
Most of the time you're working with repositories generated by the Omicron release tooling (cargo xtask releng in Omicron). There is also a binary target that is a thin CLI on top of the library, which you can build with cargo build --release --bin tufaceous.
In all commands that read from a repository, use either -r/--trust-roots to provide a list of trust roots to verify signatures against, or --blindly-trust to use the metadata/1.root.json root in the repository as the trust root. If you want to load a repository with an expired signature, use --allow-expired. For convenience you can combine --allow-expired and --blindly-trust with -f/--force-load.
tufaceous verify will read a repository, print its SHA256 checksum, and verify the metadata and checksums of all the metadata in the repository. It will also run a series of compatibility checks on the repository and warn you of any detected issues.
Scripts can use tufaceous list-targets to get a list of targets from the repository, and tufaceous show-target to write a target to stdout.
To unpack an entire repository, use unzip or ZIP tooling provided by your OS.
tufaceous build has two required flags:
--output file.zip, which provides the path to write to--system-version 1.0.0, which provides the system version
The arguments are the set of artifacts to add:
- A measurement corpus is a CoRIM file
- An OS image is a directory containing
cosmo.rom,gimlet.rom, andzfs.img, as produced byhelios-build image - An RoT, RoT bootloader, or SP image is a Hubris archive (ZIP format)
- A zone image is a tarball that starts with the
oxide.jsonOmicron brand metadata
When building a repository, an Installinator document is automatically created based on the artifacts in the repository. You can avoid this with --no-installinator-document.
Using tufaceous edit you can remove targets from a repository using --remove-targets and add new artifacts using --add-artifacts.
The Installinator document is automatically regenerated based on the artifacts in the final repository. You can avoid this with --no-installinator-document, which will keep an existing Installinator document if there is one.
In order to add a new type of artifact, you will need to make a series of changes to the library:
- Modify
KnownArtifactTagsto describe your new artifact - Add methods for adding an actual artifact and a fake artifact to
RepositoryEditor - Add a fake artifact in
Input::fake(lib/src/edit/fake.rs) - Add code for guessing this type of artifact to
Input::guess(lib/src/edit/guess.rs)
As a general rule, Tufaceous artifacts are self-describing. The "guess" code is used by the CLI and generates tags from the artifact to avoid needing an unwieldy set of command line options to describe tags. If your artifact is not self-describing, you can staple a header of some kind to the beginning of the artifact that control plane code knows to strip before using the artifact.