{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "urn:orbiplex:schema:peer-handshake:v1",
  "title": "PeerHandshake v1",
  "description": "Machine-readable schema for signed peer session establishment over the Node networking baseline. In v1 the signed surface is the deterministic CBOR image of the semantic handshake payload excluding only the `signature` field itself. Framing-only transport metadata may remain outside that signed payload. This contract remains node-scoped in the MVP baseline: it authenticates infrastructure and establishes an encrypted node-to-node session, while participant authentication happens later at the application-message layer over that established channel.",
  "type": "object",
  "additionalProperties": true,
  "x-dia-workflow": "project",
  "x-dia-status": "draft",
  "x-dia-basis": [
    "doc/project/40-proposals/014-node-transport-and-discovery-mvp.md",
    "doc/project/50-requirements/requirements-006-node-networking-mvp.md",
    "doc/project/60-solutions/000-node/000-node.md"
  ],
  "required": [
    "schema/v",
    "handshake/id",
    "handshake/mode",
    "ts",
    "sender/node-id",
    "key/alg",
    "key/public",
    "session/pub",
    "nonce",
    "signature"
  ],
  "properties": {
    "schema/v": {
      "const": 1,
      "description": "Schema version."
    },
    "handshake/id": {
      "type": "string",
      "minLength": 1,
      "description": "Stable identifier of this handshake attempt."
    },
    "handshake/mode": {
      "type": "string",
      "enum": [
        "hello",
        "ack"
      ],
      "description": "Explicit discriminator for the symmetric handshake family. In v1 both `hello` and `ack` remain artifacts of the same schema family, while `ack/of-handshake-id` provides the cryptographic response binding."
    },
    "ack/of-handshake-id": {
      "type": "string",
      "minLength": 1,
      "description": "Reference to the original handshake when `handshake/mode = ack`. This MUST be part of the signed payload."
    },
    "ts": {
      "type": "string",
      "format": "date-time",
      "description": "Timestamp of the handshake artifact."
    },
    "sender/node-id": {
      "type": "string",
      "minLength": 1,
      "pattern": "^node:did:key:z[1-9A-HJ-NP-Za-km-z]+$",
      "description": "Stable infrastructure identity of the Node sending this handshake artifact. In v1 this MUST use the canonical `node:did:key:z...` format, and it MUST NOT be replaced with participant-scoped identity material."
    },
    "recipient/node-id": {
      "type": "string",
      "minLength": 1,
      "pattern": "^node:did:key:z[1-9A-HJ-NP-Za-km-z]+$",
      "description": "Optional directed handshake recipient. If present, it is part of the signed payload."
    },
    "key/alg": {
      "type": "string",
      "enum": [
        "ed25519"
      ],
      "description": "Algorithm of the sender key."
    },
    "key/public": {
      "type": "string",
      "minLength": 1,
      "pattern": "^z[1-9A-HJ-NP-Za-km-z]+$",
      "description": "Canonical did:key fingerprint payload corresponding to `sender/node-id`."
    },
    "session/pub": {
      "type": "string",
      "minLength": 43,
      "maxLength": 43,
      "pattern": "^[A-Za-z0-9_-]{43}$",
      "description": "Fresh per-handshake X25519 public key encoded as raw unpadded base64url for the 32-byte public key. This field is ephemeral session material, not identity material, so it MUST NOT be wrapped as `did:key` or prefixed with multicodec bytes."
    },
    "protocol/version": {
      "type": "string",
      "minLength": 1,
      "description": "Protocol version for interpreting this handshake family. In v1 this is primarily interpretation context and domain-separation input, not mutable handshake business data."
    },
    "transport/profile": {
      "type": "string",
      "enum": [
        "wss"
      ],
      "description": "Framing-level or per-hop transport profile of the current session. This is not part of the signed semantic payload unless asserted as a capability claim."
    },
    "session/intent": {
      "type": "string",
      "enum": [
        "bootstrap",
        "peer-connect",
        "reconnect"
      ],
      "description": "High-level intent of this session attempt."
    },
    "probe/challenge": {
      "type": "string",
      "minLength": 1,
      "description": "Optional fresh probe challenge. If present, it is part of the signed semantic payload and is used by Seed Directory or trusted peers to prove that the endpoint answered this specific reachability probe."
    },
    "probe/purpose": {
      "type": "string",
      "enum": [
        "seed-directory-address-verification"
      ],
      "description": "Purpose of `probe/challenge`. In v1 the supported purpose is Seed Directory address verification for `node-address-attestation.v1` evidence."
    },
    "nonce": {
      "type": "string",
      "minLength": 1,
      "description": "Fresh nonce used to bind this session attempt and reduce replay risk. v1 replay protection assumes a roughly `+-30s` clock-skew window and per-peer nonce retention of about `120s`."
    },
    "capabilities/offered": {
      "type": "array",
      "uniqueItems": true,
      "items": {
        "type": "string",
        "pattern": "^(core|role|plugin)/[a-z0-9-]+(?:/[a-z0-9-]+)*$"
      },
      "description": "Optional capability claims offered as part of the signed handshake payload."
    },
    "terms/negotiated": {
      "type": "object",
      "additionalProperties": true,
      "description": "Optional negotiated handshake terms carried inside the signed payload."
    },
    "signature": {
      "$ref": "#/$defs/signature"
    },
    "policy_annotations": {
      "type": "object",
      "additionalProperties": true,
      "description": "Optional local or federation-local annotations that do not change core session semantics."
    }
  },
  "$defs": {
    "signature": {
      "type": "object",
      "additionalProperties": true,
      "required": [
        "alg",
        "value"
      ],
      "properties": {
        "alg": {
          "type": "string",
          "enum": [
            "ed25519"
          ]
        },
        "value": {
          "type": "string",
          "minLength": 1
        }
      }
    }
  },
  "allOf": [
    {
      "if": {
        "properties": {
          "handshake/mode": {
            "const": "ack"
          }
        },
        "required": [
          "handshake/mode"
        ]
      },
      "then": {
        "required": [
          "ack/of-handshake-id"
        ]
      }
    },
    {
      "if": {
        "required": [
          "probe/challenge"
        ]
      },
      "then": {
        "required": [
          "probe/purpose"
        ]
      }
    },
    {
      "if": {
        "required": [
          "probe/purpose"
        ]
      },
      "then": {
        "required": [
          "probe/challenge"
        ]
      }
    }
  ]
}
