Onboard in a day — three copy-paste recipes
ISNAD is a grading engine to embed, not a turnkey app. It grades the transmitters and the chain, not just the claim: every claim carries an ordered chain of narrators (source → scraper → model), each narrator has a grade in a living registry, the chain is capped at its weakest link, and the fabricated (mawḍūʿ) tier is active containment, not a label.
This page is the shortest honest path to real value for the three users ISNAD was audited for. Each recipe is self-contained. Read the honesty contract at the bottom before you ship any of them.
Recipe 1 — Self-maintaining knowledge base (LLM-wiki)
Section titled “Recipe 1 — Self-maintaining knowledge base (LLM-wiki)”The value: no open-source tool grades who a claim came from and how it got there. Your wiki pages become claims whose provenance is graded and re-graded as new evidence arrives.
The honest limit: ISNAD grades transmission; it does not judge the truth of your domain content by magic. It will surface “this claim entered through an unverified scraper” — it will not tell you the claim is factually right. That is the point.
# kb.py — seed a registry, submit claims, read the graded surfaceimport os
os.environ["ISNAD_DATABASE_URL"] = "sqlite:///data/kb.db"
from isnad import Registry, gradefrom isnad.core.registry import RegistryDBfrom isnad.storage.sqlalchemy import get_session, init_dbfrom isnad.types import NarratorGrade
init_db()
with get_session() as session: reg = RegistryDB(session=session) reg.load() # Warm-start: a POPULATION PRIOR, not an observation. Seeded narrators are # prior-only — they can SERVE_WITH_CAVEAT, never plain-SERVE, until the # pipeline observes them (see the honesty contract). reg.registry.seed( "source:internal-docs", "kb", NarratorGrade.RELIABLE, source="operator" ) reg.registry.seed( "model:gpt-4o@2026-01-15", "kb", NarratorGrade.ACCEPTABLE, source="operator" ) reg.flush()
# A claim, its chain, and its grade:v = grade( "The release shipped on 2026-08-29", ["source:internal-docs", "model:gpt-4o@2026-01-15"], reg.registry, domain="kb",)print(v.chain_grade, v.action, v.why) # weakest link decides; `why` is the reasonDay-one loop: seed → submit → read action from the API or the Verdict.
Claims whose only support is a seed are caveat-served; the first
record_survival / post-hoc audit on a narrator unlocks plain serve.
Give the critic your documents, not just prior claims. The content critic
checks a claim against what it was grounded in. Pass the retrieved KB
documents as corpus_docs so consistency is judged against evidence, not
against other (unverified) claims:
import requestsrequests.post( "http://localhost:8000/v1/claims", json={"claim_text": "the release shipped 2026-08-29", "domain": "kb", "chain": [{"narrator_id": "source:internal-docs"}], "corpus_docs": ["release notes: shipped 2026-08-29"]}, headers={"X-API-Key": "isnad-admin"},)(An operator-supplied corpus is an assumption, not an observation — ISNAD
records critic_corpus_operator_docs so the provenance is never hidden.)
Serve the graded surface, not the raw table:
from isnad.api.app import app # or run `uv run isnad api`# GET /v1/claims -> served-only (containment) surface# GET /v1/claims?served_only=false -> audit view (everything, incl. quarantined)Recipe 2 — Medical RAG (conservative by default)
Section titled “Recipe 2 — Medical RAG (conservative by default)”The value: RAGAS / Patronus / Galileo / TruLens grade outputs. None grades the transmission: which source, which retriever, which model, and whether that chain is trustworthy. A medical answer is only as good as its chain.
The safety setting. Put the high-stakes domain in hold mode: a prior-only (unobserved) narrator then downgrades any serve to REVIEW, so a seeded-but-never-observed source can never be served as-if-verified.
# Hold every domain, or just the high-stakes ones:export ISNAD_SERVE_GATE=hold # strictest: prior-only -> REVIEW everywhere# or: export ISNAD_SERVE_HOLD_DOMAINS=medical,pediatricfrom isnad import Registryfrom isnad.integrations.langchain import IsnadTracerfrom isnad.integrations.langchain.middleware import gate
registry = Registry() # your operator-assigned grades live here
# 1. Trace the actual retrieval -> model chain (retrieved docs are the corpus,# so the content critic judges the claim against what was actually read).tracer = IsnadTracer(registry, domain="medical")
# 2. Grade + gate each claim the moment it enters:result = gate( "ibuprofen 200mg is safe for this patient", chain=["source:uptodate", "model:med-llm@2026-06"], registry=registry, domain="medical",)if result.gated: # hold — do not surface to the clinician ...The invariant you must keep: a REVIEW/QUARANTINE/REJECT verdict is
containment, not a suggestion. Do not render gated output to a patient-facing
surface. A seeded source is an assumption; only an observed or human-vetted
narrator plain-serves.
Recipe 3 — Legal RAG (chain-of-custody for re-summarization)
Section titled “Recipe 3 — Legal RAG (chain-of-custody for re-summarization)”The value: every legal-AI vendor validates authority; none grades transmission. The unique risk in legal work is the summary-of-a-summary cited back as primary: a claim re-summarized through several agents loses its connection to the underlying document. ISNAD records that connection as a chain with full SHA-256 content hashes and an append-only audit trail.
What custody gives you: for a claim, you can answer “what document, what hash, through which models, at what time, and who changed it” — and the audit record self-hashes, so a tampered record fails verification.
# legal_rag.py — capture the chain, then export a tamper-detecting recordfrom isnad import Registryfrom isnad.core.chain import Chain, ChainLinkSpec, store_claimfrom isnad.types import TransformTypefrom isnad.audit.exporter import build_audit_recordfrom isnad.storage.sqlalchemy import get_session
registry = Registry()chain = Chain([ ChainLinkSpec("source:case-file-1234", 0, document_hashes=["<full sha256>"]), # Summarization is lossy -> destructive; the weak link caps the chain. ChainLinkSpec("model:summarizer@v3", 1, transform_type=TransformType.DESTRUCTIVE), ChainLinkSpec("model:citation-check@v2", 2, transform_type=TransformType.PASS_THROUGH),])
with get_session() as session: # The claim must be stored before its audit record can be exported. claim = store_claim(session, "<the claim text>", "case-1234", chain) record = build_audit_record( claim_id=claim.claim_id, session=session, registry=registry, )# record.record_hash -> self-hash; a re-export after any edit changes it.The invariant you must keep: custody is only as good as the ingest. The
document content hash must be the full SHA-256 of what was actually retrieved —
a truncated prefix or a fabricated timestamp breaks the chain. ISNAD records
what it observed and marks the rest “not captured” (None), never invents it.
The honesty contract (do not break)
Section titled “The honesty contract (do not break)”- No numeric confidence. Grades are ordinal (ṢAḤĪḤ/ḤASAN/ḌAʿĪF/ḌAʿĪF JIDDAN/MAWḌŪʿ) and the action is a route (serve/review/quarantine). Never emit a 0–100% score or a “probability of correctness”.
- Evidence artifacts, not conformity. Every downgrade carries its full chain + evidence log. A reviewer sees why, not a verdict in a vacuum.
- Grades are operator-assigned and local. ISNAD does not import any universal “trust score” — your registry is your evidence.
- A seed is an assumption.
prior_onlynarrators caveat-serve at best; they plain-serve only after the pipeline observes them or a human vets them. - MAWḌŪʿ is containment. A COMPROMISED (proven liar/fabricator) narrator is not “less trusted” — it is stopped, and the quarantine is permanent per-person.