Some checks failed
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Has been cancelled
A backend that bills money declares a catalog entry: currency, rates per token in billionths of a minor unit (plain input, cache reads, cache writes, output) per input-length tier, an optional per-call fee, and the source of the prices. Undeclared is not free. From the request alone the entry gives an upper bound: every declared input token at the highest input rate, every allowed output token at the output rate, in the worst tier a bill of no more than the declared input could land in. A backend that bills within the declared tokens cannot charge more. Under a money ceiling the router admits only a free backend or one whose bound fits, in the ceiling's currency, never converted; among those it still picks the fewest joules. Under a grant that bounds money the steward reserves the bound on every link before the work and settles the bill after it; a request naming no money ceiling under such a grant may spend none and reaches only a free backend. A grant is debited its running total rounded up, so hundredth-of-a-cent calls are not debited a cent each. The journal records the reservation and the bill, and a restart replays both; a serve in flight stays debited its reservation. The receipt carries the bound, the bill, the debit and the price as declared; receipts without money keep their bytes (the published pack still verifies). The OpenAI-compatible binding reads metadata.funds_ceiling and, under one, declares input as the text's byte length, an upper bound, not the chars/4 estimate. JCP: a priced hold now checks money before it admits the call into its windows, so a hold refused for money fills no window (it did, for both the lone and the chained hold). hold_chained_priced_for also checks the caller's joule ceiling in the same step, which is what Mesh needs. Falsified, each turns a test red: admitting before the money check (lone and chained), the declared tier only, rounding each call up, undeclared as free, an unreported cache split at the plain rate, no implied zero ceiling under a money grant, a replay that forgets the running total, and the binding declaring the estimate. Co-Authored-By: Claude Opus 5.5 <[email protected]>
142 lines
5.3 KiB
Rust
142 lines
5.3 KiB
Rust
//! End-to-end Mesh demo: mint a JCP grant, serve a metered inference through the
|
|
//! cheapest-sufficient backend, and print the sealed JCR-1 receipt.
|
|
//!
|
|
//! Run with the always-available estimator meter (honest `Estimator`
|
|
//! provenance):
|
|
//!
|
|
//! ```text
|
|
//! cargo run -p mesh-core --example demo
|
|
//! ```
|
|
//!
|
|
//! On Linux with RAPL (or a CUDA box with NVML), enable a hardware meter to get
|
|
//! a measured `HwShunt` span instead:
|
|
//!
|
|
//! ```text
|
|
//! cargo run -p mesh-core --example demo --features hw-meter-rapl
|
|
//! ```
|
|
|
|
use ed25519_dalek::SigningKey;
|
|
use jcp_schema::{Capability, Did, Grant};
|
|
use mesh_core::{Mesh, Presentation, INFER_ACTION};
|
|
use mesh_engine::ReferenceEngine;
|
|
use mesh_meter::Meter;
|
|
use mesh_router::BackendSpec;
|
|
use mesh_schema::{ContentId, Fidelity, ServeRequest};
|
|
|
|
fn main() {
|
|
// --- the steward (the mesh endpoint that seals receipts) ---
|
|
let steward_sk = SigningKey::from_bytes(&[7u8; 32]);
|
|
let steward = Did::from_verifying_key(&steward_sk.verifying_key()).to_did_key();
|
|
|
|
// --- the meter: hardware-backed when a feature selects one, else estimator ---
|
|
#[cfg(feature = "hw-meter")]
|
|
let meter = mesh_meter::HwMeter::detect();
|
|
#[cfg(not(feature = "hw-meter"))]
|
|
let meter = mesh_meter::EstimatorMeter::new(15.0);
|
|
println!("meter: {}", meter.name());
|
|
|
|
// --- build the mesh: a fidelity cascade of backends ---
|
|
let mut mesh = Mesh::new(&meter, steward_sk, steward.clone());
|
|
mesh.register(
|
|
BackendSpec::new("cache", "result-lut", Fidelity::F0, 10),
|
|
Box::new(ReferenceEngine::new("cache", "result-lut", Fidelity::F0, 10, 8)),
|
|
);
|
|
mesh.register(
|
|
BackendSpec::new("tiny", "ssm-1b", Fidelity::F1, 50_000),
|
|
Box::new(ReferenceEngine::new("tiny", "ssm-1b", Fidelity::F1, 50_000, 4096)),
|
|
);
|
|
mesh.register(
|
|
BackendSpec::new("merge", "evo-merge-8b", Fidelity::F2, 200_000),
|
|
Box::new(ReferenceEngine::new(
|
|
"merge", "evo-merge-8b", Fidelity::F2, 200_000, 8192,
|
|
)),
|
|
);
|
|
mesh.register(
|
|
BackendSpec::new("frontier", "wire-rpc", Fidelity::F4, 5_000_000),
|
|
Box::new(ReferenceEngine::new(
|
|
"frontier", "wire-rpc", Fidelity::F4, 5_000_000, 16384,
|
|
)),
|
|
);
|
|
|
|
// --- the request: 64 output tokens, must be at least F1, ≤ 8 J ---
|
|
let req = ServeRequest {
|
|
prompt_hash: ContentId::of_bytes(b"What is the capital of France?"),
|
|
prompt_tokens: 7,
|
|
max_tokens: 64,
|
|
fidelity_floor: Fidelity::F1,
|
|
joule_ceiling_uj: 8_000_000, // 8 J
|
|
decision: None, // a generation, not a typed decision
|
|
funds_ceiling: None,
|
|
};
|
|
|
|
// --- the caller mints a JCP grant: capability(infer over this request) ⊗ budget ---
|
|
let caller_sk = SigningKey::from_bytes(&[42u8; 32]);
|
|
let caller = Did::from_verifying_key(&caller_sk.verifying_key());
|
|
let grant = Grant {
|
|
issuer: caller,
|
|
subject: Did::from_verifying_key(&SigningKey::from_bytes(&[7u8; 32]).verifying_key()),
|
|
capability: Capability::new(req.id(), [INFER_ACTION]),
|
|
budget_uj: 8_000_000,
|
|
// Root grant: a joule ceiling only, no money ceiling and no caveats.
|
|
budget_funds: None,
|
|
caveats: Vec::new(),
|
|
expiry: 0,
|
|
parent: None,
|
|
delegate_depth: None,
|
|
nonce: [0u8; 16],
|
|
};
|
|
let signed = jcp_grant::issue(&caller_sk, grant).expect("issue grant");
|
|
// The caller presents it, signing for this request: the steward spends a
|
|
// grant only for its issuer, and each presentation only once.
|
|
let presented = Presentation::sign(vec![signed], &req, &caller_sk, [1u8; 16]);
|
|
|
|
// --- serve ---
|
|
// The prompt token payload travels with the serve call (here a small fixed
|
|
// stand-in) but is never sealed into the receipt — only `prompt_hash` is.
|
|
let prompt_tokens: Vec<u32> = vec![1, 2, 3, 4, 5, 6, 7];
|
|
let resp = mesh.serve(&req, &prompt_tokens, Some(&presented)).expect("serve");
|
|
|
|
println!("\nroute: {}", resp.route.justification);
|
|
for c in &resp.route.candidates {
|
|
let mark = if Some(&c.backend) == resp.route.chosen.as_ref() {
|
|
"→"
|
|
} else if c.admissible {
|
|
" "
|
|
} else {
|
|
"✗"
|
|
};
|
|
println!(
|
|
" {mark} {:<9} {} est {} µJ{}",
|
|
c.backend.0,
|
|
c.fidelity.label(),
|
|
c.est_total_uj,
|
|
c.reason.as_ref().map(|r| format!(" ({r})")).unwrap_or_default()
|
|
);
|
|
}
|
|
|
|
println!(
|
|
"\nserved: {} tokens, fidelity_met={}, provenance={}",
|
|
resp.outcome.tokens_out,
|
|
resp.receipt.fidelity_met,
|
|
resp.energy.provenance.jcr1_label()
|
|
);
|
|
println!("energy: {} µJ ({} J)", resp.energy.uj(), resp.energy.uj() as f64 / 1e6);
|
|
match resp.uj_per_accepted_token() {
|
|
Some(jpt) => println!("WEDGE: {jpt} µJ per accepted token"),
|
|
None => println!("WEDGE: no tokens accepted"),
|
|
}
|
|
|
|
// --- the sealed JCR-1 receipt (mesh/1 profile) ---
|
|
let wire = resp.receipt.to_jcr1_wire(mesh.steward());
|
|
println!("\nreceipt (mesh/1):\n{}", serde_json::to_string_pretty(&wire).unwrap());
|
|
|
|
// --- verify the seal ---
|
|
let ok = resp
|
|
.receipt
|
|
.verify_envelope(&resp.envelope, mesh.steward(), &mesh.verifying_key())
|
|
.is_ok();
|
|
println!(
|
|
"\nsealed envelope: {} bytes, verifies under steward key: {ok}",
|
|
resp.envelope.len()
|
|
);
|
|
}
|