PSSP v1 protocol
PSSP transports opaque byte payloads over topic-based publish/subscribe connections. Application clients decide payload meaning and persistence; the broker only authenticates, authorizes, buffers, and distributes data.
Endpoints and QoS
| Traffic | Port | Transport | Purpose |
|---|---|---|---|
| QoS 1 | 6688 | TCP; TLS 1.3 in production | Control records and data requiring broker acceptance, acknowledgements, fan-out, and replay. |
| QoS 0 | 6688 | UDP | Best-effort heartbeat or loss-tolerant data after associating the UDP endpoint with a live TCP session. |
TCP and UDP use the same numeric port but independent sockets. In production both flow through an authenticated session; UDP is associated using the temporary token supplied by the broker.
Record framing
Every PSSP record has a fixed header followed by a UTF-8 JSON control header and an opaque byte payload. Numeric lengths use big-endian ordering. TCP receivers must buffer fragments until a whole record is available.
0..1 magic: "PS" for TCP, "PU" for UDP
2 protocol version: 1
3 record type
4 flags: bit 0 = AES-256-GCM encrypted
5..6 JSON header length, unsigned 16-bit (default maximum: 1,024)
7..10 opaque payload length, unsigned 32-bit (default maximum: 65,536)
11.. UTF-8 JSON header
opaque payload bytes
GCM tag: 16 bytes, only when encrypted
For encrypted records, the fixed header and JSON header are AES-GCM additional authenticated data. The JSON header therefore cannot be modified without detection.
| Encrypted field | Rule |
|---|---|
seq | Required positive integer in every encrypted header. TCP accepts strictly increasing values. UDP keeps a 64-sequence replay window to allow ordinary datagram reordering while rejecting duplicates and old packets. |
| Session key | Fresh 32-byte AES key for each authenticated production connection. It exists in RAM only and is removed when the connection ends. |
Session lifecycle
- Connect. Open TCP on port 6688. Negotiate TLS 1.3 first only when TLS is configured.
- Negotiate. Send
HELLO; receiveHELLO_ACKcontaining protocol limits, a connection ID, and TLS/encryption policy. - Set up protection. With TLS off and PSSP AES on, verify the pinned Broker X25519 key from
HELLO_ACK, then sendKEY_REQUEST. Both peers derive a fresh AES key through X25519 + HKDF before authentication; no symmetric key is sent. With TLS + AES, authenticate first andKEYdelivers the fresh AES key inside TLS. - Authenticate. Send
AUTHwithclientId,username, andpassword. AES-only mode encrypts this record. The same valid client ID may maintain multiple concurrent sessions. - Use PSSP. Subscribe, publish QoS 1 TCP data, and optionally bind UDP for QoS 0. Production records after key setup use AES-GCM.
- Close or reconnect. The broker removes the connection's key and UDP association. A subscriber can reconnect with the same
workerIDand requestresumewhile retained data remains available.
Record types
| Record | Direction | Purpose |
|---|---|---|
HELLO / HELLO_ACK | Client ↔ broker | Version and policy negotiation. |
AUTH / AUTH_OK / AUTH_ERROR | Client ↔ broker | Credential login and per-connection identity. |
KEY_REQUEST / KEY | Client ↔ broker | TLS mode establishes AES inside TLS. AES-only mode uses a pinned X25519 Broker key plus HKDF; KEY carries no symmetric key. |
PING / PONG | Both | TCP or UDP liveness test. |
SUBSCRIBE / SUBACK | Client ↔ broker | Create a topic-filter subscription with latest, earliest, or resume starting position. |
UNSUBSCRIBE / UNSUBACK | Client ↔ broker | End one subscription. |
PUBLISH / PUBACK | Client ↔ broker | QoS 1 broker acceptance and duplicate-retry detection. |
MESSAGE / MSGACK | Broker ↔ client | Subscriber delivery and acknowledgement after application handoff. |
GAP | Broker → client | Indicates requested data was evicted from finite topic memory. |
UDP_BIND / UDP_BIND_ACK | Client ↔ broker | Associate a UDP source endpoint with the current TCP session. |
CLOSE | Client → broker | Normal session close request. |
Topics, filters, and authorization
Concrete publish topics
Slash-delimited paths, maximum 256 bytes. They cannot contain + or #, empty path elements, dot segments, or control characters.
devices/a1/audio/chunks
control/room-7/startSubscription filters
+ matches one segment. A final # matches all remaining segments.
devices/+/audio/#
control/#The broker evaluates the authenticated credential's publish and subscribe ACL filters for every request. Access is denied unless explicitly permitted.
QoS, buffering, and recovery
QoS 1 — TCP
- The publisher gives every retryable message a
publisherSessionIdand monotonically increasingpublisherSequence. - The broker checks identity, ACL, and limits; duplicate publisher identifiers do not create a second buffer entry.
- The broker appends the opaque data to the bounded ring for the topic, returns
PUBACK, and fans outMESSAGErecords to matching subscriptions. - A subscriber sends
MSGACKonly after its own durable application handoff. It can later reconnect using the sameworkerIDand requestresume.
QoS 0 — UDP
UDP is best effort. It has no PUBACK, subscriber acknowledgement, retry, buffering, or replay. Use it for heartbeats and data that the application can safely lose. Production UDP uses AES-GCM anti-replay protection.
Each topic ring has limits for bytes, message count, and age. If the broker cannot replay a requested sequence because it was evicted, it sends GAP { earliestSequence, latestSequence }. The receiving application must recover from its own archive if it needs that data.
Security modes
| Mode | Required flags | Record protection | Appropriate location |
|---|---|---|---|
| TLS + AES | tls.enabled = trueencryption.enabled = true | TLS 1.3, then per-session AES-256-GCM PSSP records. | Production networks. |
| AES only | tls.enabled = falseencryption.enabled = truepinned X25519 Broker public key | Ephemeral X25519 + HKDF derives a new AES key before encrypted authentication. No symmetric key crosses TCP. | Certificate-free protected deployments with securely provisioned client pins. |
| Plain | tls.enabled = falseencryption.enabled = falseallow_insecure_tcp_udp = true | No TLS or AES. A short-lived RAM-only UDP token still binds UDP to the authenticated TCP session. | Only a trusted/private network. |