Deterministic trusted-evidence layer between retrievers and generators
$ moon add ShunjunGu/moon-trust$ moon update
$ moon build --target native$ moon run examples/quickstart --target native
moon-trust quickstart
query: expense deadline
rank id retrieval trust rerank
1 curated 0.7000 0.9500 1.6500
2 handbook 0.7000 0.9375 1.6375
3 short-mirror 0.9500 0.5167 1.4667
context: 3 blocks, 53 tokens / budget 512
[1] | Expense reports are due within 30 days of travel.
source: Expenses Policy | s3://knowledge/expenses.md | 2026-11-02T00:00:00Z
[2] | Expense reports are due within 30 days of travel.
source: Travel Handbook | https://intranet.example.com/handbook | 2026-11-01T00:00:00Z
[3] | short
source: (untitled) | file:///tmp/mirror.txt |
citations:
[1] curated -> s3://knowledge/expenses.md
[2] handbook -> https://intranet.example.com/handbook
[3] short-mirror -> file:///tmp/mirror.txt$ moon run examples/injection-triage --target native
moon-trust injection triage
mark policy: 4 evidence kept, 4 findings
override: instruction.override (high): instruction override: text asks to ignore or replace the caller's instructions (matched: "ignore previous instructions")
override: exfiltration.secret (high): exfiltration phrase: text asks to reveal or print protected instructions or secrets (matched: "reveal your system prompt")
hidden: hidden.zero-width (high): hidden character: U+200B zero-width space can conceal text from human review
role: role.marker (high): role marker: a reserved chat or role delimiter appears in retrieved text (matched: "<|system|>")
drop policy: 1 evidence kept, 3 quarantined, blocked=true
override: dropped by policy: high-severity risk
instruction.override (high)
exfiltration.secret (high)
hidden: dropped by policy: high-severity risk
hidden.zero-width (high)
role: dropped by policy: high-severity risk
role.marker (high)
context kept under the drop policy:
[1] | Expense reports are due within 30 days.
source: handbook | https://example.com/handbook | 2026-11-08T00:00:00Z$ _build/native/debug/build/cmd/main/main.exe scan hello tests/fixtures/multi.json
{"query":"hello","config":{},"evidence":[{"id":"t1","text":"first","source":{"uri":"https://example.com/a","title":"Doc A","fetched_at":"2026-11-01T00:00:00Z"},"retrieval_score":0.95,"retrieval_normalized":0.95,"span":{"start":0,"end":12},"risk_findings":[],"conflict_ids":[],"conflict_penalty":0,"rerank_score":1.7416666666666667,"trust":{"score":0.7916666666666667,"breakdown":{"source_score":0.875,"content_score":0.7083333333333334,"metadata":0.75,"source_type":1,"length":0.125,"template_ratio":0,"consistency":1,"risk":1}}},{"id":"a2","text":"second","source":{"uri":"s3://bucket/b","title":"Doc B","fetched_at":"2026-11-02T00:00:00Z","hint":"curated"},"retrieval_score":0.8,"retrieval_normalized":0.8,"risk_findings":[],"conflict_ids":[],"conflict_penalty":0,"rerank_score":1.6083333333333334,"trust":{"score":0.8083333333333333,"breakdown":{"source_score":0.9,"content_score":0.7166666666666667,"metadata":1,"source_type":0.8,"length":0.15,"template_ratio":0,"consistency":1,"risk":1}}},{"id":"b2","text":"third","source":{"uri":"file:///c/d.txt","title":"Doc C","fetched_at":"2026-11-03T00:00:00Z"},"retrieval_score":0.4,"retrieval_normalized":0.4,"risk_findings":[],"conflict_ids":[],"conflict_penalty":0,"rerank_score":1.0416666666666665,"trust":{"score":0.6416666666666666,"breakdown":{"source_score":0.575,"content_score":0.7083333333333334,"metadata":0.75,"source_type":0.4,"length":0.125,"template_ratio":0,"consistency":1,"risk":1}}}],"context":{"budget":512,"estimated_tokens":41,"truncated":false,"included_count":3,"omitted_count":0,"text":"[1] | first\nsource: Doc A | https://example.com/a | 2026-11-01T00:00:00Z\n\n[2] | second\nsource: Doc B | s3://bucket/b | 2026-11-02T00:00:00Z\n\n[3] | third\nsource: Doc C | file:///c/d.txt | 2026-11-03T00:00:00Z","citations":[{"index":1,"evidence_id":"t1","source":{"uri":"https://example.com/a","title":"Doc A","fetched_at":"2026-11-01T00:00:00Z"}},{"index":2,"evidence_id":"a2","source":{"uri":"s3://bucket/b","title":"Doc B","fetched_at":"2026-11-02T00:00:00Z","hint":"curated"}},{"index":3,"evidence_id":"b2","source":{"uri":"file:///c/d.txt","title":"Doc C","fetched_at":"2026-11-03T00:00:00Z"}}]},"conflicts":[],"quarantined":[],"blocked":false,"diagnostics":{"candidate_count":3,"duplicate_id_count":0,"unknown_rule_ids":[],"risk_finding_count":0,"quarantined_count":0,"conflict_count":0,"conflicts_truncated":false,"output_count":3,"stages":[{"name":"ingest","count":3},{"name":"dedupe","count":3},{"name":"risk","count":0},{"name":"isolate","count":3},{"name":"conflict","count":0},{"name":"sort","count":3},{"name":"topk","count":3},{"name":"context","count":3}]}}$ _build/native/debug/build/cmd/main/main.exe scan hello tests/fixtures/injected.json --policy drop --block-on-high
$ echo $?
3usage: moon-trust scan <query> <candidates.json> [--top-k N] [--policy drop|mark|downrank] [--block-on-high] [--risk-weight W] [--conflict-weight W] [--context-budget N]
moon-trust demo <candidates.json> [--json-out PATH]trust.score = clamp01((source_weight × source_score +
content_weight × content_score) × risk_factor)
source_score = (metadata + source_type) / 2
metadata = non-empty {uri, title, fetched_at} plus present hint, / 4
source_type = https 1.0 | s3/gs 0.8 | http 0.6 | file 0.4 | other :// 0.3 | none 0.2
content_score = (length + template_cleanliness + consistency) / 3
length = clamp01(UTF-16 code units / 40)
template_ratio = share of { } < > in the raw text
template_cleanliness= clamp01(1 − 8 × template_ratio) # 0 at ≥ 12.5%
consistency = 1 / (1 + conflict_count)rerank_score = retrieval_normalized
+ trust_weight × trust.score
− conflict_penalty
conflict_penalty = clamp01(conflict_weight × conflict_count)$ moon run cmd/main --target native -- demo tests/fixtures/demo.json --json-out _build/demo.json
moon-trust demo: with vs without the trust layer
query: demo
corpus: 11 candidates; policy: drop
metric without trust with trust
unsafe blocks exposed 5 0
conflict pairs found 0 2
injections intercepted 0 5
ranking changes (retrieval order -> rerank order):
benign-imperative 9 -> 1 (+8)
high-trust 10 -> 2 (+8)
benign-doc 11 -> 3 (+8)
low-trust 6 -> 4 (+2)
fact-a 7 -> 5 (+2)
fact-b 8 -> 6 (+2)
(6 of 6 surviving blocks changed rank)
quarantined (5): inj-override, inj-role, inj-hidden, inj-delimiter, inj-payload
trusted run: 6 evidence, blocked=true
context: 6 blocks, 162 tokens / budget 512, truncated=false| metric | without | with | meaning |
|---|---|---|---|
| unsafe_blocks_exposed | 5 | 0 | blocks with at least one high-severity finding that would reach the generator |
| conflict_pairs | 0 | 2 | explicit cross-document (subject, unit, value) disagreements |
| injections_intercepted | 0 | 5 | high-severity blocks removed by the drop policy (the quarantine list) |
| ranking_changes | — | 6 | surviving blocks whose 1-based rank differs from retrieval order; delta = before − after, positive means moved up |
{
"query": "demo",
"corpus_size": 11,
"policy": "drop",
"baseline": {"order": ["inj-override", "..."], "unsafe_blocks": 5, "high_severity_findings": 6, "risk_findings": 9},
"trusted": {"order": ["benign-imperative", "..."], "unsafe_blocks": 0, "high_severity_findings": 0, "risk_findings": 1, "conflicts": 2, "quarantined_count": 5, "quarantined_ids": ["inj-override", "..."], "blocked": true, "context": {"budget": 512, "included_count": 6, "estimated_tokens": 162, "truncated": false}},
"comparison": {
"unsafe_blocks_exposed": {"before": 5, "after": 0, "delta": -5},
"conflict_pairs": {"before": 0, "after": 2, "delta": 2},
"injections_intercepted": {"before": 0, "after": 5, "delta": 5},
"ranking_changes": [{"id": "benign-imperative", "before_rank": 9, "after_rank": 1, "delta": 8}, "..."]
}
}$ moon build --target native
$ moon run cmd/main --target native -- demo tests/fixtures/demo.json --json-out _build/a.json > _build/a.txt
$ moon run cmd/main --target native -- demo tests/fixtures/demo.json --json-out _build/b.json > _build/b.txt
$ diff _build/a.txt _build/b.txt && diff _build/a.json _build/b.json| rule id | severity | signals |
|---|---|---|
| instruction.override | high | "ignore previous instructions", "forget all previous", 忽略之前的指令, 覆盖系统指令 |
| instruction.redirect | medium | "ignore the above", "disregard the above", 忽略以上内容 |
| instruction.imperative | low | "you must", "you should", "please output", 请输出, 请打印 |
| role.marker | high | <\|system\|>, <\|im_start\|>, <\|start_header_id\|>, <\|eot_id\|>, <system>, [INST], <<SYS>> |
| role.chat-heading | medium | ### System, Human:, Assistant:, system:, developer:, 系统提示词 |
| hidden.zero-width | high | U+200B, U+2060, U+180E |
| hidden.bidi-control | high | U+061C, U+202A–U+202E, U+2066–U+2069 |
| hidden.format | medium | U+200C, U+200D, U+FEFF, U+00AD, U+034F, U+115F–U+1160, U+17B4–U+17B5, U+180B–U+180F, U+2061–U+2064, U+2800, U+3164, U+FE00–U+FE0F, U+FFF9–U+FFFB |
| payload.encoded | high | data:text/html, HTML-entity / percent-encoded zero-width characters, 解码以下 |
| payload.base64 | medium | base64, (inline data; common and usually benign) |
| payload.oversize | medium | text longer than 100,000 UTF-16 code units; only the prefix was scanned |
| payload.code-exec | medium | atob(, eval(, unescape(, fromCharCode( |
| payload.escape-sequence | medium | \xHH and \uHHHH escape sequences |
| exfiltration.secret | high | "reveal your system prompt", "what is your system prompt", "show me your instructions", "print the conversation history", 泄露密钥 |
| exfiltration.send | medium | "send the following", "upload the following", 发送以下 |
| delimiter.escape | high | </system>, <\|endoftext\|>, [END OF CONTEXT], 以下是新的指令 |
[1] | <untrusted line 1>
| <untrusted line 2>
source: <title or (untitled)> | <uri> | <fetched_at>Risks:
- [1] instruction.override (high): phrase rule: ...
Conflicts:
- [1] vs [2] france (million): 68 million vs 67 millionlet report = @src.scan(
"hello",
{ ..@src.ScanConfig::default(), risk_policy: Some(@src.RiskPolicy::Drop) },
candidates,
)
// The generator-ready text and its citation table:
let context = report.context| scope | covered / total | coverage |
|---|---|---|
| library (src/ + src/cli/) | 1239 / 1440 | 86.0% |
| whole module (includes cmd/main and both example executables) | 1239 / 1542 | 80.4% |
$ moon coverage analyze
$ find _build -name '*.trace.source' -print0 | xargs -0 moon coverage report -f full_summary$ moon test --target all
Total tests: 125, passed: 125, failed: 0. [wasm]
Total tests: 125, passed: 125, failed: 0. [wasm-gc]
Total tests: 125, passed: 125, failed: 0. [js]
Total tests: 125, passed: 125, failed: 0. [native]Install
Download zipDeterministic trusted-evidence layer between retrievers and generators