From ac0003662f46ac78fcecf11a6cb34154dfd2895a Mon Sep 17 00:00:00 2001 From: scanash00 Date: Fri, 12 Jun 2026 10:29:46 -0800 Subject: [PATCH] Reconstruct profile verification state from microcosm Verifications are app.bsky.graph.verification records whose subject points at the verified DID (a Constellation backlink); each record's repo is the issuer. hydrateVerificationState fetches them, resolves issuer identity, and builds the VerificationState now attached to hydrateProfile's ProfileViewDetailed. isValid is computed per the lexicon: the handle captured in the record must still match the subject's current handle. verifiedStatus is 'valid' if any verification is valid. Caveat: the AppView only surfaces verifications from a curated trusted-verifier set, which is Bluesky policy not present in the records, so we include every issuer. Verified against bsky.app (4/4 records valid, handle bsky.app). --- src/lib/microcosm/constellation.ts | 2 + src/lib/microcosm/hydrate.ts | 80 ++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/src/lib/microcosm/constellation.ts b/src/lib/microcosm/constellation.ts index ab0681d..db82b23 100644 --- a/src/lib/microcosm/constellation.ts +++ b/src/lib/microcosm/constellation.ts @@ -90,6 +90,8 @@ export const Sources = { followers: 'app.bsky.graph.follow:subject', /** Membership records of a list (subject = list at-uri). */ listItems: 'app.bsky.graph.listitem:list', + /** Verifications issued for an identity (subject = verified DID). */ + verifications: 'app.bsky.graph.verification:subject', /** Blocks of an identity (subject = blocked DID). */ blocks: 'app.bsky.graph.block:subject', } as const diff --git a/src/lib/microcosm/hydrate.ts b/src/lib/microcosm/hydrate.ts index b820b2e..98dce9a 100644 --- a/src/lib/microcosm/hydrate.ts +++ b/src/lib/microcosm/hydrate.ts @@ -196,6 +196,81 @@ async function hydrateEmbed( return undefined } +/** + * Reconstruct a profile's `VerificationState` from microcosm. Verifications are + * `app.bsky.graph.verification` records whose `subject` points at the verified + * DID (a Constellation backlink); each record's repo is the issuer. + * + * NOTE on "trusted verifiers": the AppView only surfaces verifications from a + * curated trusted-verifier set, which is Bluesky policy not present in the + * records, so we can't reproduce that filter. We include every issuer and let + * the UI decide. `isValid` is computed structurally: the handle captured in the + * record must still match the subject's current handle (per the lexicon). + */ +async function hydrateVerificationState( + did: string, + currentHandle: string, + signal?: AbortSignal, +): Promise { + const page = await constellation + .getBacklinks( + { + subject: did, + source: constellation.Sources.verifications, + limit: 20, + }, + signal, + ) + .catch(() => undefined) + if (!page || page.records.length === 0) return undefined + + const verifications = ( + await Promise.all( + page.records.map( + async (ref): Promise => { + const uri = `at://${ref.did}/${ref.collection}/${ref.rkey}` + const rec = await getRecordByUri(uri, undefined, signal).catch( + () => undefined, + ) + if (!rec) return undefined + const value = rec.value as { + handle?: string + createdAt?: string + } + // Resolve the issuer's current identity for display. + const issuer = await resolveMiniDoc(ref.did, signal).catch( + () => undefined, + ) + const isValid = + !!currentHandle && + !!value?.handle && + value.handle === currentHandle && + currentHandle !== 'handle.invalid' + return { + $type: 'app.bsky.actor.defs#verificationView', + issuer: ref.did, + issuerHandle: issuer?.handle, + uri, + isValid, + createdAt: value?.createdAt ?? new Date(0).toISOString(), + } + }, + ), + ) + ).filter(Boolean) as AppBskyActorDefs.VerificationView[] + + if (verifications.length === 0) return undefined + + const verifiedStatus = verifications.some(v => v.isValid) ? 'valid' : 'none' + return { + $type: 'app.bsky.actor.defs#verificationState', + verifications, + verifiedStatus, + // We are not a trusted verifier ourselves; report none. + trustedVerifierStatus: 'none', + } +} + /** * Hydrate a `ProfileViewDetailed` for a DID or handle from microcosm. * @@ -247,6 +322,10 @@ export async function hydrateProfile( const profile = recordRes?.value as AppBskyActorProfile.Record | undefined + const verification = await hydrateVerificationState(did, handle, signal).catch( + () => undefined, + ) + return { $type: 'app.bsky.actor.defs#profileViewDetailed', did, @@ -262,6 +341,7 @@ export async function hydrateProfile( indexedAt: undefined, createdAt: profile?.createdAt, pinnedPost: profile?.pinnedPost, + verification, } } -- 2.51.2