What this trusts from outside
Everything the flake builds from, besides this repository:
- nixpkgs
nixos-unstable, the channel tarball fromchannels.nixos.org, pinned bynarHashinflake.lock. A different tarball fails the hash. - Stock package binaries from cache.nixos.org. The image uses nixpkgs' normal glibc package set without libc-specific package overrides, so Nix can substitute the closures the cache has instead of rebuilding them locally.
- Upstream sources nixpkgs fetches, each pinned by hash in nixpkgs, for packages that must be built locally.
- Fixed-output inputs in the build closure. CI preloads these from
cache.nixos.org where it can, with
tools/nix-fetch-sources, so that a cold build does not depend on every upstream being up. The selection includes source archives, and it can also include pinned bootstrap tools or prebuilt upstream compilers, so it is not only source. Each copied flat or NAR output is checked against its derivation's hash, and outputs that use other hash methods are rejected. - pm, the
components/pmsubmodule fromgithub.com/losos-project/pm, at the commit this tree records, built bynixos/pkgs/pm.nix. - crates.io, for the dependencies of pm, losos-security,
losos-installer and losos-hardware, each pinned by
Cargo.lockand checked by hash. - Purism's adaptive GTK patches, copied into
nixos/pkgs/patches/gtk3andgtk4from PureOS's packaging on source.puri.sm,Librem5/debs/gtk4andsebastian.krzyszkowiak/gtk, branchpureos/latest. They are reviewed source in this repository, not fetched at build time. - Device firmware, from nixpkgs'
linux-firmware, pinned by hash like any source. It is prebuilt by the hardware vendors, and nothing can compile it.hardware.nixships it because amdgpu and nouveau cannot start current GPUs without it. - NVIDIA's driver, the production branch nixpkgs pins by hash, fetched
from
download.nvidia.com. The userspace libraries and GSP firmware are NVIDIA's binaries, which its licence lets the image and the binary cache redistribute unmodified. They are the image's only unfree package, added bynixos/modules/nvidia.nix, and they load only on a machine with a card the driver supports. The open kernel module is built from NVIDIA's MIT/GPL source. - derisk, the
components/derisksubmodule fromgithub.com/losos-project/derisk, at the commit this tree records, built bynixos/pkgs/derisk.nix. Its crates, mcsapi among them, are pinned by itsCargo.lockandcargoHash. It is the desktop, the display manager and the portal backend. Thecomponents/mcsapisubmodule is the same mcsapi commit, and buildsx2mcsapi. - ocrs' OCR models,
text-detection.onnxandtext-recognition.onnx, fetched bynixos/pkgs/derisk.nixfrom ocrs' own bucket,ocrs-models.s3-accelerate.amazonaws.com, and pinned by hash. The URLs carry no version, so a changed file fails the hash rather than changing the build. derisk reads every screenshot's text with them to black out its secrets (Screenshots). They are data that rten runs, with no code in them. - The LosOS terminal client,
losos, built from the LosOS repository atgithub.com/dasmatus/losos, at the commitnixos/pkgs/losos-pin.nixnames. It is a sparsefetchgitof the directories it builds from, checked by hash, with its crates pinned by itsCargo.lockandcargoHash. A second sparse fetch of the same commit, the box's modules and lososd's crates, is read only bynix flake check, to boot a box innixos/tests/losos-box.nix; the image and the release never do. The LosOS repository's release workflow proposes each new pin as a pull request. See LosOS boxes. - Home Manager,
github.com/nix-community/home-manager, pinned by revision andnarHashinflake.lock. Onlynix flake checkreads it, to buildhomeModules.pminto a Home Manager generation innixos/tests/home-manager-pm.nix. The image and the release never do. - Halium's generic system image, from
nixos/pkgs/halium-gsi.nix. It is the Android 14 system that the Halium GSI runs the vendor's HALs under, UBports' build of Halium's AOSP tree as Droidian publishes it in its apt repository. It is pinned by the hash that repository's index gives for the package. It is prebuilt bionic code that nothing here can compile, and it runs as host root in the Android container, so it is trusted as much as the vendor blobs beside it. The phone's own kernel, vendor partitions and bootloader are the device maker's, and the GSI trusts them as Android does. - Fedora's signed shim, from
nixos/pkgs/shim-signed.nix: shim and MokManager 16.1 as Fedora's build system published them, pinned by hash, and signed by Microsoft after shim-review. They are prebuilt, since rebuilding them would lose Microsoft's signature, and they are the first code a PC with Secure Boot on runs from the ESP. shim also trusts Fedora's own certificate, as any machine that trusts Microsoft's third-party CA already does through Fedora's shim. - Flathub, for apps installed later. Its repo file, with the signing key
every install is checked against, is
nixos/modules/flathub.flatpakrepoin this tree. The image adds the remote from that copy at boot and never fetches the key. The apps themselves are Flathub's builds, sandboxed by Flatpak and verified against that key, and nothing in the image depends on any of them. - The project's binary cache in GHCR, through
proxy/. It holds optional paths that the CI job stores and signs. A client checks every narinfo against the public key it was told to trust, so the proxy and GHCR carry bytes but cannot vouch for them. - In CI only, the official Nix installer from
releases.nixos.org, pinned by version. Nix is needed to build Nix, so this binary is the one bootstrap. After it,.#nixis the nixpkgs Nix package for a build host.
The components/ submodules
derisk, mcsapi and pm are git submodules under components/, and the flake
builds them from there, through self.submodules = true in flake.nix, which
Nix 2.27 and later honour. The commit recorded for each submodule is the pin.
A clone needs git clone --recurse-submodules, or git submodule update --init, and git -C components/<name> log shows exactly what the image
builds. To move one, run git -C components/<name> checkout <rev>, commit
here, and set a new cargoHash in its nixos/pkgs/*.nix if its Cargo.lock
changed. There is no source hash to update, because the submodule commit
already names the tree. Running tools/nix-refresh-hashes --list > before.txt
before the move and tools/nix-refresh-hashes before.txt after it finds and
rewrites that cargoHash. derisk's own Cargo.lock still fetches mcsapi by
git revision, so move components/mcsapi to the revision that lock names.
Every Monday the lockfile update moves all three to releases (Keeping the
pins current), and a move by hand in between
stays.
Keeping the pins current
Every Monday the lockfiles workflow, .github/workflows/lockfiles.yml,
runs tools/update-lockfiles. A person can run it the same way. It does
three things:
nix flake updatemoves nixpkgs and Home Manager inflake.lock.cargo updatemoves everyCargo.lockoutsidecomponents/, andnpm update --package-lock-onlyeverypackage-lock.json, each only as far as its manifest allows, with cargo and npm from the new nixpkgs.- The submodules move to releases. derisk, mcsapi and pm publish GitHub
releases on the same cadences as the OS (Release
cadences), and cut their weekly ones at
02:17 UTC, before this runs.
components/deriskandcomponents/pmmove to their repository's newest weekly release, andcomponents/mcsapito the revision derisk'sCargo.locknames at that release, so the two agree. A pin only moves forward, so one moved past the release by hand stays until a newer release passes it. Inside the submodules nothing else moves, since each repository updates its own lockfiles the same way. tools/nix-refresh-hashesthen re-fetches every fixed output pinned in this repository whose derivation those changes touched, and writes back the hash it really has. It has to re-fetch, because a stale hash still names the old store path, so a machine or cache that has the old output builds with it and never notices. losos-docs' npm hash moves this way wheneverwebsite/package-lock.jsondoes.
The result is one commit, chore(deps): update lockfiles, pushed straight
to main with no pull request. Main's own ci run on it is the check, so a
lockfile that breaks the build shows there as any other push would, and a
green one publishes that night's release. If main moved while the update
ran, the commit is rebased onto it first. If the two touched the same lock,
nothing is pushed and the run fails, and running it again starts afresh.
The workflow pushes with LOCKFILE_UPDATE_TOKEN, a fine-grained token with
Contents write. GitHub starts no workflow for a push made with Actions' own
token, so without the secret the update still lands on main but ci does
not run on it until the next push. A branch rule that refuses direct pushes
to main has to let the token's owner, or Actions, bypass it.
android_translation_layer stays a fetchgit pin. It is an upstream fork,
off by default, and its tree is close to half a gigabyte, which the flake
would otherwise copy on every evaluation.
The LosOS client is a sparse fetchgit pin, because its repository is about
140 MB and the client needs 3 MB of it.