From 6dc1a1b2aea872da4c2f283c15eebdd82d2cf12e Mon Sep 17 00:00:00 2001 From: dawn <90008@klbr.net> Date: Sat, 06 Jun 2026 19:50:36 +0000 Subject: [PATCH] [api] add resolveMiniDoc aliases --- docs/xrpc/README.md | 1 + docs/xrpc/identity.md | 25 +++++++++++++++++++++++++ src/api/xrpc/describe_repo.rs | 11 +---------- src/api/xrpc/mod.rs | 10 ++++++++++ src/api/xrpc/resolve_mini_doc.rs | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 file(s) changed, 169 insertion(s)(+), 10 deletion(s)(-) diff --git a/docs/xrpc/README.md b/docs/xrpc/README.md --- a/docs/xrpc/README.md +++ b/docs/xrpc/README.md @@ -6,4 +6,5 @@ - [com.atproto.*](atproto.md): standard AT Protocol endpoints - [systems.gaze.hydrant.*](hydrant.md): hydrant-specific extensions +- [blue.microcosm.identity.*](identity.md): verified mini-doc identity lookups - [blue.microcosm.links.*](backlinks.md): backlinks (requires `--features backlinks`) diff --git a/docs/xrpc/identity.md b/docs/xrpc/identity.md new file mode 100644 --- /dev/null +++ b/docs/xrpc/identity.md @@ -0,0 +1,25 @@ +--- +title: blue.microcosm.identity.* +--- + +these queries expose the verified mini-doc view of an atproto identity. + +## blue.microcosm.identity.resolveMiniDoc + +alias of `com.bad-example.identity.resolveMiniDoc` with intention to stabilize under this name. + +| param | required | description | +| :--- | :--- | :--- | +| `identifier` | yes | handle or DID to resolve. | + +returns `{ did, handle, pds, signing_key }`. + +## com.bad-example.identity.resolveMiniDoc + +like `com.atproto.identity.resolveIdentity`, but instead of the full DID doc it returns an atproto-relevant subset. + +| param | required | description | +| :--- | :--- | :--- | +| `identifier` | yes | handle or DID to resolve. | + +returns `{ did, handle, pds, signing_key }`. diff --git a/src/api/xrpc/describe_repo.rs b/src/api/xrpc/describe_repo.rs --- a/src/api/xrpc/describe_repo.rs +++ b/src/api/xrpc/describe_repo.rs @@ -4,7 +4,6 @@ use jacquard_common::types::{did::Did, nsid::Nsid, string::Handle}; use smol_str::SmolStr; -use crate::control::repos::MiniDocError; use crate::db::types::DidKey; use super::*; @@ -56,22 +55,14 @@ ExtractXrpc(req): ExtractXrpc, ) -> XrpcResult>> { let nsid = DescribeRepoResponse::NSID; + let doc = super::resolve_mini_doc::resolve_mini_doc(&hydrant, &req.identifier, nsid); let did = hydrant .state .resolver .resolve_did(&req.identifier) .await .map_err(|e| internal_error(nsid, format!("can't resolve identifier: {e}")))?; - let repo = hydrant.repos.get(&did); - let doc = repo.mini_doc().map_err(|e| match e { - MiniDocError::NotSynced => bad_request(nsid, "repo not synced"), - MiniDocError::RepoNotFound => bad_request(nsid, "repo not found"), - MiniDocError::CouldNotResolveIdentity => { - upstream_error(nsid, "identity could not be resolved") - } - MiniDocError::Other(e) => internal_error(nsid, e), - }); let collections = repo.collections().map_err(|e| internal_error(nsid, e)); let (doc, collections) = tokio::try_join!(doc, collections)?; diff --git a/src/api/xrpc/mod.rs b/src/api/xrpc/mod.rs --- a/src/api/xrpc/mod.rs +++ b/src/api/xrpc/mod.rs @@ -58,6 +58,8 @@ mod get_repo; #[cfg(feature = "indexer")] mod list_records; +#[cfg(feature = "indexer")] +mod resolve_mini_doc; #[cfg(feature = "relay")] mod request_crawl; @@ -78,6 +80,14 @@ let r = r .route(CountRecords::PATH, get(count_records::handle)) .route(DescribeRepo::PATH, get(describe_repo::handle)) + .route( + resolve_mini_doc::BadExampleResolveMiniDoc::PATH, + get(resolve_mini_doc::handle_bad_example), + ) + .route( + resolve_mini_doc::BlueMicrocosmResolveMiniDoc::PATH, + get(resolve_mini_doc::handle_blue_microcosm), + ) .route( AtprotoDescribeRepoRequest::PATH, get(com_atproto_describe_repo::handle), diff --git a/src/api/xrpc/resolve_mini_doc.rs b/src/api/xrpc/resolve_mini_doc.rs new file mode 100644 --- /dev/null +++ b/src/api/xrpc/resolve_mini_doc.rs @@ -0,0 +1,132 @@ +use jacquard_common::types::{did::Did, string::Handle}; +use smol_str::SmolStr; + +use crate::control::repos::MiniDocError; +use crate::db::types::DidKey; + +use super::*; + +#[derive(Serialize, Deserialize, jacquard_derive::IntoStatic)] +pub struct ResolveMiniDocOutput<'d> { + #[serde(borrow)] + pub did: Did<'d>, + #[serde(borrow)] + pub handle: Handle<'d>, + #[serde(serialize_with = "crate::util::did_key_serialize_str")] + #[serde(borrow)] + pub signing_key: DidKey<'d>, + pub pds: SmolStr, +} + +pub struct BadExampleResolveMiniDocResponse; +impl XrpcResp for BadExampleResolveMiniDocResponse { + const NSID: &'static str = "com.bad-example.identity.resolveMiniDoc"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ResolveMiniDocOutput<'de>; + type Err<'de> = GenericXrpcError; +} + +pub struct BlueMicrocosmResolveMiniDocResponse; +impl XrpcResp for BlueMicrocosmResolveMiniDocResponse { + const NSID: &'static str = "blue.microcosm.identity.resolveMiniDoc"; + const ENCODING: &'static str = "application/json"; + type Output<'de> = ResolveMiniDocOutput<'de>; + type Err<'de> = GenericXrpcError; +} + +#[derive(Serialize, Deserialize, jacquard_derive::IntoStatic)] +pub struct ResolveMiniDocRequestData<'i> { + #[serde(borrow)] + pub identifier: AtIdentifier<'i>, +} + +pub struct BadExampleResolveMiniDoc; +impl XrpcRequest for ResolveMiniDocRequestData<'_> { + type Response = BadExampleResolveMiniDocResponse; + const NSID: &'static str = Self::Response::NSID; + const METHOD: XrpcMethod = XrpcMethod::Query; +} + +impl XrpcEndpoint for BadExampleResolveMiniDoc { + const PATH: &'static str = "/xrpc/com.bad-example.identity.resolveMiniDoc"; + const METHOD: XrpcMethod = XrpcMethod::Query; + type Request<'de> = ResolveMiniDocRequestData<'de>; + type Response = BadExampleResolveMiniDocResponse; +} + +#[derive(Serialize, Deserialize, jacquard_derive::IntoStatic)] +pub struct BlueMicrocosmResolveMiniDocRequestData<'i> { + #[serde(borrow)] + pub identifier: AtIdentifier<'i>, +} + +impl XrpcRequest for BlueMicrocosmResolveMiniDocRequestData<'_> { + type Response = BlueMicrocosmResolveMiniDocResponse; + const NSID: &'static str = Self::Response::NSID; + const METHOD: XrpcMethod = XrpcMethod::Query; +} + +pub struct BlueMicrocosmResolveMiniDoc; +impl XrpcEndpoint for BlueMicrocosmResolveMiniDoc { + const PATH: &'static str = "/xrpc/blue.microcosm.identity.resolveMiniDoc"; + const METHOD: XrpcMethod = XrpcMethod::Query; + type Request<'de> = BlueMicrocosmResolveMiniDocRequestData<'de>; + type Response = BlueMicrocosmResolveMiniDocResponse; +} + +pub(super) async fn resolve_mini_doc( + hydrant: &Hydrant, + identifier: &AtIdentifier<'_>, + nsid: &'static str, +) -> XrpcResult> { + let did = hydrant + .state + .resolver + .resolve_did(identifier) + .await + .map_err(|e| bad_request(nsid, format!("can't resolve identifier: {e}")))?; + + let doc = hydrant.repos.get(&did).mini_doc().await.map_err(|e| match e { + MiniDocError::NotSynced => bad_request(nsid, "repo not synced"), + MiniDocError::RepoNotFound => bad_request(nsid, "repo not found"), + MiniDocError::CouldNotResolveIdentity => { + upstream_error(nsid, "identity could not be resolved") + } + MiniDocError::Other(e) => internal_error(nsid, e), + })?; + + Ok(ResolveMiniDocOutput { + did: doc.did, + handle: doc.handle, + pds: doc.pds.to_smolstr(), + signing_key: doc.signing_key, + }) +} + +pub async fn handle_bad_example( + State(hydrant): State, + ExtractXrpc(req): ExtractXrpc, +) -> XrpcResult>> { + Ok(Json( + resolve_mini_doc( + &hydrant, + &req.identifier, + BadExampleResolveMiniDocResponse::NSID, + ) + .await?, + )) +} + +pub async fn handle_blue_microcosm( + State(hydrant): State, + ExtractXrpc(req): ExtractXrpc, +) -> XrpcResult>> { + Ok(Json( + resolve_mini_doc( + &hydrant, + &req.identifier, + BlueMicrocosmResolveMiniDocResponse::NSID, + ) + .await?, + )) +} -- tangled.sh