Configuration Reference¶
File locations¶
| Path | Purpose |
|---|---|
~/.config/trustsight/config.toml |
Main configuration (weights, limits). |
~/.config/trustsight/rules.toml |
Definitions for every R-series rule. H-series heuristics are emitted from code and have no entry here. Per-rule enabled and weight_override controls live in config.toml. |
~/.config/trustsight/trusted_domains.toml |
Domain classification lists for source bucket assignment. |
~/.config/trustsight/iocs.toml |
H056 indicator list: confirmed-malicious package names, domains, and artifact hashes, each with provenance and a confidence tier. Ships empty. |
~/.cache/trustsight/repos/ |
Cloned AUR package repositories (bare git repos). |
~/.local/share/trustsight/ |
SQLite database (analysis history, source URL tracking, maintainer tracking). |
All directories and default files are created on first run by ensure_default_configs() in src/trustsight/config.py.
config.toml¶
TOML file at ~/.config/trustsight/config.toml.
[severity_weights]¶
Map each severity level to its numeric contribution to the base score. FATAL rules short-circuit to score 100 regardless of weight.
| Key | Type | Default | Effect |
|---|---|---|---|
FATAL |
int | 0 |
Hard-stop score at 100; weight not used. |
CRITICAL |
int | 40 |
Added to score for each CRITICAL rule fired. |
HIGH |
int | 25 |
Added to score for each HIGH rule fired. |
MEDIUM |
int | 15 |
Added to score for each MEDIUM rule fired. |
LOW |
int | 5 |
Added to score for each LOW rule fired. |
INFO |
int | 0 |
Informational only; no score effect. |
[source_bucket_weights]¶
| Key | Type | Default | Effect |
|---|---|---|---|
trusted_forge |
int | 0 |
Well-known forges (github.com, gitlab.com, etc.). Neutral: hosting on a forge is a declared fact reported as P007, never a credit (B10). |
official |
int | 0 |
Official project domains (kernel.org, python.org, etc.). No score change. |
raw_hosting |
int | 15 |
Raw/paste hosting (raw.githubusercontent.com, pastebin.com, etc.). |
unknown |
int | 20 |
Domain not in any allowlist. |
homograph_attack |
int | 30 |
Domain contains visually confusable non-ASCII characters (Cyrillic homoglyphs, etc.). |
URLs in the diff are classified by classify_url() in src/trustsight/buckets.py.
[novelty_weights]¶
Raw weights for Tier C novelty signals. These are multiplied by the maturity multiplier (observation_count / 50, capped at 1.0) before being added to the score.
| Key | Type | Default | Effect |
|---|---|---|---|
url_first_in_package |
int | 5 |
Raw weight for a URL never seen before in this package's history. |
url_first_globally |
int | 10 |
Raw weight for a URL never seen before in any package in the corpus. |
maintainer_first_in_package |
int | 15 |
Raw weight for a maintainer never seen before for this package. |
[review]¶
Controls the review workload, not score arithmetic or risk bands. A report is
flagged when its score is above the selected profile's threshold. JSON records
the profile, effective threshold, and flag; the configuration fingerprint covers
the selected policy too.
| Profile | Default threshold | Intended use |
|---|---|---|
default |
20 |
Historical behavior; about 11.9% of locked benign-corpus diffs enter the review queue. |
quiet |
40 |
Smaller queue; it does not claim the same labelled-fixture coverage as default. |
strict |
10 |
Broader queue for operators who prefer sensitivity over review volume. |
[review]
profile = "quiet"
## Optional local changes to the three published workload choices.
[review.profiles]
quiet = 45
Only default, quiet, and strict are accepted. Thresholds must be integers
from 0 through 100. Changing a profile does not change a score, risk band, or
calibration result; it changes only the reports marked for review.
Removed: [verification_evidence] and [pinning_weights]¶
Both sections applied negative weights for declared checksums, PGP keys, GPG
verification and source pinning. They are gone, and setting them in a local
config.toml now does nothing.
Everything TrustSight sees is attacker-declared, and TrustSight never fetches,
so it never confirms that a declared key signs anything or that a pinned commit
holds what it claims. A signal an attacker can assert for free must not be able
to lower a score. These facts are now reported as weight-0 declared-practice
findings in the P namespace (P001-P008); see
the security model.
Pinning classification via classify_pinning_level() in
src/trustsight/buckets.py still runs; it decides which P finding is
emitted, not a score.
[ports]¶
| Key | Type | Default | Description |
|---|---|---|---|
standard |
array of ints | [80, 443, 8080, 8443] |
Ports excluded from R047 (non-standard port detection). Add custom standard ports to suppress false positives. |
[domains]¶
| Key | Type | Default | Description |
|---|---|---|---|
free_registrar_tlds |
array of strings | ["tk", "ml", "ga", "cf", "gq", "pw"] |
TLDs flagged by R048 (source URL on free registrar TLD). Update this list as new free TLDs appear. |
[tools]¶
| Key | Type | Default | Description |
|---|---|---|---|
network_makedepends |
array of strings | ["curl", "wget", "aria2", "git", ...] |
Package names that D003 treats as network-accessible makedepends. |
[rules]¶
| Key | Type | Default | Description |
|---|---|---|---|
experimental |
bool | false |
Run rules marked experimental = true in rules.toml. The R039 to R059 set is calibrated and runs unconditionally; this gates future additions whose false-positive rate has not been measured. |
[rules.R###]¶
Per-rule controls belong in config.toml, keyed by the rule ID:
enabled and weight_override apply only to rules defined in rules.toml,
which since the R/H split means every R id and only those. They only have an
effective scoring use for non-FATAL rules: a FATAL rule cannot be disabled and
always hard-stops the score at 100.
An H rule is a heuristic emitted from an analysis module and has no TOML
definition, so a [rules.H###] block does nothing. That is what the prefix is
for - if you can name it with an R, you can configure it here. Use the
documented dedicated settings for heuristics where available, such as
[experimental_rules] below.
A [rules.R###] block naming an id this release retired (see
the mapping) also does nothing, and did
nothing before the rename either: those rules were always code-emitted. Retired
ids are never reassigned, so such a block cannot later attach itself to an
unrelated rule.
[experimental_rules]¶
Rules emitted from code rather than rules.toml, so the experimental flag above cannot reach them. All default to true, a value set by corpus calibration; see Fire Rates.
A config written before this section existed still gets these defaults: load_config() reads the file verbatim without merging defaults, so the fallbacks live in code (_EXPERIMENTAL_DEFAULTS in src/trustsight/analysis/base.py). Setting a key here always overrides them.
| Key | Type | Default | Description |
|---|---|---|---|
D001 |
bool | true |
Optional corpus-context signal: fire when a dependency name is added that has never been observed anywhere in the AUR. Requires a seeded dependency_names table; with no seed the rule stays silent rather than flagging everything. This avoids measuring an empty database rather than an unusual dependency. |
D002 |
bool | true |
Fire when a novel dependency name is within one or two edits of a popular one (openss1 for openssl). Refines D001: a name is only compared once D001 has found it globally unknown. |
D003 |
bool | true |
Fire when makedepends gains a network-capable tool (curl, git, python-requests, …), meaning the build can now fetch code that no checksum covers. |
H015 |
bool | true |
Report that the diff modifies build(), prepare(), check(), or package(). INFO severity, so it carries weight 0 and cannot move a score: it fires on 21.4% of benign diffs and exists as reviewer context. On by default for that reason. |
D004 |
bool | true |
Fire when provides/replaces claims an established package unrelated to this one, which installs it in front of the real thing. Variants and siblings (htop-vim providing htop, linux-cachyos providing linux-headers) do not fire. |
H016 |
bool | true |
Fire when a download inside a build function targets a URL absent from source=(). |
H017 |
bool | true |
Fire when a .install hook body fetches over the network or performs a privileged operation (chmod u+s, systemctl enable, eval). Hooks run as root. |
H018 |
bool | true |
Fire when a patch is applied from outside the build tree: a URL, an absolute path, or process substitution. Does not check source=() membership, since patches legitimately arrive inside the extracted tarball. |
H019 |
bool | true |
Fire when a source= URL is downgraded from https:// to http://. |
[seed]¶
| Key | Type | Default | Description |
|---|---|---|---|
auto_import |
bool | true |
On an eligible first CLI review or inspect, optionally import the novelty seed when the database has neither a seed nor analysis history. The seed lives on the release channel as baseline-seed.tar.gz; the fetch verifies it and skips silently when offline or verification fails. Set this to false to use structural detection without seeded context. Other commands do not fetch it automatically. See trustsight seed-db. |
[baselines]¶
Container for optional federated baseline sources. Currently only the IOC baseline stage is implemented. Baselines add attributed context; they never replace local structural detection or alter rules, weights, or thresholds.
[baselines.ioc]¶
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Run the IOC baseline match stage during analysis. |
sources |
array of strings | [] |
Baseline source names to consult. An empty list means "all imported sources". |
[[baselines.ioc.feeds]]¶
Configured feed entries for trustsight ioc update. TrustSight ships with
no default feeds; operators add trusted sources here. A feed whose url
names the TrustSight release channel
is updated automatically: ioc update downloads the pair
baseline-ioc-<prefix>-manifest.json and baseline-ioc-<prefix>-iocs.jsonl
(plus their detached signatures), verifies both against the pinned
distribution key, then imports with the curator-key check the normal
ioc import path performs. Any other url is refused with an explicit
"not implemented" error; there is no scheme in which an unverified remote
baseline is imported.
| Key | Type | Description |
|---|---|---|
name |
string | Feed identifier (a [a-z0-9.-] slug by default, or set asset). |
url |
string | Feed URL. Release-channel URLs (https://github.com/emiliano-go/trustsight/releases) trigger verified updates. |
asset |
string | Optional asset prefix override. Defaults to name; the assets fetched are baseline-ioc-<prefix>-manifest.json and baseline-ioc-<prefix>-iocs.jsonl. |
enabled |
bool | Whether the feed is active. |
[depth]¶
How far into a package's AUR dependency closure to analyse. 0 disables it,
1 (the default) analyses direct AUR dependencies, n analyses n levels,
and -1 walks every level there is.
-1 is bounded, and it has to be: the dependency graph is written by the
party under review, so an unbounded walk would let a crafted recipe decide
how many repositories this machine clones. The ceilings are
depth.MAX_DEPTH_LEVELS (8) and depth.MAX_DEPTH_NODES (200 dependencies
per run), and a walk cut short by either records the deps_not_scanned
coverage gap. A walk that completed is not a gap: asking for depth 1 and
getting depth 1 answers the question that was asked.
Each dependency is analysed exactly as a package - its own score, its own
band, its own row in the database. Nothing is folded into the parent's score,
because depth is deliberately absent from the config fingerprint and a
score that moved with --depth would break
B1
for anyone comparing two runs.
Overridden per run by --depth.
[deep]¶
Reserved, not implemented
These keys are written to the default config but no code reads them. Setting them has no effect.
Deep analysis mode: reserved.
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Enable deep analysis mode. |
threshold |
int | 80 |
Minimum score to trigger deep analysis. |
[diff]¶
| Key | Type | Default | Description |
|---|---|---|---|
max_context_lines |
int | 3 |
Number of context lines in git diffs passed to pygit2.Diff. |
max_diff_bytes |
int | 5242880 |
Maximum UTF-8 byte prefix analysed from one diff (5 MiB). A larger diff sets diff_truncated and the diff_truncated coverage gap; the score describes only that prefix. This is independent of the rules.MAX_SCANNED_LINES line cap, which can set scan_truncated even when the byte cap was not reached. |
[discovery]¶
Controls which packages are scanned when no --repo/--foreign/--all-repos flags are given on the command line. See CLI Reference for the full precedence rules.
| Key | Type | Default | Description |
|---|---|---|---|
default_repos |
array of strings | [] |
List of repository names to scan by default when no CLI flags are given. |
include_foreign |
bool | false |
Whether to also include foreign packages (pacman -Qm) when default_repos is non-empty or all_repos is true. When all defaults are empty/false, foreign packages are scanned as a fallback. |
all_repos |
bool | false |
If true, automatically detect all local repositories from /etc/pacman.conf (excluding official repos) and use them as the default scope. default_repos are added to the auto-detected list. |
show_unmatched |
bool | true |
With --all, include installed packages that are absent from the AUR metadata snapshot (orphaned, very new, removed from the AUR). Set to false to skip them. |
cache_ttl_minutes |
int | 60 |
Minutes an AUR RPC response is cached for. Applies to the RPC fallback path, not to the metadata snapshot. 0 disables the cache. |
metadata_ttl_minutes |
int | 60 |
Minutes the offline AUR metadata snapshot is used before review refetches it (~60 MB). A snapshot past this age would report every installed package as current, so refreshing it is what keeps "no outdated packages" a fact rather than an artefact of age. 0 never refreshes automatically: comparisons stay as current as the snapshot on disk, which is the right setting only for an offline machine. If the refresh fails, the old snapshot is used and review warns that a newer package may go unreported. |
If none of these settings are explicitly configured, the tool scans foreign packages only (backward-compatible default).
[limits]¶
| Key | Type | Default | Description |
|---|---|---|---|
default_review_limit |
int | 0 |
How many packages trustsight review reads when --limit is not given. 0 means all of them, which is the default because a review that stops early has not looked at the rest. Any other value is honoured, and the packages left unread are named in the summary rather than dropped quietly. An explicit --limit 0 always wins over this. Before 0.13.2 the key shipped as 20 and was never read: the flag's own default won every time, so setting it did nothing. |
network_connect_timeout |
int | 10 |
Seconds libgit2 may spend connecting to the AUR before aborting a clone/fetch. |
network_transfer_timeout |
int | 30 |
Seconds libgit2 may wait for data on an established connection. Without it a silently stalled connection hangs a fetch indefinitely. |
prefetch_timeout |
int | 120 |
Seconds trustsight review waits for the whole prefetch batch. Whatever has not arrived is abandoned and fetched again during analysis. |
watch_interval |
int | 3600 |
Seconds between cycles of trustsight full-aur --watch. |
watch_min_interval |
int | 60 |
Floor applied to --interval. The AUR regenerates its metadata dump every few minutes, so a shorter interval only re-downloads the same snapshot. |
corpus_fetch_workers |
int | 5 |
How many PKGBUILD fetches trustsight full-aur runs concurrently during a corpus build. Analysis stays serial and ordered; only the network fetch is parallelised. Not written to the shipped config, but honoured if you add it. A global aggregate rate cap in the fetcher (~5 requests/second) is the real limiter, because the AUR's cgit rate-limits per IP and now runs anti-scraping; raising this past what the cap can keep busy only idles threads. |
corpus_max_per_cycle |
int | 2000 |
Maximum packages trustsight full-aur processes per invocation. A larger delta, or a bootstrap, advances in bounded, resumable chunks: the cycle stops after this many, saves progress, and the next run continues. Set to 0 to disable the cap and process the whole delta in one run. Not written to the shipped config, but honoured if you add it. |
The pattern and threshold files¶
config.toml holds weights and limits. The lists a rule matches against live
in sibling files, so a rule can be retuned without touching code. Each is
written on first run and never rewritten, so an edited file is always kept.
hosts.toml¶
| Key | Rules | Contents |
|---|---|---|
paste_hosts |
H041, source buckets | Paste and ephemeral file-drop hosts. As source= URLs they are weighted by the raw_hosting bucket; as upload destinations inside a function they are H041's. |
standard_ports |
R047 | Ports a build may legitimately contact. |
free_registrar_tlds |
R048 | TLDs available at no cost, where a throwaway domain is cheap. |
source_schemes |
H034 | Allowlisted source= schemes. The base of a transport+base token is judged, so git+https reads as https. |
confusable_domains |
R013b | Popular domains a homoglyph label is tested against. A mixed-script label that resembles none of them stays quiet. |
covert_egress_endpoints |
H071 | DNS-over-HTTPS endpoints. |
covert_egress_clients |
H071 | Tunnelling and proxy clients, matched only at a command position. |
For the overlapping settings, hosts.toml has precedence: standard_ports
overrides [ports] standard, and free_registrar_tlds overrides [domains]
free_registrar_tlds. An empty sibling list falls back to the corresponding
config.toml value and then the shipped default. The other host lists are read
directly by their named rules; they do not merge with a generic host setting.
patterns.toml¶
| Key | Rules | Contents |
|---|---|---|
foreign_pkg_managers |
H035 | Package managers that are not pacman. |
obfuscation_indicators |
H036 | Per-line obfuscation markers, counted against a density threshold. |
anti_analysis_probes |
H067 | Debugger, VM, sandbox and CI probes. |
recon_commands |
H040 | Host-profiling commands, command-position anchored. |
parse_time_fetch |
H077 | Network clients whose invocation outside every function runs when the recipe is sourced. |
upload_flags |
H041 | curl/wget flags that send a request body, which is what separates an upload from a download. |
network_tools |
D003 | Package names that grant a build network access. |
security_relevant_flags |
H047, H079 | Hardening flags whose appearance or disappearance changes the mitigation set. |
security_relevant_libraries |
H048 | Libraries whose vendoring bypasses distribution security updates. |
These lists are consumed directly by their named rules. network_tools is the
exception with a legacy fallback: D003 reads patterns.toml first, then
[tools] network_makedepends in config.toml, then the shipped default. There
is no general precedence rule across sibling files.
naming.toml¶
Ecosystem prefixes (D004, H064) and variant suffixes (D002, H029, H052, H053). These decide when two package names belong to the same project, which is what keeps a package claiming its own project's names from firing a scope-expansion rule.
thresholds.toml¶
| Key | Rule | Default | Meaning |
|---|---|---|---|
h036.obfuscation_density |
H036 | 3 |
Distinct obfuscation indicators on one line before it is reported. |
h043.attack_chain_stages |
H043 | 3 |
Distinct kill-chain stages that must co-occur. |
h045.min_packages / h045.window_days |
H045 | 10 / 7 |
Cluster size and window for mass adoption. |
h052.min_packages |
H052 | 3 |
Unrelated packages that must share a source repository. |
h055.min_packages / h055.window_hours |
H055 | 5 / 24 |
Cluster size and window for an attribute burst. |
h057.min_hops / h060.min_hops |
H057, H060 | 2 |
Hops that make an exposure transitive rather than direct, keeping both out of H046's lane. |
h058.min_history_cycles / h058.z_score / h058.min_activity |
H058 | 3 / 2.0 / 3 |
Baseline length, deviation and floor for maintainer activity. |
h061.min_dependents |
H061 | 50 |
Dependents that make a package a hub. |
h073.min_history_cycles / h073.z_score / h073.min_introduced |
H073 | 3 / 3.0 / 3 |
Baseline length, deviation and floor for the corpus introduction rate. |
h064.widely_provided_observations |
H064 | 25 |
Observations that make a provided name widely provided. |
h074.window_days |
H074 | 14 |
How recent the modification must be after an adoption. |
longitudinal.stability_floor |
Class C | 10 |
Consecutive observations a property must hold before a change is reported at all. |
iocs.toml¶
[meta] version plus [[entries]] of type (package, domain or hash),
value, confidence, provenance, campaign and added. The confidence
tier decides severity: confirmed is FATAL, high is CRITICAL, medium is
HIGH. The shipped file is empty, and a miss is uninformative.
Environment variables¶
TrustSight reads one environment variable. Everything else that changes behaviour is a config key or a command flag.
| Variable | Values | Effect |
|---|---|---|
TRUSTSIGHT_OFFLINE |
1, true, yes (case-insensitive, surrounding whitespace ignored); anything else is off |
Forbids outbound requests to the release channel. |
Every path that would reach the release channel checks it first, so an air-gapped machine or a CI runner is pinned to what is already on disk. The commands that need a channel refuse with a message naming the variable rather than failing as a transport error:
$ TRUSTSIGHT_OFFLINE=1 trustsight seed fetch
The release channel is disabled (TRUSTSIGHT_OFFLINE is set).
Four commands stop, and they do not all stop for the same reason:
| Command | Channel refused |
|---|---|
seed fetch |
Release channel. Exits 2. |
ioc update |
Release channel. Reported as skipped; exits 0. |
baseline build |
AUR network channel. |
full-aur |
AUR network channel. |
It does not make the program offline. review and inspect still clone
the package's own AUR repository, because that clone is how a diff exists at
all; fetcher does not consult this variable. What the variable governs is
the bulk and release channels, not the per-package fetch that
A3 already constrains to one host keyed by
package name. To stop all outbound traffic, deny it at the network layer.
Defined in release.offline().
Default configuration¶
The full default config is embedded in src/trustsight/config.py as DEFAULT_CONFIG and written to ~/.config/trustsight/config.toml on first invocation. Users may edit it freely.