Documentation

Models

Layer Files Where it comes from
L1 none built into the binary
L2 models/cache/guardspec-classifier/v3b/ (model_int8.onnx, tokenizer.json, config.json, thresholds.json, calibration.json) models pull: Hugging Face once published, else the GitHub Release model-guardspec-classifier-v3b; SHA-256 pinned in models/manifest.yaml. Older installs in models/local/guardspec-classifier-v3b/ (then -v2, -v1, -v0) still load
L3 models/cache/multilingual-e5-small/614241f/ (model.onnx, tokenizer.json, config.json) models pull: Hugging Face, pinned by commit and SHA-256. The older models/cache/multilingual-e5-small/ still loads

Compose pulls both before the gateway starts (the models-init service). Versions, pinning, rollback and air-gapped installs: docs/models.md.

The model layers need a gateway built with -tags onnx (cgo, ONNX Runtime). The image that docker compose builds from build/gateway.Dockerfile is, and so are the release gateway images and the _onnx linux tarballs: they run L1 + L2 + L3 when the models are mounted. The plain release archives are static builds without -tags onnx and run L1 only (see Install from a release). GET /readyz lists the active layers.

L1 only. This is what you get without model files. The gateway logs a warning per missing layer and keeps running. DETECTOR_LAYERS=l1 silences the warnings. DETECTOR_REQUIRE_ML=true makes it refuse to start instead (not enforced with GUARD_ENV=dev).

Pulling by hand.

docker compose --profile models run --rm models-pull   # recommended versions -> models/cache/<name>/<version>
# or, without Docker: go run ./cmd/models pull
go run ./cmd/models list --available                   # versions in the manifest

L2 classifier from source. Instead of pulling it:

  1. Build it from train/ (deterministic data build; training takes about 40 minutes on an Apple M3 Max with MPS; see train/MODEL_CARD.md). v3b also trains on owner-supplied rows that are not in this repository; without them these commands reproduce the v3 recipe, not v3b exactly:

    cd train
    uv sync
    uv run python data/build_data.py
    uv run python train.py
    uv run python export.py      # writes ../models/local/guardspec-classifier-v3b
    

    MPS training is not bit-reproducible, so your weights and numbers can differ slightly from ours.

  2. Copy an existing guardspec-classifier-v3b directory (from a teammate or an earlier build) to models/local/guardspec-classifier-v3b.

Then restart the gateway (docker compose up -d gateway); the log line L3 embeddings loaded and the absence of L2: warnings confirm the layers are on. models/ is mounted read-only at /models and never baked into an image.

Trying a new version safely. Run it in shadow mode first (SHADOW_L2_MODEL=guardspec-classifier@<candidate>): it scores the same traffic as the active classifier and only records what it would have decided. The admin UI's Models page compares the two (agreement rate, disagreements in each direction, score distributions) and says when the manifest recommends a version other than the running one. Switch without a restart with kill -HUP or POST /admin/models/reload. Details in Shadow mode and model reload.

Know before enabling L2/L3. On our eval set they raise recall but also block about 1.4% of benign prompts with classifier v3b (L1 alone: 0.1%), mostly Vietnamese teencode (3.5%) and English (1.7%). See Benchmarks. Where false positives cost more than missed attacks, keep DETECTOR_LAYERS=l1.

Source: README.md#models @ ca69dad29f02