Skip to content

Lexicons

Reference
TypeEffect
recordAdds the collection to the Jetstream subscription filter and indexes records into the database. Supports record scripts
queryRegisters a GET /xrpc/{nsid} endpoint that queries indexed records
procedureRegisters a POST /xrpc/{nsid} endpoint that proxies writes to the user’s PDS
definitionsStored but does not generate routes or subscriptions

A typical setup has three lexicons working together: a record lexicon that defines the data and triggers indexing, a query lexicon that exposes a read endpoint, and a procedure lexicon that exposes a write endpoint.

Query and procedure lexicons don’t store data themselves. They operate on records stored by a record-type lexicon. The target_collection field tells HappyView which record collection to read from or write to. Without it, default queries and procedures won’t know which DB records to operate on.

For example, a query lexicon xyz.statusphere.listStatuses would set target_collection to xyz.statusphere.status to read from that record collection.

In a project, set it under [lexicons."<NSID>"] in happyview.toml. When uploading, the admin API takes it as a field.

A project lexicon backfills only when backfill = true is set under its [lexicons."<NSID>"] table, and then only once per collection: at boot, after a deploy, or after a rebuild under hpy happyview dev when [dev] backfill is on. A network lexicon declared in happyview.toml never backfills on its own.

When an uploaded record lexicon is added for the first time, HappyView automatically creates a backfill job to discover existing records. The job appears on the Backfill page like any other, with the same progress, pause, cancel, and retry controls, and the upload response returns its id as backfill_job_id.

Only the first upload starts one. Editing a lexicon afterwards re-saves it without re-crawling the network — if you want to backfill again, start one from the Backfill page. If you only want to index new records going forward, set backfill to false.

When record-type lexicons change (uploaded or deleted), HappyView reconnects to Jetstream with an updated collection filter. HappyView always includes com.atproto.lexicon.schema in the filter to track network lexicon updates.

Deleting a lexicon stops live indexing for that collection but does not remove previously indexed records from the database. If you want to start fresh, you’ll need to delete the records separately (e.g. via the admin API or directly in the database) before re-adding the lexicon and running a backfill.

If a lexicon has already been published, you don’t need to upload the JSON manually. Point HappyView at the NSID and it fetches the lexicon directly from the network. Network lexicons are kept updated automatically via the Jetstream subscription. If the publisher updates their schema, your instance will pick up the change.

Lexicons are stored as records themselves with the com.atproto.lexicon.schema NSID and the rkey set to the lexicon’s NSID. To find which repo holds a lexicon, HappyView resolves the NSID’s authority:

  1. Extract the authority from the NSID (all segments except the last). For example, xyz.statusphere.status has authority xyz.statusphere.
  2. Reverse the authority segments to form a domain: statusphere.xyz.
  3. Look up the DNS TXT record at _lexicon.{domain} (e.g. _lexicon.statusphere.xyz).
  4. Parse the TXT record for a did=<DID> value.
  5. Resolve the DID to a PDS endpoint via the PLC directory.

Once the authority DID and PDS endpoint are known, HappyView calls com.atproto.repo.getRecord with:

  • repo = the authority DID
  • collection = com.atproto.lexicon.schema
  • rkey = the NSID

The value field of the response is the raw lexicon JSON.

HappyView’s Jetstream subscription always includes the com.atproto.lexicon.schema collection, so it receives real-time events whenever a lexicon schema record is created, updated, or deleted on the network. When an event arrives, HappyView checks whether the record’s DID and rkey (the NSID) match any tracked network lexicon:

  • create/update: The new schema is parsed and upserted into the happyview_lexicons table and the in-memory registry. If it’s a record-type lexicon, Jetstream collection filters are updated to include the new collection.
  • delete: The lexicon is removed from the happyview_lexicons table and registry, and collection filters are updated accordingly.

On every startup, HappyView re-fetches all network lexicons from their respective PDSes. This ensures consistency even if events were missed while offline. Failures are logged as warnings but don’t block startup.

When a client calls /xrpc/{method} and HappyView has a local lexicon for that NSID, the request is handled by the lexicon’s script (or HappyView’s default behavior if no script is attached). Otherwise the request falls to the XRPC proxy, which System > XRPC Proxy in the dashboard configures with two settings (XRPC proxy):

  • Mode decides whether a method may be proxied at all: open (the default), allowlist, blocklist or disabled. A method the mode refuses gets 403 with NSID not allowed by proxy policy.
  • Routing decides where it goes: authority (the default) or serviceproxy, which forwards to the caller’s own PDS as the caller.

With authority routing, HappyView finds the method’s home authority using the same DNS-based authority resolution described above:

  1. Extract the authority from the NSID (all segments except the last). com.example.foo.getBar → authority com.example.foo.
  2. Reverse it to form a domain: foo.example.com.
  3. Look up the _lexicon.foo.example.com TXT record for the authority’s DID.
  4. Resolve that DID to a PDS endpoint via the PLC directory.
  5. Proxy the request to {pds_endpoint}/xrpc/{method}.

A few things to note:

  • HappyView does not proxy to the reversed hostname directly. foo.example.com is only the DNS host for the TXT record — the actual XRPC request goes to whatever PDS endpoint the authority DID resolves to.
  • authority routing forwards the request anonymously, with no credentials of the caller’s, for queries and procedures alike. To act as the caller, for example to call com.atproto.repo.createRecord on their PDS, switch routing to serviceproxy.
  • If authority resolution fails — no TXT record, unresolvable DID, or the target PDS doesn’t support the method — the client gets an error back. HappyView does not fall back to the other routing.
  • A query or procedure lexicon you track as a network lexicon is served locally, like one you uploaded.

In short: to serve an XRPC method on your instance, it needs a lexicon there. Otherwise the proxy settings decide what happens to the request.