{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://openspatials.com/developers/spec/0.5/signed-avatar-record.schema.json",
  "title": "OpenSpatials avatar record, signed form (core version 0.5): a Universal Manifest v0.4 document",
  "description": "Spec: src/spec/core/AVATAR-RECORD.md section 1.2. The avatar record carried as a Universal Manifest v0.4 document (Base, Tier 0, Signature Profile A): \"openspatials\": \"0.5\" beside \"manifestVersion\": \"0.4\"; one openspatials:Character facet (name, color, looks, ownership, requires) and one openspatials:Item facet per item, optional consents (the signing tool writes a show consent for the character and each item, purpose openspatials:game-crossing) and at most one openspatials:PlainCopy pointer. The character's fields follow the plain form's rules (character.schema.json). The signature is optional here: an unsigned record arrives unverified. Reason codes through x-openspatials-reason, as in character.schema.json: unknown-version > unsafe-file > too-large > invalid-file. Checked by code, not by this schema: the 64 KiB limit and duplicate JSON keys (on the bytes); exactly one character facet, whose @id, ref and entity @id equal subject; item facet @id equal to <subject>#item-<itemId> and no itemId twice; at most one plain copy pointer; the reading game's reads listing \"0.5\"; the signature, the key and freshness (src/ownership/index.js verifyAvatarRecord).",
  "type": "object",
  "allOf": [
    {
      "$ref": "#/$defs/noCode"
    },
    {
      "required": [
        "openspatials"
      ]
    }
  ],
  "required": [
    "@context",
    "@id",
    "@type",
    "manifestVersion",
    "subject",
    "issuedAt",
    "expiresAt",
    "facets"
  ],
  "properties": {
    "@context": {
      "description": "The pinned Universal Manifest v0.4 context, as a string (or an array holding it) (OWNERSHIP-AND-UM.md section 2).",
      "$ref": "https://universalmanifest.net/ns/universal-manifest/v0.4/schema.json#/properties/@context"
    },
    "@id": {
      "description": "A new urn:uuid at each signing.",
      "type": "string",
      "minLength": 1,
      "maxLength": 2048
    },
    "@type": {
      "$ref": "https://universalmanifest.net/ns/universal-manifest/v0.4/schema.json#/properties/@type"
    },
    "manifestVersion": {
      "const": "0.4"
    },
    "openspatials": {
      "description": "The core version of the signed form: \"0.5\". Any other string is unknown-version; a document with no version field is invalid-file.",
      "type": "string",
      "allOf": [
        {
          "x-openspatials-reason": "unknown-version",
          "const": "0.5"
        }
      ]
    },
    "subject": {
      "$ref": "#/$defs/absoluteUrl"
    },
    "issuedAt": {
      "type": "string",
      "format": "date-time"
    },
    "expiresAt": {
      "type": "string",
      "format": "date-time"
    },
    "consents": {
      "type": "array",
      "maxItems": 256,
      "items": {
        "$ref": "https://universalmanifest.net/ns/universal-manifest/v0.4/schema.json#/$defs/consent"
      }
    },
    "signature": {
      "$ref": "#/$defs/signature"
    },
    "facets": {
      "type": "array",
      "minItems": 1,
      "contains": {
        "properties": {
          "entity": {
            "type": "object",
            "required": [
              "@type"
            ],
            "properties": {
              "@type": {
                "anyOf": [
                  {
                    "enum": [
                      "openspatials:Character",
                      "openspatials:Character"
                    ]
                  },
                  {
                    "type": "array",
                    "contains": {
                      "enum": [
                        "openspatials:Character",
                        "openspatials:Character"
                      ]
                    }
                  }
                ]
              }
            }
          }
        },
        "required": [
          "entity"
        ]
      },
      "items": {
        "allOf": [
          {
            "$ref": "https://universalmanifest.net/ns/universal-manifest/v0.4/schema.json#/$defs/facet"
          },
          {
            "if": {
              "properties": {
                "entity": {
                  "type": "object",
                  "required": [
                    "@type"
                  ],
                  "properties": {
                    "@type": {
                      "anyOf": [
                        {
                          "enum": [
                            "openspatials:Character",
                            "openspatials:Character"
                          ]
                        },
                        {
                          "type": "array",
                          "contains": {
                            "enum": [
                              "openspatials:Character",
                              "openspatials:Character"
                            ]
                          }
                        }
                      ]
                    }
                  }
                }
              },
              "required": [
                "entity"
              ]
            },
            "then": {
              "$ref": "#/$defs/characterFacet"
            }
          },
          {
            "if": {
              "properties": {
                "entity": {
                  "type": "object",
                  "required": [
                    "@type"
                  ],
                  "properties": {
                    "@type": {
                      "anyOf": [
                        {
                          "enum": [
                            "openspatials:Item",
                            "openspatials:Item"
                          ]
                        },
                        {
                          "type": "array",
                          "contains": {
                            "enum": [
                              "openspatials:Item",
                              "openspatials:Item"
                            ]
                          }
                        }
                      ]
                    }
                  }
                }
              },
              "required": [
                "entity"
              ]
            },
            "then": {
              "$ref": "#/$defs/itemFacet"
            }
          }
        ]
      }
    },
    "pointers": {
      "description": "Universal Manifest pointers. One of @type openspatials:PlainCopy (or the old openspatials:PlainCopy) names the record's plain copy (AVATAR-RECORD.md section 1.3); its target may be relative to the record. Other pointer types are ignored.",
      "type": "array",
      "items": {
        "allOf": [
          {
            "$ref": "https://universalmanifest.net/ns/universal-manifest/v0.4/schema.json#/$defs/pointer"
          },
          {
            "if": {
              "required": [
                "@type"
              ],
              "properties": {
                "@type": {
                  "anyOf": [
                    {
                      "enum": [
                        "openspatials:PlainCopy",
                        "openspatials:PlainCopy"
                      ]
                    },
                    {
                      "type": "array",
                      "contains": {
                        "enum": [
                          "openspatials:PlainCopy",
                          "openspatials:PlainCopy"
                        ]
                      }
                    }
                  ]
                }
              }
            },
            "then": {
              "required": [
                "target"
              ],
              "properties": {
                "target": {
                  "$ref": "https://openspatials.com/developers/spec/0.5/character.schema.json#/$defs/webUrl"
                }
              }
            }
          }
        ]
      }
    }
  },
  "$defs": {
    "absoluteUrl": {
      "description": "An absolute http(s) address with no #fragment. The address rules (https to a public host, http only on loopback) are checked at run time (checkUrl).",
      "type": "string",
      "maxLength": 2048,
      "pattern": "^https?://[^\\s#]+$"
    },
    "signature": {
      "description": "Signature Profile A only (OWNERSHIP-AND-UM.md section 2): Ed25519 over the RFC 8785 canonical JSON of the document without its signature. keyRef: <address of the issuing game's profile (openspatials-world.json, or openspatials-world.json)>#<key id>, or a did:key. A document with no signature is read and arrives or loads unverified; a signature of another profile is invalid-file. The signature itself, the key and freshness are checked by code (src/ownership/index.js), not by this schema.",
      "type": "object",
      "required": [
        "algorithm",
        "canonicalization",
        "keyRef",
        "value"
      ],
      "properties": {
        "algorithm": {
          "const": "Ed25519"
        },
        "canonicalization": {
          "const": "JCS-RFC8785"
        },
        "keyRef": {
          "type": "string",
          "minLength": 1,
          "maxLength": 2200,
          "pattern": "^\\S+$"
        },
        "publicKeySpkiB64": {
          "type": "string",
          "minLength": 1,
          "maxLength": 256,
          "pattern": "^[A-Za-z0-9+/_-]+={0,2}$"
        },
        "created": {
          "type": "string",
          "format": "date-time"
        },
        "value": {
          "type": "string",
          "minLength": 1,
          "maxLength": 256,
          "pattern": "^[A-Za-z0-9_-]+={0,2}$"
        }
      }
    },
    "noCode": {
      "description": "04 section 1, closed list, at any depth: a key named script, scripts, eval or javascript; a key that is 'on' followed by a capital letter; a string value starting with javascript:, vbscript: or data:text/html (any case, after leading white space).",
      "x-openspatials-reason": "unsafe-file",
      "propertyNames": {
        "not": {
          "pattern": "^(?:script|scripts|eval|javascript|on[A-Z].*)$"
        }
      },
      "additionalProperties": {
        "$ref": "#/$defs/noCode"
      },
      "items": {
        "$ref": "#/$defs/noCode"
      },
      "not": {
        "type": "string",
        "pattern": "^\\s*(?:[Jj][Aa][Vv][Aa][Ss][Cc][Rr][Ii][Pp][Tt]:|[Vv][Bb][Ss][Cc][Rr][Ii][Pp][Tt]:|[Dd][Aa][Tt][Aa]:[Tt][Ee][Xx][Tt]/[Hh][Tt][Mm][Ll])"
      }
    },
    "characterFacet": {
      "description": "The openspatials:Character facet (or the old openspatials:Character): @id, ref and entity @id are the record's subject (equality checked by code).",
      "type": "object",
      "required": [
        "@id",
        "entity"
      ],
      "properties": {
        "@id": {
          "$ref": "#/$defs/absoluteUrl"
        },
        "ref": {
          "$ref": "#/$defs/absoluteUrl"
        },
        "entity": {
          "type": "object",
          "required": [
            "@id",
            "@type",
            "name"
          ],
          "properties": {
            "@id": {
              "$ref": "#/$defs/absoluteUrl"
            },
            "name": {
              "$ref": "https://openspatials.com/developers/spec/0.5/character.schema.json#/properties/name"
            },
            "color": {
              "$ref": "https://openspatials.com/developers/spec/0.5/character.schema.json#/properties/color"
            },
            "looks": {
              "$ref": "https://openspatials.com/developers/spec/0.5/character.schema.json#/properties/looks"
            },
            "ownership": {
              "description": "The character's ownership record: one address (OWNERSHIP-AND-UM.md section 5.1 allows a list).",
              "anyOf": [
                {
                  "$ref": "https://openspatials.com/developers/spec/0.5/character.schema.json#/$defs/webUrl"
                },
                {
                  "type": "array",
                  "minItems": 1,
                  "maxItems": 8,
                  "items": {
                    "$ref": "https://openspatials.com/developers/spec/0.5/character.schema.json#/$defs/webUrl"
                  }
                }
              ]
            },
            "requires": {
              "description": "Feature identifiers the receiving game must understand (VERSIONS-AND-NEGOTIATION.md section 3).",
              "type": "array",
              "maxItems": 32,
              "items": {
                "type": "string",
                "pattern": "^[a-z0-9][a-z0-9._:-]{0,63}$"
              }
            }
          }
        }
      }
    },
    "itemFacet": {
      "description": "An openspatials:Item facet (or the old openspatials:Item): @id is <subject>#item-<itemId> (equality checked by code). The entity carries the plain item's fields, with itemId for id.",
      "type": "object",
      "required": [
        "@id",
        "entity"
      ],
      "properties": {
        "@id": {
          "type": "string",
          "maxLength": 2200,
          "pattern": "^https?://[^\\s#]+#item-[A-Za-z0-9._-]{1,64}$"
        },
        "entity": {
          "type": "object",
          "required": [
            "@id",
            "@type",
            "itemId",
            "name",
            "kind"
          ],
          "anyOf": [
            {
              "required": [
                "model"
              ]
            },
            {
              "required": [
                "icon"
              ]
            }
          ],
          "properties": {
            "itemId": {
              "$ref": "https://openspatials.com/developers/spec/0.5/character.schema.json#/$defs/item/properties/id"
            },
            "id": {
              "$ref": "https://openspatials.com/developers/spec/0.5/character.schema.json#/$defs/item/properties/id"
            },
            "name": {
              "$ref": "https://openspatials.com/developers/spec/0.5/character.schema.json#/$defs/item/properties/name"
            },
            "kind": {
              "$ref": "https://openspatials.com/developers/spec/0.5/character.schema.json#/$defs/item/properties/kind"
            },
            "slot": {
              "$ref": "https://openspatials.com/developers/spec/0.5/character.schema.json#/$defs/item/properties/slot"
            },
            "model": {
              "$ref": "https://openspatials.com/developers/spec/0.5/character.schema.json#/$defs/itemModel"
            },
            "icon": {
              "$ref": "https://openspatials.com/developers/spec/0.5/character.schema.json#/$defs/icon"
            }
          }
        }
      }
    }
  }
}
