Practical LLM Security · RAG and Knowledge Security · Part 4 of 5
Keep citations true after the answer is written: bind them to versioned source spans, revalidate on display, and stop summaries and agents from laundering unverifiable sources.
A citation is a claim with a lifetime
The site's citation verification pipeline explains how to prove at generation time that each reference is real, permitted, intact, and safe to render. This chapter addresses what happens afterwards. Answers are saved in tickets, shared in chat threads, stored in conversation memory, and quoted by other agents. The cited document can be edited, superseded, deleted, or have its permissions revoked. A citation that was correct on Monday can be false or unauthorized on Friday while still looking authoritative.
Citation integrity therefore has three parts: binding each claim to an exact source version and span, revalidating when the citation is displayed or reused, and tracking derived text so a summary cannot launder an unverifiable source into a trusted one.
A concrete failure: the superseded refund window
In March, Northstar's assistant answers a question about the refund window for opened electronics with "30 days", citing section 4 of the returns policy. A support lead pastes the answer, with its citation, into a team runbook. In June, legal reduces the window to 14 days and edits the same policy page. The citation link still opens the policy page, now showing 14 days in section 4, but the runbook continues to say 30 days with a link that appears to support it. Customers receive inconsistent decisions until an audit notices the mismatch.
A second variant is worse. A research agent summarizes several policies into a memory note, and a later answer cites the memory note as its source. One of the summarized documents was a draft that never had general visibility. The final citation points at an internal note that every agent can read, so the permission check at display time passes even though the underlying claim came from a restricted draft.
Architecture: content-addressed spans and a citation ledger
| Field | Example | Used for |
|---|---|---|
| doc_id and version | policy-returns, v42 | Detecting edits and supersession |
| span hash | sha256 of normalized section text | Detecting silent changes |
| ACL epoch | 1187 | Detecting revocation after the answer |
| derivation | direct or derived from ids | Following summaries back to originals |
| verifier result | supported, partial, unsupported | Rendering decision |
When the answer is generated, each citation becomes a ledger record that stores these fields, not just a URL. The rendered answer includes a citation identifier that resolves through the ledger. Display surfaces such as the chat interface, ticket view, and runbook embed call a small revalidation service before rendering the citation as supported.
Derived text carries its lineage. A memory note or summary created by an agent records the citation ledger identifiers of the spans it used. When anything cites the derived text, revalidation walks the lineage to the original spans and applies the strictest result. A summary is never more authoritative or more widely visible than the most restricted source it was built from.
Implementation: revalidation on display
Illustrative python; adapt the persistence and identity adapters to your service.
from enum import Enum
class CitationState(Enum):
CURRENT = "current"
CHANGED = "source_changed"
REVOKED = "no_longer_permitted"
MISSING = "source_removed"
def revalidate(record, store, principal, acl):
"""Re-check a stored citation for the person who is viewing it now."""
for span in record.lineage_spans():
doc = store.get(span.doc_id)
if doc is None or doc.retired:
return CitationState.MISSING
if not acl.can_read(principal, span.doc_id):
return CitationState.REVOKED
if store.span_hash(span.doc_id, doc.version, span.locator) != span.sha256:
return CitationState.CHANGED
return CitationState.CURRENTThe order of checks is deliberate. A removed source and a revoked permission both stop rendering the cited text, and the viewer sees only a neutral notice that the source is unavailable to them; revealing that a now-restricted document changed would itself leak information. A changed span renders the citation with a visible flag and a link to the current version, so the reader can see that the claim needs review. Because the check runs for the viewing principal, a manager and a contractor looking at the same runbook can legitimately see different citation states.
Caching revalidation results is acceptable if the cache key includes the ACL epoch and document version, so revocation and edits invalidate the result.
Laundering through agents and summaries
Multi-agent systems create many derived texts: plans, research notes, tool results, and handoff messages. Each one is an opportunity to drop provenance. Enforce three rules. First, an agent may only emit a citation that resolves to a ledger record; free-form URLs from tool output are rendered as untrusted links, not citations. Second, when one agent passes text to another, it passes ledger identifiers with it, and the receiving agent cannot upgrade the verifier result. Third, memory writes that include factual claims store lineage, as described in the memory provenance chapter.
Where external references are involved, apply the URL safety rules from the earlier citation guide and never fetch a cited URL from a server with internal network access just because a model produced it.
Tradeoffs and worked case
Revalidation on display adds a dependency to every render. Make it fast and cacheable, and decide the failure mode per surface: in the support console, render citations as unverified when the service is unavailable; in an automated workflow that applies policy, stop and retry. Span hashing needs stable normalization; whitespace and formatting changes should not trigger false alarms, so hash normalized text and record the normalizer version. Lineage storage grows with agent activity, so retain it as long as the derived text is retained, and delete it together with that text.
Replay the refund window case. When legal edits section 4, the span hash changes. The next time anyone opens the runbook, revalidation returns source_changed and the citation renders with a review flag and a link to version 43. The memory note variant is caught by lineage: the note records that one span came from a draft, the draft is readable only by legal, and revalidation for a support agent returns no_longer_permitted for the whole claim.
Rendering states people can act on
A citation state is only useful if the reader understands what to do with it. Use a small, fixed vocabulary in the interface. Current means the cited span is unchanged and readable by this viewer. Source changed means the claim may be outdated; show the date of the change and a link to the current version, and make it easy to ask the assistant again. Unavailable covers both removal and revocation, deliberately without distinguishing them, because telling a viewer that a document still exists but is now restricted can disclose more than the claim itself.
Apply the same states to machine consumers. An agent that reads a stored answer should receive the citation state as structured data and treat anything other than current as a reason to retrieve again rather than to repeat the claim. Exports, such as a PDF of a ticket, should embed the state at export time and the date it was computed, because the exported copy can no longer be revalidated.
Avoid two tempting shortcuts. Do not replace the stored answer text silently with a newer answer when the source changes, because the record of what was said, and when, is evidence for support and compliance reviews. And do not suppress the citation while leaving the claim in place, which produces an authoritative-looking statement with no visible basis.
Verification steps
Create fixtures for each state: an unchanged span, an edited span, a whitespace-only edit, a deleted document, a revoked ACL, and a summary derived from one restricted and one public source. Assert the state for multiple principals. Add a regression test that a free-form URL in tool output never renders as a supported citation. Monitor the share of displayed citations in each state; a spike in source_changed after a policy release is expected, while a steady background of no_longer_permitted may indicate that answers are being shared more widely than their sources allow. The companion secure retrieval lab shows how a cached answer can outlive a revoked citation.
Practice and continue
Open the Secure retrieval: permissions, caches, and citations lab to change the assumptions with deterministic synthetic data and inspect each decision. This chapter belongs to the RAG and Knowledge Security group of Practical LLM Security, which builds on the 25-chapter abuse and fraud prevention series.
Sources and implementation scope
Primary references checked October 16, 2026. Framework details follow the linked documentation and, for ADK, the google-adk 2.12.0 package source; pin and test the version your application uses. The Northstar scenarios, policies, figures, and code are original illustrative designs, not measured production results or complete integrations.