Configuration¶
Without an adopter policy, RigorFoundry uses portable defaults and emits a governance candidate explaining that the repository-specific contract is missing.
Policy discovery is deterministic:
- an explicit repository-relative CLI path;
rigor-foundry-policy.json;config/rigor-foundry/policy.json.
The selected policy must be a tracked, non-symlink UTF-8 file inside the audited repository. External, parent-relative, ignored, and symlinked policy inputs fail closed rather than silently controlling the audit.
rigor bootstrap creates a starting policy only when the adopter explicitly
names the policy, canonical TODO, review ledger, and every source/test root.
The policy path must be trackable; internal paths must already be Git-ignored.
Parents and code roots must already exist. The generated policy uses observe
mode, production/test line thresholds of 700/1000, and marks all audit domains
required. It does not infer applicability, production packages, size
registries, native commands, or internal paths. Review those fields before the
first baseline; see First repository.
Candidate anchor schemas¶
Candidate-anchor schema 1.0 is a strict discriminated union:
tracked-blobbinds a canonical path, inclusive line span, scanned Git blob identity, and content SHA-256;repository-treebinds a path locus, fixed state span1:1, HEAD tree identity, and complete tracked-content SHA-256.
Report schema 1.3 requires one anchor on every candidate and records
git_object_format. Scanner version 0.3.0 verifies all anchors against the
inventory that produced the report. Campaign schema 1.5 carries the object
format into independent-run input comparison. Campaign schema 1.9 additionally
binds ignored inventory, campaign purpose, witness requirements, explicit
inference identity, and structured built-in adapter evidence. Earlier report or campaign
records must be regenerated; unknown or mixed anchor fields have no
compatibility fallback.
Repository policy controls source and test roots, production packages, mandatory audit domains, size registries, enforcement mode, the expected rule-maturity policy digest, and declared native adapters. The desired-state API adds typed adopter variables, namespaced custom controls, applicability decisions, exact standard-pack selections, and secret-provider references.
Source and test roots are interpreted as repository-relative component
sequences; absolute and parent-traversing values never match. A source root
must prefix a path by whole components; when roots overlap, the longest
component sequence owns the module. A test root may occur
below a workspace prefix, but it must match complete contiguous components, so
tests does not match contest or tests-old. Common test_, _test.py,
.test., and .spec. conventions apply to the generic scanners. Polyglot
ownership additionally recognises native _test.<suffix> and
_tests.<suffix> names. These naming profiles are fixed scanner capabilities,
not policy-controlled aliases. Python module ownership deliberately preserves
the narrower historical contract: configured test roots are repository
prefixes, and only test_/_test.py names apply outside them.
Declared ignored inventory¶
Policy schema 1.3 and later accept an ignored_inventory array sorted by
evidence_id, path, and capture. Every declaration contains a unique
portable evidence_id, a unique exact normalised repository-relative path,
and presence, file-sha256, or directory-sha256. Globs, absolute paths,
parent traversal, tracked paths, and paths outside Git ignore rules are
rejected.
Collection never records file content, symlink targets, environment values, or
undeclared roots. directory-sha256 recursively hashes only the declared tree
through descriptor-relative no-follow reads, stores byte paths as hexadecimal
inside a versioned canonical manifest, and caps depth, entry count, and total
regular-file bytes. Evidence is limited to observed, missing, or
unavailable, the observed entry kind, bounded byte size, an optional SHA-256,
and a finite reason. Symlinks, special files, hard links, resource-limit
breaches, and concurrent mutation fail closed. Missing and unavailable
observations are evidence gaps, not automatic control failures.
Rule-maturity policy and cases¶
Rule-maturity policy is deliberately separate from repository scan policy. It
controls whether a rule may influence ratchet or zero enforcement, not whether
the scanner may emit a candidate. The schema-1.0 policy requires these exact
integer fields and its canonical policy_digest:
minimum_adjudicated_reviews;minimum_distinct_repositories;minimum_distinct_reviewers;minimum_positive_reviews;maximum_false_positive_basis_points, from 0 through 10,000;maximum_median_effort_seconds; andmaximum_p90_effort_seconds.
All minima are at least one, positive reviews cannot exceed the total review minimum, and the median limit cannot exceed the p90 limit. RigorFoundry supplies no universal threshold values; adopters must justify them against their corpus and operating costs.
Repository policy schema 1.3 binds that separate calibration through
maturity_policy_digest. Observe mode may leave it null, but a repository
whose enforcement_mode is ratchet or zero must name the exact 64-character
lowercase policy digest. A CLI request may strengthen observe mode only when
the repository already binds the supplied maturity report's policy digest.
Consequently an operator cannot select an empty or weaker calibration report
at gate time and silently turn repository candidates into probation.
The case manifest has exact top-level fields schema_version, policy, and
cases. Every case has repository_id, report, review, candidate_id,
reviewer_effort_seconds, and non-empty effort_evidence. A source-validated
case projects the exact tree, tracked-content, scan-policy, report, candidate,
rule, review, reviewer, decision, timestamp, duration, and effort references
into one evidence digest. Reusing the same report/candidate pair twice is
rejected. The resulting maturity report binds the current rule-pack version and
digest, complete threshold policy, sorted evidence, all rule assessments, and
one maturity_digest.
External source retrieval policy¶
External source provenance uses a standalone schema 1.0 retrieval policy; it is not silently inherited from repository scan policy. The policy requires a sorted unique host allowlist, explicit cross-origin redirect decision, at most ten redirects, positive timeout and freshness bounds, a byte limit no greater than 32 MiB, and sorted lowercase media types without parameters. Capture metadata must reproduce the complete policy and its digest. See Verified external source provenance.
Git executable trust¶
Git bootstrap trust is runtime configuration, not repository-controlled policy:
the audited repository cannot choose which executable reads its own index. By
default, RigorFoundry searches the basename git only in fixed platform roots,
never in ambient PATH. The default supported interval is Git 2.35.2 or newer
and lower than 3.0.0.
Every Git-using CLI command accepts the same controls:
rigor scan --root /workspace \
--git-executable /opt/toolchain/bin/git \
--git-trust-root /opt/toolchain/bin \
--git-min-version 2.43.0 \
--git-max-version-exclusive 3.0.0
--git-trust-root is repeatable and ordered. Roots must be normalised absolute
directories. An absolute executable requires at least one explicit root; a
relative executable must be one basename and is searched only below the roots.
The root, intervening components, and executable must not be symlinks.
The executable must be a single-link regular file; POSIX mode checks also
reject group/world-write, set-user-ID, and set-group-ID bits.
Each Git invocation overrides repository-local core.fsmonitor and
core.hooksPath. Hooks are redirected to a reserved absent directory below the
selected trust root; if that reserved path exists, execution fails closed.
Execution also fails closed on platforms that cannot execute the already
validated open descriptor.
campaign-run must receive the same explicit options used by
campaign-create; otherwise frozen Git provenance diverges and the run is
rejected. It must also receive explicit provider, exact-model,
model-correlation-family, and operator identities. These values are evidence,
not labels inferred from a session or agent name. Operator declarations must be
authenticated by the surrounding deployment; the campaign record does not
claim a cryptographic operator signature.
Python callers pass GitTrustPolicy through the keyword-only
git_trust_policy argument of scan_repository, load_git_inventory, campaign
workflow functions, ignored-path storage, or TODO promotion. The durable report
records GitExecutableProvenance: resolved path, selected root, parsed version,
executable SHA-256, complete versioned trust policy, derived policy SHA-256,
and its own identity digest. Portable policy JSON uses canonical forward-slash
POSIX or Windows absolute paths and can be verified on another host. The trust
root is an operator assertion and must be protected from untrusted writers.
Campaign storage and TODO promotion reproduce the report or campaign identity
before using Git ignore rules; a different policy-compliant binary is still
treated as input divergence.
Classified coverage residuals¶
Repository self-governance uses the tracked coverage-residuals.json manifest.
It is separate from adopter audit policy because it describes this tool's own
test-evidence boundary, not a finding in the repository being audited.
Each residual must identify one exact source symbol and guard, bind the
symbol's SHA-256, use one of platform-primitive, runtime-invariant, or
race-window, name an accountable owner, cite public regression tests, list
specific revisit triggers, and expire no later than 90 days after review. A
source edit, removed guard, missing test, expired review, unknown field, or
duplicate identifier fails validation.
The manifest also contains preregistered regular-expression searches over
explicit test owners. Their expected result is empty. They currently reject
private production-helper calls, object.__new__ construction, monkeypatched
production internals, and direct use of the private preflight runner.
Validate the contract directly with:
rigor residuals-check --root .
python -m tools.audit, make audit, local preflight, and the CI quality job
run the same validator. Residuals never count as covered lines and do not lower
the aggregate branch-coverage threshold.
Repository guard CLIs print only fixed pass/fail status. Precise diagnostic strings remain available to trusted in-process callers, while validation exceptions also fail closed without a traceback. Neither enters shared logs, because repository paths and metadata can be attacker-controlled. The secret guard additionally replaces every programmatic path with its full SHA-256 identifier.
Desired-state inputs¶
A StandardPack defines versioned controls, evidence contracts, remediation
contracts, licence and source metadata, a detached signature envelope, and a
content digest. A ProjectProfile selects exact pack versions and digests and
records project intent without copying or weakening the pack silently.
Standard-pack schema 1.1, signature-envelope schema 1.0, and pack-verification
schema 1.0 require and bind the rigor-foundry.standard-pack.v1 signature
domain. Reviewer-attestation schema 2.0 requires
rigor-foundry.reviewer-attestation.v1. Both sign the public
ed25519_signature_message() encoding: the fixed
RIGOR-FOUNDRY-ED25519\0v1\0 prefix, a two-byte big-endian domain length, the
ASCII domain, and the 32 payload-digest bytes. Earlier pack schema 1.0,
unversioned reviewer records, missing domains, and raw-digest signatures must be
regenerated and re-signed; the parser and verifier provide no legacy fallback.
Project variables support string, integer, number, boolean, and
string-list values. Every variable declares project, environment, or control
scope and public, internal, or secret sensitivity. Definitions can constrain
allowed values, regular-expression matches, numeric ranges, and list sizes.
Assignments and definitions are digest-bound so a stale value cannot be
mistaken for the current contract.
Secret values do not belong in profiles or effective locks. A secret variable contains only an opaque provider, reference, and version. Secret resolution is an authorised adapter responsibility and the resulting bytes must not enter reports, plans, logs, or public output.
Declared native adapters are executable policy, not passive configuration.
gate and campaign-run refuse to run them unless the operator supplies
--allow-native-audits. Consent still uses a fixed credential-free
environment, a no-network read-only bubblewrap sandbox, mandatory timeouts,
process-tree termination, disabled nested user namespaces, and a streaming
output hard cap. Native execution currently requires the Debian-family
/usr/bin/bwrap and /usr/bin/dpkg-query surfaces, a root-owned single-link
launcher and query executable without group/world-write, set-user-ID, or
set-group-ID bits, Bubblewrap version 0.9.0 or newer and lower
than 1.0.0, an installed bubblewrap package,
and every option named by BubblewrapCompatibilityPolicy. Missing dpkg
association, an unsupported version, option drift, or identity change during
an adapter run fails closed. Gate and campaign records embed the complete
policy and observed secret-free package-database evidence. The dpkg fields do
not prove repository signatures or installed-payload checksums; the executable
SHA-256 identifies the bytes inspected and executed.
Policy schema 1.3 also accepts immutable built-in Semgrep, offline Trivy, and offline OSV lockfile profiles. They project an exact clean tracked-only workspace, bind structured profile evidence, and withhold domain coverage for partial or unavailable results. The OSV profile additionally verifies an exact operator-provided local database snapshot against a tracked manifest; it never downloads or updates vulnerability data. Adopters cannot override a profile's command, parser, or domain mapping. See Built-in adapter profiles.
Conditions use a bounded declarative expression tree. They can read declared context values and combine supported comparisons and boolean operations; they cannot import code, invoke a shell, access the network, or mutate the audited repository. Missing references and malformed or oversized expressions fail closed.
Profile resolution verifies exact pack pins and actual domain-separated
Ed25519 signature bytes against an explicit public-key trust store, resolves
typed assignments, applies overlays and finite waivers, retains contradictions,
and emits an EffectiveProfileLock. The verification record binds the key,
signature domain, signature, pack, and trust-store digests; a caller-supplied
valid label is never accepted. A lock is ready only when required inputs are
complete and no blocking contradiction remains. Stronger targets and explicit
denial win over unauthorised weakening.
Every weakening waiver is exact: control, field, previous value, new value,
and active time window must match. Risk acceptance uses a dedicated
assessment-status waiver from fail to accepted-risk; its identifier is
kept separate from applicability, target, and mode waiver identifiers. Those
other waiver classes cannot authorise an accepted-risk assessment.
Evidence names an exact AdapterLock digest whose declared domains include the
control domain. Reviewer attestations carry an Ed25519 signature over the exact
assessment body, identity, decision, and validity window. Clearance reverifies
that signature against an explicit reviewer trust store and binds that store's
digest into the assessment before counting distinct reviewer identities and
keys. Remediation and rollback argv must match the exact command digest recorded
in their adapter locks.
Local campaign records, review ledgers, and optional TODO promotion default to
.rigor/. The adopter must keep that directory Git-ignored; commands that
would write there fail closed when the ignore boundary is absent.
Campaign creation validates and prepares that ignored storage root before
freezing the repository input projection, so the tool's own durable storage
cannot create a post-freeze ignored-inventory divergence.
Diagnostic campaigns may use one model witness. Promotion campaigns require at least two expected runs, at least two correlation witnesses, and at least two declared operator identities. Repeated runs from one model family or the same provider/exact-model pair remain useful for repeatability evidence but join one transitive correlation component and count as one promotion witness. Witness schema 1.2 stores each provider/exact-model pair and forbids families or exact pairs from appearing in more than one durable comparison component.
Configuration schemas are versioned. Unknown schema versions, contradictory variables, unavailable adapters, and unresolved controls remain explicit errors or evidence gaps.
The desired-state records are currently a Python API. Stable CLI profile import/export and an authorised remediation executor remain roadmap work.