---
title: "Skill trust and integrity controls"
manual: "LLM Foundation"
version: "main"
permalink: "https://docs.typo3.org/permalink/netresearch/nr-llm:skill-trust-and-integrity-controls@main"
source: "Administration/SkillTrust.rst"
rendered: "2026-11-12T15:41:35+00:00"
---

# Skill trust and integrity controls {#skill-trust-and-integrity-controls}

## Isolation controls: trust, fingerprint, injection scan, audit {#administration-skills-isolation}

On top of the SHA-pin and checksum controls above, each source and skill
carries the isolation controls introduced in [ADR-061](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-061@main).

**Publisher trust level.** Every source is classified — `untrusted` (the
default, for anonymous public GitHub content), `community`, `verified` or
`first_party` (operator-controlled). This is *provenance*, independent of the
`partial` support badge. Each synced skill denormalises its source's level, so
re-classifying a source takes effect on the next sync. The instance-wide floor
`skills.minTrustLevel` (extension configuration, default `untrusted`) gates
use: a skill below the floor is dropped from **both** prompt injection and the
allowed-tools union. Raising the floor to `verified` therefore hides every
community/untrusted skill without deleting it. Trust is *separate from* the
`enabled = false` default — an `untrusted` skill still needs an explicit
enable. An unreadable or mistyped floor falls back to `untrusted`; the value
actually in force is shown under [Effective policy](https://docs.typo3.org/permalink/netresearch/nr-llm:administration-governance@main).

**Skill-block byte budget.** `skills.maxBytes` (extension configuration,
default `24000`) caps the composed block that is prepended to the user
prompt. The measure is bytes, not tokens — a deliberate over-estimate, since no
tokenizer is available. When the block exceeds the budget, skills are dropped
from the tail first: task-additive skills go before the configuration baseline,
and every drop is logged as a warning. Lower the value to reserve more of the
model's context window for the conversation itself; raise it if a large
configuration baseline is being trimmed. An empty, non-numeric or zero value
falls back to `24000` — the cap cannot be switched off, so an emptied field
never puts an unbounded block on the wire. The budget is instance-wide and
independent of the model's context window; the per-request window bound is
handled separately by the context-window manager ([ADR-107](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-107@main)).

The budget bounds the prompt block only, **not** the `allowed-tools` union
([Gating tools](https://docs.typo3.org/permalink/netresearch/nr-llm:administration-tools-allowed@main)). The union is computed
over the effective skills before the block is assembled, so a skill dropped
for the budget still grants its tools while its usage rules stay out of the
prompt. That is deliberate — a budget-aware union would *widen* the gate,
because dropping the last declaring skill removes the restriction entirely.
Watch the drop warnings when lowering the value: they name every skill whose
prose stopped shipping while its tools kept being offered.

**Manifest fingerprint (optional).** A source may declare an
`expected_fingerprint` over its collected identifiers and body checksums.
Sort the identifiers, encode each as `identifier:body-checksum`, join the
lines with LF without a final LF, then hash those UTF-8 bytes with SHA-256.
Body checksums are SHA-256 hashes of the parsed markdown bodies. This
fingerprint does not include frontmatter, trust labels or approval state;
the version digest used for approval is a separate control.

When an expected fingerprint is set,
the digest is recomputed at sync and verified before anything is materialised; a
mismatch fails closed (no skill is imported, the source goes to `error`) and
leaves the last known-good skills untouched. Leave it empty to rely on the
commit-SHA pin alone. This binds the reviewed bytes to a publisher-declared
identity beyond "these bytes from this URL"; it is a declared digest, not a
public-key signature.

**Prompt-injection scan.** Each body is scanned at ingest for known injection
signatures. A **high-confidence** jailbreak marker (e.g. "ignore all previous
instructions", role reset, chat-template control tokens) force-disables the
skill at import — even a single-file source that would otherwise default
enabled — and must be re-reviewed before enabling. Lower-confidence findings are
recorded on the skill (`Injection scan findings`) for review without blocking.
The scanner matches a fixed set of heuristic signatures, including control
markers and long encoded blobs. It can also flag quoted examples; an absence
of findings does not establish that a skill is trustworthy.

**Audit trail and retention.** Every ingest, enable, disable and fail-closed
rejection is written to `tx_nrllm_skill_audit` with who / when / source / SHA /
checksum / trust level. Scan output and free-form details follow the central
privacy policy. Ordinary writes append rows and there is no update path.
The explicit retention exception is `nrllm:privacy:purge`: it deletes
rows created strictly before the cutoff for the skill-audit category. Retained
rows support provenance reconstruction; purged rows are no longer available.
See [ADR-064](https://docs.typo3.org/permalink/netresearch/nr-llm:adr-064@main) and
[Data retention](https://docs.typo3.org/permalink/netresearch/nr-llm:administration-data-retention@main).
