# at.rolld.play

> Published by [rolld.at](https://lexicon.garden/identity/did:plc:vjpkp7kuxs6iicilpqlibvl4)

✓ This is the authoritative definition for this NSID.

## Links

- [View on Lexicon Garden](https://lexicon.garden/lexicon/did:plc:vjpkp7kuxs6iicilpqlibvl4/at.rolld.play)
- [Documentation](https://lexicon.garden/lexicon/did:plc:vjpkp7kuxs6iicilpqlibvl4/at.rolld.play/docs)
- [Examples](https://lexicon.garden/lexicon/did:plc:vjpkp7kuxs6iicilpqlibvl4/at.rolld.play/examples)

## Definitions

### `at.rolld.play`

**Type**: `record`

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`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `game` | `ref` → `at.rolld.defs#gameRef` | Yes | The game played, referenced by BGG id. |
| `images` | `array` | No | 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. |
| `target` | `string` (uri) | Yes | 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. |
| `content` | `union` | No | 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. |
| `players` | `array` | No | Who played. Each entry may carry an atproto DID (for real handles) and/or a plain display name. |
| `location` | `string` | No | Free text: whose house, which convention, which cafe. |
| `playedAt` | `string` (datetime) | Yes | When the play session occurred (may differ from createdAt if logged later). |
| `createdAt` | `string` (datetime) | Yes | When this record was created. |
| `expansions` | `array` | No | Expansions in play, by BGG id. |
| `winnerDids` | `array` | No | DIDs of winning players, if any were recorded as identities. |
| `bskyPostRef` | `ref` → `com.atproto.repo.strongRef` | No | 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). |
| `textContent` | `string` | No | 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. |
| `winnerNames` | `array` | No | Names of winners not represented by a DID. |
| `durationMinutes` | `integer` | No | How long the session actually took. |

## Raw Schema

```json
{
  "id": "at.rolld.play",
  "defs": {
    "main": {
      "key": "tid",
      "type": "record",
      "record": {
        "type": "object",
        "required": [
          "game",
          "target",
          "playedAt",
          "createdAt"
        ],
        "properties": {
          "game": {
            "ref": "at.rolld.defs#gameRef",
            "type": "ref",
            "description": "The game played, referenced by BGG id."
          },
          "images": {
            "type": "array",
            "items": {
              "ref": "at.rolld.defs#image",
              "type": "ref"
            },
            "maxLength": 4,
            "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."
          },
          "target": {
            "type": "string",
            "format": "uri",
            "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."
          },
          "content": {
            "refs": [
              "at.markpub.markdown"
            ],
            "type": "union",
            "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."
          },
          "players": {
            "type": "array",
            "items": {
              "ref": "at.rolld.defs#player",
              "type": "ref"
            },
            "description": "Who played. Each entry may carry an atproto DID (for real handles) and/or a plain display name."
          },
          "location": {
            "type": "string",
            "maxLength": 500,
            "description": "Free text: whose house, which convention, which cafe."
          },
          "playedAt": {
            "type": "string",
            "format": "datetime",
            "description": "When the play session occurred (may differ from createdAt if logged later)."
          },
          "createdAt": {
            "type": "string",
            "format": "datetime",
            "description": "When this record was created."
          },
          "expansions": {
            "type": "array",
            "items": {
              "ref": "at.rolld.defs#gameRef",
              "type": "ref"
            },
            "description": "Expansions in play, by BGG id."
          },
          "winnerDids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "did"
            },
            "description": "DIDs of winning players, if any were recorded as identities."
          },
          "bskyPostRef": {
            "ref": "com.atproto.repo.strongRef",
            "type": "ref",
            "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)."
          },
          "textContent": {
            "type": "string",
            "maxLength": 100000,
            "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."
          },
          "winnerNames": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 320
            },
            "description": "Names of winners not represented by a DID."
          },
          "durationMinutes": {
            "type": "integer",
            "minimum": 0,
            "description": "How long the session actually took."
          }
        }
      },
      "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."
    }
  },
  "$type": "com.atproto.lexicon.schema",
  "lexicon": 1
}
```
