at.rolld.play
A single logged session of playing a board game. The high-volume event record. Reads ('times played', 'who played this game') are served by querying Constellation for backlinks to the 'target' URL — derived live, never stored.
Details · record, key tid
| Kind | record |
|---|---|
| Record key | tid |
| Authority | rolld.at |
| Indexed |
Checks
Validlarge-stringdefs.main.record.properties.textContentmaxLength is over 20 KB. Use a blob for content that large.
{
"$type": "com.atproto.lexicon.schema",
"defs": {
"main": {
"description": "A single logged session of playing a board game. The high-volume event record. Reads ('times played', 'who played this game') are served by querying Constellation for backlinks to the 'target' URL — derived live, never stored.",
"key": "tid",
"record": {
"properties": {
"bskyPostRef": {
"description": "Strong ref (uri + cid) to a companion app.bsky.feed.post, if the user chose to share this play on Bluesky. The post links back to the per-entry permalink (/@<handle>/play/<rkey>). Storing the ref also enables showing Bluesky replies as discussion on the entry later (the Leaflet POSSE pattern).",
"ref": "com.atproto.repo.strongRef",
"type": "ref"
},
"content": {
"description": "Optional rich note about how the play went. Open union (bring-your-own-content, like standard.site's content field) so the format can grow without a breaking change. v1 accepts markpub Markdown. Render via the $type; consumers that don't understand it fall back to textContent.",
"refs": [
"at.markpub.markdown"
],
"type": "union"
},
"createdAt": {
"description": "When this record was created.",
"format": "datetime",
"type": "string"
},
"durationMinutes": {
"description": "How long the session actually took.",
"minimum": 0,
"type": "integer"
},
"expansions": {
"description": "Expansions in play, by BGG id.",
"items": {
"ref": "at.rolld.defs#gameRef",
"type": "ref"
},
"type": "array"
},
"game": {
"description": "The game played, referenced by BGG id.",
"ref": "at.rolld.defs#gameRef",
"type": "ref"
},
"images": {
"description": "Optional photos of the play (board, table, scores). Up to 4, matching Bluesky's per-post image limit so the same blobs can be embedded in a companion app.bsky.feed.post. Each blob lives in the user's PDS and is referenced here; the photo is part of the play (not a standalone record). Powers the per-game photo gallery via the existing plays-of-this-game Constellation query (filter plays that have images). See DECISIONS D-33.",
"items": {
"ref": "at.rolld.defs#image",
"type": "ref"
},
"maxLength": 4,
"type": "array"
},
"location": {
"description": "Free text: whose house, which convention, which cafe.",
"maxLength": 500,
"type": "string"
},
"playedAt": {
"description": "When the play session occurred (may differ from createdAt if logged later).",
"format": "datetime",
"type": "string"
},
"players": {
"description": "Who played. Each entry may carry an atproto DID (for real handles) and/or a plain display name.",
"items": {
"ref": "at.rolld.defs#player",
"type": "ref"
},
"type": "array"
},
"target": {
"description": "Canonical per-game web URL (https://rolld.at/g/<bggId>) that Constellation indexes as a backlink target. FROZEN format, emitted from one shared helper shared by write and read paths. This is the linchpin of the serverless read model — backlinks to this URL ARE the play aggregation. No catalog segment: game identity is permanently BGG-keyed.",
"format": "uri",
"type": "string"
},
"textContent": {
"description": "Plain-text rendering of `content`, always populated when `content` is present. The universal fallback for any consumer that can't render the content union's $type, and the field to index for search/previews. Same role as standard.site's textContent. No grapheme limit.",
"maxLength": 100000,
"type": "string"
},
"winnerDids": {
"description": "DIDs of winning players, if any were recorded as identities.",
"items": {
"format": "did",
"type": "string"
},
"type": "array"
},
"winnerNames": {
"description": "Names of winners not represented by a DID.",
"items": {
"maxLength": 320,
"type": "string"
},
"type": "array"
}
},
"required": [
"game",
"target",
"playedAt",
"createdAt"
],
"type": "object"
},
"type": "record"
}
},
"id": "at.rolld.play",
"lexicon": 1
}