what is a record?
a record is the atomic unit of data in ATProto - a JSON object with a type and content. examples:- a post is a record
- a like is a record
- a follow is a record
- your profile is a record
- $type: lexicon schema it follows
- data: the actual content
- metadata: creation time, creator
record structure
$type field identifies which lexicon schema this record follows.
lexicon schemas
lexicons are schema definitions that specify:- what fields are required
- what types those fields have
- validation constraints
- relationships to other records
what is a collection?
a collection groups all records of the same type together in a repository.- reverse-DNS style:
app.bsky.feed.post - scoped by authority:
app.bsky.*is Bluesky’s namespace - globally unique
common collections
each has a lexicon defining its structure.
record keys (rkeys)
within a collection, each record has a unique rkey:tid format (most common)
most rkeys use tid (timestamp identifier):- example:
3jwdwj2ctlk26 - base32 encoded timestamp + random bits
- sortable chronologically
- unique across all records
- auto-generated on creation
- chronological ordering within collection
- collision-resistant
self format (profiles)
some collections use fixed rkeys:self.
custom rkeys
some lexicons allow custom rkeys (rare):- must match:
[a-zA-Z0-9_\-\.]+ - max 512 characters
- client chooses on creation
how pdsx interacts with records
creating records
- constructs record json
- auto-adds
$typeandcreatedAt - submits to pds
- pds validates against lexicon
- pds assigns tid
- pds stores in repository mst
- returns uri + cid
reading records
- queries pds for collection
- pds traverses mst at that path
- returns records in chronological order (tid-sorted)
- pdsx displays to user
updating records
- fetches current record
- merges your changes
- submits updated record
- pds validates
- pds creates new commit
- old version preserved in history
deleting records
- instructs pds to delete
- pds removes from mst
- pds creates commit reflecting deletion
- record gone from current state
record references
records can reference other records:subject field is a strong reference:
- uri: identifies which record
- cid: content hash for verification
- detect if referenced record changed
- verify integrity
- handle deleted references
collection organization benefits
grouping by collection enables: efficient enumeration:why understanding records/collections matters for pdsx
collection names are required: you must specify collection in most commandspagination and cursors
collections can contain thousands of records. the PDS returns them in pages using cursor-based pagination: cursors are opaque tokens:- NOT timestamps or offsets
- NOT page numbers
- opaque strings that encode pagination state
- implementation details are PDS-specific
- for json/yaml output: cursor appears on stderr (doesn’t corrupt structured data)
- for compact/table output: cursor appears on stdout after results
credentials and security
where to store credentials:- environment variables (
ATPROTO_HANDLE,ATPROTO_PASSWORD) - shell rc files with
export - password managers or keychains
.envfiles (add to.gitignore!)
- go to Bluesky settings → App Passwords
- format:
xxxx-xxxx-xxxx-xxxx(not your account password) - scoped permissions, revocable
limitations
no client-side lexicon validation
pdsx currently doesn’t validate records against lexicon schemas. this means:- invalid records fail server-side
- error messages come from pds, not pdsx
- you might waste round-trips on obvious errors
no schema discovery
pdsx doesn’t know which collections exist or what fields they have. you need to know:- collection names (nsids)
- required fields
- field types