Build with the local Soterias SDK. Get started with Python and TypeScript.
SoteriasDocsBuild
Workflows

Verify facts from multiple sources

This guide uses Python soterias==0.1.4, native 0.1.0rc5, and TypeScript/MCP 0.1.0-rc.5. Install the matching packages:

Terminal
python -m pip install "soterias==0.1.4"
npm install @soterias/core@0.1.0-rc.5 @soterias/mcp-server@0.1.0-rc.5

Download semantic.py, semantic.mts, semantic.mjs and semantic-mcp.mjs. Run python semantic.py, node semantic.mjs or node semantic-mcp.mjs from your project directory.

The accounting example creates two synthetic invoice records, retains the real files in encrypted local storage, pins a trusted application rule, extracts a typed amount claim and asks Rust to promote it. It checks the promotion after closing and reopening the store, then verifies that source revocation invalidates the previously promoted fact. It uses real local files, SQLite and encryption. There is no network request or model; provider tokens remain null, not fabricated.

Expected output is status: PASS, a preserved temporary directory and the actual elapsed milliseconds. The ledger and bank fixtures are synthetic; their independence is an operator assertion for the example, not a discovered real-world fact.

Origin labels, rule installation, promotion and keys belong to the trusted host. They must never be made available as agent tools. Agents use a narrowly bound read-only query surface after host authentication. A fact reference is DATA, not a warrant or effect permit. Existing broker admission remains required for writes. Reports in this example are unsigned; they are not signed broker receipts. Semantic rule installation and promotion use trusted application ownership; they do not consume a separate signed human approval or issue a signed fact receipt. An already-returned observation is a captured snapshot. Recheck before use and retain normal broker admission; revocation-atomic policy/effect integration remains an open gate beyond this component.

The initial verifier supports exact text, boolean and signed64 integer values under JSON Pointer mappings. It does not perform free-form natural-language reasoning. Contrary observations, missing fields and unresolved uncertainty prevent promotion; malformed or inaccessible evidence refuses. Full arithmetic, relational and temporal reasoning remains part of the existing typed policy IR and requires separate integration verification. See docs/SEMANTIC_FACTS.md.

node semantic-mcp.mjs additionally runs a real local HTTP MCP client/server component check with the same native evidence. It uses the exact MCP dependency versions declared by the matching MCP package. It checks authentication rejection, the single read-only tool, strict inputs, host-selected lookup names and refusal after revocation. Its disposable application authentication is not a Soterias warrant, and it is not a named-agent acceptance run. The example prints seven passed checks and closes its HTTP server and native owner.

The host registration in your already-authenticated MCP server is:

JavaScript
import {registerSemanticFactTool} from '@soterias/mcp-server/semantic-facts';
registerSemanticFactTool(server, {
  repository: principalBoundEvidence,
  facts: [{name: 'invoice_amount', reference: trustedFactReference}],
});

Do not expose install_semantic_rule, installSemanticRule, promotion methods, unlock material or reviewer keys as tools. Scope each server to its authenticated principal; the repository's principal labels do not themselves authenticate users.

Complete Python program

Python
"""Local accounting corroboration through the native Rust verifier; no model/account."""
import json
from pathlib import Path
import tempfile
import time
from soterias import Citation, TrustedFactReference, SourceStore, SourceStoreError

root = Path(tempfile.mkdtemp(prefix="soterias-semantic-"))
started = time.perf_counter_ns()
now = time.time_ns() // 1_000_000
unlock = b"public-disposable-example-unlock"
store = SourceStore.create(root / "store", passphrase=unlock, tenant_id="demo", store_id="audit")
evidence = store.evidence(principal="auditor", purpose="transaction-testing")
sources, citations = [], []
for origin in ("ledger", "bank"):
    text = json.dumps({"invoice": "INV-1", "total": 1200, "origin": origin})
    path = root / (origin + ".json")
    path.write_bytes(text.encode())
    source = store.ingest_file(path, media="utf8_text", permitted_uses=["transaction-testing"], captured_at_ms=now)
    evidence.register_source(source, identity=origin, revision="v1", readers=["auditor"],
                             stale_after_ms=now+600_000, retain_until_ms=now+1_200_000)
    sources.append(source)
    citations.append(Citation(source, start=0, end=len(text), quote=text))

# Trusted application setup. The operator reviews independence and the complete
# evidence set. Agents must never receive this configuration or source-store keys.
rule = evidence.install_semantic_rule(predicate="amount_minor_units", subject_pointer="/invoice",
    value_pointer="/total", sources=list(zip(("ledger-origin", "bank-origin"), citations)))
claim = json.dumps({"subject": "INV-1", "predicate": "amount_minor_units",
                    "value": {"type": "integer", "value": "1200"}})
# Empty uncertainty is a candidate assertion, never proof. Rust independently
# verifies the value under the preconfigured rule before persisting any promotion.
candidate = evidence.extract_interpretation(claim, citations=citations, uncertainty=())
promotion = evidence.promote_fact(candidate, rule=rule)
assert promotion["promoted"] is True
assert promotion["report"]["classification"] == "corroborated"
reference = TrustedFactReference(promotion["reference"]["digest"])
assert evidence.check_trusted_fact(reference)["report"]["truth"] == "proven"
checkpoint = store.checkpoint()
store.close()
store = SourceStore.open(root / "store", passphrase=unlock, checkpoint=checkpoint)
evidence = store.evidence(principal="auditor", purpose="transaction-testing")
assert evidence.check_trusted_fact(reference)["report"]["truth"] == "proven"
evidence.revoke_source(sources[0])
try:
    evidence.check_trusted_fact(reference)
    raise AssertionError("revoked source was accepted")
except SourceStoreError as error:
    assert error.code == "evidence_source_revoked"
finally:
    store.close()
print(json.dumps({"status":"PASS", "root":str(root), "elapsed_ms":(time.perf_counter_ns()-started)/1e6,
                  "model_calls":0, "provider_tokens":None, "reports_signed":False,
                  "scope":"host-pinned typed verification; not universal truth"}))

Complete TypeScript program

TypeScript
// Real encrypted accounting evidence, shared Rust semantics; no model/account.
import assert from 'node:assert/strict';
import {mkdtempSync,writeFileSync} from 'node:fs';
import {tmpdir} from 'node:os';
import {join} from 'node:path';
import {Citation,TrustedFactReference,SourceStore,SourceStoreError} from '@soterias/core';
import type {SourceReference} from '@soterias/core';
const started=performance.now(),root=mkdtempSync(join(tmpdir(),'soterias-semantic-')),now=Date.now();
const unlock=Buffer.from('public-disposable-example-unlock');
let store=SourceStore.create({directory:join(root,'store'),passphrase:unlock,tenantId:'demo',storeId:'audit'});
let evidence=store.evidence({principal:'auditor',purpose:'transaction-testing'});
const sources:SourceReference[]=[],citations:Citation[]=[];
for(const origin of ['ledger','bank']){
  const text=JSON.stringify({invoice:'INV-1',total:1200,origin}),path=join(root,origin+'.json');
  writeFileSync(path,text);
  const source=store.ingestFile(path,{media:'utf8_text',permittedUses:['transaction-testing'],capturedAtMs:now});
  evidence.registerSource(source,{identity:origin,revision:'v1',readers:['auditor'],staleAfterMs:now+600000,retainUntilMs:now+1200000});
  sources.push(source);citations.push(new Citation(source,{start:0,end:[...text].length,quote:text}));
}
// Trusted application setup; never expose this rule or keys as agent tools.
const rule=evidence.installSemanticRule({predicate:'amount_minor_units',subjectPointer:'/invoice',valuePointer:'/total',sources:citations.map((citation,i)=>({origin:['ledger-origin','bank-origin'][i],citation}))});
const claim=JSON.stringify({subject:'INV-1',predicate:'amount_minor_units',value:{type:'integer',value:'1200'}});
const candidate=evidence.extractInterpretation(claim,{citations,uncertainty:[]});
const promotion=evidence.promoteFact(candidate,{rule});
assert(promotion.promoted);assert.equal(promotion.report.classification,'corroborated');
const reference=new TrustedFactReference(promotion.reference.digest);
const check=()=>{const result=evidence.checkTrustedFact(reference);assert('report' in result);return result.report;};
assert.equal(check().truth,'proven');
const checkpoint=store.checkpoint();store.close();
store=SourceStore.open({directory:join(root,'store'),passphrase:unlock,checkpoint});
evidence=store.evidence({principal:'auditor',purpose:'transaction-testing'});
assert.equal(check().truth,'proven');
evidence.revokeSource(sources[0]);
assert.throws(()=>evidence.checkTrustedFact(reference),e=>e instanceof SourceStoreError&&e.code==='evidence_source_revoked');
store.close();unlock.fill(0);
console.log(JSON.stringify({status:'PASS',root,elapsed_ms:performance.now()-started,model_calls:0,provider_tokens:null,reports_signed:false,scope:'host-pinned typed verification; not universal truth'}));

Search guides, examples and API reference.