AT Intents#
A standard, registry-free way for atproto apps to declare what they can do — so other apps (and agents) can discover those capabilities from a user's own repo and route to them. No central directory: an app you use leaves a small record behind, and that footprint is the discovery surface.
THE USER'S REPO THE APPS' REPOS
(your discovery footprint) (capability authority)
┌────────────────────────────────────────┐ ┌──────────────────────────────────────┐
│ dev.at-intent.usage "I use Skyreader" │ ──DID─► │ Skyreader dev.at-intent.capability │
│ dev.at-intent.usage "I use Sill" │ ──DID─► │ × 4 (verb, subject, …) │
│ │ ├──────────────────────────────────────┤
│ left behind by each app you use — │ │ Sill dev.at-intent.capability │
│ no registry, no install step │ │ × 1 (passive) │
└────────────────────────────────────────┘ └──────────────────────────────────────┘
│ a consumer (app or agent) walks the loop — no hardcoded app list
▼
┌─ 1 RESOLVE ──┬─ 2 DISCOVER ──┬─ 3 MATCH ────┬─ 4 AUTHORIZE ─┬─ 5 ACT ──────┐
│ usage→appDID │ appDID→capab. │ "who can │ union of repo │ passive: — │
│ read-only, │ records │ <verb> this │ scopes; new │ repo: write │
│ no auth │ (cache by DID)│ <subject>?" │ auth if needed│ service: call│
└──────────────┴───────────────┴──────────────┴───────────────┴──────────────┘
Every capability is a (verb, subject, handler) triple plus a delivery
method (passive / repo / service) that says how the action actually reaches
the app. The same record doubles as an agent tool definition — discovered
from the user's data at runtime, scoped by their existing grants.
Start here#
| You are… | Read |
|---|---|
| understanding the system | docs/overview.md |
| an app that does things (save/subscribe/share/annotate) | docs/producers.md |
| an app that discovers + routes (menus, "open with", agents) | docs/consumers.md |
| an AI agent asked to author records for a repo | docs/agent-guide.md |
| writing a capability record | docs/capability-spec.md |
| watching the loop run live in a browser (OAuth + real PDS writes) | demo/ |
Try it in 30 seconds#
# See the discovery loop run offline (no network, no auth):
node resolver/cli.mjs --demo
node resolver/cli.mjs --demo --feed https://blog.example/rss # who can subscribe?
# Validate + preview a capability record without writing it (zero deps):
cd tools
node write-capability.mjs examples/subscribe.capability.json --dry-run --identifier myapp.com
See it run for real — the share-sheet demo#
The CLI above runs the loop offline against fixtures. The demo/ is the
same loop in a browser, against a live network: a "Share with…" share sheet that
discovers the handlers already in your repo footprint and runs an action for real.
cd demo
npm install
npm run dev # builds the bundle, then serves → http://127.0.0.1:8787/demo/
It walks the full pitch end to end:
- Discover (read-only, no login) — scan your repo for
dev.at-intent.usage, resolve each app'sdev.at-intent.capabilityrecords into one action graph. - Match — keep the actions whose
subjectaccepts the current page (a bareuri); the rest are shown greyed, with the subject they'd need. - Consent — compute the minimal granular OAuth scopes those handlers declare
(
repo:…/rpc:…, never blanket write) and request exactly that set. - Act — run an action live:
repowrites a record into your PDS,serviceproxies an XRPC call through it,opennavigates. Discovery is the upstreamresolveActionGraphfromresolver/, unchanged.
Because the scopes are granular, discovery has to run first to know what to ask
for — which is exactly why "ask for what the repo says is needed, and nothing more"
is the whole point. See demo/README.md for the auth model
(loopback vs hosted), the code map, and the @atproto/api service-call wrinkles.
What's in this repo#
lexicons/ dev.at-intent.capability + dev.at-intent.usage (the two records)
tools/ write capability/usage records — from a JSON file or interactively
resolver/ the consumer-side discovery loop (lib + CLI + offline fixtures)
demo/ share-sheet browser consumer — discovery + OAuth + live PDS actions
docs/ overview, producer/consumer guides, agent guide, spec reference
Status#
Exploration / draft. The namespace dev.at-intent.* is a working placeholder
(likely long-term home: community.lexicon.*); it's defined in one constant
(tools/lib/nsid.mjs) plus the lexicon id fields, so
re-homing is mechanical. No app publishes these records on the live network at scale
yet — the resolver fixtures stand in for that on the CLI — but the demo/
already runs the whole loop against a real repo footprint over live OAuth, so the
end-to-end path (discover → consent → act) works today, not just in fixtures.
Prior art & context#
Descends from Web / Android / Apple App Intents — but replaces their handler
registry with the user's repo footprint, which is what W3C Web Intents
(~2012) lacked. Builds on atproto OAuth permission sets
(proposal 0011),
lexicon resolution, and lexicon embeds. See docs/overview.md
for the full picture.