What a Model Artifact Actually Contains
“Load the model” sounds like one operation. In a production system it is a chain of claims:
source identity → complete file set → verified bytes → compatible architecture and runtime → matching tokenizer / processor / template → admitted memory and device plan → loaded model instance → behavioral smoke and eval gate → active routeSkipping a link can produce a clean crash, a silent quality regression, a security incident, or an unlicensed deployment. A checksum catches changed bytes; it does not prove that the tokenizer matches the weights. A loader accepting a file proves syntax compatibility; it does not prove the runtime implements the right operators. A model generating fluent text proves neither the chat template nor stopping behavior is correct.
This chapter builds the first Part 3 companion component: mosaic-models. It parses a strict model
bundle manifest, validates component completeness and resource bounds, rejects unsafe paths and
symbolic links, streams every file through SHA-256, and derives an order-independent bundle
identity. The fixture is deliberately not a trained checkpoint. It is a tiny, honest artifact
set for testing the activation boundary without downloading gigabytes or pretending to perform
inference.
Learning objectives
Section titled “Learning objectives”By the end you will be able to:
- distinguish topology/configuration, tensor values, tokenizer, processor, prompt template, generation settings, adapters, projection layers, model cards and licenses;
- explain what SafeTensors, GGUF and ONNX package—and what each may leave outside its main file;
- interpret shapes, dtypes, quantization, sharding and external tensor data as loading constraints;
- pin a model source to an immutable revision rather than a moving branch;
- define a provider-neutral execution contract without claiming all model formats are equivalent;
- validate unknown fields, duplicates, required components, normalized paths, counts and byte budgets before disk I/O;
- distinguish a file digest, manifest identity, signature, provenance statement and behavioral eval;
- verify a bundle with bounded memory and reject tampering or symlink escape;
- stage downloads and activate a complete version atomically;
- estimate disk, host-memory, accelerator-memory and transient-loading requirements separately;
- investigate “the model loaded, but quality collapsed” as an artifact-composition failure;
- decide when to use a multi-file bundle, GGUF, ONNX, a runtime-native engine, or a remote model ID.
Dependencies and completion artifact
Section titled “Dependencies and completion artifact”This chapter depends on the production material about:
- bytes, paths and Unicode;
- Serde contracts and unknown-field rejection;
- content-addressed artifacts;
- blocking-work scheduling;
- configuration, secrets and dependency policy;
- container lifecycle, readiness and rollback.
The completion artifact is a six-file synthetic chat bundle and this command:
cd rust/rust-ai-engineeringcargo run -q -p mosaic-models --example inspect_model_bundleThe checked-in bundle currently reports:
{ "manifest_identity": "82eadf133d42f26d1e250044ca5b16f924a8929038efb2d1834e819fbe2e759b", "verified_files": 6, "verified_bytes": 1062}That output proves the six declared local files match the checked-in manifest. It does not claim that the fixture can infer, that its source is signed, or that a real model is safe or useful.
Mental model: model, bundle, release
Section titled “Mental model: model, bundle, release”Use three distinct nouns:
Model means a learned mathematical function plus the architecture needed to evaluate it. Weights are learned values. Architecture defines how values and inputs flow through operations.
Model bundle means the exact machine-consumable artifacts needed for one execution contract: model structure, weights, tokenizer or media processor, formatting rules, and supporting metadata. One physical file can provide multiple logical components; one component can span many files.
AI release means the bundle plus the harness that uses it: prompts, retrieval index, tools, policies, parsers, eval set, thresholds, routing and deployment configuration. The same bundle under two harnesses can be two materially different products.
That distinction prevents a common rollback error. Restoring yesterday’s weights while retaining today’s tokenizer, template, tools or retrieval schema does not restore yesterday’s behavior.
Inventory: what may be in the bundle
Section titled “Inventory: what may be in the bundle”Architecture or executable graph
Section titled “Architecture or executable graph”An architecture-based runtime such as Candle may compile model implementations into the
application. A config.json supplies parameters such as model type, layer count, hidden size,
attention heads, vocabulary size and positional behavior. The runtime selects code and constructs
the graph; the config is not the weights.
ONNX serializes a computation graph with nodes, inputs, outputs, initializers, operator-set imports and version metadata. That is more explicit, but “ONNX” still does not mean every runtime supports every operator, opset, extension domain, data type, shape or device placement. The runtime must reject unsupported contracts rather than improvise.
GGUF is intended as an inference format for GGML-family executors. It stores key-value metadata and tensors in one binary and can carry architecture and tokenizer information. “Single file” improves distribution; it does not remove runtime-version, template, license or provenance decisions.
Tensor values
Section titled “Tensor values”A tensor has:
- a stable name interpreted by the architecture;
- a dtype or quantized representation;
- a shape;
- contiguous bytes or format-specific blocks;
- sometimes placement, layout or quantization metadata.
SafeTensors begins with an eight-byte little-endian header length, a bounded JSON header describing tensor names, dtypes, shapes and byte offsets, then a byte buffer. It prohibits holes in the buffer and duplicate keys in the format contract. It avoids executable pickle semantics, but safe parsing does not make the numerical values trustworthy. NaNs, poisoned behavior and a mismatched model can still be valid tensor bytes.
Large checkpoints are commonly sharded. An index maps parameter names to shard files. Sharding changes distribution and peak loading behavior; it must not change the logical parameter set. Every shard and the index belong to the identity. A missing final shard should fail before allocating a model.
Tokenizer
Section titled “Tokenizer”A text model consumes token IDs, not strings. A tokenizer defines normalization, pre-tokenization, vocabulary, merge rules or model, special tokens, added tokens, and ID assignments. The same text under a different tokenizer can become a different ID sequence. A vocabulary-size match is necessary but not sufficient: two vocabularies can have equal length and incompatible IDs.
Therefore:
weights + wrong tokenizer ≠ slightly different modelweights + wrong tokenizer = invalid composed systemRecord the tokenizer bytes in the same release identity and test sentinel strings, Unicode, whitespace, tool delimiters, image/audio placeholders, BOS/EOS/PAD behavior and round trips.
Prompt or chat template
Section titled “Prompt or chat template”An instruction-tuned model learned a particular serialization of roles and content. A template may insert begin/end tokens, role markers, generation prompts, tool schemas and multimodal placeholders. A visually plausible alternative can silently reduce quality or prevent stopping.
Current Transformers documentation stores standalone chat templates as Jinja and warns that multimodal templates belong to the processor because content contains typed image/video parts. It also notes cross-language Jinja differences: Python-specific methods and implicit collection rendering can behave differently in a Rust implementation. A template is executable-ish configuration: parse it under limits, restrict features, render golden conversations, then tokenize and compare exact IDs.
Media processor
Section titled “Media processor”Image, audio and video models need more than a tokenizer:
- image color mode, resize policy, interpolation, crop, orientation and channel normalization;
- audio decoder, channel mixing, sample rate, window, hop, mel filters and normalization;
- video decoder, timestamp basis, frame sampler, rotation, pixel conversion and clip policy;
- special placeholders and projection layers that align media features with a language model.
Changing bilinear to nearest-neighbor resize or 16 kHz to 44.1 kHz is an input-contract change even if the model file is untouched. Part 4 makes these transformations explicit evidence stages.
Adapters and projections
Section titled “Adapters and projections”LoRA adapters are deltas interpreted relative to a specific base model, target module names, rank, scaling and merge policy. A vision-language projector joins components with particular hidden dimensions. Treat these as dependency edges:
adapter identity → exact base identity + adapter configprojector identity → exact vision encoder + exact language model + processor“Compatible architecture family” is too weak. Activation should validate exact expected base identity and tensor dimensions.
Generation configuration
Section titled “Generation configuration”Default sampling temperature, top-p/top-k, repetition penalties, maximum output, stop IDs and decoder-specific settings can alter behavior without changing weights. Store defaults as versioned configuration, but let the harness own request-specific policy. Never let untrusted model metadata silently override service safety, budget or stop limits.
Model card, license and provenance
Section titled “Model card, license and provenance”A model card communicates intended use, limitations, training/evaluation context, datasets and known risks. It is human-facing evidence, not an executable compatibility check. License terms answer whether and how the artifact can be used or redistributed. A repository’s code license does not automatically license model weights, datasets or outputs.
Provenance should bind:
- original repository and immutable revision;
- producer/conversion tool and reviewed version;
- source checkpoint identity;
- conversion, quantization or merge parameters;
- expected architecture/runtime/device contract;
- each output digest;
- model card, license and relevant evaluation report;
- approver or automated policy decision.
The companion requires both license and model_card as explicit required components. This is a
local activation policy, not a claim that those files make a model legally or ethically acceptable.
Format is a packaging decision, not a capability
Section titled “Format is a packaging decision, not a capability”| Format or layout | What it represents well | Common companion needs | Important boundary |
|---|---|---|---|
| SafeTensors | Named dense tensor values with dtype, shape and offsets | Architecture config, tokenizer/processor, template, generation config, card/license; index if sharded | No executable graph; safe parsing is not model trust |
| GGUF | GGML-oriented metadata and tensors, often quantized, often one file | Runtime compatibility, optional external template/card/license/provenance | Executor support is architecture/key/version dependent |
| ONNX | Versioned computation graph, operators and tensor initializers | Tokenizer/processor, semantic input/output mapping, card/license; external data may be separate | Portable specification does not guarantee every runtime/provider supports the graph |
| Runtime engine | Optimized graph/weights for a target stack | Exact builder/runtime/driver/hardware metadata and preprocessors | Often less portable and expensive to rebuild |
| Remote model ID | Provider-side route to a managed model | Provider, exact version/snapshot when available, capability/schema/limits, eval baseline | You may not possess or hash implementation bytes; provider behavior can change |
The bundle manifest describes logical components rather than assuming a filename layout. A GGUF
entry can declare that one file provides model_graph, weights and tokenizer. Six SafeTensors
shards can each provide weights, while an index entry provides weight_index. The execution
contract states which components are required for this exact deployment.
Step 1: define a strict manifest
Section titled “Step 1: define a strict manifest”The companion uses Serde structs with deny_unknown_fields:
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]#[serde(deny_unknown_fields)]pub struct ModelBundleManifest { pub schema_version: u16, pub artifact_id: String, pub source: ModelSource, pub execution: ExecutionContract, pub files: Vec<ModelFile>,}The top level separates origin, execution assumptions and physical files. schema_version is an
integer with one supported value. A loader must reject unknown future semantics, not partially
interpret them.
The source requires an immutable revision:
pub struct ModelSource { pub repository: String, pub immutable_revision: String, pub producer: String, pub created_at: String,}The teaching contract accepts a 40-character Git-style hex revision or 64-character content digest.
That syntax cannot prove a remote server actually treats the revision as immutable; the fetcher
must resolve and verify that fact. Never put main, latest or a mutable tag into a release
manifest.
The execution contract is intentionally closed:
pub struct ExecutionContract { pub architecture: String, pub runtime: RuntimeFamily, pub numeric_format: NumericFormat, pub maximum_context_tokens: Option<u64>, pub capabilities: Vec<ModelCapability>, pub required_components: Vec<ComponentRole>,}RuntimeFamily names a compatibility family, not a full runtime lock. A production manifest should
also bind a tested version range, features, target triple, accelerator backend, driver/API range and
kernel/quantization support. Those fields arrive in the later local-inference milestone.
Capabilities describe interfaces the bundle is intended to support. They are not eval claims.
Declaring image_understanding says an adapter may accept typed image evidence; only a frozen eval
can say how well it does so.
Step 2: let files provide logical components
Section titled “Step 2: let files provide logical components”The file record is:
pub struct ModelFile { pub path: PathBuf, pub media_type: String, pub size_bytes: u64, pub sha256: String, pub provides: Vec<ComponentRole>,}Why store size as well as digest?
- reject unexpected size before hashing;
- calculate download and disk budgets without opening files;
- detect truncation cheaply;
- bound total work;
- preallocate or admit storage;
- make accidental wrong-file diagnosis clearer.
Why not use file extension as role? model.bin is ambiguous; extensions do not prove content.
Role and media type are declared, then a format-specific parser must validate bytes before loading.
The checked-in fixture has:
model-config.json → model_graphweights.fixture → weightstokenizer.json → tokenizerchat_template.jinja → prompt_templateLICENSE.txt → licenseREADME.md → model_cardThe .fixture file explicitly says it is not an inference checkpoint. Giving fake bytes a
.safetensors extension would teach the wrong lesson.
Step 3: validate before touching model bytes
Section titled “Step 3: validate before touching model bytes”validate enforces structural and budget invariants:
manifest.validate(ValidationLimits { maximum_files: 16, maximum_total_bytes: 32 * 1024,})?;The implementation checks:
- exact schema version;
- bounded, printable identity/provenance fields;
- immutable-revision syntax and explicit UTC timestamp;
- nonzero context limit when present;
- nonempty, duplicate-free capabilities and required components;
- explicit governance components;
- file-count bound;
- normalized relative paths only;
- unique paths;
- valid bounded media types;
- nonzero file sizes and lowercase SHA-256 syntax;
- nonempty, duplicate-free component declarations per file;
- checked total-byte addition and bundle-size bound;
- every required component is provided.
Vec is deliberate even when the values act like a set. Deserializing directly into a set would
silently collapse duplicate declarations, hiding malformed producer output.
Relative paths accept only normal components. They reject absolute paths, .., ., root and
platform prefixes. This protects the declaration, but it is not enough: a safe-looking path can
still pass through a symbolic link.
Validation has no model allocation and no network activity. Run it before reserving accelerator memory or opening format parsers.
Step 4: derive a bundle identity
Section titled “Step 4: derive a bundle identity”Hashing the raw JSON is fragile because insignificant whitespace and object order change bytes. Re-serializing JSON is also a contract: map ordering, number handling and serializer changes must be versioned.
identity() uses an explicit length-prefixed field encoding. It:
- validates first;
- writes each named scalar field in a fixed order;
- sorts capability and required-component enums;
- sorts files by normalized path;
- sorts each file’s provided roles;
- includes path, media type, size, digest and component roles;
- hashes the stream with SHA-256.
Length prefixes prevent ambiguous concatenation. For example, ab + c and a + bc cannot
collide at the encoding layer. The manifest schema version also versions identity semantics.
The result identifies the manifest contract, not the concatenated model bytes. Changing a license, template, runtime family or required component changes identity even when weights do not.
For interoperability across languages, publish the encoding specification and cross-language golden vectors or adopt a standardized canonical serialization. This book’s encoding is intentionally small and local; it is not presented as an external standard.
Step 5: verify the local bundle
Section titled “Step 5: verify the local bundle”verify_bundle first validates, then:
- canonicalizes the root and requires a directory;
- walks every relative path component using
symlink_metadata; - rejects any symbolic link in the path;
- canonicalizes the final file and checks it remains under the root;
- requires a regular file;
- compares actual and declared sizes;
- reads through a 64 KiB buffer into SHA-256;
- compares the lowercase digest;
- returns only after every file passes.
The streaming loop is bounded:
let mut reader = BufReader::with_capacity(64 * 1024, file);let mut buffer = [0_u8; 64 * 1024];let mut hasher = Sha256::new();
loop { let read = reader.read(&mut buffer)?; if read == 0 { break; } hasher.update(&buffer[..read]);}This uses constant application memory rather than read_to_end on a multi-gigabyte shard. Hashing
is blocking disk work; call it during controlled startup or through spawn_blocking, with a
semaphore that prevents many large bundles from saturating storage.
The verifier rejects symlinks, but path verification followed by open still has a time-of-check to
time-of-use race if an attacker can mutate the directory concurrently. A hardened Linux loader can
stage into a directory writable only by the fetcher, use directory-relative handles and
openat2-style resolution restrictions, verify an opened handle, then make the tree immutable and
publish it. Process/container isolation remains important.
Step 6: stage, verify, publish, activate
Section titled “Step 6: stage, verify, publish, activate”Never download directly into the active model directory.
resolve immutable source revision → create private staging directory → fetch explicit allowlisted paths under per-file + total limits → fsync files and directory as required → parse strict manifest → verify size + digest + confinement → format-specific metadata validation → compatibility + resource admission → model construction and warm-up → frozen smoke/eval suite → atomically publish content-addressed directory → atomically switch route to new release identityIf any step fails, the previous release remains active. Do not mutate files beneath a loaded memory map. Publish a new immutable version and switch a small pointer or routing record.
A shared cache can store blobs by digest and construct snapshot trees for manifests. Pin active and rollback versions so garbage collection cannot delete them. Lock per digest or use atomic no-clobber publication to avoid duplicate writers. Quotas must include staged, active, rollback and temporary conversion space.
Partial HTTP downloads need:
- expected length and digest;
- bounded redirects and approved schemes/hosts;
- timeouts, retries and range semantics;
- a unique temporary name;
- no credential leakage into logs or redirects;
- restart-safe state or deliberate restart;
- verification before rename;
- cleanup policy for abandoned staging.
The Hub’s documented cache similarly separates repository references, immutable snapshots and content blobs. That architecture is useful; your production trust policy still decides which revision and files may activate.
Step 7: perform format-specific validation
Section titled “Step 7: perform format-specific validation”The outer SHA-256 proves bytes match the manifest. Next parse the format under explicit limits.
For SafeTensors:
- cap header length before allocation;
- reject malformed JSON, duplicates, unsupported dtypes and impossible shapes;
- validate offsets are ordered, in bounds and cover the buffer according to the format;
- compare tensor names, shapes and dtypes with the chosen architecture;
- if sharded, parse the index and prove every parameter maps to exactly one declared shard;
- decide whether memory mapping is safe for the lifecycle and filesystem.
For GGUF:
- check magic/version and bounded counts/string lengths;
- validate metadata and tensor offsets/alignment;
- require supported architecture and quantization types in the exact executor version;
- treat embedded tokenizer/template metadata as the components it actually provides;
- reject unknown required keys rather than applying defaults that alter behavior.
For ONNX:
- validate the IR and every imported opset/domain;
- check graph inputs/outputs, dtypes, ranks and semantic names;
- account for external tensor data;
- ask the selected execution provider whether every node is supported;
- decide whether fallback to CPU is permitted or a deployment error.
Parser success still precedes allocation. First inspect metadata, calculate a memory plan, reserve capacity, then create tensors.
Memory is not file size
Section titled “Memory is not file size”A rough dense weight payload is:
parameter_count × bytes_per_parameterBut runtime peak also includes:
- quantization scales/metadata and possible dequantization buffers;
- memory-mapped page residency;
- duplicated host and device copies during upload;
- graph/runtime structures and kernels;
- KV cache, often proportional to layers × context × batch;
- input tensors, vision/audio features and temporary activations;
- allocator fragmentation and backend workspaces;
- concurrent requests and batches;
- adapter merges or converted dtype copies.
A 4-bit file can expand to fp16/fp32 if a loader does not execute the quantized representation natively. Conversely, a mapped file’s virtual address range is not the same as resident memory. Measure:
disk bytesstartup host peaksteady host resident setdevice committed and peak byteswarm-up latencyper-request KV/activation slope by context and batchAdmission uses measured upper bounds plus headroom, not the checkpoint size printed on a model page.
Integrity, authenticity, provenance and quality
Section titled “Integrity, authenticity, provenance and quality”These controls answer different questions:
| Evidence | Question answered | What it does not answer |
|---|---|---|
| SHA-256 file digest | Did these bytes change relative to this manifest? | Who produced or approved them? |
| Manifest identity | Did any bound component/contract metadata change? | Is the manifest authoritative? |
| Signature | Did a holder of a key sign this subject? | Was the key/workflow trusted, or is content safe? |
| Provenance/attestation | Which declared workflow and inputs produced the subject? | Whether declarations are true without verification; whether behavior is good |
| License/model card | What use terms and documented intent/limits are stated? | Runtime compatibility or actual quality |
| Structural parser | Are bytes valid for this format and supported contract? | Whether the learned behavior is acceptable |
| Eval report | Did a specific release pass defined cases/thresholds? | Universal correctness or future-distribution behavior |
Production policy composes them. For example: accept only a digest from an approved registry, signed by a CI identity, with provenance from a reviewed converter revision, an allowed license, supported tensor metadata, a passing malware/secret scan where applicable, and an eval report bound to the complete AI release identity.
Avoid pickle-shaped formats for untrusted acquisition because deserialization may execute code. “Trust remote code” is code execution, not a compatibility flag. If custom code is unavoidable, review and pin it, build it as ordinary software, scan dependencies, and run it in a constrained conversion environment—not inside the serving process.
Failure investigations
Section titled “Failure investigations”Failure 1: weights verify, output becomes nonsense
Section titled “Failure 1: weights verify, output becomes nonsense”Symptoms:
- fluent fragments mixed with invalid characters;
- control tokens appear in output;
- stopping never fires;
- quality collapses while latency looks normal.
Investigation:
- record bundle and complete AI release identities;
- compare tokenizer and template digests with the evaluated release;
- render a frozen conversation and compare exact bytes and token IDs;
- inspect BOS/EOS, added tokens and special-token IDs;
- verify vocabulary size and selected sentinel ID mappings;
- compare runtime generation defaults and stop IDs;
- replay the same deterministic-enough input against the prior bundle.
Root cause is often composition, not corrupted weights. Repair by restoring the evaluated tokenizer/template/generation contract and adding golden tokenization tests.
Failure 2: a sharded model fails late after a long download
Section titled “Failure 2: a sharded model fails late after a long download”Symptoms: five shards hash, model construction reports missing parameter names from shard six.
Cause: downloader inferred files from a glob and missed the index or final shard. Repair:
- acquire the pinned manifest/index first;
- derive an explicit file plan under count/size limits;
- fetch all declared files into staging;
- verify completeness and hashes before construction;
- retain resumable verified blobs;
- test missing, duplicate, wrong-size and wrong-digest shards.
Failure 3: “works on CPU,” silently crawls on GPU deployment
Section titled “Failure 3: “works on CPU,” silently crawls on GPU deployment”Symptoms: process is ready, device utilization is low, latency is many times the benchmark.
Possible cause: unsupported ONNX nodes fell back to CPU, tensors cross host/device repeatedly, or the requested dtype was converted. Log bounded facts: runtime/provider version, selected device, unsupported-node count, actual dtype/quantization, placement summary and memory plan. Decide policy: explicitly allow measured fallback for some nodes or fail readiness. Never advertise the GPU path based only on configuration intent.
Failure 4: mutable source changes beneath a familiar model name
Section titled “Failure 4: mutable source changes beneath a familiar model name”Symptoms: a cold node downloads different bytes, only new replicas regress, rollback by model name does not restore behavior.
Cause: branch/tag/model alias was the deployment identity. Resolve an immutable revision during release preparation, record every digest, mirror approved artifacts if availability matters, and route only by complete release identity.
Failure 5: valid digest, malicious source
Section titled “Failure 5: valid digest, malicious source”A malicious producer can publish a manifest and matching hashes. Digest verification will pass perfectly. Enforce source trust, signature/provenance policy, safe formats, isolation, resource limits, license review and behavioral/safety evals. Integrity is necessary and insufficient.
Local and remote model contracts
Section titled “Local and remote model contracts”With local inference, you can normally bind exact artifact bytes, runtime build, hardware class and pre/postprocessing. You also own downloads, disk, memory, kernels, batching and CVE response.
With remote inference, the provider may expose only a model name or dated snapshot. Your manifest should instead bind the strongest observable contract:
- provider and endpoint/region policy;
- exact version or dated snapshot when supported;
- capabilities and media/schema limits;
- tokenizer/counting endpoint semantics if exposed;
- request defaults and response schema;
- data retention/training/residency policy version;
- rate, latency and cost assumptions;
- probe/eval results and change-detection policy.
Do not fabricate a SHA-256 for weights you cannot inspect. Hash your provider configuration and
harness release, store provider-returned version/fingerprint fields, and continuously evaluate
observable behavior. A provider-neutral trait comes later; it must preserve these capability
differences rather than reducing every backend to prompt -> String.
Alternatives and trade-offs
Section titled “Alternatives and trade-offs”One manifest per bundle vs one organizational catalog
Section titled “One manifest per bundle vs one organizational catalog”A bundle-local manifest is portable, immutable and easy to verify offline. A central catalog adds approval, tenancy, revocation and rollout state. Use both: the bundle declares facts; the catalog records organizational policy and mutable activation decisions.
Hash every file vs hash an archive
Section titled “Hash every file vs hash an archive”An archive digest is useful for transport, but extraction adds path, link, permission and decompression risks. Per-file digests support dedupe and precise diagnosis. A robust package can bind both archive and expanded manifest, extract under strict limits, then verify every result.
Memory map vs buffered reads
Section titled “Memory map vs buffered reads”Memory mapping can reduce copies and defer page loading. It binds file lifetime to tensor lifetime, interacts with page cache, and may expose the process to later file mutation or truncated network filesystems. Use immutable local files, keep handles/lifetimes explicit, and benchmark. The SafeTensors Candle API offers both mapped and buffered approaches; choose from lifecycle and memory evidence rather than slogans.
Convert at startup vs ahead of release
Section titled “Convert at startup vs ahead of release”Startup conversion makes pods slow, nondeterministic and resource-hungry. Prefer an isolated, versioned build pipeline that emits a verified runtime artifact plus provenance and evals. Runtime conversion may be acceptable for development; never report ready until it and validation finish.
Test the boundary
Section titled “Test the boundary”The crate has seven library tests and one focused example test:
- valid checked-in bundle verifies under tight limits;
- file/list ordering does not change identity;
- a missing component fails before I/O;
- parent traversal is rejected;
- unknown JSON fields are rejected;
- changed bytes cannot activate;
- a symlinked model file is rejected on Unix;
- the runnable example verifies exactly six files.
Run:
cd rust/rust-ai-engineeringcargo test -p mosaic-models --all-targetscargo clippy -p mosaic-models --all-targets -- -D warningscargo run -q -p mosaic-models --example inspect_model_bundleTry a controlled failure:
cp -R fixtures/models/tiny-chat-bundle target/tampered-model-bundleprintf 'tampered\n' >> target/tampered-model-bundle/weights.fixturecargo run -q -p mosaic-models --example inspect_model_bundleThe last command still checks the checked-in fixture because the example fixes its root. For a
real operator CLI, accept an explicit root and manifest path only after adding path policy. To
exercise tampering today, use the tampered_bytes_do_not_activate test; it copies the fixture,
changes the weight bytes, verifies rejection, and removes the temporary tree.
Exercises
Section titled “Exercises”Exercise 1: format-aware metadata probe
Section titled “Exercise 1: format-aware metadata probe”Add a FormatKind to ModelFile and implement a bounded SafeTensors header reader. It must read
only eight bytes plus the declared header, cap the header before allocation, reject an invalid
opening byte, and return tensor name/dtype/shape metadata without loading tensor values. Add a
minimal valid fixture and malformed length/offset cases.
Exercise 2: sharded checkpoint completeness
Section titled “Exercise 2: sharded checkpoint completeness”Add a strict index fixture mapping tensor names to two shards. Prove:
- every mapped shard is declared;
- every declared weight shard is referenced unless policy permits auxiliary shards;
- tensor names are unique;
- empty maps and absolute/traversal shard paths fail;
- index total size agrees with declared shards under clearly documented semantics.
Exercise 3: compatibility receipt
Section titled “Exercise 3: compatibility receipt”Define a CompatibilityReceipt containing manifest identity, runtime name/version/features, target
triple, device class, selected dtype, supported context, inspected tensor summary and timestamp.
Make activation require the receipt plus a smoke/eval report bound to the same manifest identity.
Exercise 4: atomic activation registry
Section titled “Exercise 4: atomic activation registry”Build a local registry with staging/, blobs/, snapshots/ and an active pointer. Simulate a
crash after each step. Recovery must expose either the old complete version or the new complete
version—never a partial snapshot. Pin one rollback version and implement safe garbage collection.
Exercise 5: template/token golden vector
Section titled “Exercise 5: template/token golden vector”Create a message fixture containing system/user/assistant roles, Unicode, empty content, a tool call and an image placeholder. Store expected rendered bytes and token IDs. Demonstrate that a one-token template change alters release identity and fails the golden before inference.
Solution sketches
Section titled “Solution sketches”- Read the little-endian
u64header length, convert withusize::try_from, compare with both a configured limit and file length, then use a duplicate-aware JSON parser or the official SafeTensors crate. Validate offsets with checked arithmetic. Never trust header sizes to allocate. - Parse the index with
deny_unknown_fields; normalize each referenced path using the same path policy; compare sets in both directions; calculate sums withchecked_add; report the first precise invariant violation. - Treat compatibility as cached evidence keyed by manifest identity plus exact runtime/hardware identity. Invalidate on either side changing. A receipt is not transferable to an untested GPU class merely because the model digest is the same.
- Write unique staging state, fsync where crash durability requires it, publish immutable blobs with no-clobber semantics, create a complete snapshot, then atomically replace a small pointer. On startup, ignore or quarantine incomplete staging directories.
- Golden test the render and token IDs as byte/number arrays, not a screenshot. Bind the processor, template and tokenizer digests to the release; show a readable diff when the expected contract intentionally changes.
Completion checklist
Section titled “Completion checklist”Do not advance merely because a framework loaded one model. You are complete when you can show:
- a strict, versioned manifest rejects unknown fields;
- source is pinned to an immutable revision and every local file has size plus digest;
- required runtime components are explicit for each format/layout;
- tokenizer/processor/template are versioned with the weights;
- license, model card and conversion provenance are present and reviewed;
- file count, total bytes, header sizes, shapes and allocations have bounds;
- absolute, parent-traversal, symlink and out-of-root paths fail;
- hashing streams in bounded memory and runs outside latency-sensitive async workers;
- format-specific parsing happens before model allocation;
- runtime/operator/dtype/device compatibility is tested, not inferred from extension;
- measured startup and steady-state host/device memory fit with headroom;
- warm-up and frozen behavioral eval bind to the complete AI release identity;
- active and rollback versions are immutable and atomically selectable;
- failure output identifies exact file/component/contract without leaking credentials;
- tamper, missing shard, wrong tokenizer/template and unsupported runtime paths are tested.
References
Section titled “References”- SafeTensors format and security-oriented layout
- SafeTensors metadata range parsing
- GGUF specification
- ONNX intermediate representation specification
- Transformers sharded-checkpoint loading and index
- Transformers chat-template contract
- Writing portable text and multimodal chat templates
- Hugging Face model cards
- Hugging Face immutable snapshot and blob cache design
- Candle SafeTensors loading APIs
The bundle is now a checked input to inference, but we have not yet explained what its tensors do. Next: tokens, embeddings, tensor shapes and transformer mechanics from the application engineer’s point of view.