4.7 KiB
Hermes Pairing Protocol v1 (hermes-pair)
1. URI Format
The pairing URI is used to transmit connection information securely to the Hermes client, typically via QR codes or deep links.
- Canonical Format:
hermes://pair?data=<base64url_payload> - Tolerated Formats: Clients MUST also tolerate single slashes (e.g.,
hermes:/pair?data=...) to accommodate aggressive URL normalization applied by certain OS deep link handlers or third-party QR scanners.
2. Payload Fields & Types
The data query parameter contains a Base64 encoded JSON string representing the payload.
| Field | Type | Description |
|---|---|---|
v |
u32 |
Protocol version. Must be exactly 1. |
type |
string |
Payload type. Must be exactly "hermes-pair". |
host_id |
string |
A valid UUID (canonical string representation, 8-4-4-4-12) identifying the host server. |
name |
string |
Human-readable string containing the name of the server/host. |
host |
string |
The IP address (IPv4, or IPv6 enclosed in brackets e.g. [::1]) or a valid hostname of the server. |
port |
u16 |
TCP port number (1-65535). Cannot be 0. |
scheme |
string |
The HTTP scheme to use. Must be either "http" or "https". |
expires_at |
u64 |
Expiration time as a Unix timestamp in seconds since epoch. |
nonce |
string |
16 bytes of random data, encoded as a Base64URL string (unpadded). Ensures QR code uniqueness and helps prevent replay attacks. |
3. Decisions on the 4 Divergences
-
UUID Version (host_id): The
host_idMUST be validated as a legitimate UUID according to RFC 4122. Any valid UUID version (such as v4, v7, etc.) is accepted, provided it parses correctly as a standard 128-bit UUID. Justification: Limiting strict validation to only v4 limits future upgrades (e.g., migrating to v7 for time-sorting), while enforcing any valid UUID check provides the required uniqueness and collision resistance. -
Base64 Encoding (data): The canonical encoding for the
dataparameter (and thenonceinside) is URL-safe Base64 without padding (RFC 4648 Section 5). Tolerance: Parsers MUST be robust and accept URL-safe Base64 with padding, as well as standard Base64 encoding. Justification: This maximizes interoperability with various generation libraries and ecosystem tools that may apply padding or default standard Base64 despite requests to be URL-safe. -
Data Extraction (URL query parsing): Parsers MUST use robust, standard URL query parameter extraction mechanisms (e.g., standard
URLDecoderin Java/Kotlin, orquery_pairs()in Rust). They MUST handle extraneous query parameters gracefully by ignoring them, and strictly tolerate anomalous empty segments such as double ampersands&&. -
URI Scheme: The
hermes://pairschema/host structure is strictly canonical.
4. Validation Rules & Error Classification
During parsing and validation, clients MUST return distinct typed errors corresponding to the following failure modes to allow for proper UX messaging or fallback behavior:
| Error Code | Description |
|---|---|
InvalidUriFormat |
The URI does not match hermes://pair or hermes:/pair. |
MissingDataParam |
The data query parameter is missing from the URI. |
Base64DecodeError |
The data string cannot be decoded as Base64 (corrupt). |
JsonSyntaxError |
The decoded string is not valid JSON. |
InvalidPayloadType |
The type field is missing or not "hermes-pair". |
UnsupportedProtocolVersion |
The v field is not 1. |
InvalidHostId |
The host_id is missing or not a valid UUID string. |
InvalidPort |
The port is missing, 0, or out of valid u16 range. |
InvalidScheme |
The scheme is missing, or not "http" or "https". |
InvalidNonce |
The nonce is missing, incorrectly sized, or malformed. |
ExpiredPayload |
The expires_at timestamp is historically in the past. |
ClockSkewError |
The expires_at timestamp is excessively in the future or rejected due to extreme local clock skew anomalies. |
5. Nonce and Replay Prevention
The nonce field serves to ensure that every generated QR code payload string is unique, preventing predictable QR patterns.
To prevent replay attacks (where an intercepted QR code is reused maliciously by a third party), clients and servers SHOULD implement a single-use tracking mechanism:
- Client-side cache: Clients should record the
nonceof successfully parsed and used pairing payloads. - Rejection: If a payload is presented containing an already-seen
nonce, it must be rejected. - Cache Eviction: The cache entries only need to be retained until the
expires_attimestamp of the payload, after which the basicExpiredPayloadcheck will naturally reject it, saving memory.