//! End-to-end Action Receipt on real hardware. //! //! ```text //! cargo run -p jcp-action-receipt --example end_to_end -- /tmp/action.hex //! ``` //! //! Implements §5 step 6 of the blueprint: **one agent action, under a real //! delegated grant, with energy read off this host's own counters, sealed into //! an AR-1 receipt that a third party verifies from the published spec alone.** //! //! Only the workload is a stand-in, so the example runs anywhere. Everything //! around it is the real machinery: //! //! - the grant is **really issued** and **really attenuated**, and amplification //! is really refused; //! - the authorisation is a **real `decide` call** against a real budget, and //! the receipt records the verdict that call returned. It is decided before //! the work runs, so the cost it debits is the action's estimate //! ([`authorised_cost`], `Estimator`) — the work's reading is step 3's; //! - the joules are **metered**, and the span's provenance is carried through //! verbatim: `HwShunt` only when every record the meter returned is a //! hardware reading. Where the host has no live counter — or the span's CPU //! counter held — the meter returns its `Estimator` record, and this example //! says so rather than inventing `HwShunt`; //! - the tier is stamped from what is **actually running**. This is in-process //! with no isolation boundary crossed, so it is `S0`. Claiming `S5` without a //! microVM is exactly the overreach the ladder exists to prevent. use ed25519_dalek::SigningKey; use jcp_action_receipt::{ActionReceipt, Effect, EffectClass, Egress, EgressClass, verify}; use jcp_grant::{attenuate, issue}; use jcp_receipt as authority; use jcp_runtime::Runtime; use jcp_schema::{ Capability, ContentId, Did, Energy, Frame, FrameKind, Grant, Trust, Verdict, }; use sandbox_meter::{DeviceEnergy, PowerMeter, Receipt as WorkReceipt}; use sandbox_schema::Tier; const STEWARD: &str = "did:example:ar-e2e"; fn main() -> Result<(), Box> { let out = std::env::args().nth(1).unwrap_or_else(|| "action.hex".into()); // ── 1. A real grant, really delegated ──────────────────────────── let issuer = SigningKey::from_bytes(&[11u8; 32]); let agent = SigningKey::from_bytes(&[22u8; 32]); let subagent = SigningKey::from_bytes(&[33u8; 32]); let agent_did = Did::from_verifying_key(&agent.verifying_key()); let sub_did = Did::from_verifying_key(&subagent.verifying_key()); let resource = ContentId::of_bytes(b"resource/quarterly-report"); let root = Grant { issuer: Did::from_verifying_key(&issuer.verifying_key()), subject: agent_did, capability: Capability::new(resource, ["read", "summarise"]), budget_uj: 50_000_000, budget_funds: None, caveats: Vec::new(), expiry: 0, parent: None, delegate_depth: None, nonce: [1u8; 16], }; let sg = issue(&issuer, root.clone())?; // Strictly narrower: read only, one fifth of the ceiling. let delegated = attenuate( &sg, &agent, sub_did, Capability::new(resource, ["read"]), 10_000_000, 0, [2u8; 16], )?; // And prove the runtime really refuses amplification, rather than asserting it. let amplified = attenuate( &sg, &agent, sub_did, Capability::new(resource, ["read", "delete"]), 10_000_000, 0, [3u8; 16], ); assert!(amplified.is_err(), "amplification must be refused"); println!("1. grant issued, attenuated to read-only — amplification refused: {}", amplified.unwrap_err()); // ── 2. A real authorisation decision ───────────────────────────── let mut rt = Runtime::new(); let frame = Frame { kind: FrameKind::ToolCall, trust: Trust::Control, grant_id: delegated.grant.id(), action: "read".into(), resource, payload_hash: ContentId::of_bytes(b"input/quarterly-report"), model: None, }; let decision = rt.decide(&frame, &delegated, sub_did, authorised_cost(), 0); let verdict = match decision.verdict { Verdict::Allow => "allow", Verdict::Deny(_) => "deny", }; // Seal the REAL authority receipt from the decision that just happened, and // take its content id from the bytes rather than naming a placeholder. let steward_key = SigningKey::from_bytes(&[44u8; 32]); let authority_env = authority::seal(&frame, &delegated.grant, &decision, &steward_key, STEWARD)?; let authority_core = authority::signed_core(&frame, &delegated.grant, &decision, STEWARD)?; let authority_id = ContentId::of_bytes(&authority_core); // Prove it verifies on its own terms before binding it. authority::verify( &frame, &delegated.grant, &decision, &authority_env, STEWARD, steward_key.verifying_key().as_bytes(), )?; println!( "2. decide() → {verdict}, {} µJ remaining; authority receipt sealed + verified, id {}", decision.budget_remaining_uj, &authority_id.to_hex()[..12] ); // ── 3. Real work, really measured ──────────────────────────────── let mut meter = PowerMeter::detect(); let sources = meter.sources().join(","); let is_hw = meter.is_hardware(); let span = meter.start(); let mut acc: u64 = 0; for i in 0..40_000_000u64 { acc = acc.wrapping_add(i ^ (acc >> 7)).rotate_left(3); } let measurement = span.stop(&mut meter); std::hint::black_box(acc); let joules_uj = measurement.total_uj(); // The span's own provenance, the weakest of its records. A hardware meter // still returns its estimator record for a span whose CPU counter held, // so `is_hardware()` says nothing about this span. let provenance = { use sandbox_meter::Provenance as P; let records = &measurement.devices; if records.is_empty() || records.iter().any(|d| d.provenance == P::Estimator) { "Estimator" } else if records.iter().all(|d| d.provenance == P::HwShunt && d.measured_uj.is_some()) { "HwShunt" } else { "ModelBased" } }; println!( "3. work metered — {joules_uj} µJ [{provenance}] over {:.2}s via {sources}", measurement.wall.as_secs_f64() ); if provenance != "HwShunt" { println!( " note: this span was not a hardware reading; provenance is {provenance}, not HwShunt ({})", if is_hw { "its CPU counter held" } else { "no live energy counter here" } ); } // Build the REAL work receipt from the devices the meter actually reported. // `Receipt::build` enforces the provenance/measured coupling itself, so a // mislabelled device is refused here rather than propagating into AR-1. let devices: Vec = measurement .devices .iter() .map(|d| DeviceEnergy { class: d.class, measured_uj: d.measured_uj, estimated_uj: d.estimated_uj, provenance: d.provenance, }) .collect(); let work = WorkReceipt::build( sandbox_schema::ContentId::of_bytes(b"unit/quarterly-report-summarise"), Tier::S0, devices, 1, [7u8; 8], )?; work.validate()?; // The two families agree on BLAKE3 and disagree only on presentation: // sandbox renders `b3:`, jcp carries raw 32 bytes. Re-addressing // the same canonical core with jcp's hasher yields the identical digest, so // the binding is exact rather than a hash of a hash. let work_id = ContentId::of_bytes(&work.canonical_core()); debug_assert_eq!( work.content_id().as_str().trim_start_matches("b3:").len(), 43, "sandbox content id should be b3:" ); println!( " work receipt sealed over {} metered device(s), id {}", measurement.devices.len(), &work_id.to_hex()[..12] ); // ── 4. Seal the Action Receipt ─────────────────────────────────── let receipt = ActionReceipt { closed_at: "2026-08-25T12:00:00Z".into(), grant_id: delegated.grant.id(), chain_ids: vec![sg.grant.id(), delegated.grant.id()], authority_receipt: authority_id, verdict: verdict.into(), attestation_id: ContentId::of_bytes(b"attest/e2e-local"), proof_cell_id: None, tier: "S0".into(), grounding: if is_hw { "hardware" } else { "software" }.into(), work_receipt: work_id, input_hash: ContentId::of_bytes(b"input/quarterly-report").to_hex(), output_hash: None, joules_uj, energy_provenance: provenance.into(), effect: Effect { // read-and-summarise changed nothing in the world class: EffectClass::None, target: resource, before: None, after: None, compensator: None, plan_receipt: None, }, egress: Egress { // resolved entirely in this process class: EgressClass::None, bytes: 0, peer: None, }, steward: STEWARD.into(), }; let envelope = receipt.seal(&steward_key)?; println!("4. sealed — receipt id {}", receipt.id()?.to_hex()); // ── 5. Verify it back, as a third party would ──────────────────── let v = verify(&envelope, &steward_key.verifying_key(), STEWARD)?; assert_eq!(v.id, receipt.id()?); println!("5. verified from the envelope alone — {} byte canonical core", v.core.len()); std::fs::write(&out, hex::encode(&envelope))?; println!(); println!("wrote {out}"); println!( "verify independently:\n ar-verify {out} --pubkey {} --steward {STEWARD}", hex::encode(steward_key.verifying_key().as_bytes()) ); Ok(()) } /// The cost the authorisation debits: decided before the work runs, it is /// the action's estimate, 0.25 J, graded `Estimator` with no measured figure. /// Sealed as a reading, the authority receipt would claim a meter read work /// that had not yet happened; the joules a meter did read are the work /// receipt's, and the action receipt's. pub fn authorised_cost() -> Energy { Energy::estimator(250_000) }