ngerakines/atproto-skills/atproto-publish-lexicon
Claude Code and agent skill by ngerakines/atproto-skills
- community
Checking that the SKILL.md file is still in the repository.
This skill should be used when the user is publishing an AT Protocol lexicon as a `com.atproto.lexicon.schema` record on the network, or resolving such a record back to a lexicon document. Triggers on phrases like "publish my lexicon", "publish lexicon to PDS", "lexicon publication", "lexicon registry", "schema registry", "who owns this NSID", "NSID authority", "`_lexicon.` TXT record", "lexicon DNS record", "lexicon resolution", "resolve an NSID to a lexicon", "describe_lexicon", "`com.atproto.lexicon.schema`", "putRecord lexicon", "createRecord lexicon", "lexicon revision", "lexicon versioning", "publish NSID", "how do clients fetch my lexicon", "rkey is the NSID". Also covers error surfaces like "InvalidRecord" when publishing a lexicon, the `rkey = <full NSID>` convention, and the authority-to-DID binding that gates resolution at consumer time. Uses the `lexicon-garden` and `atpmcp` MCP tools (`validate_lexicon`, `check_compatibility`, `create_record_cid`, `invoke_xrpc`, `describe_lexicon`, `get_record`) rather than hand-rolled validators. Does NOT cover authoring the lexicon JSON document, catalog loading, codegen, or client-side record/XRPC validation (see `atproto-lexicon`); DID/handle resolution internals or the `_atproto.` TXT record (see `atproto-identity-resolution`); CAR/MST/commit signing (see `atproto-repository`); CID computation internals (see `atproto-cid`); or Bluesky-domain (`app.bsky.*`) lexicons (out of scope for this plugin).
Install
npx vskill@latest install ngerakines/atproto-skills/atproto-publish-lexiconSKILL.md
AT Protocol Lexicon Publication
A lexicon isn't published until it's a record. This skill covers the protocol-level workflow that takes a validated lexicon JSON document, wraps it as a com.atproto.lexicon.schema record, writes it to the authoring DID's repo, and makes it resolvable by NSID. The inverse — consumers resolving an NSID back to a lexicon — is the other half.
Authoring the JSON document itself is the sibling skill atproto-lexicon's job. This skill starts the moment you're ready to put that document on the network.
When to use
- The user is publishing a new lexicon or a revision to an existing one (
putRecord/createRecordoncom.atproto.lexicon.schema). - The user is debugging why their published lexicon doesn't resolve — authority mismatch, missing
_lexicon.TXT, wrong rkey. - The user is consuming a lexicon by NSID at runtime and wants to understand the resolution chain (NSID → authority → DNS → DID → PDS → record).
- The user is planning a breaking change and needs to know how
revisionand the authority model interact.
What this skill provides
- A publish pipeline: form the record, validate, check authority, diff against any prior revision, compute the CID, publish, verify.
- The `com.atproto.lexicon.schema` record shape — what's top-level, what
idmust equal, whatlexicon: 1means. - The resolution flow for consumers: reverse-DNS the NSID, look up
_lexicon.<authority>TXT, resolve the DID, fetch the record. - The authority model: who's allowed to publish under which NSID prefix, and why the PDS doesn't enforce it.
- Revision discipline: monotonic integers, no semver, how to gate breaking changes.
Detail lives in references/; this file is the procedure.
Procedure
Follow these steps in order. Skipping authority or compatibility checks is how lexicons ship broken.
- Form the record. A
com.atproto.lexicon.schemarecord is the lexicon document with a$typestamped on top. Top-level fields:
$type:com.atproto.lexicon.schema(required)lexicon:1— the lexicon language version, fixed at 1 today (not a semver of your doc)id: the NSID, e.g.com.example.foo.getBar— MUST equal the rkey you'll publish underrevision: optional integer; omit for the first publish, bump monotonically thereafterdescription: optional top-level stringdefs: the map of definition names to def objects (main, plus any secondary defs)
See references/record-shape.md for a field-by-field breakdown and a canonical example.
- Validate the lexicon body. Call one of:
lexicon-garden.validate_lexicon(doc)— spec-conformant validator, remote.atpmcp.validate_lexicon_schema(doc)— local equivalent.
Fix every error before touching the network. A malformed defs map, an unresolvable internal #ref, or a missing main when the NSID names a record/query/procedure/subscription will all be rejected here. Don't paper over warnings — publishing a broken lexicon is worse than not publishing at all, because other actors may cache the CID.
- Check authority. Derive the authority domain from the NSID by reversing the first segments except the final name segment:
com.example.foo.getBar→ authority =example.comapp.bsky.feed.post→ authority =bsky.appsocial.pdsls.tools.listBookmarks→ authority =pdsls.social(final segment is the name; everything before it reverses to the domain)
Then:
- Query the DNS
_lexicon.<authority>TXT record. It containsdid=did:plc:...(or any DID method). - That DID is the only one whose publications under this NSID prefix will be honored by consumers.
- The DID the user is publishing from must match. If it doesn't, stop — the publish will succeed on their PDS but silently no-op at resolution time.
For resolving the DID itself to a PDS endpoint, defer to atproto-identity-resolution. See references/authority-and-ownership.md for the squatting model and edge cases.
- Check for a prior version. Fetch the current on-network record, if any:
atpmcp.get_record(at://<publisher-did>/com.atproto.lexicon.schema/<nsid>), oratpmcp.invoke_xrpc("com.atproto.repo.getRecord", {repo: <did>, collection: "com.atproto.lexicon.schema", rkey: <nsid>}), or thelexicon-gardenequivalents.
If one exists:
- Run
lexicon-garden.check_compatibility(old_doc, new_doc). - Bump
revisiontoold.revision + 1(or start at1if absent). - Refuse to publish breaking changes silently. If the check flags a break, require the user to either mint a new NSID (e.g.
com.example.foo.getBarV2) or explicitly acknowledge the break. Therevisionspec is a social signal, not a gate — surfacing the break is your job, not the PDS's.
Full break/non-break matrix: defer to skills/atproto-lexicon/shared/backward-compat.md rather than duplicating. Revision-specific rules: references/backward-compat-revisions.md.
- Compute the canonical CID. Run
lexicon-garden.create_record_cid(record)(or theatpmcpequivalent). This is a sanity check — it round-trips the record through DRISL canonical CBOR. If the CID your encoder produces disagrees with the MCP tool's, your encoder is non-canonical somewhere and the PDS will compute a different CID than you expect. Investigate before publishing; a wrong CID means consumers can't verify the record against any reference you hand them. DRISL details live inskills/atproto-repository§drisl; CID encoding inskills/atproto-cid.
- Publish. Call
com.atproto.repo.putRecord:
`` collection: com.atproto.lexicon.schema rkey: <the full NSID, verbatim — e.g. com.example.foo.getBar> record: <the doc from step 1> validate: true ``
Use putRecord (idempotent) rather than createRecord for updates; either works for a first publish. The PDS will re-validate the record against the com.atproto.lexicon.schema lexicon. The most common reject is id != rkey.
For SDK-level snippets in your stack, defer to:
- Rust →
skills/atproto-lexicon/rust/xrpc-client.md - TypeScript →
skills/atproto-lexicon/typescript/xrpc-client.md - Go →
skills/atproto-lexicon/go/xrpc-client.md
This skill does not duplicate those.
- Verify resolution end-to-end. Immediately after publish:
- Re-fetch via
getRecordand compare the returned CID to the one from step 5. They should match exactly. - Optionally call
lexicon-garden.describe_lexicon(<nsid>)to confirm the network's spec-conformant resolver can see your record. A 404 here whengetRecordworks usually means your_lexicon.TXT is missing or points at the wrong DID.
If resolution fails but getRecord succeeds, the record is published but unclaimable — re-check step 3.
See references/publish-checklist.md for a condensed pre-flight list you can walk through each time.
Resolution (consumer side)
The inverse of publishing. Given an NSID, how does a client fetch the lexicon?
- Reverse-DNS the NSID to extract the authority domain (same rule as step 3 above: reverse all segments except the final name segment).
- Resolve authority → DID via the
_lexicon.<authority>TXT record, parsing thedid=key. - Resolve DID → PDS via standard DID-doc resolution — defer to
atproto-identity-resolution. - Fetch the record with
com.atproto.repo.getRecord:
repo = <did>collection = com.atproto.lexicon.schemarkey = <the full NSID>
- Use the record as a lexicon document. The record's fields (
id,lexicon,defs,revision, ...) are the lexicon document. Hand it to your catalog (atproto-lexiconcovers catalog loading).
lexicon-garden.describe_lexicon(nsid) collapses all five steps behind one MCP call; call it first unless you're specifically debugging the resolution chain.
Full diagram and edge cases: references/resolution-flow.md.
MCP tools this skill drives
- `lexicon-garden` →
validate_lexicon(doc),check_compatibility(old, new),create_record_cid(record),describe_lexicon(nsid),invoke_xrpc(method, params, input?). - `atpmcp` →
validate_lexicon_schema(doc),get_record(at_uri),get_lexicon(nsid),create_record_cid(record),invoke_xrpc(method, params, input?).
Prefer lexicon-garden for spec-conformant checks and network resolution. Prefer atpmcp when working against a local dev PDS or when you need deterministic behavior in scripts.
References
references/record-shape.md—com.atproto.lexicon.schemafield-by-field, with example.references/resolution-flow.md— NSID → authority → DNS → DID → PDS → record, with diagram.references/authority-and-ownership.md— why the PDS doesn't enforce NSID authority, and why consumers do.references/backward-compat-revisions.md—revisionmonotonicity, deprecation windows, and when to mint a new NSID instead.references/publish-checklist.md— pre-flight checklist to walk through before every publish.
External specs:
- <https://atproto.com/specs/lexicon> — lexicon document + resolution.
- <https://atproto.com/specs/nsid> — NSID grammar and authority rules.
lexicons/com/atproto/lexicon/schema.jsoninbluesky-social/atproto— the record lexicon this skill publishes against.
Adjacent skills:
atproto-lexicon— authoring the JSON, catalog loading, codegen, client-side validation, XRPC invocation.atproto-identity-resolution— DID/handle resolution,_atproto.TXT, DID-doc shape.atproto-repository— CAR/MST/commit signing, DRISL canonical CBOR.atproto-cid— CID parsing/construction, tag 42.
Examples
Publish trace
Resolution trace
Backlog
- A
scripts/verify-authority.sh(or similar) that given an NSID + DID resolves_lexicon.<authority>and reports match/mismatch. Useful for CI gates on lexicon repos. Not in v0.1; add if users report hitting authority errors frequently.
Security
- Security scanPassed, 95 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/atproto-publish-lexicon/SKILL.md
Popularity
- Repository stars
- 51
- Repository forks
- 3
- 7-day trend score
- 20
Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/ngerakines/atproto-skills/atproto-publish-lexicon)