//! An agent answers a question, records what it READ, and seals a receipt that //! commits the provenance. Then a reviewer checks the three things a rendered //! citation link cannot tell them: is the answer unedited, are the sources //! intact, and which sources may this reviewer not open. //! //! ```text //! cargo run -p jcp-cite --example cite //! ``` //! //! The retrieval is illustrative: no document is fetched, so the energy the //! citation records is a chosen figure, and the decision it is debited under //! seals it as an estimate ([`retrieval_cost`]) — never as a hardware reading //! of work no meter saw. use ed25519_dalek::SigningKey; use jcp_cite::{verify, Citation, Source}; use jcp_grant::issue; use jcp_receipt::{extract, seal}; use jcp_runtime::Runtime; use jcp_schema::{Capability, ContentId, Did, Energy, Grant}; fn main() { let issuer = SigningKey::from_bytes(&[1u8; 32]); let agent = SigningKey::from_bytes(&[2u8; 32]); let agent_did = Did::from_verifying_key(&agent.verifying_key()); let corpus = ContentId::of_bytes(b"acme-workspace"); // The agent holds two grants — one per sensitivity domain. let g_finance = ContentId::of_bytes(b"grant:finance"); let g_people = ContentId::of_bytes(b"grant:people"); // It reads two documents to answer, and records EACH with the content it saw // and the grant it read under. let doc_a = b"Q3 revenue was 4.2M, up 18% YoY."; let doc_b = b"Headcount plan: 12 hires in Q4."; let answer = b"Revenue grew 18%, and 12 hires are planned for Q4."; let citation = Citation::new( agent_did, ContentId::of_bytes(answer), vec![ Source::new(ContentId::of_bytes(doc_a), g_finance).at("/finance/q3-report"), Source::new(ContentId::of_bytes(doc_b), g_people).at("/people/q4-plan"), ], 4_200, // µJ the retrieval is put at: illustrative, nothing read it [7; 16], ); println!( "agent read {} sources → answer, {} µJ (illustrative: sealed as an estimate)", citation.sources.len(), citation.energy_uj ); // The retrieval is decided and metered under a real grant, and the receipt // commits the citation by content id. let grant = Grant { issuer: Did::from_verifying_key(&issuer.verifying_key()), subject: agent_did, capability: Capability::new(corpus, ["retrieve"]), budget_uj: 1_000_000, budget_funds: None, caveats: Vec::new(), expiry: 0, parent: None, delegate_depth: None, nonce: [0; 16], }; let sg = issue(&issuer, grant.clone()).unwrap(); let frame = citation.frame(grant.id(), corpus).unwrap(); let decision = Runtime::new().decide(&frame, &sg, agent_did, retrieval_cost(&citation), 1); let steward = SigningKey::from_bytes(&[9u8; 32]); let receipt = seal(&frame, &grant, &decision, &steward, "did:example:steward").unwrap(); let core = extract(&receipt, "did:example:steward", &steward.verifying_key().to_bytes()).unwrap(); let sealed = citation.clone().seal(&agent).unwrap(); println!( "receipt commits the citation: {} ({} µJ on the receipt)\n", core.payload_hash.as_deref() == Some(citation.id().unwrap().to_hex().as_str()), core.cost_uj ); // ---- a reviewer, holding only the receipt + the signed citation ---- println!("── reviewer checks ──"); println!(" citation signature valid: {}", verify(&sealed).is_ok()); println!(" answer unedited: {}", sealed.citation.verify_answer(answer).is_ok()); println!(" source 0 intact: {}", sealed.citation.verify_source(0, doc_a).is_ok()); // Now the finance doc is edited after the fact. A URL would still resolve — // to the NEW text. The content-bound citation catches it. let edited = b"Q3 revenue was 9.9M."; println!( " source 0 after an edit: {} ← dangling detected, not silently wrong", sealed.citation.verify_source(0, edited).is_ok() ); // A reviewer cleared only for finance is TOLD which sources they may not open. let unreadable = sealed.citation.unreadable_by(&[g_finance]); println!(" sources this reviewer may not open: {:?} (of {})", unreadable, sealed.citation.sources.len()); for i in &unreadable { if let Some(loc) = sealed.citation.sources[*i].locator.as_deref() { println!(" → {loc} requires authority you do not hold"); } } } /// The cost the retrieval is debited and sealed at: the citation's recorded /// figure, graded `Estimator` with no measured figure, because nothing in /// this example read it. pub fn retrieval_cost(citation: &Citation) -> Energy { Energy::estimator(citation.energy_uj) }