{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://real-life.org/rltp/v1/schemas/access-operation-envelope.schema.json",
  "title": "RLTP Access Operation Envelope (transcription of Access Layer draft 0.24 section 3.3)",
  "description": "TRANSCRIPTION, not the definition: the Access layer owns this shape; this schema exists so Membership Tasks and key-delivery validate payloads offline (Delivery Contract offline schema rule). Self-addressing id (oid: + base64url SHA-256 over the JCS serialization with id empty and proof omitted); every signature covers exactly that serialization. Root is OPEN (additionalProperties: true): per Access 3.3 unknown fields not named in crit MUST be IGNORED, so the schema passes them through — rejection of unknown restrictive extensions is the crit mechanism's job at the Access layer, never the schema's. Three proof mechanisms per Access 4.3. Body profiles per operation are transcribed in the if/then blocks (Access 4.5); bodies of unlisted registered operations validate as objects and are judged by materialization.",
  "type": "object",
  "required": [
    "v",
    "op",
    "group",
    "epoch",
    "policyVersion",
    "prev",
    "body",
    "id",
    "author",
    "proof"
  ],
  "additionalProperties": true,
  "properties": {
    "v": {
      "const": "rltp-access/0.24"
    },
    "op": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9-]*\\.[a-z][a-z0-9-]*$",
      "examples": [
        "group.genesis",
        "member.add",
        "member.remove",
        "member.leave",
        "epoch.rotate",
        "policy.change",
        "visibility.change",
        "history.expose",
        "lineage.repair",
        "document.attach",
        "document.detach",
        "group.dissolve",
        "service-identity.announce"
      ]
    },
    "group": {
      "$ref": "#/$defs/didKey"
    },
    "epoch": {
      "type": "integer",
      "minimum": 0
    },
    "policyVersion": {
      "type": "integer",
      "minimum": 0
    },
    "prev": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/oid"
      },
      "maxItems": 32
    },
    "body": {
      "type": "object"
    },
    "crit": {
      "type": "array",
      "items": {
        "type": "string",
        "maxLength": 64
      },
      "maxItems": 16
    },
    "id": {
      "$ref": "#/$defs/oid"
    },
    "author": {
      "$ref": "#/$defs/didKey"
    },
    "proof": {
      "type": "object",
      "required": [
        "mechanism"
      ],
      "additionalProperties": false,
      "properties": {
        "mechanism": {
          "enum": [
            "signature-set",
            "encounter-presentation",
            "composite"
          ]
        },
        "signatures": {
          "type": "array",
          "minItems": 1,
          "maxItems": 8192,
          "items": {
            "type": "object",
            "required": [
              "signer",
              "sig"
            ],
            "additionalProperties": false,
            "properties": {
              "signer": {
                "$ref": "#/$defs/didKey"
              },
              "sig": {
                "type": "string",
                "pattern": "^z[1-9A-HJ-NP-Za-km-z]+$",
                "maxLength": 128
              }
            }
          }
        },
        "credentials": {
          "type": "array",
          "minItems": 1,
          "maxItems": 8192,
          "items": {
            "type": "object",
            "description": "complete encounter credential per Encounter section 7; validated against encounter-credential.schema.json by the consumer"
          }
        }
      },
      "allOf": [
        {
          "required": [
            "signatures"
          ]
        },
        {
          "if": {
            "properties": {
              "mechanism": {
                "enum": [
                  "encounter-presentation",
                  "composite"
                ]
              }
            }
          },
          "then": {
            "required": [
              "credentials"
            ]
          }
        },
        {
          "if": {
            "properties": {
              "mechanism": {
                "const": "signature-set"
              }
            }
          },
          "then": {
            "not": {
              "required": [
                "credentials"
              ]
            }
          }
        }
      ]
    }
  },
  "allOf": [
    {
      "if": {
        "properties": {
          "op": {
            "const": "group.genesis"
          }
        }
      },
      "then": {
        "properties": {
          "epoch": {
            "const": 0
          },
          "policyVersion": {
            "const": 1
          },
          "prev": {
            "maxItems": 0
          },
          "body": {
            "type": "object",
            "required": [
              "members",
              "card",
              "policy",
              "visibility",
              "adapter",
              "contentKeyCommitment",
              "serviceIdentity"
            ],
            "additionalProperties": false,
            "properties": {
              "members": {
                "type": "array",
                "minItems": 1,
                "maxItems": 1,
                "items": {
                  "$ref": "#/$defs/didKey"
                }
              },
              "card": {
                "$ref": "https://real-life.org/rltp/v1/schemas/contact-card.schema.json",
                "description": "the founder's contact card in the displayed form of Encounter section 6 (proof under its anchor = the founder; no sentTo/boundTo); its key-agreement key receives the founder's future keyDist envelopes"
              },
              "policy": {
                "type": "object",
                "required": [
                  "policyVersion",
                  "rules"
                ]
              },
              "visibility": {
                "enum": [
                  "private",
                  "open"
                ]
              },
              "adapter": {
                "type": "string",
                "maxLength": 64
              },
              "contentKeyCommitment": {
                "$ref": "#/$defs/digest"
              },
              "serviceIdentity": {
                "$ref": "#/$defs/didKey"
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "op": {
            "const": "member.add"
          }
        }
      },
      "then": {
        "properties": {
          "body": {
            "type": "object",
            "required": [
              "subject",
              "admission"
            ],
            "additionalProperties": false,
            "properties": {
              "subject": {
                "$ref": "#/$defs/didKey"
              },
              "admission": {
                "type": "object",
                "required": [
                  "invite",
                  "accept",
                  "welcome"
                ],
                "additionalProperties": false,
                "properties": {
                  "invite": {
                    "type": "object",
                    "description": "complete membership-invite document incl. proof (Membership 3.1)"
                  },
                  "accept": {
                    "type": "object",
                    "description": "complete membership-accept document incl. proof (Membership 3.2)"
                  },
                  "welcome": {
                    "$ref": "#/$defs/digest"
                  }
                }
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "op": {
            "const": "member.remove"
          }
        }
      },
      "then": {
        "properties": {
          "body": {
            "type": "object",
            "required": [
              "subject",
              "transition"
            ],
            "additionalProperties": false,
            "properties": {
              "subject": {
                "$ref": "#/$defs/didKey"
              },
              "transition": {
                "$ref": "#/$defs/transition"
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "op": {
            "const": "epoch.rotate"
          }
        }
      },
      "then": {
        "properties": {
          "body": {
            "type": "object",
            "required": [
              "transition"
            ],
            "additionalProperties": false,
            "properties": {
              "transition": {
                "$ref": "#/$defs/transition"
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "op": {
            "const": "visibility.change"
          }
        }
      },
      "then": {
        "properties": {
          "body": {
            "type": "object",
            "required": [
              "mode",
              "transition"
            ],
            "additionalProperties": false,
            "properties": {
              "mode": {
                "enum": [
                  "private",
                  "open"
                ]
              },
              "transition": {
                "$ref": "#/$defs/transition"
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "op": {
            "const": "document.detach"
          }
        }
      },
      "then": {
        "properties": {
          "body": {
            "type": "object",
            "required": [
              "document",
              "transition"
            ],
            "additionalProperties": false,
            "properties": {
              "document": {
                "type": "string",
                "maxLength": 256
              },
              "transition": {
                "$ref": "#/$defs/transition"
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "op": {
            "const": "document.attach"
          }
        }
      },
      "then": {
        "properties": {
          "body": {
            "type": "object",
            "required": [
              "document",
              "dataPolicy"
            ],
            "additionalProperties": false,
            "properties": {
              "document": {
                "type": "string",
                "maxLength": 256
              },
              "dataPolicy": {
                "type": "object"
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "op": {
            "const": "policy.change"
          }
        }
      },
      "then": {
        "properties": {
          "body": {
            "type": "object",
            "required": [
              "policy",
              "transition"
            ],
            "additionalProperties": false,
            "properties": {
              "policy": {
                "type": "object",
                "required": [
                  "policyVersion",
                  "rules"
                ]
              },
              "transition": {
                "$ref": "#/$defs/transition",
                "description": "policy.change is an enforcement operation (Access 4.5): a constitutional change carries its epoch transition atomically, which is what removed the same-base race and the suppression cascade (Access 3.6)"
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "op": {
            "const": "history.expose"
          }
        }
      },
      "then": {
        "properties": {
          "body": {
            "type": "object",
            "required": [
              "fromEpoch",
              "keys"
            ],
            "additionalProperties": false,
            "properties": {
              "fromEpoch": {
                "type": "integer",
                "minimum": 0
              },
              "toEpoch": {
                "type": "integer",
                "minimum": 1,
                "description": "optional exclusive range end (default: the opening epoch E); fromEpoch < toEpoch <= E; keys covers exactly [fromEpoch, toEpoch), at most 4096 epochs per operation — longer histories compose from adjacent ranges (Access section 8)"
              },
              "keys": {
                "type": "array",
                "minItems": 1,
                "maxItems": 4096,
                "items": {
                  "type": "string",
                  "maxLength": 128
                }
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "op": {
            "const": "lineage.repair"
          }
        }
      },
      "then": {
        "properties": {
          "body": {
            "type": "object",
            "required": [
              "epoch",
              "opens",
              "ct"
            ],
            "additionalProperties": false,
            "properties": {
              "epoch": {
                "type": "integer",
                "minimum": 1,
                "description": "the newEpoch of the transition whose lineage entry is re-published or bridged"
              },
              "opens": {
                "type": "integer",
                "minimum": 0,
                "description": "the epoch this entry opens (< epoch)"
              },
              "ct": {
                "$ref": "#/$defs/b64ct",
                "description": "AEAD ciphertext of epoch opens's content key under epoch epoch's content key, AAD = JCS of {genesis, newEpoch: epoch, opens} — Access 7.1"
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "op": {
            "const": "service-identity.announce"
          }
        }
      },
      "then": {
        "properties": {
          "body": {
            "type": "object",
            "required": [
              "serviceIdentity"
            ],
            "additionalProperties": false,
            "properties": {
              "serviceIdentity": {
                "$ref": "#/$defs/didKey"
              }
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "op": {
            "enum": [
              "member.leave",
              "group.dissolve"
            ]
          }
        }
      },
      "then": {
        "properties": {
          "body": {
            "type": "object",
            "maxProperties": 0
          }
        }
      }
    }
  ],
  "$defs": {
    "didKey": {
      "type": "string",
      "pattern": "^did:key:z6Mk[1-9A-HJ-NP-Za-km-z]{44}$",
      "description": "did:key over an Ed25519 public key; MUST additionally decode to multicodec ed25519-pub + 32 bytes (Encounter 2.3)"
    },
    "oid": {
      "type": "string",
      "pattern": "^oid:[A-Za-z0-9_-]{43}$",
      "description": "self-addressing operation id: base64url(SHA-256) without padding over the JCS serialization with id empty and proof omitted"
    },
    "digest": {
      "type": "string",
      "pattern": "^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$",
      "maxLength": 128,
      "description": "multibase multihash (Encounter 2.3: producers emit u, verifiers accept u and z)"
    },
    "transition": {
      "type": "object",
      "required": [
        "newEpoch",
        "contentKeyCommitment",
        "keyDist"
      ],
      "additionalProperties": false,
      "properties": {
        "newEpoch": {
          "type": "integer",
          "minimum": 1
        },
        "contentKeyCommitment": {
          "$ref": "#/$defs/digest",
          "description": "multibase multihash over the RAW key bytes (Access 7.1 commitment rule), not over any JSON or base64url form"
        },
        "keyDist": {
          "type": "array",
          "maxItems": 8192,
          "uniqueItems": true,
          "items": {
            "type": "object",
            "required": [
              "recipient",
              "envelope"
            ],
            "additionalProperties": false,
            "properties": {
              "recipient": {
                "$ref": "#/$defs/didKey"
              },
              "envelope": {
                "$ref": "#/$defs/digest"
              }
            }
          },
          "description": "one entry per retained member (Access 7.1 computed retained set); recipients MUST be pairwise distinct (schema uniqueItems is object-level; the per-recipient uniqueness rule is normative text + vector)"
        },
        "lineage": {
          "type": "object",
          "required": [
            "opens",
            "ct"
          ],
          "additionalProperties": false,
          "properties": {
            "opens": {
              "type": "integer",
              "minimum": 0,
              "description": "the epoch this entry opens; default = previous epoch; earlier = recovery form across a key-void (Access 7.1)"
            },
            "ct": {
              "$ref": "#/$defs/b64ct"
            }
          },
          "description": "the EMBEDDED AEAD ciphertext of epoch opens's content key under the new epoch's content key, AAD = UTF-8 of JCS {genesis, newEpoch, opens} — no operation id in the AAD (hash-fixed-point rule, Access 1.3/7.1)"
        },
        "historyNarrow": {
          "const": true,
          "description": "explicit narrowing act; requires the history.narrow aspect rule (default strongest) satisfied by this operation's proof in addition to the operation's own rule"
        },
        "lineageForm": {
          "type": "string",
          "maxLength": 64,
          "description": "registered adapter id whose non-native P3 form (e.g. causal encryption) carries the history invariant for this transition (Access 9.4)"
        },
        "lineageVoid": {
          "const": true,
          "description": "declared unbridged state, valid ONLY on a leave-discharging epoch.rotate (Access 7.1/5.4): the discharger holds no prior epoch key; ungated, surfaced as damage, under the standing repair duty of key-holding members; the discharge-only restriction is a materialization rule, not a schema rule"
        }
      },
      "oneOf": [
        {
          "required": [
            "lineage"
          ],
          "allOf": [
            {
              "not": {
                "required": [
                  "historyNarrow"
                ]
              }
            },
            {
              "not": {
                "required": [
                  "lineageForm"
                ]
              }
            },
            {
              "not": {
                "required": [
                  "lineageVoid"
                ]
              }
            }
          ]
        },
        {
          "required": [
            "historyNarrow"
          ],
          "allOf": [
            {
              "not": {
                "required": [
                  "lineage"
                ]
              }
            },
            {
              "not": {
                "required": [
                  "lineageForm"
                ]
              }
            },
            {
              "not": {
                "required": [
                  "lineageVoid"
                ]
              }
            }
          ]
        },
        {
          "required": [
            "lineageForm"
          ],
          "allOf": [
            {
              "not": {
                "required": [
                  "lineage"
                ]
              }
            },
            {
              "not": {
                "required": [
                  "historyNarrow"
                ]
              }
            },
            {
              "not": {
                "required": [
                  "lineageVoid"
                ]
              }
            }
          ]
        },
        {
          "required": [
            "lineageVoid"
          ],
          "allOf": [
            {
              "not": {
                "required": [
                  "lineage"
                ]
              }
            },
            {
              "not": {
                "required": [
                  "historyNarrow"
                ]
              }
            },
            {
              "not": {
                "required": [
                  "lineageForm"
                ]
              }
            }
          ]
        }
      ]
    },
    "b64ct": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{80,}$",
      "maxLength": 512,
      "description": "base64url (unpadded) AEAD ciphertext incl. nonce and tag; minimum 80 chars = 32-byte key + 12-byte nonce + 16-byte tag"
    }
  }
}
