App Lexicon Working Group — Proposal Draft#
Status: DRAFT — for review before posting to discourse.atprotocol.community
Author: pixeline.be (Alexandre Plennevaux)
Original Discussion: https://discourse.atprotocol.community/t/a-community-app-lexicon/656/49
Date: 2026-03-20
Part 1: Lexicon Design Proposal#
Context#
The AT Protocol ecosystem is growing fast. Multiple independent efforts currently track apps, tools, and services — from Semble collections (200+ apps) to BlueskyDirectory.com, atproto.brussels, AlternativeProto, sdk.blue, and various spreadsheets. At one point, there were 21 separate lists.
There is no shared, machine-readable way to describe what an app is, what it does, or how it plugs into ATProto. This makes discovery fragmented and maintenance duplicative.
Prior Art#
- atproto.garden (dame.is): The most developed starting point. Defines
garden.atproto.directory.submission— a record type stored on users' PDS with fields like name, tagline, description, url, category, tags, projectState, creators, logo, screenshots, and lexicon metadata. Open source at github.com/dame-is/atproto-garden. - store.stucco.software (nikolas.ws): Working implementation that creates records to PDS and checks
rel=mefor verification. Building an AppView that listens to the firehose. - BlueskyDirectory.com (jluther.net): Largest meta-listing. Willing to participate.
- atproto.brussels (pixeline.be): Maintains a curated directory with fields like name, url, platform, short description, category, alternative_to, last_checked. Experiments with PDS-stored user ratings (
brussels.loves.appRating). - schema.org/SoftwareApplication: Established web vocabulary for describing software applications.
- W3C Web Application Manifest / MASL: Manifest structures for web apps, noted as structurally similar by ngerakines.me and bmann.ca.
- ATProto OAuth client metadata: Already contains some app info (name, logo, urls) for apps that implement OAuth.
Design Principles#
- Start minimal — resist scope creep (bmann.ca's consistent message). Ship a small, useful set; iterate via PRs.
- Apps first — leave SDKs, infrastructure, and libraries for a future, separate lexicon.
- Resolve, don't store — handles should be resolved from DIDs, not stored redundantly (byarielm.fyi).
- Signals over taxonomy — rather than a rigid category tree, expose small composable signals (type, capabilities, tags) that consumers can assemble into their own views (pixeline.be).
selfrkey convention — if the app's official ATProto account publishes its own record with rkeyself, that's a self-attestation of ownership (zicklag.dev).- Verification is out-of-band — verification (via
rel=me,.well-known, or other methods) is a concern for directories/clients, not the lexicon itself. The lexicon just provides the data.
Proposed Lexicon: community.lexicon.app.entry#
Below is a proposed minimal lexicon for describing an app in the ATProto ecosystem. It draws heavily from the atproto.garden schema, simplified per thread consensus.
Note on NSID: The namespace
community.lexiconis used as a placeholder. The actual NSID should be decided by the working group (e.g., under the Lexicon Community's domain).
{
"lexicon": 1,
"id": "community.lexicon.app.entry",
"defs": {
"main": {
"type": "record",
"description": "An entry describing an app built on or for the AT Protocol. Lives on the submitter's PDS. Use rkey 'self' to signal that the publishing account is the app's official account; use a TID rkey for third-party submissions.",
"key": "tid",
"record": {
"type": "object",
"required": ["name", "url", "createdAt"],
"properties": {
"name": {
"type": "string",
"maxLength": 200,
"maxGraphemes": 100,
"description": "The display name of the app."
},
"url": {
"type": "string",
"format": "uri",
"description": "The primary URL for the app (website or landing page)."
},
"description": {
"type": "string",
"maxLength": 1000,
"maxGraphemes": 300,
"description": "A short description of what the app does (1-3 sentences)."
},
"logo": {
"type": "blob",
"accept": ["image/png", "image/jpeg", "image/webp", "image/svg+xml"],
"maxSize": 500000,
"description": "App logo (must be square aspect ratio)."
},
"category": {
"type": "string",
"knownValues": [
"client",
"tool",
"service",
"game",
"labeler",
"other"
],
"description": "The primary category of the app."
},
"tags": {
"type": "array",
"maxLength": 10,
"items": {
"type": "string",
"maxLength": 64,
"maxGraphemes": 32
},
"description": "Free-form tags for filtering and discovery (e.g. 'photos', 'messaging', 'publishing', 'feeds', 'moderation')."
},
"platforms": {
"type": "array",
"maxLength": 6,
"items": {
"type": "string",
"knownValues": [
"web",
"ios",
"android",
"macos",
"windows",
"linux"
]
},
"description": "Platforms the app runs on."
},
"projectState": {
"type": "string",
"knownValues": [
"released",
"beta",
"developing",
"unmaintained",
"archived"
],
"description": "Current development state."
},
"brandDid": {
"type": "string",
"format": "did",
"description": "The DID of the app's official/brand ATProto account, if different from the submitter."
},
"sourceUrl": {
"type": "string",
"format": "uri",
"description": "Link to the source code repository (if open source)."
},
"createdAt": {
"type": "string",
"format": "datetime",
"description": "Timestamp when this record was created."
},
"updatedAt": {
"type": "string",
"format": "datetime",
"description": "Timestamp when this record was last updated."
}
}
}
}
}
}
Possible improvements#
These were discussed but deferred to keep v1 minimal:
- Screenshots, keyFeatures — useful for rich directories but not core metadata.
- customLexicons / supportedLexicons — valuable for developer-facing directories, but unlikely to be kept up-to-date by most submitters. Can be a v2 addition or a separate linked record.
- creators array — the submitter's DID is already the record author. A
brandDidcovers the brand/org case. Full contributor lists can come later. - alternative_to — interesting idea (raised by pixeline.be, supported by yamarten.bsky.social), but adds complexity. Better as a separate record type or tag convention.
- Verification — per thread consensus, verification is out-of-band. Directories can verify using
selfrkey convention,rel=me,.well-known, or their own criteria. - Ratings / reviews — atproto.garden already has separate lexicons for these (
garden.atproto.directory.upvote,.review, etc.). These should remain separate from the directory entry itself.