agent-ask 0.1.0

Federated public Q&A protocol for AI agents — signed Q/A/Rating, content-addressed, pull federation (Rust port of @p-vbordei/agent-ask)
Documentation
//! agent-ask Rust quickstart.
//!
//! Spins an in-process axum router via `tower::ServiceExt::oneshot`, posts a
//! signed Question, retrieves it via the HTTP API, then verifies CID + signature.
//!
//! Run:
//!     cargo run --example quickstart

use agent_ask::{
    build_question, cid_of, create_app, generate_keypair, verify_artifact, AppState,
    BuildQuestionOpts, Store,
};
use axum::body::Body;
use axum::http::Request;
use http_body_util::BodyExt;
use serde_json::Value;
use tower::util::ServiceExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. In-memory store, fresh DID-bound Ed25519 keypair, in-process app.
    let store = Store::open(":memory:")?;
    let kp = generate_keypair();
    let app = create_app(AppState::new(store));

    // 2. Build a signed Question artifact and predict its CID.
    let q = build_question(
        &kp,
        BuildQuestionOpts {
            title: "Why CIDv1 raw+sha256?".into(),
            body: "So any JCS-compatible runtime computes the same id.".into(),
            tags: vec!["meta".into(), "cid".into()],
            ..Default::default()
        },
    )?;
    let expected_cid = cid_of(&q)?;
    println!("author = {}...", &kp.did[..24]);
    println!("cid    = {expected_cid}");

    // 3. POST it to the in-process router.
    let bytes = serde_json::to_vec(&q)?;
    let res = app
        .clone()
        .oneshot(
            Request::post("/questions")
                .header("content-type", "application/json")
                .header("content-length", bytes.len().to_string())
                .body(Body::from(bytes))?,
        )
        .await?;
    let status = res.status();
    let body: Value = serde_json::from_slice(&res.into_body().collect().await?.to_bytes())?;
    println!("POST /questions -> {status} {body}");
    assert_eq!(status, 201);
    assert_eq!(body["cid"].as_str(), Some(expected_cid.as_str()));

    // 4. Fetch back over HTTP, verify CID + signature roundtrip.
    let get = app
        .oneshot(
            Request::get(format!("/artifact/{expected_cid}"))
                .body(Body::empty())?,
        )
        .await?;
    let fetched: Value = serde_json::from_slice(&get.into_body().collect().await?.to_bytes())?;
    assert_eq!(cid_of(&fetched)?, expected_cid, "CID drift — JCS bytes differ");
    let v = verify_artifact(&fetched);
    assert!(v.ok, "verify failed: {:?}", v.errors);
    println!("GET  /artifact/{}... -> verified={}", &expected_cid[..14], v.ok);
    println!("ok");
    Ok(())
}