BNA1981#Schedule1/P1.1 — in its source_refs. A key on its own says nothing:
it is a placeholder for “this rule comes from somewhere”. Provenance is the step
that resolves those placeholders into verified references: at publish time
you supply, per key, the document it cites and the verbatim words it relies on.
The engine fetches or loads the document, digests the exact bytes, checks the
quote occurs in them, and records an immutable reference on the published cut.
If any declared key fails, the whole publish is rejected and nothing is
stored — including the references that did verify. Provenance fails closed.
The two kinds of citation target
Each entry names exactly one target. Both or neither is a422.
The split exists because the two answer different questions. A URL citation
says “this rule comes from that public authority, and here is the text it
relies on”. An artefact citation says “this rule comes from the document I
gave you, and you still have it” — the guidance PDF, the internal policy note,
the operator manual that has no public URL to point at.
Publish with citations
From the CLI
aethis publish --source-targets takes a YAML (or JSON) file keyed by citation
key. Each entry names url or file; a file entry is uploaded to the
project first and cited by the source_id it comes back with.
title / authority / licence /
quote.exact, a readable file, an HTTPS URL, and unknown keys (a silently
ignored typo is how a citation loses its locator on an immutable ruleset). A
malformed targets file costs zero round trips.
The CLI then prints what each key resolved to, keeping the two kinds visibly
distinct — a url line is a public link, an artefact line is an
authenticated download:
--source-targets needs aethis-cli v0.31.0+. Check what PyPI serves
today on Capabilities and access — if it is still
behind, use the REST call below, which is what the CLI sends.Over REST
source_targets is a map from citation key to target, on the publish body
(illustrative — identifiers are placeholders; field names and shapes are
exact):
- Only keys the compiled ruleset declares are resolved. A target whose key
no criterion declares is ignored, not rejected — so a typo’d key publishes
successfully with that citation simply missing. Read the ruleset back with
/explainand check the keys landed. (The CLI does this for you and reports the count.) - A private publish that supplies
source_targetsresolves them now. The references are recorded on the published cut, so a later public flip finds a citation-complete ruleset instead of a dead end. Supplying no targets on a private publish leaves the keys unresolved, which is fine until something needs them.
source_targets on PATCH /rulesets/{ruleset_id}/visibility
when flipping a ruleset public — the flip resolves and validates them
fail-closed before it proceeds.
What a stored reference looks like
Published references come back on every criterion fromGET /rulesets/{ruleset_id}/explain.
A schema v1 reference — an HTTPS citation (illustrative):
url on a v2 reference is a relative, authenticated path
Fetching the retained artefact, then, is:
X-Source-SHA256 carries its
digest — verify it against the reference’s content_digest and you have proven,
without trusting the engine, that you are holding the bytes the quote was
checked against. 404 means the source predates artefact retention (only its
extracted text survives); 500 means the source records a digest but its
artefact is missing, which is an integrity error reported loudly rather than
disguised as a legacy upload.
Uploaded sources are also protected from disappearing under a live citation:
DELETE on a source that any persisted ruleset cites returns 409. Mark it
superseded or reference_only instead — it drops out of generation while its
citations stay resolvable.
Public rulesets cite URLs only
A public ruleset’s references are served anonymously, and an artefact reference points at a private project source. So an artefact-backed ruleset can never become publicly resolvable. The engine rejects it — with reason codeartefact_reference_public_visibility_forbidden — on every lane to public:
- publishing a ruleset that will be public,
PATCH /rulesets/{ruleset_id}/visibilitytopublic,PATCH /rulebooks/{rulebook_id}/visibilitytopublic(a public rulebook would expose a private member’s references),- attaching or promoting a member into an already-public rulebook.
What verification does and does not attest
The check is verbatim after whitespace normalisation. Runs of whitespace collapse to single spaces on both sides; HTML is tag-, script- and style-stripped, because a quote cites prose rather than markup; PDFs are checked against extracted page text. There is no stemming, no case folding and no fuzzy matching — the quote’s words, in that order, must appear exactly. The snapshot is what makes a citation survive its source. When a page is rewritten or a URL goes dead, the quote and the digest still resolve to the bytes the engine kept, so a decision made a year ago remains explainable. Identical bytes are stored once, keyed by digest. Pin the bytes you reviewed withexpected_digest on a target: a fetch whose
digest differs then fails the publish rather than silently citing a changed
document.
When a citation fails
Every failure is reported per key, with a stablereason_code, in a 422
carrying reason_code: source_reference_resolution_failed and a failures
array. Nothing is published and no snapshot is retained.
URL targets additionally fail closed on the fetch itself — a non-HTTPS scheme,
credentials in the URL, an IP literal or private destination, too many
redirects, an oversized body or a timeout each carry their own reason code.
Reading citations back
source_refs (the declared keys) and
source_references (the resolved, verified references):
Both fields are served by
api.aethis.ai today, and the response above is
what that call returns right now. The first-party showcase rulesets do not yet
declare citation keys, so nothing resolves against them and both fields come
back null — the reference shapes shown earlier on this page are taken from
the live API schema, not from a showcase response. Treat the array as optional
in client code: null and [] both mean “this criterion carries no verified
reference”, never “verification failed”.See also
- Rule generation — the generation-time grounding report, which is a different thing: it scores how well generated rules trace to your uploaded sources, and never blocks. The citations on this page are publish-time, verified, immutable and fail-closed.
- CLI — the
--source-targetsflag. - REST API — the
projects:readscope that the artefact download route requires. - Decision envelope — the per-decision reproducibility fields that sit alongside provenance.