Home / AI & GenAI

AI & GenAIChecklistAdvanced

Checklist: vetting open-weight models before you serve them - safetensors, pinned revisions, hashes, remote code and a private mirror

A pre-production checklist for open-weight models covering serialization formats, ModelScan, trust_remote_code, pinned commits, hash verification, licence review and an internal mirror the GPU fleet pulls from.

Deepak Bhardwaj7 min read

Applies toHugging Face Hub and hf CLI, safetensors, ModelScan, vLLM / TGI / Transformers; any Kubernetes or VM inference fleet

Why this matters

A model repository is executable supply chain, not just data. A pickle-based checkpoint can run arbitrary code when it is loaded. A repo that needs trust_remote_code runs its own Python inside your inference process. An unpinned main branch can change under you between two pod restarts. Inference nodes usually have GPU drivers, cloud credentials and network access to internal systems, so they make an attractive target.

Work through this checklist before any open-weight model or fine-tune reaches a production node pool. Run it again when you change the revision.

1. Source and identity

  • The publisher is the organisation you expect: check the org name on the Hub, not only the model name. Typo-squatted orgs and re-uploads of popular models exist.
  • The licence allows your use (commercial, redistribution, fine-tune outputs). Record the licence file's commit along with the model.
  • For a gated model, the download token belongs to a service identity with read-only scope, not a person's account.
  • Fine-tunes and merges list their base model. Assess the base as well: a problem in it carries through.

2. Pin an exact revision

Never deploy a branch name. Resolve it to a commit and pin that commit everywhere:

hf download meta-llama/Llama-3.1-8B-Instruct \
  --revision <40-character-commit-sha> \
  --local-dir /models/llama-3.1-8b-instruct

Get the current commit SHA from the API:

from huggingface_hub import HfApi
info = HfApi().model_info("meta-llama/Llama-3.1-8B-Instruct", revision="main", files_metadata=True)
print(info.sha)
for f in info.siblings:
    if f.lfs:
        print(f.rfilename, f.lfs.sha256, f.size)
  • The serving config pins the same SHA: vllm serve ... --revision <sha> or revision= in from_pretrained.
  • The tokenizer and chat template come from the same revision as the weights. A mismatched template gives quietly bad output, not an error.

Warning

--revision main together with a cache that gets wiped on node replacement means a new node can download different weights from the rest of the fleet. You end up with two models behind one service name, and evaluation results that no longer describe production.

3. Serialization format

Format Risk on load Action
*.safetensors No code execution. Tensors only Preferred
*.bin, *.pt, *.pth, *.ckpt (pickle) Can run arbitrary code Scan, then convert to safetensors in a sandbox, or reject
*.gguf Tensor format. Parser bugs have happened Keep llama.cpp / Ollama patched
*.h5, Keras Lambda layers Can contain serialized code Scan, or reject
ONNX / TensorRT engines Custom ops can load native libraries Build engines yourself from vetted weights
  • The repo has safetensors weights. If both formats are present, download only safetensors:
from huggingface_hub import snapshot_download
snapshot_download(
    repo_id="<org>/<model>",
    revision="<sha>",
    allow_patterns=["*.safetensors", "*.json", "tokenizer*", "*.model", "*.jinja"],
    local_dir="/staging/<model>",
)
  • Loader settings refuse pickle where the library supports it: use_safetensors=True in Transformers, and torch.load(..., weights_only=True) for any of your own checkpoints.

4. Scan the files

Run ModelScan on the staged directory, in CI, on a machine with no credentials:

pip install modelscan
modelscan -p /staging/<model>
echo "exit code: $?"

ModelScan exit codes (check the docs for your version): 0 no issues, 1 issues found, 2 scan error, 3 no supported files, 4 usage error.

  • The pipeline fails on any non-zero exit except 3 (3 is expected for a safetensors-only directory).
  • Run a malware scanner over the directory as well. Repos sometimes contain scripts, notebooks and archives besides the weights.
  • No unexpected executables or archives: find /staging/<model> -type f ! -name '*.safetensors' ! -name '*.json' ! -name '*.txt' ! -name '*.md' ! -name '*.model' ! -name '*.jinja'.

Tip

A clean scan doesn't prove the model behaves well. Scanners catch serialization exploits, not backdoored weights that answer normally until they see a trigger phrase. That is what the evaluation step (section 7) is for.

5. Remote code

  • Search the repo for custom code: ls /staging/<model>/*.py and check auto_map in config.json.
  • If the model needs trust_remote_code=True, someone reviews every .py file at the pinned SHA, and the review is recorded with the SHA.
  • Prefer a release where the architecture is supported natively by your serving engine version, so you don't need remote code at all.
  • --trust-remote-code never appears in a shared base image or Helm default. It's set per model, with a reference to the review.

6. Hash, mirror and promote

Production nodes should never pull from the public Hub. Promote the vetted files to an internal store and verify the hashes on every load.

cd /staging/<model>
find . -type f -print0 | sort -z | xargs -0 sha256sum > ../<model>-<sha>.sha256
az storage blob upload-batch --account-name mlmodels --destination models/<model>/<sha> \
  --source . --auth-mode login
# or: aws s3 sync . s3://ml-models/<model>/<sha>/

In the pod's init container, verify before the server starts:

cd /models/<model> && sha256sum --quiet -c /manifests/<model>-<sha>.sha256 || exit 1
  • For LFS files, the hashes match the lfs.sha256 values from the Hub API in section 2.
  • The store path includes the commit SHA, and the path is immutable (versioning, or a write-once policy).
  • Inference pods set HF_HUB_OFFLINE=1, and egress to huggingface.co is blocked from the inference namespace, so a missing file fails the pod instead of quietly downloading from the internet.
  • Only the promotion pipeline's identity can write to the model store. Inference identities can only read it.

7. Behavioural checks before promotion

  • Run your standard evaluation suite against the exact staged files, served with the production engine version and flags.
  • Add safety probes: refusal behaviour, prompt injection resistance, and a set of trigger-style prompts (unusual tokens, rare strings) compared against the base model's output.
  • Compare against the version currently in production. Look for large unexplained shifts in answer length, refusal rate or tool-call format.
  • Record the model SHA, scan result, eval scores and approver in the model registry entry.

8. Runtime hardening

Control Why
Model volume mounted read-only A compromised process can't swap the weights
Inference pods run as non-root, no privilege escalation Limits what a loader exploit can do
No cloud credentials on inference pods beyond reading the model store Contains the blast radius
Egress NetworkPolicy: only the gateway, model store and telemetry Blocks exfiltration and silent downloads
Image and model SHA logged at startup You can tell which weights served a given request
kubectl -n inference get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[0].args}{"\n"}{end}' | grep -o -- '--revision [a-f0-9]*' | sort | uniq -c

If that command prints more than one revision for a single service, the fleet is serving mixed weights.

Prevent it next time

  • Make the checklist a pipeline: download at a pinned SHA, scan, hash, evaluate, promote. Nobody copies weights by hand.
  • Re-run scans when you upgrade ModelScan or your serving engine. New detections and loader changes apply to old models too.
  • Review the model inventory every quarter: drop unused models, and confirm every production model still has a recorded SHA, licence and approver.