techne-sdk 0.3.6

TechneSDK's logic written once: version ranking (eventState) and offline record verification over the published check, shipped to TypeScript as WebAssembly; with opt-in atproto plumbing (service auth, DID resolution, signed labels, proven record fetches, PDS password sessions) behind the `crypto` and `net` features.
Documentation
//! P3.2: one-off fetches read `com.atproto.sync.getRecord` and verify it (`techne_sdk::sync`, ported from the
//! platform indexer's tests, P20.7 G4).
//!
//! `tests/fixtures/` holds two real answers, fetched 2026-10-11 from
//! enoki.us-east.host.bsky.network for did:plc:ewvi7nxzyoun6zhxrhs64oiz (@atproto.com, a public
//! account): its `app.bsky.actor.profile/self` (present; `com.atproto.repo.getRecord` named the same
//! CID at the time) and `app.bsky.actor.profile/nope` (the PDS's proof of absence). The key is that
//! DID's `#atproto` key from plc.directory on the same day.

#![cfg(feature = "crypto")]

use std::collections::BTreeMap;
#[cfg(feature = "net")]
use std::collections::HashMap;

#[cfg(feature = "net")]
use axum::extract::{Query, State};
#[cfg(feature = "net")]
use axum::response::{IntoResponse, Response};
#[cfg(feature = "net")]
use techne_sdk::sync::fetch_proven;
use techne_sdk::sync::{build_proof, multikey, read_car, verify_record_proof, ProofError, Proven, RepoKey};
use serde_json::json;

const DID: &str = "did:plc:ewvi7nxzyoun6zhxrhs64oiz";
const KEY: &str = "zQ3shunBKsXixLxKtC5qeSG9E4J5RkGN57im31pcTzbNQnm5w";
const PROFILE: &str = "app.bsky.actor.profile";
const PROFILE_CID: &str = "bafyreicdwixhubhirckrrt7mqcoiq4u47b7quxlm24r547qcth4bc2ubq4";
const PRESENT: &[u8] = include_bytes!("fixtures/atproto-profile-self.car");
const ABSENT: &[u8] = include_bytes!("fixtures/atproto-profile-absent.car");

fn key() -> RepoKey {
    RepoKey::from_multikey(KEY).expect("a secp256k1 multikey")
}

#[test]
fn a_real_pds_proof_verifies() {
    let Proven::Present(r) = verify_record_proof(PRESENT, DID, PROFILE, "self", &key()).unwrap() else { panic!("present") };
    assert_eq!(r.cid, PROFILE_CID);
    assert_eq!(r.value["$type"], PROFILE);
    // The JSON we hand the indexer re-encodes to the committed CID (a blob ref included).
    assert_eq!(techne_sdk::check::dag_cbor::cid_for_json(&r.value).unwrap().to_string(), PROFILE_CID);
    assert_eq!(r.rev.len(), 13, "a TID revision: {}", r.rev);
    assert!(matches!(verify_record_proof(ABSENT, DID, PROFILE, "nope", &key()).unwrap(), Proven::Absent { .. }));
}

/// The byte offset of `needle` in `hay`.
fn find(hay: &[u8], needle: &[u8]) -> usize {
    hay.windows(needle.len()).position(|w| w == needle).expect("present")
}

#[test]
fn a_tampered_response_is_refused() {
    // 1. The record body changed in flight (the PDS, or anything between): its block no longer hashes
    //    to its CID.
    let mut body = PRESENT.to_vec();
    let at = find(&body, b"displayName") + 13;
    body[at] ^= 0x01;
    assert!(matches!(verify_record_proof(&body, DID, PROFILE, "self", &key()), Err(ProofError::BlockHash(_))));

    // 2. A signature that is not the repo's: verified against another key.
    let other = RepoKey::from_multikey(&multikey(&k256::ecdsa::SigningKey::from_slice(&[7u8; 32]).unwrap())).unwrap();
    assert_eq!(verify_record_proof(PRESENT, DID, PROFILE, "self", &other), Err(ProofError::Signature));

    // 3. Someone else's (validly signed) repo, answered for this DID.
    let err = verify_record_proof(PRESENT, "did:plc:someoneelsesomeoneelseso", PROFILE, "self", &key()).unwrap_err();
    assert!(matches!(err, ProofError::WrongRepo { .. }), "{err:?}");

    // 4. A proof with a block left out (the record's): incomplete, never "absent".
    let (root, blocks) = read_car(PRESENT).unwrap();
    let record_cid: ipld_core::cid::Cid = PROFILE_CID.parse().unwrap();
    assert!(blocks.contains_key(&record_cid) && blocks.contains_key(&root));
    let without = rebuild_car(root, blocks.iter().filter(|(c, _)| **c != record_cid));
    assert!(matches!(verify_record_proof(&without, DID, PROFILE, "self", &key()), Err(ProofError::Missing(_))));

    // 5. A forger who rebuilds the whole repo consistently (new record, new MST, new commit, every CID
    //    right) but cannot sign as the repo.
    let forger = k256::ecdsa::SigningKey::from_slice(&[9u8; 32]).unwrap();
    let forged = build_proof(DID, "3mzzzzzzzzzz2", &forger, &BTreeMap::from([(format!("{PROFILE}/self"), json!({"$type": PROFILE, "displayName": "not atproto"}))]));
    assert_eq!(verify_record_proof(&forged, DID, PROFILE, "self", &key()), Err(ProofError::Signature));

    // 6. Not a CAR at all (a getRecord JSON body served where the proof belongs).
    assert!(matches!(verify_record_proof(br#"{"uri":"at://x","cid":"y","value":{}}"#, DID, PROFILE, "self", &key()), Err(ProofError::Car(_))));
}

fn rebuild_car<'a>(root: ipld_core::cid::Cid, blocks: impl Iterator<Item = (&'a ipld_core::cid::Cid, &'a Vec<u8>)>) -> Vec<u8> {
    fn varint(out: &mut Vec<u8>, mut n: u64) {
        loop {
            let b = (n & 0x7f) as u8;
            n >>= 7;
            if n == 0 {
                out.push(b);
                return;
            }
            out.push(b | 0x80);
        }
    }
    let header = ipld_core::ipld::Ipld::Map(BTreeMap::from([
        ("version".to_owned(), ipld_core::ipld::Ipld::Integer(1)),
        ("roots".to_owned(), ipld_core::ipld::Ipld::List(vec![ipld_core::ipld::Ipld::Link(root)])),
    ]));
    let header = techne_sdk::check::dag_cbor::canonical_encode(&header).unwrap();
    let mut out = Vec::new();
    varint(&mut out, header.len() as u64);
    out.extend_from_slice(&header);
    for (cid, data) in blocks {
        let c = cid.to_bytes();
        varint(&mut out, (c.len() + data.len()) as u64);
        out.extend_from_slice(&c);
        out.extend_from_slice(data);
    }
    out
}

#[test]
fn built_proofs_verify_and_prove_absence() {
    let signer = k256::ecdsa::SigningKey::from_slice(&[3u8; 32]).unwrap();
    let key = RepoKey::from_multikey(&multikey(&signer)).unwrap();
    let records = BTreeMap::from([
        ("org.example.event/3m2a".to_owned(), json!({"$type": "org.example.event", "name": "A"})),
        ("org.example.event/3m2b".to_owned(), json!({"$type": "org.example.event", "name": "B"})),
        ("org.example.place/p1".to_owned(), json!({"$type": "org.example.place", "name": "P"})),
    ]);
    let car = build_proof("did:plc:builtbuiltbuiltbuiltbuil", "3m2revrevrev2", &signer, &records);
    for (path, value) in &records {
        let (c, r) = path.split_once('/').unwrap();
        let Proven::Present(p) = verify_record_proof(&car, "did:plc:builtbuiltbuiltbuiltbuil", c, r, &key).unwrap() else { panic!("{path}") };
        assert_eq!(&p.value, value);
        assert_eq!(p.rev, "3m2revrevrev2");
    }
    for (c, r) in [("org.example.event", "3m2c"), ("a.b.c", "x"), ("zzz.z", "z")] {
        assert!(matches!(verify_record_proof(&car, "did:plc:builtbuiltbuiltbuiltbuil", c, r, &key).unwrap(), Proven::Absent { .. }), "{c}/{r}");
    }
}

#[test]
fn p256_keys_parse() {
    // did:key of a P-256 key (multicodec 0x8024), from the atproto spec's examples.
    assert!(matches!(RepoKey::from_multikey("zDnaembgSGUhZULN2Caob4HLJPaxBh92N7rtH21TErzqf8HQo"), Some(RepoKey::P256(_))));
    assert!(matches!(RepoKey::from_multikey(KEY), Some(RepoKey::K256(_))));
    assert!(RepoKey::from_multikey("zNotAKey").is_none());
}

#[cfg(feature = "net")]
/// A PDS serving `sync.getRecord` from canned CARs (by rkey); unknown rkeys answer RecordNotFound.
async fn fake_pds(cars: HashMap<String, Vec<u8>>) -> String {
    async fn get_record(State(cars): State<HashMap<String, Vec<u8>>>, Query(q): Query<HashMap<String, String>>) -> Response {
        match cars.get(&q["rkey"]) {
            Some(car) => ([("content-type", "application/vnd.ipld.car")], car.clone()).into_response(),
            None => (axum::http::StatusCode::BAD_REQUEST, axum::Json(json!({"error": "RecordNotFound"}))).into_response(),
        }
    }
    let app = axum::Router::new().route("/xrpc/com.atproto.sync.getRecord", axum::routing::get(get_record)).with_state(cars);
    let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
    let addr = listener.local_addr().unwrap();
    tokio::spawn(async move { axum::serve(listener, app).await.unwrap() });
    format!("http://{addr}")
}

#[cfg(feature = "net")]
#[tokio::test]
async fn fetch_proven_hands_over_only_what_verifies() {
    let mut tampered = PRESENT.to_vec();
    let at = find(&tampered, b"displayName") + 13;
    tampered[at] ^= 0x01;
    let pds = fake_pds(HashMap::from([
        ("self".to_owned(), PRESENT.to_vec()),
        ("nope".to_owned(), ABSENT.to_vec()),
        ("tampered".to_owned(), tampered),
    ]))
    .await;
    let http = reqwest::Client::new();
    let got = fetch_proven(&http, &pds, DID, PROFILE, "self", &key()).await.unwrap().expect("present");
    assert_eq!(got.cid, PROFILE_CID);
    assert_eq!(fetch_proven(&http, &pds, DID, PROFILE, "nope", &key()).await.unwrap(), None, "a proven absence");
    assert_eq!(fetch_proven(&http, &pds, DID, PROFILE, "missing", &key()).await.unwrap(), None, "RecordNotFound");
    let err = fetch_proven(&http, &pds, DID, PROFILE, "tampered", &key()).await.unwrap_err();
    assert!(format!("{err:#}").contains("does not hash to its CID"), "{err:#}");
    let _: serde_json::Value = got.value;
}