Skip to content

JSON Schema

Source: docs/spec/punch-messages.schema.json in the upstream repository.

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://punch.thast.se/spec/punch-messages.schema.json",
"title": "Punch WebSocket message envelope",
"description": "Machine-readable schema for every message exchanged on the Punch signalling WebSocket. Mirrors src/protocol.ts; the TypeScript and JSON Schema definitions are kept in lock-step.",
"$comment": "Version 1.0.0. The schema is normative; src/protocol.ts is the implementation reference.",
"oneOf": [
{ "$ref": "#/$defs/PeerMessage" },
{ "$ref": "#/$defs/ServerMessage" }
],
"$defs": {
"SessionState": {
"type": "string",
"enum": ["WAITING", "READY", "CONNECTED", "RELAYING", "CLOSED"]
},
"ErrorCode": {
"type": "string",
"enum": [
"SESSION_FULL",
"SESSION_EXPIRED",
"STREAM_TAKEN",
"AUTH_FAILED",
"INVALID_MESSAGE",
"SERVER_ERROR"
]
},
"SrtState": {
"type": "string",
"enum": ["connecting", "connected", "disconnected", "error"]
},
"TallyState": {
"type": "string",
"enum": ["off", "preview", "program"]
},
"HealthStatus": {
"type": "string",
"enum": ["healthy", "warning", "critical"]
},
"SessionName": {
"type": "string",
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$",
"description": "Session name; 1-64 characters, alphanumeric plus '.', '_', '-', leading character must be alphanumeric."
},
"StreamName": {
"type": "string",
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$",
"description": "Stream identifier within a session. Same character class as SessionName."
},
"Port": {
"type": "integer",
"minimum": 1024,
"maximum": 65535
},
"PeerMessage": {
"description": "Messages sent by a peer to the Punch server.",
"oneOf": [
{ "$ref": "#/$defs/RegisterMessage" },
{ "$ref": "#/$defs/HealthMessage" },
{ "$ref": "#/$defs/StatusMessage" },
{ "$ref": "#/$defs/TallyMessage" },
{ "$ref": "#/$defs/ReadyMessage" }
]
},
"RegisterMessage": {
"type": "object",
"required": ["type", "port"],
"additionalProperties": false,
"properties": {
"type": { "const": "register" },
"stream": { "$ref": "#/$defs/StreamName" },
"port": { "$ref": "#/$defs/Port" },
"role": { "type": "string", "enum": ["sender", "receiver"] },
"meta": { "type": "object", "additionalProperties": true }
}
},
"HealthMessage": {
"type": "object",
"required": ["type", "rtt", "retransmit", "bitrate"],
"additionalProperties": false,
"properties": {
"type": { "const": "health" },
"stream": { "$ref": "#/$defs/StreamName" },
"rtt": { "type": "number", "minimum": 0, "description": "Round-trip time in milliseconds." },
"retransmit": { "type": "number", "minimum": 0, "description": "Retransmit ratio (percent). 0 = no retransmits." },
"bitrate": { "type": "number", "minimum": 0, "description": "Current bitrate in bits per second." },
"dropped": { "type": "number", "minimum": 0 },
"buffer": { "type": "number", "minimum": 0 }
}
},
"StatusMessage": {
"type": "object",
"required": ["type", "srt"],
"additionalProperties": false,
"properties": {
"type": { "const": "status" },
"stream": { "$ref": "#/$defs/StreamName" },
"srt": { "$ref": "#/$defs/SrtState" }
}
},
"TallyMessage": {
"type": "object",
"required": ["type", "state"],
"additionalProperties": false,
"properties": {
"type": { "const": "tally" },
"stream": { "$ref": "#/$defs/StreamName" },
"state": { "$ref": "#/$defs/TallyState" }
}
},
"ReadyMessage": {
"type": "object",
"required": ["type"],
"additionalProperties": false,
"properties": {
"type": { "const": "ready" },
"stream": { "$ref": "#/$defs/StreamName" }
},
"description": "Peer signals it has read the connection string and will start SRT."
},
"ServerMessage": {
"description": "Messages sent by the Punch server to a peer.",
"oneOf": [
{ "$ref": "#/$defs/PeerInfoMessage" },
{ "$ref": "#/$defs/PeerMatchMessage" },
{ "$ref": "#/$defs/SessionUpdateMessage" },
{ "$ref": "#/$defs/TallyBroadcast" },
{ "$ref": "#/$defs/HealthReportMessage" },
{ "$ref": "#/$defs/ErrorMessage" },
{ "$ref": "#/$defs/SessionClosedMessage" },
{ "$ref": "#/$defs/StartMessage" }
]
},
"PeerInfoMessage": {
"type": "object",
"required": ["type", "ip", "port", "localPort", "passphrase", "latency"],
"additionalProperties": false,
"properties": {
"type": { "const": "peer" },
"stream": { "$ref": "#/$defs/StreamName" },
"ip": { "type": "string", "description": "Remote peer's reported public IP." },
"port": { "type": "integer", "minimum": 1, "maximum": 65535 },
"localPort": { "$ref": "#/$defs/Port" },
"passphrase": { "type": "string", "minLength": 10, "description": "Pre-shared passphrase for SRT AES." },
"latency": { "type": "integer", "minimum": 20, "maximum": 8000, "description": "Suggested SRT latency in milliseconds." },
"meta": { "type": "object", "additionalProperties": true }
}
},
"PeerMatchMessage": {
"type": "object",
"required": ["type", "stream", "peer_a", "peer_b", "localPort", "passphrase", "latency"],
"additionalProperties": false,
"properties": {
"type": { "const": "peer_match" },
"stream": { "$ref": "#/$defs/StreamName" },
"peer_a": { "$ref": "#/$defs/PeerEndpoint" },
"peer_b": { "$ref": "#/$defs/PeerEndpoint" },
"localPort": { "$ref": "#/$defs/Port" },
"passphrase": { "type": "string", "minLength": 10 },
"latency": { "type": "integer", "minimum": 20, "maximum": 8000 }
},
"description": "Sent only to admin observers when a stream's peers are matched."
},
"PeerEndpoint": {
"type": "object",
"required": ["ip", "port"],
"additionalProperties": false,
"properties": {
"ip": { "type": "string" },
"port": { "type": "integer", "minimum": 1, "maximum": 65535 }
}
},
"StreamStatus": {
"type": "object",
"required": ["state", "peers"],
"additionalProperties": false,
"properties": {
"state": { "type": "string" },
"peers": { "type": "integer", "minimum": 0 }
}
},
"SessionUpdateMessage": {
"type": "object",
"required": ["type", "state", "streams", "connected", "total"],
"additionalProperties": false,
"properties": {
"type": { "const": "session" },
"state": { "$ref": "#/$defs/SessionState" },
"streams": {
"type": "object",
"additionalProperties": { "$ref": "#/$defs/StreamStatus" }
},
"connected": { "type": "integer", "minimum": 0 },
"total": { "type": "integer", "minimum": 0 }
}
},
"TallyBroadcast": {
"type": "object",
"required": ["type", "stream", "state"],
"additionalProperties": false,
"properties": {
"type": { "const": "tally" },
"stream": { "$ref": "#/$defs/StreamName" },
"state": { "$ref": "#/$defs/TallyState" }
}
},
"HealthStreamReport": {
"type": "object",
"required": ["rtt", "retransmit", "bitrate", "status"],
"additionalProperties": false,
"properties": {
"rtt": { "type": "number", "minimum": 0 },
"retransmit": { "type": "number", "minimum": 0 },
"bitrate": { "type": "number", "minimum": 0 },
"status": { "$ref": "#/$defs/HealthStatus" }
}
},
"HealthReportMessage": {
"type": "object",
"required": ["type", "streams"],
"additionalProperties": false,
"properties": {
"type": { "const": "health_report" },
"streams": {
"type": "object",
"additionalProperties": { "$ref": "#/$defs/HealthStreamReport" }
}
}
},
"ErrorMessage": {
"type": "object",
"required": ["type", "code", "message"],
"additionalProperties": false,
"properties": {
"type": { "const": "error" },
"code": { "$ref": "#/$defs/ErrorCode" },
"message": { "type": "string" }
}
},
"SessionClosedMessage": {
"type": "object",
"required": ["type", "reason"],
"additionalProperties": false,
"properties": {
"type": { "const": "session_closed" },
"reason": { "type": "string", "enum": ["admin_closed", "ttl_expired", "error"] }
}
},
"StartMessage": {
"type": "object",
"required": ["type", "stream"],
"additionalProperties": false,
"properties": {
"type": { "const": "start" },
"stream": { "$ref": "#/$defs/StreamName" }
},
"description": "Coordinated 'go' signal — sent to all stream peers when both sides have signalled ready."
}
}
}