Skip to content

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 route

Skipping 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.

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.

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:

Terminal window
cd rust/rust-ai-engineering
cargo run -q -p mosaic-models --example inspect_model_bundle

The 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.

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.

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.

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.

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 model
weights + wrong tokenizer = invalid composed system

Record 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.

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.

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.

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 config
projector identity → exact vision encoder + exact language model + processor

“Compatible architecture family” is too weak. Activation should validate exact expected base identity and tensor dimensions.

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.

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 layoutWhat it represents wellCommon companion needsImportant boundary
SafeTensorsNamed dense tensor values with dtype, shape and offsetsArchitecture config, tokenizer/processor, template, generation config, card/license; index if shardedNo executable graph; safe parsing is not model trust
GGUFGGML-oriented metadata and tensors, often quantized, often one fileRuntime compatibility, optional external template/card/license/provenanceExecutor support is architecture/key/version dependent
ONNXVersioned computation graph, operators and tensor initializersTokenizer/processor, semantic input/output mapping, card/license; external data may be separatePortable specification does not guarantee every runtime/provider supports the graph
Runtime engineOptimized graph/weights for a target stackExact builder/runtime/driver/hardware metadata and preprocessorsOften less portable and expensive to rebuild
Remote model IDProvider-side route to a managed modelProvider, exact version/snapshot when available, capability/schema/limits, eval baselineYou 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.

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_graph
weights.fixture → weights
tokenizer.json → tokenizer
chat_template.jinja → prompt_template
LICENSE.txt → license
README.md → model_card

The .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:

  1. exact schema version;
  2. bounded, printable identity/provenance fields;
  3. immutable-revision syntax and explicit UTC timestamp;
  4. nonzero context limit when present;
  5. nonempty, duplicate-free capabilities and required components;
  6. explicit governance components;
  7. file-count bound;
  8. normalized relative paths only;
  9. unique paths;
  10. valid bounded media types;
  11. nonzero file sizes and lowercase SHA-256 syntax;
  12. nonempty, duplicate-free component declarations per file;
  13. checked total-byte addition and bundle-size bound;
  14. 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.

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.

verify_bundle first validates, then:

  1. canonicalizes the root and requires a directory;
  2. walks every relative path component using symlink_metadata;
  3. rejects any symbolic link in the path;
  4. canonicalizes the final file and checks it remains under the root;
  5. requires a regular file;
  6. compares actual and declared sizes;
  7. reads through a 64 KiB buffer into SHA-256;
  8. compares the lowercase digest;
  9. 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.

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 identity

If 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.

A rough dense weight payload is:

parameter_count × bytes_per_parameter

But 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 bytes
startup host peak
steady host resident set
device committed and peak bytes
warm-up latency
per-request KV/activation slope by context and batch

Admission 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:

EvidenceQuestion answeredWhat it does not answer
SHA-256 file digestDid these bytes change relative to this manifest?Who produced or approved them?
Manifest identityDid any bound component/contract metadata change?Is the manifest authoritative?
SignatureDid a holder of a key sign this subject?Was the key/workflow trusted, or is content safe?
Provenance/attestationWhich declared workflow and inputs produced the subject?Whether declarations are true without verification; whether behavior is good
License/model cardWhat use terms and documented intent/limits are stated?Runtime compatibility or actual quality
Structural parserAre bytes valid for this format and supported contract?Whether the learned behavior is acceptable
Eval reportDid 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 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:

  1. record bundle and complete AI release identities;
  2. compare tokenizer and template digests with the evaluated release;
  3. render a frozen conversation and compare exact bytes and token IDs;
  4. inspect BOS/EOS, added tokens and special-token IDs;
  5. verify vocabulary size and selected sentinel ID mappings;
  6. compare runtime generation defaults and stop IDs;
  7. 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.

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.

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.

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.

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 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.

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.

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:

Terminal window
cd rust/rust-ai-engineering
cargo test -p mosaic-models --all-targets
cargo clippy -p mosaic-models --all-targets -- -D warnings
cargo run -q -p mosaic-models --example inspect_model_bundle

Try a controlled failure:

Terminal window
cp -R fixtures/models/tiny-chat-bundle target/tampered-model-bundle
printf 'tampered\n' >> target/tampered-model-bundle/weights.fixture
cargo run -q -p mosaic-models --example inspect_model_bundle

The 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.

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.

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.

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.

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.

  1. Read the little-endian u64 header length, convert with usize::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.
  2. Parse the index with deny_unknown_fields; normalize each referenced path using the same path policy; compare sets in both directions; calculate sums with checked_add; report the first precise invariant violation.
  3. 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.
  4. 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.
  5. 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.

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.

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.