Reference · Wire Format · v1
Envelope Wire Format Specification
A HexaEight envelope is a single UTF-8 string. There are two variants. A decoder is a one-line dispatch on the prefix.
Variant B — Sessioned (supported)
"hsha:" <session-hash> "|" <ciphertext> | Token | Type | Constraints |
|---|---|---|
hsha: | literal | Exactly the 5-byte ASCII sequence 0x68 0x73 0x68 0x61 0x3A. |
session-hash | hex | SHA-256 of the session identifier, lowercase hex. Exactly 64 chars. |
ciphertext | Base64URL (RFC 4648 §5) | MQ-V4 ciphertext with embedded HMAC-SHA256 tag. No padding. |
The sender's identity is not on the wire. It is carried (cryptographically
asserted) inside the encrypted JSON payload as a SENDER field and exposed
to receivers as DecryptedEnvelope.Sender.
Variant A — Standard (preview)
<source-id> "|" <kgt> "|" <ciphertext> | Token | Type | Constraints |
|---|---|---|
source-id | ASCII | Sender's login-token prefix. Implementation-dependent length. |
kgt | decimal integer | Unix minute, floored to nearest 15. Fits in int64. |
ciphertext | Base64URL | MQ-V4 ciphertext with embedded HMAC-SHA256 tag. |
source-id token depends on the sender's login-token format. An
anonymous-sender mode using this wire shape (no inner sender claim) is planned.
For now, use Variant B.
Variant detection
if envelope.startsWith("hsha:"):
parse as Variant B (Sessioned)
else:
parse as Variant A (Standard)
No identity prefix is allowed to be exactly hsha. The platform reserves
this 4-byte prefix.
Encoding rules
- The envelope is always valid UTF-8. The routing tokens are pure ASCII; the ciphertext is Base64URL (a strict subset of ASCII).
- No leading or trailing whitespace. A decoder MUST reject envelopes with surrounding whitespace.
- No quoting, no escaping. The format is line-safe — an envelope contains no
\n, no\r, no NUL. - Length is unbounded but the practical ceiling is ~333 MB envelope (= ~250 MB plaintext × 1.49 ratio + Base64URL overhead).
Ciphertext internals
The ciphertext blob is the output of the HexaEight MQ-V4 trapdoor encryption mode. Its internal layout is:
| Offset | Length | Field |
|---|---|---|
| 0 | 2 bytes | Version tag (V3 or V39 mode flag) |
| 2 | 32 bytes | HMAC-SHA256 integrity tag |
| 34 | n bytes | Encrypted block stream |
Inside the decrypted ciphertext sits a JSON object with at minimum the fields
SENDER, RECEIVER, BODY. The Bridge surfaces
SENDER as DecryptedEnvelope.Sender — the cryptographically
verified sender identity.
Reference parser (pseudocode)
function parse(envelope: string): ParsedEnvelope {
if envelope.length < 6 throw FormatError
if envelope.startsWith("hsha:") {
let rest = envelope.slice(5)
let pipe = rest.indexOf("|")
if pipe != 64 throw FormatError
return {
kind: Sessioned,
sessionHash: rest.slice(0, 64),
ciphertext: base64UrlDecode(rest.slice(65))
}
} else {
let p1 = envelope.indexOf("|")
let p2 = envelope.indexOf("|", p1 + 1)
if p1 < 1 || p2 < p1 + 2 throw FormatError
return {
kind: Standard,
sourceId: envelope.slice(0, p1),
kgt: parseInt(envelope.slice(p1 + 1, p2)),
ciphertext: base64UrlDecode(envelope.slice(p2 + 1))
}
}
} Versioning
The format described here is wire format v1. The variant prefix serves as the discriminator — there is no separate version byte for the envelope structure. Future variants will introduce new prefixes rather than mutate the existing two. Decoders SHOULD reject unknown prefixes loudly.
Conformance for new SDK implementations
An SDK is conformant if it can:
- Encrypt a Variant B envelope and have the .NET Bridge decrypt it given the matching session.
- Decrypt a Variant B envelope produced by the .NET Bridge given the matching session.
- Surface
SENDERfrom inside the encrypted JSON as a top-level field on the decrypted result. - Reject envelopes with leading/trailing whitespace, NUL bytes, embedded newlines, or unknown variant prefixes.
A test vector pack will be published at github.com/HexaEightTeam/pqc-review.