Facts is a Go port of Puppet Facter: it discovers and reports facts about the system it runs on — embeddable as a Go library, and shipped as the facts CLI (ADR-0008; no facter alias).
Facts:
The project, the Go library, and the identity of everything user-visible: package facts, module github.com/ncode/facts, the facts binary, the Facts diagnostics token.
Avoid: Facter (that's the upstream Ruby product we interoperate with)
facts (the CLI):
The CLI binary shipped by Facts (ADR-0008, superseding ADR-0004). There is no facter alias; the facter-named inputs (facter.conf paths, FACTER_* environment facts, puppetlabs fact directories) keep working as the compatibility tier of the input surface, with the facts-native names (facts.conf, FACTS_*, /etc/facts/facts.d and friends) taking precedence.
Fact:
A named piece of system information addressed by dot-notation (e.g. os.name), whose value is a scalar, list, or map.
Avoid: property, attribute, metric
Core fact: A fact discovered by the engine's built-in resolvers from the host system itself. Avoid: built-in fact, native fact
Registered fact: A fact registered programmatically on an engine at construction by the embedding Go program. Avoid: custom fact (that's upstream Facter's Ruby-DSL concept, which Facts does not support)
External fact: A fact supplied from outside the engine — structured data files, executables, or environment variables — that takes precedence over core and registered facts.
Legacy fact:
A flat, pre-structured fact name (e.g. operatingsystem) from Ruby Facter's deprecated alias layer. Removed entirely (ADR-0007): no legacy alias resolves anywhere; the structured tree is the only fact surface. Unrelated to the legacy text format (the default key => value output), which stays.
Not-applicable fact: A fact whose preconditions don't hold on this host (e.g. EC2 metadata off-cloud). It is simply absent from the canonical tree — never an error. Only facts that were supposed to resolve and didn't count as failures. Avoid: failed fact, missing fact (that means "no such fact name")
Disabled fact:
A fact removed from discovery by the disabled set — the union of the CLI --disable, the FACTS_DISABLE environment variable, and the facts.conf disable key (the Facter blocklist key is accepted as its compatibility alias). Every fact is enabled by default; there is no opt-in. A disabled fact backed by its own resolver is not resolved at all (resolution-gating, e.g. packages); a fact that shares a multi-output resolver, or a disabled sub-fact, is pruned after resolution. --no-block re-enables everything for a run.
Avoid: blocked fact (use only for the Facter-compatible blocklist config spelling), hidden fact (disabling a standalone-resolver fact skips resolution, not just display)
Supported fact: A fact documented in the schema as part of Facts' supported output contract for one or more supported release targets. Avoid: available fact (too host-specific), implemented fact (too code-centric)
Canonical fact spelling: The one documented dot-notation path for a supported fact concept across supported release targets. Facts favors schema predictability over preserving upstream spelling differences for the same concept. Avoid: platform alias, compatibility duplicate
Facts-native extension: A supported fact that exposes accurate host information beyond Ruby Facter's fact set for that platform. It must use canonical fact spelling, be documented in the schema, and be validated like any other supported fact. Avoid: byte-parity exception, extra Facter fact
Supported release target: A platform that Facts builds, validates, and treats as release-blocking. Supported release targets are broader than the host running a single discovery and narrower than every OS family with some compatible code path. Avoid: supported OS, supported platform (unless the context is casual)
Candidate release target: A platform being evaluated for promotion into the supported release target set. It may have lab validation and working facts, but it is not release-blocking until an OpenSpec change updates the target set and validation gates. Avoid: supported target, best-effort support
illumos target: The SunOS-family release target validated by Facts through OmniOS. Solaris Facter behavior can inform shared SunOS-compatible facts, but Oracle Solaris is a separate target until Facts has a repeatable Oracle Solaris validation host. Avoid: Solaris when referring to the OmniOS lab guest
Oracle Solaris target: A separate SunOS-family candidate release target for Oracle Solaris. It is not covered by the illumos target and needs its own repeatable validation host before Facts treats Oracle Solaris behavior as release-blocking. Avoid: assuming OmniOS validates Oracle Solaris
DragonFly target identity:
DragonFly BSD reports as DragonFly for os.name, os.family, and kernel.name when Facts promotes DragonFly facts. It follows the BSD-family shape without collapsing into FreeBSD or generic BSD naming.
Avoid: FreeBSD, BSD
illumos target identity:
The illumos target reports the validated distribution as os.name (OmniOS in the lab), the OS family as illumos, and the kernel as SunOS.
Avoid: Solaris for os.name or os.family on OmniOS
Output contract: The externally observable shape of resolved facts — fact names, nesting, value normalization, and formatter output (JSON, YAML, HOCON, legacy text). Canonical fact spelling is binding for supported facts; Ruby Facter compatibility guides structure and values, but does not require preserving upstream spelling differences for the same concept.
Input contract:
The accepted sources and semantics of operator-supplied facts — external fact files/executables/env vars, and the config file (facts-native facts.conf first, facter.conf as the compat read) — which must keep working unchanged. Binding; not negotiable in the library work. The Ruby custom-fact DSL is deliberately outside the contract: .rb fact files are not read anywhere. Puppet's runtime surface is outside it too: Facter's own external-fact directories (including the puppetlabs/facter/facts.d defaults) are in, but Puppet's agent cache (puppet/cache/facts.d pluginfactdest), pluginsync, and .rb plugin loading are not — Puppet's cache, like any directory, is reachable only through the explicit external-fact directory inputs.
Engine:
An isolated, immutable unit of fact-discovery configuration — fact registrations, sources, and diagnostics are fixed at construction, and nothing mutates afterward. There is no package-global engine and no global mutable state; every consumer (the facts CLI included) constructs its own. Engines are hermetic at birth: they discover core facts only, until explicitly configured with registered facts, external fact sources, config files, or caches.
Avoid: collector, client, instance API
Snapshot: The immutable result of one discovery run: the canonical tree plus pure query and decode operations over it. Facts within a snapshot are mutually consistent; freshness is obtained by discovering again, never by mutating. Discovery is expensive and explicit; querying a snapshot is pure and free. Avoid: session, fact cache
Compatibility boundary:
The facts CLI process edge — the only place Ruby Facter compatibility is promised, via the output contract and input contract. The Go API itself makes no Ruby-compatibility promises.
Avoid: Ruby-compat facade (removed; the Go-level Ruby API no longer exists)
Canonical tree: The single post-precedence dynamic representation of all resolved facts — the one structure both formatters and library consumers read. There is no second model. Avoid: fact hash, output map
Typed view: A decode of part of the canonical tree into a caller-supplied Go type, failing loudly on shape mismatch. A view never resolves facts independently of the canonical tree. Avoid: typed fact, fact struct
Package:
A unit of installed software tracked by a package source (a dpkg entry, an rpm header, a macOS installer receipt, a nix profile entry). The packages fact reports packages, not arbitrary installable artifacts; an installed .app bundle is reported too, but as its own apps source, never folded into database packages.
Avoid: application, artifact, software (too broad — an unpackaged binary is not a package)
Package source:
An authoritative inventory of installed software on the host — usually an installation database (the rpm database, the dpkg database, the FreeBSD pkg database, macOS installer receipts, the nix store), or a structured filesystem inventory where the OS keeps no database (macOS .app bundles → apps). A host has several coexisting sources, so the packages fact namespaces by source — packages.<source> — and never merges records across sources. Frontends that write to the same database are the same source (apt/aptitude → dpkg; dnf/yum/zypper → rpm), so the source key is the database, never the frontend or the file format.
Avoid: package manager (ambiguous — frontend vs database), package format (deb/rpm name the artifact, not the source)
Package record:
One installed package's entry in a source's list — a map identified by its fields (name and version always; plus per-source identity fields such as architecture, type/tap, store_path, bundle_id/path, or the Windows uninstall subkey/ProductCode), never by position and never by a unique map key. version is the package manager's verbatim native string. The same software appearing in two sources (a macOS .pkg receipt and its .app bundle) is two records, not one.
Avoid: package entry, package map (records live in a list, not a name-keyed map)
Dev: A consumer's monitoring agent wants
networking.ipbut with their own registered fact overriding it. Is there a global registry they add it to?Expert: No globals exist — they construct their own engine and register the fact on it. The
factsCLI is just another consumer that wires up its own system-following engine; two programs in one process never share state.Dev: Their override comes from a script dropped in a directory — registered fact?
Expert: External fact. Registered facts exist only in Go code, on the engine that registered them; anything delivered as executables or data files is external, and it wins over core and registered facts. External facts arrive through the input contract, so the library work must not change how they load or resolve. And if someone drops a
.rbfile in that directory, nothing reads it — the Ruby DSL is outside the contract.