Resolver — the consumer side#
A read-only, zero-auth, zero-dependency resolver that discovers a user's action graph from their repo footprint, and a CLI over it. This is the reference implementation a consumer embeds.
resolver.mjs core lib (Node 18+ and browser; fetch-based)
cli.mjs prints the action graph / answers "which app can do X?"
fixtures/ demo records (not yet on the network) for the offline loop
The loop#
actor (handle/DID)
-> scan repo for dev.at-intent.usage records (discovery)
-> resolve each app DID -> its dev.at-intent.capability records
-> assemble (verb, subject, handler) action graph (the triple)
matchActions(graph, {verb, subject}) -> "which handler can do X?"
requiredScopes(actions) -> the include:<nsid> scopes to request
CLI#
node cli.mjs --demo # full action graph (offline fixtures)
node cli.mjs --demo --feed https://blog/rss # "which of my apps subscribe to a feed?"
node cli.mjs --demo --scopes # scopes to request to enable everything
node cli.mjs --demo --json # raw graph
node cli.mjs alice.bsky.social # live: real handle, real repo scan
Live mode does real atproto plumbing — handle→DID, DID doc→PDS,
com.atproto.repo.listRecords for usage/capability records. Until apps publish
these records it finds nothing against live handles — which is itself proof
the plumbing is real, not mocked. The fixtures stand in for the network so the
full loop runs offline today.
Library#
import { resolveActionGraph, matchActions, requiredScopes } from "./resolver.mjs";
const graph = await resolveActionGraph("alice.bsky.social", { cache: new Map() });
const handlers = matchActions(graph, { verb: "subscribe", subject: { kind: "uri" } });
const scopes = requiredScopes(handlers);
opts: fixtures (offline records), cache (Map, persist with a long TTL),
staleDays (default 180), includeStale, fetchImpl, now.
Caveats#
- No real records exist yet — everything non-demo resolves to empty until an
app publishes
dev.at-intent.*records. - Browser live mode can hit CORS —
listRecordsagainst arbitrary PDSes may be blocked from a web origin; a tiny read proxy fixes it. The Node path and demo mode have no such limit. - Routing is read-only here —
repo/servicecapabilities show what they would do; performing the write/call is the consumer's job (see consumers.md).