Architecture¶
How it works¶
- Resolve: emulate the
ld.sosearch order from the ELF itself (RPATH is transitive, RUNPATH is not,$ORIGINis per-object). An object that has RUNPATH searches no RPATH, neither its own nor one it inherits. Each RPATH or RUNPATH string is a:list, and its entries are searched in order. An empty entry means the directory the program starts in. A build cannot know that directory, so Scratchsmith does not search an empty entry. If the split entries miss, a string that holds:is also tried whole as one directory, before the default directories. The loader finds aDT_NEEDEDlibrary there only through the image'sld.so.cache. Ifldconfigdoes not cache the library under the needed name, Scratchsmith does not take it. The name must start withliborld-and hold.so, or start withld.so.orld64.so.. The library's own soname must be that name, or absent. An--includefile in that directory stages with no such check. Scratchsmith never scrapes the host'sldd,ld.so.cache, orLD_LIBRARY_PATH, so the result is deterministic. - Stage: copy the interpreter to its verbatim path, mirror the libraries, recreate
versioned-soname symlinks, regenerate
ld.so.cache, and add the glibc NSS pieces. - Assemble: build a non-root image with reproducible layers (sorted entries, zeroed
mtime/uid/gid). The uncompressed
diff_idand the gzip layer digest are computed separately, which avoids the classic unpullable-image bug.
Stability¶
Scratchsmith follows Semantic Versioning. As of 1.0, these surfaces are a stable contract. A breaking change to any of them requires a new major version:
- CLI flags: the names and meaning of
pack/lint/doctor/index/graph/diff/unpackflags, and the top-level--completions. New flags arrive in minor releases. A removed or renamed flag, or a changed default, is a major change. scratchsmith.toml: the configuration keys and their types (the Configuration reference).--format json: the field names and types of thepack,index,graph,diff, andunpackreports (the schema CI gates consume). Each is pinned by a golden test, so a change is always deliberate.- Exit codes:
0on success.2on a usage error: an argument-parse error, or a command line that scratchsmith refuses after the parse, such as--watch --format jsonorpackwith no binary.1on any other failure, such as a failed pack. Before 2.0.0, a refusal made after the parse exited1. Forscratchsmith.tomlthe same rule holds. A value that parses and is then refused exits2, for example alabelwith no=. A file that cannot be read or parsed exits1, for example a value of the wrong type or an unknown key. A nested[profile.a.profile.b]table is refused when the file loads, so it exits1too.
Not frozen (these can change in any release): human-readable text and warning wording, log
output, doctor's exact phrasing, the Rust library API, and the exact bytes of the produced
image. The crate's pub items exist for its own tests, not as a supported API. Layers stay
reproducible for identical inputs, but that is a property, not a frozen API.
COMPATIBILITY.md covers how the contract changes over time. It describes the deprecation cycle (keep it working, warn on stderr, remove only in the next major), and how a test guards each surface.