Threat model¶
This document enumerates the threats the SKI Framework reference implementation defends against, the assumptions behind those defences, and the threats explicitly out of scope. It is intentionally specific so that auditors can verify each control end-to-end.
The framework is presumed to be operated by a regulated organisation inside its own infrastructure boundary. The threat model is written from the operator's perspective.
Trust model¶
| Entity | Trusted for | Not trusted for |
|---|---|---|
| Operator (the regulated org) | Running the stack, holding KG signing keys, reviewing FLAG/DISCRETIONARY verdicts | Modifying the audit ledger, bypassing append-only triggers, downgrading conformance |
| KG editor | Producing the signed Knowledge Graph | Producing telemetry, deciding rule routing at runtime |
| Telemetry source (SCADA, sensors, ETL) | Producing well-formed telemetry records | Deciding which rule applies (the Tag Registry decides) |
| External auditor | Reading the audit ledger to re-verify | Modifying the ledger or its hash chain |
| SKI Framework maintainers | Publishing signed releases | Accessing operator data; the framework never phones home |
| LLM weights publisher (Ollama, etc.) | Providing the model artifact | Modifying behaviour after deployment; the SHA-256 pin (SKI_MODEL_FILE_SHA256) prevents silent substitution |
In-scope threats¶
T-1: Tampering with recorded verdicts¶
Goal: an insider rewrites a FLAG verdict to CLEAR to hide a breach.
Controls:
- Postgres BEFORE UPDATE / DELETE / TRUNCATE triggers on
ledger_entriesandtelemetry_bufferraise an exception. (reference-implementation/src/ledger/append_only.sql) - The audit ledger entry hash chains to the prior entry; any post-hoc edit breaks the chain.
- The canonical serialization is documented (
audit_ledger/canonical.py) so an external auditor can re-verify independently. - Database role separation: the
ski_modelservice connects with a role that hasINSERTonly on the ledger tables;UPDATE/DELETEwould require a different role with elevated privileges.
Residual risk: a sufficiently privileged DBA can disable the triggers. Mitigated by Postgres audit logging (operator responsibility) and out-of-band ledger archival (Level 3 conformance).
T-2: Loading a tampered Knowledge Graph¶
Goal: an attacker swaps the KG file to weaken or invert the rules.
Controls:
- Ed25519 signature verification on KG load. The SKI Model service
refuses to start with
KG signature verification FAILED. KG_REQUIRE_SIGNATURE=falseis permitted only for local demos and immediately disqualifies the deployment from any conformance level (specification B2.4).- Public key is mounted read-only into the container.
Residual risk: the signing key is held offline by the KG editor; key custody is the operator's responsibility. Loss of the private key permits forged KGs.
T-3: Non-deterministic inference output¶
Goal: the local LLM drifts (model substitution, library version change, GPU non-determinism) so the same input produces different verdicts.
Controls:
SKI_MODEL_FILE_SHA256is checked against the actual model file at service start. Mismatch -> refuse to start (specification B3.4).- Inference uses
temperature=0, fixedseed=42, structured output enforcement. - The Symbolic Verifier independently re-checks every
formalizable assertion the LLM emits; a drifted or substituted model
surfaces as
LLM_CONTRADICTION/NEURO_SYMBOLIC_DIVERGENCEstatuses in the verdict envelope. - The agreement monitor tracks the rolling LLM-verifier agreement rate and alerts on a sustained drop below threshold (default 0.95).
- Every envelope records the model-weight hash, KG version hash,
prompt-template hash, and decoder seed;
audit-ledger replayre-evaluates the symbolically verifiable part and verifies the signed transcript for the rest.
Residual risk: assertions outside the formalizable subset are
UNVERIFIABLE by construction; defensibility for them rests on the
signed transcript (reconstructible provenance), not re-generation.
T-4: Producer claiming a rule_id¶
Goal: a compromised producer attaches rule_id: "lenient_rule" to
its telemetry, hoping the runtime will trust it.
Controls:
- The sidecar and
send-telemetry.pyreject any record containing arule_idkey (client-side,B4.3). - The SKI Model server ignores any
rule_idit receives and always asks the Tag Registry to resolvesubject -> rule. - Conformance test
test_demo_telemetry_has_no_rule_idenforces this on the demo telemetry.
Residual risk: none for the documented APIs; an operator who bypasses the sidecar and writes directly to the SKI Model can still have their rule_id ignored, so this is fully mitigated.
T-5: Data exfiltration via cloud LLM¶
Goal: operational data leaks because the runtime calls a cloud API.
Controls:
- Default backend is
ollama(local). Theanthropicbackend is labelled non-conformant in.env.exampleand the SKI Model logs a bright warning if it is selected. - The
ski-internalDocker network has no external connectivity in the air-gapped profile (networks.ski-internal.internal: true). - The service makes no outbound network calls during inference when
the default backend is used. Verified by the
sovereignty/test_no_outbound_callsconformance test and the runtimetest_no_egresssuite.
Residual risk: the operator can intentionally choose a cloud backend. This is a policy violation, not a framework bug.
T-6: Replay-attack against the ledger¶
Goal: an attacker replays a CLEAR verdict to mask a later FLAG.
Controls:
- Each ledger entry includes
sequence_number(monotonic, unique) andtelemetry_id(unique per record). A duplicate insertion would collide on the unique constraint. - The chain hash binds each entry to its predecessor; a replayed entry would break the chain.
Residual risk: none.
T-7: Secrets leakage¶
Goal: an attacker reads the Postgres / API / Grafana password.
Controls:
scripts/setup.shgenerates strong random secrets and writes.envwith mode0600.- No defaults are present; the stack refuses to start without secrets
(Docker compose uses
${VAR:?error}syntax). .envis in.gitignore.- TLS certificates (self-signed by setup) live under
tls/with modego-rwx.
Residual risk: operator-side handling of .env is the operator's
responsibility.
T-8: Supply-chain compromise¶
Goal: a malicious dependency is pulled in via pip.
Controls:
- All dependencies pinned in
requirements-dev.txtand per-toolrequirements.txt. - Dependabot is enabled with grouped minor/patch updates and a monthly cadence.
- CI runs
pip-auditand Trivy against every PR. - CycloneDX SBOM generated for each release.
- (v0.3 planned) Sigstore / cosign signing of release artifacts and container images; SLSA Level 3 provenance.
Residual risk: a typosquat slipping past pip-audit. Mitigated by
review-before-merge.
Out of scope¶
The following are not defended against by the framework itself. They are the operator's responsibility.
- Host OS compromise. If the host running Docker is rooted, all bets are off.
- Insider with full Postgres superuser. A DBA can disable triggers; mitigation is administrative, not technical.
- Compromise of the offline signing key. Key custody is the operator's responsibility.
- Side-channel attacks on the LLM. Timing / cache attacks on the inference path are not in scope. The Symbolic Verifier's checks are pure predicate evaluation with no LLM in the path and are unaffected.
- Denial of service. Rate limiting and admission control are out of scope for the reference implementation; production deployments are expected to put the SKI Model behind an ingress controller that handles this.
- Compromise of upstream LLM weights publishers. The SHA-256 pin prevents silent substitution but cannot detect a backdoor in the originally-published weights.
Re-verification¶
An external auditor can independently verify every control above:
- Append-only triggers: query
pg_triggerand read the trigger bodies. RunUPDATE ledger_entries SET verdict = 'CLEAR' WHERE sequence_number = 1— it must error. - Signature requirement: corrupt one byte of
kg.json; the SKI Model service must refuse to start. - Agreement monitor:
GET /api/canary; confirmstatusishealthyandagreement_rateis at or above the threshold. - Replay:
audit-ledger replay --from 1 --to N --kg-path kg.json --strict. Must exit zero; symbolically verifiable parts replay exactly, LLM reasoning re-verifies via the signed transcript. - Chain integrity:
audit-ledger verify. Must reportchain_link_verified = N / N.
If any of these checks fails on a deployment claiming conformance, the operator's claim is invalid.