Skip to content

Docs,What a report says, and how it was measured

Threat model

docs/threat-model.md in the Socair repository at v0.1.0-rc.6 (commit e3d1bf2a6cb2), the release Get started installs. The repository is the source of truth: view it on GitHub.

What Socair protects, whom it trusts for what, which mechanism defends each boundary, and what it does not defend against. It describes the code as it is. Where a defence depends on how you deploy Socair, it says so; the deployment itself is in intake-host.md and kubernetes.md.

What it protects

  • The binding between the verdict and the bytes. An attestation must describe exactly the bytes that were checked, and only those bytes may cross into the clean store on it.

  • The promotion decision. Only an artifact whose attestation, signed by a key the store trusts, authorizes promotion reaches clean/. A conditional promotion also needs a signed acceptance from a separate key.

  • The keys:

    • the operator key, which signs attestations and inventory snapshots;
    • the acceptor key, which signs acceptances of untested rows;
    • the feed signer's key, which signs reference data;
    • the publisher keys and roots an operator trusts for OpenSSF Model Signing signatures.

    No private key needs to be on the airlock host: the store holds public keys only.

  • The record: the activity log (log.jsonl) and exported inventory snapshots.

Who is trusted for what

Party Trusted for Not trusted for
Model publisher Nothing by default. Its files are hostile input. A publisher signature counts only when the operator trusts the publisher's key or root, and then as origin and integrity, not safety. Anything about the file's contents
Model hub and CDN Listing hashes and serving bytes that match them Being uncompromised: a hub that lists the hashes of whatever it serves is not caught by the pull itself
Network Nothing Everything: only pull (through an allowlist) and the OCI commands attach and verify <oci-ref> (to the registry named) reach it
OCI registry Storing and serving what was pushed, by digest Anything it says about content: every manifest and blob is hashed on receipt, and a referrer it lists is checked against its own subject and its signed binding (oci.md)
Operator Running the checks and signing the report they stand behind. The operator key is the root of trust for promotion. Making a report say more than its rows: a signed report must still validate, and its promotion state must follow from its check rows
Acceptor Accepting named untested rows of one report, until an expiry Clearing a FAIL or a LEAD, which no acceptance clears; signing an acceptance with the key that signed the report
Feed signer The reference data it signs, until the feed expires Clearing structural evidence: a reviewed template clears language leads only, and code reach still FAILs
Scan host and store Not being compromised (an assumption, below) —
Verifier (socair verify, airlock promote, LLMKube's gate through socair-verify) Checking signature, statement, subject digest, and promotion state against the keys it was given Trusting any key it was not given
Local API caller Nothing beyond the loopback interface: the API has no authentication Reaching it from another origin or host; having it read a file outside its scan roots
Socair's release pipeline Building the release binaries from the tag Being taken on faith: each binary is checkable (verify-release.md)

What each mechanism defends

Mechanism Defends against
Scan snapshot. The engine copies the artifact into a private read-only file, hashing while it copies, and every check reads the copy (internal/engine/snapshot.go). A file swapped or rewritten between hashing and checking, which would earn a report under the hash of other bytes
Hashing on promote. promote hashes while copying into clean/, and a directory is renamed into place only when its recomputed manifest digest is the attested subject. Bytes crossing on an attestation for other bytes
Signed promotion and validation. promote checks the signature against trusted-keys/, the statement and predicate types, the subject against the document's hash, the recorded document hash, and that the document validates, including that its promotion state follows from its check rows (report.Validate). A bare or hand-edited report; an "authorized" state over a FAIL, a LEAD, or an unaccepted gap
Acceptance key separation. Acceptances verify against acceptor-keys/, a list separate from trusted-keys/, must be signed by a different key than the attestation, must name exactly the report's untested rows, and must be current. Self-acceptance; an acceptance stretched to rows nobody reviewed; a stale acceptance
Issuer check. The store names each trusted key, and promote and verify refuse an attestation whose claimed issuer contradicts that name. An attestation that claims to come from someone the key does not belong to
Controlled egress. Every host a pull touches, including each redirect hop, must be on the allowlist; an https-to-http redirect is refused; SOCAIR_EGRESS=deny refuses everything. Socair itself fetching from, or being redirected to, an unexpected host
Pinned pulls. A whole-repo pull must name a full commit or the expected digest, and every file is checked against the hub's hash at that commit. A branch delivering different bytes later; a corrupted or substituted transfer, the CDN included
Token host binding. HF_TOKEN is sent only to the endpoint's own host and port over https (or loopback), stripped on any cross-host redirect, and never logged or put in an error. API pulls never send it. The token leaking to a CDN, another host, the log, or a page that can reach the API
OCI binding. attach signs a statement about the OCI manifest digest only after the attestation verifies and every layer is shown to be an attested file's raw bytes (at its path, for a directory); verify <oci-ref> checks the referrer's subject, the binding's subject, the attestation hash it names, and the layers again. Registry credentials go only to the registry host and the token realm its challenge names, over https or loopback, and never across a redirect to another host. An attestation carried by an artifact whose layers are other bytes; a binding copied to another artifact; registry credentials leaking to a storage host
Path and shape checks. Hashes, repo ids, revisions, and file names are checked before any path is built, and paths must stay under the store or cache root. Path traversal through a crafted hash, name, or listing
Hash-chained log. Each entry carries the hash of the line before it; log --verify --expect-head checks the chain to a head recorded off the box. Careless edits to the log, and, with a recorded head, a rewrite of anything before that head
Signed feeds. A feed verifies against SOCAIR_FEED_KEYS, every file must match its signed digest, no unlisted file may be present, and an expired or unverified feed stops the scan. Tampered, padded, or stale reference data changing verdicts
Feed rollback protection. With a store, a feed issued before the newest one the store has used stops the scan or recheck, whichever key or issuer signed it. The record has two witnesses, feed-record.json and the log's feed entries, which must agree; going back takes socair airlock feed accept with a logged reason (feed.md). A scan with no store keeps no record and has no such protection. An older feed, still inside its validity window, swapped in to undo a newer one's known-bad entries or reviews
Private key permissions. A private key file readable by other users is refused. A signing key exposed to other users on the machine
Local API defences. It binds loopback, refuses a non-loopback bind unless SOCAIR_API_ALLOW_PUBLIC=1, serves only a loopback Host, and refuses cross-origin and cross-site requests (api.md). DNS rebinding and cross-origin requests from a page in the operator's browser
Scan roots. The API's scan, ingest, and promote routes read a path a request names only when it is under a scan root (--scan-root, or the store's incoming/) as written and with its symlinks resolved, and refuse every other path with one message whether or not it exists. With no root, they refuse every path. The wizard's static files are read through an os.Root on the build directory (api.md). A caller having the API read, hash, or probe for files outside the directories that hold models, through .., an absolute path, or a symlink. It does not stop someone who can write inside a root from swapping a checked path for a symlink between the check and the engine's open; api.md says so.
Parser hardening. The readers are bounded against hostile counts, lengths, and nesting, and fuzz targets for the GGUF, safetensors, ONNX, OpenVINO IR, HDF5, Keras, pickle, Jinja, OMS, acceptance, and feed parsers, and the OCI reference and registry challenge parsers, run in CI. An audit against the bug classes other model loaders have had CVEs for is open (#166). A small hostile file crashing or hanging the scanner
Release pipeline. Builds are reproducible from the tag with vendored dependencies and no network, every binary, the chart, and the image carry a build-provenance attestation, releases are immutable, and release tags cannot be moved or deleted. A tampered Socair binary, chart, or image

Assumptions

  • The scan host, the store's file system, and the user Socair runs as are not compromised. Within Socair only promote writes to clean/; make the store writable only by that user.
  • Private keys are kept off the airlock host, or at least readable only by their owner.
  • Whoever adds a key to a trust list checks whose key it is. A key's issuer name is a claim until the store names it.
  • Serving machines load models only from the clean store, mounted read-only, or through an admission gate that checks the attestation. The clean store is a directory of bytes and attestations, not a gate by itself.
  • If a rewrite of the log matters to you, you record its head somewhere the airlock host cannot change.

Out of scope

  • An attacker who controls the scan host, the store, or a trusted private key. They can sign what they like or rewrite the log after the last head you recorded.
  • What a model does when it runs. Tier 1 checks files, not behavior. Tier 2 measurements can withhold promotion but never show that something is absent (tier2.md).
  • Anything on the detection ceiling. It lists what Socair does not claim to catch.
  • A compromised hub serving consistent hashes. The pull checks the hub's own hashes; only a provenance record or a trusted publisher signature speaks to origin.
  • Resource use proportional to a large input. A small input that crashes or hangs the scanner, or makes it allocate far more than its size, is in scope (SECURITY.md).

All the docs