{
  "path": "/3mjfjsk24qk2i",
  "site": "at://did:plc:fpruhuo22xkm5o7ttr2ktxdo/site.standard.publication/3m23dstduds2v",
  "tags": [
    "atproto"
  ],
  "$type": "site.standard.document",
  "title": "atproto made simple: publishing lexicons",
  "content": {
    "$type": "pub.leaflet.content",
    "pages": [
      {
        "id": "019c73c0-68bb-755b-a4be-ae6e3516e03c",
        "$type": "pub.leaflet.pages.linearDocument",
        "blocks": [
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 92,
                    "byteStart": 73
                  },
                  "features": [
                    {
                      "uri": "https://atproto.com/specs/lexicon#lexicon-publication-and-resolution",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "i find some atproto documentation extremely confusing. the section about publishing lexicons is one of those parts that makes me want to tear my hair out. it sounds so hopelessly complicated."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 67,
                    "byteStart": 24
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                }
              ],
              "plaintext": "so i'm here to tell you publishing lexicons is actually very simple. "
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 25,
                    "byteStart": 21
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 47,
                    "byteStart": 34
                  },
                  "features": [
                    {
                      "uri": "https://atproto.com/guides/publishing-lexicons",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "yes, you can use the goat tool to do it for you, but i want to give you a clear mental model so you know what it does and why."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "what are lexicons anyway"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "i'll assume you're familiar with lexicons but let's recap."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "lexicons are type definitions (written in JSON) which let atproto apps understand data from each other. you've probably seen these:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "json",
              "plaintext": "{\n  \"lexicon\": 1,\n  \"id\": \"app.bsky.actor.profile\",\n  \"defs\": {\n    \"main\": {\n      \"type\": \"record\",\n      \"key\": \"self\",\n      \"record\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"displayName\": { \"type\": \"string\", \"maxGraphemes\": 64 },\n          \"description\": { \"type\": \"string\", \"maxGraphemes\": 256 }\n        }\n      }\n    }\n  }\n}"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "if you squint at it, you could see it's trying to say something like"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "plaintext",
              "plaintext": "namespace app.bsky.actor {\n  @record('self')\n  type profile {\n    @maxGraphemes(64) displayName: string\n    @maxGraphemes(256) description: string\n  }\n}"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "(this is made-up syntax.) so -- it's just a type definition."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 51,
                    "byteStart": 29
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 122,
                    "byteStart": 100
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 187,
                    "byteStart": 164
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "lexicons are mostly used for typing atproto records (e.g. \"a Bluesky profile\" is a record following app.bsky.actor.profile lexicon, \"a Leaflet publication\" follows pub.leaflet.publication lexicon, and so on). if you're making an atproto app that stores its own data, you'll probably want to define a lexicon per data type you store."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 12,
                    "byteStart": 8
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 55,
                    "byteStart": 22
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 105,
                    "byteStart": 79
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 169,
                    "byteStart": 144
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "they're also used for typing atproto-flavored API calls, e.g. a PDS implements com.atproto.repo.getRecord lexicon, a Bluesky appview implements app.bsky.feed.searchPosts lexicon, and so on. i've never wanted to define one but sometimes i need to call those."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "okay, so lexicons are just type definitions in a JSON dialect, and they can be used to define the shape of records or of API calls."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "so how do you \"publish\" one, and why would you want that?"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "lexicons are just records"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "the point of type definitions is to let apps understand each other, so there needs to be some canonical place where people can find them."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 59,
                    "byteStart": 37
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "for example, if you want to know the app.bsky.actor.profile definition, where do you go looking for it? where should tooling look for it? ideally there should be a canonical place where it lives."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 76,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 132,
                    "byteStart": 76
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    },
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "in atproto, this is done in a wonderfully simple way -- a lexicon itself is just a regular record in the app developer's repository. "
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "recall that records in atproto look like this:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 24,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "at://did/collection/rkey"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "for example:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 40,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      }
                    ],
                    "plaintext": "at://did:bla/app.bsky.actor.profile/self"
                  }
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 45,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      }
                    ],
                    "plaintext": "at://did:bla/app.bsky.feed.post/3mjffqvyo4c2f"
                  }
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 45,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      }
                    ],
                    "plaintext": "at://did:bla/dev.npmx.feed.like/3me7fhoewyp2k"
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 132,
                    "byteStart": 128
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 283,
                    "byteStart": 238
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 309,
                    "byteStart": 283
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    },
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 321,
                    "byteStart": 309
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                }
              ],
              "plaintext": "the right part (\"record key\") identifies the record among its siblings. it's often a timestamp but is conventionally a constant self for profiles (since you only need one profile). for lexicons, however, it'll be the lexicon name itself. lexicons themselves are records in a special com.atproto.lexicon.schema collection:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 62,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      }
                    ],
                    "plaintext": "at://did:bla/com.atproto.lexicon.schema/app.bsky.actor.profile"
                  }
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 58,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      }
                    ],
                    "plaintext": "at://did:bla/com.atproto.lexicon.schema/app.bsky.feed.post"
                  }
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 58,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      }
                    ],
                    "plaintext": "at://did:bla/com.atproto.lexicon.schema/dev.npmx.feed.like"
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "so to publish a lexicon, you need to create a record like this."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "publishing a lexicon"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "let's walk through actually publishing some lexicon."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "step 1: create a record"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "first, choose account (did) where you want to put your lexicons. you could choose any account, it doesn't matter what its handle is."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "you can start with your personal one and maybe change it later."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 95,
                    "byteStart": 69
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "to publish a lexicon, put your lexicon file into a collection called com.atproto.lexicon.schema, with lexicon name as record key."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 40,
                    "byteStart": 24
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 88,
                    "byteStart": 62
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "for example, to publish app.example.post lexicon, put it into com.atproto.lexicon.schema collection of some account you own:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 56,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      }
                    ],
                    "plaintext": "at://did:bla/com.atproto.lexicon.schema/app.example.post"
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 35,
                    "byteStart": 30
                  },
                  "features": [
                    {
                      "uri": "https://pdsls.dev/",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 120,
                    "byteStart": 108
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "you can do this directly from pdsls via \"create record\" flow. the record is your lexicon JSON. don't forget \"lexicon\": 1 in it."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "json",
              "plaintext": "// at://did:bla/com.atproto.lexicon.schema/app.example.post\n{\n  \"lexicon\": 1,\n  \"id\": \"app.example.post\",\n  \"defs\": {\n    // ... your lexicon, as usual ...\n  }\n}"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "step 2: prove you own the domain"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "but wait!"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 58,
                    "byteStart": 54
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 103,
                    "byteStart": 100
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 134,
                    "byteStart": 121
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "anyone can publish records. how does anyone know that your lexicon is the \"true\" version, i.e. that you actually control app.example.*?"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 142,
                    "byteStart": 130
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 153,
                    "byteStart": 146
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "the answer is very similar to how you connect domain to atproto handles -- you do this with a DNS record. for example, to connect @example.app to did:foo, you had to add a DNS record like:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 49,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      }
                    ],
                    "plaintext": "_atproto.example.app.      TXT      \"did=did:foo\""
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 49,
                    "byteStart": 41
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 76,
                    "byteStart": 68
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 97,
                    "byteStart": 90
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 116,
                    "byteStart": 103
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "the mechanism is similar, but instead of _atproto you're gonna need _lexicon. to say that did:foo owns app.example.* lexicons, put:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 8,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          },
                          {
                            "$type": "pub.leaflet.richtext.facet#bold"
                          }
                        ]
                      },
                      {
                        "index": {
                          "byteEnd": 49,
                          "byteStart": 8
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      }
                    ],
                    "plaintext": "_lexicon.example.app.      TXT      \"did=did:foo\""
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 20,
                    "byteStart": 9
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "into the example.app DNS settings."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 23,
                    "byteStart": 16
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 68,
                    "byteStart": 55
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "that's all! now did:foo is the \"lexicon authority\" for app.example.*"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 5,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 103,
                    "byteStart": 6
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 121,
                    "byteStart": 104
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 165,
                    "byteStart": 123
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 189,
                    "byteStart": 165
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    },
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 190,
                    "byteStart": 189
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "note: this doesn't work transitively for subdomains. to also show you \"control\" a nested namespace like app.example.xxx.*, you'd have to create a DNS TXT record for _lexicon.xxx.example.app."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "verifying it worked"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "once you've done these two steps, your lexicons are hooked up."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 57,
                    "byteStart": 39
                  },
                  "features": [
                    {
                      "uri": "https://pdsls.dev/",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "you can just write a lexicon name into https://pdsls.dev/ and it'll resolve it via DNS and show your lexicon if it is published."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 39,
                    "byteStart": 17
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "for example take app.bsky.actor.profile."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 51,
                    "byteStart": 37
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 89,
                    "byteStart": 82
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 210,
                    "byteStart": 123
                  },
                  "features": [
                    {
                      "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.actor.profile",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "it consists of the \"namespace\" part (app.bsky.actor) and the actual lexicon name (profile). currently pdsls resolves it to at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.actor.profile. but how does pdsls find that did?"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 26,
                    "byteStart": 18
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 62,
                    "byteStart": 48
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "it checks DNS for _lexicon + reverse namespace (actor.bsky.app):"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "plaintext": "dig TXT _lexicon.actor.bsky.app"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 94,
                    "byteStart": 56
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "that DNS record currently says this lexicon is owned by \"did=did:plc:4v4y5r3lwsbtmsxhile2ljac\""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 74,
                    "byteStart": 48
                  },
                  "features": [
                    {
                      "did": "did:plc:4v4y5r3lwsbtmsxhile2ljac",
                      "$type": "pub.leaflet.richtext.facet#didMention"
                    }
                  ]
                }
              ],
              "plaintext": "what's that account by the way? apparently it's @bsky-lexicons.bsky.social. it even has a fancy profile picture."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 132,
                    "byteStart": 50
                  },
                  "features": [
                    {
                      "uri": "https://pdsls.dev/at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 160,
                    "byteStart": 134
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "you can browse all lexicons published by it here: https://pdsls.dev/at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema. com.atproto.lexicon.schema is just a collection like any other so you can browse it on pdsls just like Bluesky posts."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 134,
                    "byteStart": 118
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "if you published your lexicon, you should be able to do the same with your lexicon as well, i.e. pdsls should resolve app.example.post."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "publishing lexicons is awesome because they start showing up in pdsls and all the other tooling. you should publish your lexicons."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "updating a lexicon"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 119,
                    "byteStart": 101
                  },
                  "features": [
                    {
                      "uri": "https://atproto.com/guides/lexicon-style-guide#lexicon-evolution",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 225,
                    "byteStart": 200
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "to update a lexicon, you just update that record. keep in mind that lexicons in active use should be evolved gracefully. in short, you can add new optional fields and open union cases, but you should neither tighten nor relax existing constraints since that breaks applications."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "if your app has zero users, don't overthink it -- just update it."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "that's it?"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "yeah."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 76,
                    "byteStart": 72
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 106,
                    "byteStart": 102
                  },
                  "features": [
                    {
                      "uri": "https://lexicon.garden/help/adding-lexicons",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 115,
                    "byteStart": 111
                  },
                  "features": [
                    {
                      "uri": "https://atproto.com/guides/publishing-lexicons",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "you'll probably want to validate lexicons before you publish them which goat can help you with. check this and this guide too."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "hope this helps!"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.horizontalRule"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "optional: homework"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 57,
                    "byteStart": 31
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 91,
                    "byteStart": 88
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "find the account that owns the com.atproto.lexicon.schema lexicon. hint: you might find dig helpful again. (yes, this is delightfully meta, though also kind of useless as you'll see.)"
            }
          }
        ]
      }
    ]
  },
  "bskyPostRef": {
    "cid": "bafyreiafuvbih6mqyjfmonarfp5ki3jxwaionw7dzsxsok7pogusptepda",
    "uri": "at://did:plc:fpruhuo22xkm5o7ttr2ktxdo/app.bsky.feed.post/3mjfjstm6os2i",
    "commit": {
      "cid": "bafyreiciusqso4k2tq57xgvwghnclcryalhhy7wrqlfgzst36diigun7le",
      "rev": "3mjfjstp5ow2l"
    },
    "validationStatus": "valid"
  },
  "description": "",
  "publishedAt": "2026-04-13T18:49:22.122Z"
}