Skip to content

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:

[rules.R007]
enabled = false
weight_override = 15

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]

[depth]
levels = 1

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]

[notify]
webhook = "https://example.invalid/hooks/aur"

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.

[layers]
H099 = "L1"

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.