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:
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-v3bMPS training is not bit-reproducible, so your weights and numbers can differ slightly from ours.
Copy an existing
guardspec-classifier-v3bdirectory (from a teammate or an earlier build) tomodels/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