Repository navigation
Truth Rules
The truth check of nself-ci keeps false facts out of a repository. It reads rule files, scans the files of the repository and reports every line that breaks a rule. It never touches the network. The engine is free/ci/internal/gates/truth; the org rules are data in .github/truth/.
| File | Holds | Kinds |
|---|---|---|
.github/truth/org.yaml |
dead hosts, retired terms, banned literals |
dead-host, retired-term, banned-literal
|
.github/truth/claims.yaml |
hand-typed counts, current-version literals, retired vocabulary |
numeric-claim, version-claim, plus two vocabulary rules |
The claim kinds live in their own file so that a pinned nself-ci release that predates them keeps parsing org.yaml. A repo lists claims.yaml only after its pinned release carries the claim kinds.
A repo enforces the rules with .nself/ci.yaml:
version: 1
checks:
truth:
rules: [.github/truth.yaml]and a .github/truth.yaml that includes the vendored org rules and adds its own exemptions:
version: 1
include: [.github/truth/org.yaml, .github/truth/claims.yaml]
overrides:
claims.current-version:
facts: .github/facts.json
exempt_paths:
- {glob: "docs/history/**", reason: dated notes keep the version that was current then}All rule files are version: 1. A rule has id, kind, message (what to write instead) and optional paths (globs; default every file). Unknown keys, duplicate ids, bad patterns, exemptions without a reason and rule files outside the repository are E644 and name the file and key.
| Key | Kinds | Meaning |
|---|---|---|
pattern |
numeric-claim, version-claim | RE2, matched case-insensitively against one line or two adjacent lines. Required, with paths. |
facts |
version-claim | Repo-relative JSON or YAML file with a top-level version. A literal equal to it passes (a leading v is ignored). |
exempt_paths |
all |
[{glob, reason}]: files the rule skips. |
exempt_matches |
all but dead-host |
[{regex, reason}]: a hit inside a match of the regex is exempt. |
exempt_regions |
retired-term, banned-literal |
true makes the rule honour the markers below (the claim kinds always do). |
term ending in (s)
|
retired-term | Matches with and without the plural s. |
A reason has at least 8 characters.
A claim rule (and a vocabulary rule that sets exempt_regions: true) skips a hit that sits in:
- A generated region:
<!-- BEGIN GENERATED:<id> -->to<!-- END GENERATED:<id> -->(in.mdx:{/* BEGIN GENERATED:<id> */}). Both markers and a declared id must be present; an unclosed or repeatedBEGINexempts nothing. - A JSX expression
{...}in an.astroor.mdxfile. Braces must balance within a paragraph, inline code and code fences do not count, and an expression that is only a quoted string or a template literal without${}is typed text, so it is not exempt. Astro frontmatter is scanned conservatively, including strings and comments;<script>and<style>blocks remain skipped. - The line after a comment that is the whole line:
<!-- truth: exempt <reason> -->(in.mdx:{/* truth: exempt <reason> */}). Text that merely mentions the marker, in prose, inline code or a fence, exempts nothing. A comment with no reason (fewer than four letters or digits) is a finding (truth.exempt-without-reason) and exempts nothing. - A reasoned
exempt_pathsentry for a generated file. AGENERATED BYheader alone grants no exemption.
Claim rules never read Go source, go.mod, go.sum or go.work, and skip changelogs, release notes (release-notes/, releases/v*), blog posts (content/blog, blog/*.md), ADRs, history folders, testdata, vendor and node_modules by default. They apply to prose paths only: Markdown, MDX, Astro (including frontmatter), TSX, JSX, HTML, README*, text files, i18n JSON values and packaging descriptions. TSX, JSX and frontmatter are scanned conservatively as text, including comments. Path globs ignore case, including exemption paths.
Generated region ids must be declared in a loaded rule file with a reason of at least eight characters. The shipped rules declare only catalog; repositories can declare ids that their generators own:
generated_regions:
facts: generated by the repository facts generator and checked in CIUnknown region ids exempt nothing. A header saying GENERATED BY also exempts nothing; use declared region markers or a reasoned path exemption instead.
Binary, UTF-16 and files over 4 MiB still run through dead-host matching up to a bounded 16 MiB read. Unscannable prose in a claim rule's scope produces truth.unscanned-file; a file above the host limit produces the same finding when any rule applies. NUL bytes anywhere in a file trigger this handling. Only explicitly excluded paths bypass it. Finding paths and matches that contain a dead host are redacted. Zero-width spaces, joiners, word joiners and BOMs cannot hide matches. Claims can span two adjacent lines, and the shipped count rule also catches labelled counts such as Plugins: 62.
Known limits: claims split over more than two lines, full-width digits, all 62 free and paid plugins and 62 <b>plugins</b> are outside the count pattern. Astro <script> and <style> blocks and dynamic expressions in Astro/MDX remain exempt.
A rule file may carry an overrides block keyed by rule id. It adds exempt_paths (and, for a version-claim, facts) to a rule declared in that file or in a file it includes, directly or not. An override that names an unknown rule id, or a rule the file does not include, is E644. facts is resolved against the scanned root, and a facts file reached through a symlink out of the tree is refused.
| Id | Kind | Catches | Write instead |
|---|---|---|---|
claims.counts |
numeric-claim | a count of plugins, commands, verbs, bundles or integrations | render it from the generated facts, or use words |
claims.counts-ar |
numeric-claim | the same count in Arabic i18n values (Western or Arabic-Indic digits) | render it from the generated facts |
claims.current-version |
version-claim | a version typed next to "nself" or "current/latest release" | render it from the facts file |
vocab.pro-plugin |
retired-term | the retired phrase for licensed plugins | "licensed plugin(s)" |
vocab.bundles-repo-slug |
banned-literal | the old name of the Bundles repository | nself-org/bundles |
vocab.bundles-repo-slug exempts Go module paths (exempt_matches): renaming a module path is a breaking change and GitHub redirects the old name.
Examples (each line below is exempt from the rules it shows, so this page stays clean):
- fails:
We ship 129 free plugins.
- passes: a line preceded by
<!-- truth: exempt a quoted line from the 2019 launch post -->
Human mode prints one line per finding, then the rule's message:
truth: <rule-id> <file>:<line>: <match>
fix: <message>
A dead-host finding prints a hash prefix, never the host. --json prints one document with data.result (pass, fail, error), per-check results and the findings.
From free/ci of this repository (the plugin binary does not link the check until the V15 engine does):
go run -mod=vendor ./internal/gates/truth/run.go check --root DIR [--json] [--kind K|rules] RULEFILE...Exit 0 pass, 1 findings, 2 rule-file or scan error. --kind rules validates the files without scanning.
bash scripts/truth/vendor-rules.sh TARGET_DIR # writes TARGET_DIR/.github/truth/{org,claims}.yaml
bash scripts/truth/vendor-rules.sh --check TARGET_DIR # exit 1 when a copy is missing or differsEach copy starts with # VENDORED FROM nself-org/plugins@<sha> — DO NOT HAND EDIT, then this checkout's bytes unchanged. --check ignores the header line. The truth-sweep drill runs --check on every public clone that carries .github/truth/, so a drifted copy fails the nightly sweep.
- Commands
- File Processing Commands
- GitHub Commands
- ID.me Commands
- Jobs Commands
- Notifications Commands
- Realtime Commands
- Shopify Commands
- Stripe Commands
View All: Home (or see the full alphabetical list below, synced with registry.json)
- Access-Controls
- Admin-Api
- AI-CLI
- AI-Studio
- Alerts
- Analytics
- API
- Audit
- Audit-Analytics
- Audit-Log
- Auth-Enterprise
- Backup
- BYOK
- CDC
- CDN
- CI
- Claw-CLI
- Cloudflare
- Compliance
- Content-Acquisition
- Content-Progress
- Content-Safety
- Costs
- CRDT
- Cron
- DDNS
- Devices
- DLQ
- Documents
- Dogfood
- Donorbox
- DR
- E2EE
- Encryption
- Entitlements
- Event-Bus
- Family-Ancestry
- Family-FamilySearch
- Family-GEDCOM
- Family-MyHeritage
- Family-WikiTree
- Feature-Flags
- Federation
- File-Processing
- Flags
- Forgejo
- Functions-V8
- Game-Metadata
- Gateway
- Gauth
- GDPR
- Geocoding
- GitHub
- GitHub-Runner
- HIPAA
- Home
- IDme
- Infra
- Invitations
- Job-Queue
- Jobs
- K8s
- Link-Preview
- Maintenance
- MDNS
- Media-Processing
- Meetings
- MLflow
- Model
- Monitor
- Monitoring
- Notifications
- Notify
- nSelf-Cloud
- nSelf-Eval-Gate
- nSelf-Geo
- nSelf-Image
- nSelf-PDF
- nSelf-Scan
- nSelf-Sync
- nSelf-Vault
- Object-Storage
- Observability
- Ollama
- Payments
- PayPal
- Pentest
- Pentest-Kit
- Plugin-ClawDE
- Plugin-Gauth
- Plugin-LLM-Gateway
- Plugin-PTY
- Plugin-Retrieval
- Podcast
- Post
- Push
- Queue
- Region
- Release
- Retro-Gaming
- Rom-Discovery
- Search
- Sentry-CLI
- Shared-Utils
- Shopify
- SIEM
- SMS
- Soak
- Sports
- Storage
- Stripe
- Subtitle-Manager
- Tenant
- Tenant-Controller
- TMDB
- Tokens
- Torrent-Manager
- Traefik
- VPN
- WAF
- Watchdog
- Web3
- Webhooks
- Workflows
- Getting started with nself ci
- CI architecture
- CI configuration and policy
- Evidence and machine output
- Nodes and Runners
- CI trust, security and privacy
- CI scheduling and cost
- Hosted providers
- Cache and artifacts
- Walkthrough: a home lab
- Walkthrough: several projects on one fleet
- Walkthrough: a team
- Walkthrough: agents
- Migrating to the new CI engine
- CI troubleshooting
- CI reference
- CI agent reference
- Auto placement and pools
- Watch and services
- macOS node ownership checks
Related