Configuration Reference¶
The compatibility standards for shipped defaults, config sync and overrides are in Standards.
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, artifact hashes, file paths and PKGBUILD patterns, each with provenance and a confidence tier. Ships empty. |
~/.cache/trustsight/repos/ |
Cloned AUR package repositories. |
~/.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 findings contribute their declared weight (0 by default) like any other finding; the FATAL signal is carried by the verdict label and the unsuppressible entry in score_breakdown, not by the number.
| Key | Type | Default | Effect |
|---|---|---|---|
FATAL |
int | 0 |
FATAL's weight. The signal itself rides the verdict label (Low (FATAL: R012)) and the unsuppressible score_breakdown entry; overrides cannot remove it. |
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. |
declared_upstream |
int | 5 |
Registered domain of the package's own url=, when this diff does not also change url=. |
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 7.1% 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-P009, P004 skipped); 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. |
free_hosting_domains |
array of strings | [] |
Free-hosting / dynamic-DNS / tunnel domains flagged by R152 (source URL on an attacker-controllable host). Empty falls back to hosts.toml free_hosting_domains and then the shipped list. |
[tools]¶
| Key | Type | Default | Description |
|---|---|---|---|
network_makedepends |
array of strings | ["curl", "wget", "aria2", "git", ...] |
Package names that D003 treats as network-accessible makedepends. |
[source_host_divergence]¶
| Key | Type | Default | Description |
|---|---|---|---|
allow |
array of strings | ["waterfox.com", "torproject.org", "arduino.cc", "cachyos.org", "freedesktop.org", "archlinux.org", "winehq.org"] |
Registered domains (eTLD+1) exempted from C011. Add a legitimate non-forge distribution CDN or mirror here when C011 fires on a prebuilt -bin package that sources from it. |
config.toml is written once and read verbatim, so an install that predates this table keeps the shipped default (DEFAULT_SOURCE_DIVERGENCE_ALLOW in src/trustsight/config.py). The table is part of the configuration fingerprint: changing it changes which packages C011 reports, so two operators with different lists are different instruments.
[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
its signal rides the verdict label and the unsuppressible score_breakdown
entry rather than the score, so an override changes nothing visible.
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
[code_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.
[code_rules]¶
Rules emitted from code rather than rules.toml, so the experimental flag above cannot reach them. They are not experimental: 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 (_CODE_RULE_DEFAULTS in src/trustsight/analysis/base.py). Setting a key here always overrides them.
This table was named [experimental_rules] before the rename. The old name is still read as a fallback, with a deprecation warning, so an existing config.toml keeps the rules it turned on or off; rename it to [code_rules].
| 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 19.7% 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. |
baseline_tag |
string | baseline-2026-10-18 |
Fallback release tag for the seed when the GitHub releases API cannot be reached (offline or rate-limited). The seed lives only on baseline-* releases, so the default is never latest. Bumped in the release that publishes a new baseline; set it to "" to disable the fallback so an unreachable API refuses instead. |
[release]¶
The baseline release channel TrustSight follows for its signed seed and
corpus baselines. The official channel (emiliano-go/trustsight) is the
default and is verified against the distribution key pinned in the package.
Pointing this at another repository makes that publisher's key the trust
anchor, so a non-official import warns every time and a channel with no
configured key is refused rather than verified against the official key.
| Key | Type | Default | Description |
|---|---|---|---|
channel |
string | "" |
Baseline release channel as owner/repo. Empty means the official channel (emiliano-go/trustsight). A non-official channel needs key. |
key |
string | "" |
The pinned ed25519 public key for a non-official channel: either 64 hex characters or a path to a raw key file. Ignored for the official channel, which uses the key shipped in the package. A non-official channel with no key is refused. |
[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". |
tag |
string | "" |
Fallback release tag for the dedicated IOC channel when the GitHub releases API cannot be reached (offline or rate-limited). Empty means "resolve the newest ioc-* release", which is then refused if unreachable. Set it to pin an exact channel, e.g. "ioc-2026-10-18". A feed's own tag overrides this. |
[[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) from the newest ioc-* release (the
dedicated IOC channel), verifies both against the pinned distribution key,
then imports with the curator-key check the normal ioc import path performs.
Any other http(s) url is
read as a publisher's federation directory (manifest.json + iocs.jsonl) and
verified against the feed's own pinned key; a feed with no key is refused,
and a non-http(s) url is refused with an explicit error. The import is the
same curator-key-checked ioc import path in every case.
| Key | Type | Description |
|---|---|---|
name |
string | Feed identifier (a [a-z0-9.-] slug by default, or set asset). |
url |
string | Feed URL. A release-channel URL (https://github.com/emiliano-go/trustsight/releases, or the configured [release] channel) triggers a verified channel update. Any other http(s) URL is a federation directory. |
asset |
string | Optional asset prefix override. Defaults to name; the assets fetched are baseline-ioc-<prefix>-manifest.json and baseline-ioc-<prefix>-iocs.jsonl. |
tag |
string | Optional release tag override. Pins the exact channel release for this feed's pair, overriding [baselines.ioc] tag and channel discovery. Must be [A-Za-z0-9._-]. |
key |
string | Ed25519 public key pinning a non-release-channel publisher (64 hex characters or a path to a raw key file). Required for a federation url; ignored for a release-channel url, which uses the channel's key. |
enabled |
bool | Whether the feed is active. |
[baselines.watchlist]¶
The imported watchlist. A signed watchlist-* release carries public package
names and salted maintainer hashes (never plaintext); trustsight watchlist
update downloads watchlist-official.json (plus its detached signature) from
the newest watchlist-* release, verifies it against the pinned distribution
key, and imports it. The imported set is stored in the database and unioned
with the operator's hand-curated file at load, so a release import never
clobbers a local entry.
Maintainer hashes are handled exactly as a baseline seed's: the release records the salt they were built under, and an import adopts it when the database has none. When a different salt is already stored, the maintainer hashes are skipped (they would be unreachable in the local namespace) while the package names still import.
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Import the release and offer the pre-command "a newer watchlist is available" notice. |
tag |
string | "" |
Fallback release tag for the watchlist channel when the GitHub releases API cannot be reached. Empty means "resolve the newest watchlist-* release", which is then refused if unreachable. Set it to pin an exact channel, e.g. "watchlist-2026-10-20". |
[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.
[notify]¶
Where full-aur --watch pushes new alert clusters. With webhook set, a
cycle that produced new alerts POSTs one JSON document (the cycle counts
plus the package/rule_id pairs) to the URL; a quiet cycle sends nothing.
Any receiver that accepts a JSON POST works, and ntfy shows the document as
the message text. A dead receiver is logged and swallowed: a notification
must never kill the watch loop. Overridden per run by --notify.
[alerts]¶
| Key | Type | Default | Description |
|---|---|---|---|
max_rendered_findings |
int | 50 |
Findings the terminal renders in full before it groups the remainder as ...and N more findings (see --json). The JSON report is never filtered. A weight-0 W010 (Alert Volume Capped) boundary records the saturation. 0 disables the cap. |
max_cycle_alerts |
int | 0 |
Flagged packages a single full-aur --watch webhook names before it degrades to counts plus the top-K by score. The stored record keeps every package. 0 disables the cycle cap. |
max_active_campaigns |
int | 8 |
How many campaigns may be active at once in campaign alert mode. A new activation beyond the cap evicts the oldest only when its TTL has also lapsed; a live TTL is never shortened. |
campaign_min_reactivation_observations |
int | 5 |
Minimum spacing, in observations, between re-activations of the same campaign class (a G-rule id). A class that re-arms faster is still refreshed (re-activation is never blocked), but the churn is surfaced as a weight-0 M006 (Campaign Re-armed Too Often) tool-health note in the correlation cycle's log. 0 disables the check. |
[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).
[verify]¶
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Enable the optional L9 build lane. When false, trustsight verify-build reports l9_unavailable. When true, the lane may build the package inside the project's rootless, networkless container and inspect the artifact; L9 findings are never blended into the static score. |
[layers]¶
Override a rule's evidence category (its "layer"). The category
names the kind of evidence the rule's findings represent, L1 (structural
coherence) to L8 (campaign correlation). The shipped per-series default and
per-rule overrides apply unless a rule id is named here.
Each entry maps a rule id to L1-L8. A value outside that range is
ignored (the set is closed), as is a rule id this build does not ship. The
security gate layer overrides stay within the closed set pins that.
[contacts]¶
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Whether the Contact Ledger records first contacts (weight 0, warm-only). With false no first-contact fact is recorded and every report renders the standing contact_ledger_unavailable note in place of the "First contact" panel. This is the cold-replay lane: the determinism gate runs the corpus with it off and proves the score is identical. |
The ledger itself lives in the database, append-only and versioned by cycle;
[thresholds] g010 sets the shared-first-seen-host plurality floor. Neither
contributes to the score.
[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 | 16 |
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. Rate limiting is per host: the GitHub AUR mirror the fetcher tries first (codeload.github.com) is a CDN with a much smaller spacing than the AUR's cgit, so more workers buy throughput there, while the cgit fallback (which rate-limits per IP and runs anti-scraping, ~5 requests/second aggregate) simply idles the surplus. |
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. |
free_hosting_domains |
R152 | Free-hosting, dynamic-DNS and tunnel domains. A source= URL on one of these is flagged; trim the free/static-hosting entries if a legitimate release asset lives there. |
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 | Hardening flags whose appearance or disappearance changes the mitigation set. |
security_relevant_libraries |
H048 | Libraries whose vendoring bypasses distribution security updates. |
manifest_filenames |
U-series | Which committed files are dependency manifests, as format::name; the name is a glob (requirements*.txt, *.csproj, pylock.*.toml), matched against the basename, or against the path when it contains /. The most specific match wins, so an exact requirements.txt beats requirements*.txt. |
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¶
| Key | Rules | Default | Contents |
|---|---|---|---|
variant_suffixes |
D002, D004, H029, H052, H053 | shipped list | Suffixes that mark a variant of the same upstream project rather than a different project. |
ecosystem_prefixes |
D004, H064 | shipped list | Prefixes shared by thousands of unrelated packages; never evidence of a common project. |
known_suffixes |
H029 | shipped list | Expected package-variant suffixes stripped before the edit-distance comparison, so foo-git is never confused with foo. |
confusable_chars |
H029 | shipped list | Confusable character groups: a differing character pair drawn from one group is a homoglyph/digit edit, not a coincidence. |
h029_min_candidate_observations |
H029 | 100 |
Dependency observations a candidate name must have before any edit can fire. The dependency corpus is long-tailed, so the floor keeps globally obscure names out. |
h029_plain_min_observations |
H029 | 1000 |
Higher floor for a plain edit (two unrelated real words one or two edits apart); a confusable edit fires at the lower floor. |
Ecosystem prefixes (D004, H064) and variant suffixes 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 |
|---|---|---|---|
cofire_pairs |
M004 | [] |
Expected co-fire pairs ([precursor, expected]). A construct whose rule fired while its usual partner did not is reported to the cofire_health tool-health channel, never scored. Empty until a pair is measured. |
c001.require_capability_signal |
C001 | true |
A version move only downgrades the checksum-tamper signal to C002 when the diff adds no new fetch or script execution in a critical function. One flip restores the historical behaviour. |
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. |
h098.min_signals |
H098 | 2 |
Distinct naming/deception signals a package's findings must carry before the cluster is scored. One signal can be a coincidence; two is a method. |
u005.jump_factor |
U005 | 3.0 |
How much a committed manifest's dependency count must multiply in one update before the surface-jump ledger entry is reported (weight 0). |
longitudinal.stability_floor |
Class C | 10 |
Consecutive observations a property must hold before a change is reported at all. |
t_key.min_observations |
T001 | 1 |
Ecosystem signing keys recorded before T001 judges a new validpgpkeys fingerprint novel; below the floor the rule declines silently. |
t_dom.min_observations / t_dom.window_days |
T002 | 1 / 30 |
Domains recorded before T002 judges a maintainer domain novel, and the observation window that counts as "new". |
t006.min_divergence_hours |
T006 | 24 |
Hours the AUR LastModified may lead the git tip commit date before T006 reports a history rewrite. |
e001.max_ratio |
E001 | empty | gzip ratio below which added lines read as repetitive/padded. Ships empty: silent until the calibration job or an operator sets it. |
e002.min_fraction / e002.min_length |
E002 | empty / 64 |
Fraction of added tokens that must be base64/base32/hex runs, and the run-length floor. |
e003.min_entropy / e003.max_entropy / e003.min_bigram / e003.max_bigram |
E003 | empty | Unigram and bigram entropy band for newly assigned variable/function names; generated names sit low, packed names high. |
g_host.min_packages |
G001 | 5 |
Distinct packages that must add the same canonical host within the cycle window. |
g_blob.min_length / g_blob.min_packages |
G002 | 256 / 2 |
Length floor for a shared added literal and the number of unrelated packages that must carry it. |
g_adopt.min_members |
G003 | 3 |
Packages with a recorded adoption predicate one maintainer key must be joined to among a cycle's processed packages for a coordinated-adoption finding. |
g003.novel_days / g003.min_dormant / g003.dormant_days |
G003 | 30 / 2 / 180 |
Window in which an adopter counts as novel, the dormant targets that escalate the finding to HIGH, and the age that makes a target dormant. |
g008.min_packages / g008.window_days / g008.min_dormant / g008.dormant_days |
G008 | 5 / 7 / 3 / 180 |
First-seen account adoption floor, its window, the dormant-target floor and the dormancy age. |
g009.enabled / g009.path |
G009 | true / empty |
Whether the watchlist is consulted, and the watchlist file (~/.config/trustsight/watchlist.json when empty). The file holds maintainer entries under entries (salted hashes) and package entries under packages (plain names). A non-string path is ignored (the default file is used), and the file is read under a 1 MiB cap: an oversized or malformed file is treated as absent, never as an analysis error. |
g010.min_packages / g010.window_days |
G010 | 5 / 7 |
Packages that must share one first-seen host, and the first-seen window. |
m002.min_layers |
M002 | 3 |
Distinct evidence categories that must fire (none above the alert bar) before M002 escalates the reported band one step. |
A malformed threshold value (a non-numeric string, null, or a list where a
number is expected) falls back to the shipped default instead of aborting a
corpus cycle. Counts are clamped to at least one, and a never-negative window
or length is clamped to zero.
The E-series thresholds (e001/e002/e003) ship empty by design: the
series is silent until a threshold is set from the first corpus measurement
(scripts/entropy_calibrate.py prints conservative floors in the calibration
CI job). The g003, g008, g009, g010 and t006 tables ship in
thresholds.toml. The older G keys (g_host, g_blob, g_adopt), the T
keys (t_key, t_dom), m002 and cofire_pairs are honoured if you add them,
with the code defaults in the table above.
iocs.toml¶
[meta] version plus [[entries]] of type (package, domain, hash,
file_path or pkgbuild_pattern), 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 sixteen environment variables. 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. |
TRUSTSIGHT_NO_UPDATE_CHECK |
1, true, yes (case-insensitive); anything else is off |
Disables the once-a-command "a newer release exists" notice, and with it the baseline/IOC pending notice below. |
TRUSTSIGHT_NO_PENDING_CHECK |
1, true, yes (case-insensitive); anything else is off |
Disables only the once-a-command "a newer baseline/IOC is available" notice and its interactive pull prompt, keeping the program-version notice. |
NO_COLOR |
Any non-empty value | Disables Rich output; terminal and pipe both get the plain renderer. |
FORCE_COLOR |
Any non-empty value | Forces Rich output even when stdout is not a terminal. |
TRUSTSIGHT_FORCE_RICH |
Any non-empty value | The same as FORCE_COLOR, for callers that already use FORCE_COLOR for something else. |
TRUSTSIGHT_DOC_CACHE |
A directory path | Overrides where the parsed-document cache stores its JSON entries. Default: $XDG_CACHE_HOME/trustsight/documents. |
XDG_CACHE_HOME |
A directory path | Base for the document cache when TRUSTSIGHT_DOC_CACHE is unset. |
TRUSTSIGHT_SQLITE_SYNCHRONOUS |
OFF, NORMAL, FULL, EXTRA (case-insensitive); an unknown value falls back to NORMAL |
SQLite commit durability. NORMAL (the default) keeps WAL's crash-safety without an fsync per commit; FULL is the old behaviour. |
TRUSTSIGHT_SQLITE_BUSY_TIMEOUT |
A non-negative integer of milliseconds; an invalid value falls back to 30000 |
How long a connection waits for a locked database before erroring. 30000 (the default) lets a seed fetch and a corpus cycle overlap without a spurious "database is locked"; raise it if a single cycle writes for longer. |
TRUSTSIGHT_LOG_LEVEL |
DEBUG, INFO, WARNING, ERROR (case-insensitive); an unknown value falls back to INFO |
The level of the watcher's log on stderr. Records are sanitised before rendering: control sequences and bidi characters are removed, and a message cannot forge an extra log line. |
TRUSTSIGHT_LOG_TIMESTAMPS |
Any non-empty value | Prefix each log line with a timestamp and level, for an unattended watcher whose docker logs is the only record. |
TRUSTSIGHT_NO_SYNC_RULES |
Any non-empty value | Skip the ruleset sync a full-aur --watch runs once at startup. By default the watch syncs rules.toml to the shipped rules the safe way (adds missing rules, fixes non-pattern drift and superseded patterns, never touches a pattern the user edited) and logs what it changed, so an unattended watcher never analyses with a stale ruleset. |
TRUSTSIGHT_FEED_URL |
A URL | The signed feed ingest endpoint. The watcher POSTs every alert there as well as to ntfy, so the feed ingests directly instead of polling ntfy. Overrides [notify] feed; empty and no [notify] feed skips the feed sink. |
TRUSTSIGHT_FEED_KEY |
A base64 PKCS#8 Ed25519 private key | Signs each feed POST (webhook-id, webhook-timestamp, webhook-signature). The feed holds only the public half, so a leaked feed secret cannot forge a document. With no key the feed sink is skipped and only ntfy is notified. |
TRUSTSIGHT_FEED_KEY_ID |
A short identifier | Names the signing key so the feed can rotate (it accepts a current and a previous key). |
Rich output is the default only when the target stream is a terminal:
renderers look at stdout, and progress bars look at stderr. A pipe, a
redirect or a captured stream therefore gets plain text with no escape
sequences. If NO_COLOR and FORCE_COLOR are both set, FORCE_COLOR wins.
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.