MMotor OS
Developing

Lorry: the package manager

Lorry is Motor OS's Rust package manager: a smaller, stricter Cargo replacement for security-sensitive environments. It is Cargo-compatible: it takes a package's Cargo.toml and Cargo.lock as they are and aims to produce byte-identical binaries. What it changes is how dependencies get in: builds are offline, from dependencies that were fetched once, checked, shown to a person, and recorded, and Lorry never invokes Cargo. It runs on Linux and on Motor OS, where it is the Rust build driver because the developer image has no Cargo.

Cargo compatibility

A Lorry project is a Cargo project. Nothing about the package changes, and Cargo can still build it:

The compatibility is with the package model Lorry supports, which is deliberately smaller than Cargo's; what is left out is listed under The package model, and anything outside it is rejected with a clear error rather than approximated.

Why a smaller, stricter Cargo replacement

In many corporate environments a build tool that goes out to the Internet on its own and silently downloads and runs code from hundreds of crates is a weakness, not a convenience. That is what cargo build does: the lock file names the crates, Cargo fetches them, runs their build scripts, and loads their procedural macros into the compiler, and nothing in that sequence asks anyone anything. Lorry is designed for the environments where that is not acceptable:

Lorry does not replace reading the code you depend on; it makes sure that nothing enters a build without having been seen, and nothing runs during a build without having been allowed.

Commands

lorry new hello                  # a package with an edition-2024 binary and a lock file
cd hello
lorry build [--release]          # build, offline
lorry run [-- args...]           # build and run the binary, forwarding arguments
lorry test [--no-run] [-- args]  # build and run the library, binary, and integration tests
lorry clean
lorry vendor [--accept-all]      # fetch, check, and record dependencies (the only networked command)
lorry vendor upgrade PKG[@OLD] --to VERSION
lorry review                     # print the canonical review of the dependency set
lorry cache clean
OptionMeaning
+toolchainOn Linux, a rustup toolchain selector, as with Cargo. On Motor OS the compiler is /devtools/bin/rustc.
-p NAMESelect a member of an explicit workspace.
--bin NAME, --test NAMEChoose a binary or an integration test. run otherwise takes package.default-run or the sole binary.
--releaseThe release profile: lto, strip, and codegen-units are honored in addition to the dev profile's settings.
--target TRIPLEAn installed target; custom JSON targets are rejected.
--bundlePackage the test harnesses and the binary into one self-extracting executable for the target, to run on another machine.
--strict-validationStricter checks for build, run, and test.
--use-cargo-registryBuild from an already populated local Cargo cache instead of a Lorry repository, after verifying it (Linux).
-q, -v, --colorOutput control.

lorry run executes the program directly, without a shell, and returns its exit status. lorry test builds separate harnesses for the library, the binary, and each tests/*.rs file and runs them in Cargo's order, stopping at the first failure; arguments after -- go to every harness. Usage errors exit 1; build, vendoring, policy, and operational failures exit 101; an interrupted run exits 130. There is no lorry clippy.

Where things live

On Motor OSPurpose
/devtools/cfg/lorry.tomlSystem configuration: the Cargo compatibility version (1.99), the curl program and CA bundle, vendor targets, policy limits, native C tools, and the reviewed grants for build scripts and procedural macros.
/user/cfg/lorry.tomlUser configuration; on the image it names the user repository.
/user/cfg/lorry/vendorThe user repository: content-addressed, immutable objects under objects/crates-io/sha256/ and objects/seeded-git/sha256/, a .staging area, and a repository.toml. Ships empty.
/user/cfg/lorry/cacheThe cache of compiled dependency units, shared between projects.
/user/cfg/lorry-redirect-sites.tomlThe list of sites whose HTTP redirects have been allowed or denied.
PROJECT/.lorry/dependencies-v2.tomlThe project's admission record: which packages, at which checksums, were reviewed for which host and target pairs. Committed with the project, never edited by hand.
PROJECT/target/lorry/Build output, in Cargo-shaped debug and release directories, plus incremental state for the root package.

Configuration files are merged in order: the system file, the user file, and the nearest lorry.toml above the project. Every file declares config-version = 1, and an unknown key is an error. On Linux the user file is $HOME/.config/lorry/lorry.toml and the cache is $HOME/.cache/lorry. Repositories come in three roles, system (read-only), user, and local; lookup goes local, then user, then system, and vendoring writes to the local repository if one is configured, otherwise to the user's.

Vendoring and admission

lorry vendor is the only command that touches the network. It reads the package's Cargo.toml and Cargo.lock, resolves what is missing from the repositories, and for each new package fetches it over HTTPS, checks the checksum and the archive structure, applies the policy, and asks for approval, showing the exact version, checksum, license, whether the package has a build script or is a procedural macro, its size, and the dependency edges it adds. --accept-all answers yes to those questions after all checks have passed; it cannot bypass an integrity failure, a policy denial, the redirect list, or a native-tool restriction, and it cannot change an existing admission record. Approved objects are written with fsync and published atomically; an object is never replaced. Cargo.lock is committed last.

Policy

The policy in lorry.toml decides what may be admitted. It has a default action, optional path roots, limits (on the developer image: at most 192 packages, a dependency depth of 16, 16 MiB per package archive and 128 MiB extracted, 256 MiB per transaction and 1 GiB extracted, a build script limited to 300 seconds and 8 MiB of output), and per-package rules. A rule names the package, its exact version, its source, and its checksum, and it is the only way to grant the two things that run code at build time:

[policy.rules.serde_derive-1_0_229]
action = "allow"
name = "serde_derive"
version = "=1.0.229"
source = "crates.io"
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
allow-proc-macro = true

[policy.rules.libc-0_2_189]
action = "allow"
name = "libc"
version = "=0.2.189"
source = "crates.io"
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
allow-build-script = true

allow-build-script and allow-proc-macro are independent grants, required even when the default action is allow. A build script that needs a C compiler or an archiver uses the tools declared under [native-tools."x86_64-unknown-motor"], which on the image are /devtools/bin/cc and /devtools/llvm/bin/llvm ar, and only with the build-script grant. The developer image ships grants for the common crates that the in-tree programs need (serde and its derive macro, libc, proc-macro2, quote, rustls, thiserror, and others).

Building

The package model

Supported: one root package, optionally a member of an explicit workspace chosen with -p; at most one library and 64 binaries; top-level tests/*.rs; a current Cargo.lock (version 3 or 4); crates.io, Git, and path dependencies, including renamed and optional ones, default and forwarded features, target-conditioned dependencies, dependency build scripts, procedural-macro dependencies, and required local patches; editions 2015 through 2024; resolvers 1 through 3; rust-version, autobins, default-run, and [lints.rust].

Not supported, by decision: root build scripts and root build-dependencies, root dev-dependencies selected for the build target, workspace-wide commands and workspace inheritance, --manifest-path and parent-directory discovery, alternative registries, a procedural-macro package as the root, examples, benches, explicit [[test]] targets, feature selection on the command line, custom JSON targets and build-std, and rustc wrappers. One known defect is stated in Lorry's own documentation: a dependency-free root build.rs is accepted by the parser but never run, so such a package should not be built with Lorry until that is fixed.

Self-hosting

Lorry builds Motor OS programs on Motor OS. The developer-image test vendors and builds red and then Lorry itself natively in a VM, from the source snapshots under /devtools/src, and checks that the freshly linked build-script and program files carry the expected permissions. Replacing the repository's Cargo-driven build of every component with Lorry is not there yet; Lorry's full-native-build.md records what remains: the root build.rs defect above, a few Cargo features the in-tree packages use (lock version 3, a profile override through the environment, root feature flags), the kernel's custom target and its build-std needs, the imager's Linux-only dependencies, and the absence of a native Clippy.

Tests

src/bin/lorry/tests/test-all.sh runs Lorry's whole suite within a fixed time budget: the unit and integration tests offline, a dependency-resolution oracle that compares Lorry's resolution with Cargo's, contract scripts for the review format, Cargo identity, workspaces, procedural macros, the registry protocol, and curl, and a native leg that boots a Motor OS VM and proves that the cross-built Lorry rebuilds itself there byte-identically and reuses its incremental state across two builds. The developer-image test run (src/tests/full-test-dev.sh) includes it; the main full-test.sh does not. Lorry's own README, specification, and design document are under src/bin/lorry/ in the repository.