Internet-Draft HCTP August 2026
Ruvalcaba Expires 12 February 2027 [Page]
Workgroup:
Individual Submission
Internet-Draft:
draft-ruvalcaba-hctp-00
Published:
Intended Status:
Standards Track
Expires:
Author:
C.X. Ruvalcaba
Saluca LLC

The Hash-Chain Context Transfer Protocol (HCTP)

Abstract

The Hash-Chain Context Transfer Protocol (HCTP) is a payload format and synchronization protocol for incrementally transferring an ordered, append-only sequence of conversational context between two endpoints over a bandwidth-constrained channel. Acknowledged history is represented by a single fixed-size rolling hash commitment (the "static root"); only not-yet-acknowledged context blocks (the "dynamic window") are transmitted. As a result, the per-message wire overhead attributable to history is constant and independent of the total number of previously acknowledged turns. HCTP is transport-agnostic and carries no confidentiality or peer authentication of its own; it is intended to run over a secure transport. This document specifies the HCTP data model, wire format, rolling-root computation, and synchronization state machine.

Status of This Memo

This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.

Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.

Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."

This Internet-Draft will expire on 12 February 2027.

Table of Contents

1. Introduction

Conversational and agentic systems increasingly need to share an evolving record of interaction ("context") between components: between a client and a service, between cooperating agents, or between a live process and a resumable checkpoint. The naive approach retransmits the entire conversation on every exchange, so the wire cost grows without bound as the conversation lengthens. This is wasteful on any channel and prohibitive on a constrained one.

HCTP addresses this by partitioning the context into two parts:

Each context block commits to the raw text of one turn via a content hash and additionally carries a short semantic summary, so that an endpoint may operate on the summary as context without ever receiving the raw text, while retaining the ability to verify the block against the raw text if it later obtains it (Section 8).

HCTP provides ordering and tamper-evidence through a hash chain. It does not provide confidentiality, peer authentication, or non-repudiation, and its integrity guarantees are meaningful only when it is carried over a transport that authenticates the peer and protects the payload (Section 11). HCTP is deliberately independent of any particular transport: it defines the payload octets, and any datagram or stream transport MAY carry them.

1.1. Scope and Non-Goals

This document specifies only the transport-agnostic core: the block format, the rolling-root commitment, the SYNC/ACK exchange, and the rules for advancing the chain. Mechanisms for peer authentication, key establishment, message confidentiality, and the specific method by which a summary is produced are out of scope and are expected to be supplied by the surrounding system.

2. Conventions and Terminology

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

Context block:
The unit of context corresponding to one conversational turn (Section 3.1).
Static root:
A 32-octet rolling hash committing to the ordered sequence of acknowledged context blocks.
Dynamic window:
The ordered set of pending context blocks not yet acknowledged by the peer.
SYNC:
A packet sent to convey the dynamic window and the current static root.
ACK:
A packet sent to acknowledge a SYNC and report the acknowledged static root.
Fold:
The operation that advances the static root by incorporating one context block (Section 4).
Sender / Receiver:
Roles with respect to a single SYNC exchange. An endpoint MAY act in both roles; see Section 7.

All multi-octet integers in this document are unsigned and encoded in network byte order (big-endian). Octet strings written as hexadecimal are shown for illustration only; on the wire they are raw octets.

3. Data Model

3.1. Context Block

A context block is a logical record with the following fields:

seq:
An unsigned 32-bit sequence number, monotonically increasing from 0 within a session, identifying the block's position in the ordered context.
role:
A short UTF-8 label identifying the originator of the turn (for example "user" or "agent"). Its interpretation is application-defined.
content_hash:
The lowercase hexadecimal SHA-256 [RFC6234] digest of the UTF-8 encoding of the raw turn text.
summary:
A UTF-8 semantic summary of the turn, of at most MAX_SUMMARY_LEN (Section 9) Unicode code points.

For hashing and for on-the-wire serialization, a context block MUST be encoded as canonical JSON: a JSON object containing exactly the members "content_hash", "role", "seq", and "summary", with member names sorted in ascending lexicographic (Unicode code point) order, no insignificant whitespace, and no member separators other than a single "," and ":". The canonical encoding of a block B is denoted canonical(B).

3.2. Static and Dynamic Sections

Each endpoint maintains, per session:

  • a static root: a 32-octet value initialized to the genesis root (Section 4) and advanced only as blocks are acknowledged; and
  • a dynamic window: an ordered list of pending context blocks.

Acknowledged blocks are never retransmitted; they are represented solely by the static root, whose size is constant and independent of the number of blocks it commits to.

4. Rolling-Root Computation

The static root is a SHA-256 hash chain over acknowledged blocks. Let "||" denote octet-string concatenation and SHA256(x) denote the 32-octet SHA-256 digest of x.

The genesis root is defined as:

  root_0 = SHA256(GENESIS_SEED)

where GENESIS_SEED is the ASCII octet string "hctp-genesis-v1" (15 octets, no trailing NUL).

Given a current root R and a block B, folding B advances the root:

  fold(R, B) = SHA256( R || SHA256(canonical(B)) )

The static root after acknowledging blocks B_1, ..., B_n (in order) is therefore:

  root_n = fold( fold( ... fold(root_0, B_1) ..., B_{n-1}), B_n )

Because each root embeds every prior acknowledged root, any modification, reordering, insertion, or deletion of acknowledged history produces a different static root, and the mismatch is detected at the next SYNC (Section 6.3).

5. Wire Format

HCTP defines two packet types, SYNC and ACK. Every packet begins with a 1-octet version and a 1-octet packet type. The version specified by this document is 0x01.

5.1. SYNC Packet

  0        1        2        3        4        5        6
  +--------+--------+--------------------------+
  |version | ptype  |          seq             |
  | 0x01   | 0x53   |     (uint32, 4 octets)   |
  +--------+--------+--------------------------+
  |                 static_root                |
  |                 (32 octets)                |
  +--------------------------------------------+
  |               dynamic_len                  |
  |            (uint32, 4 octets)              |
  +--------------------------------------------+
  |         dynamic_data (dynamic_len octets)  |
  |                    ...                     |
  +--------------------------------------------+
  |               combined_hash                |
  |                (32 octets)                 |
  +--------------------------------------------+
version (1 octet):
MUST be 0x01. A receiver MUST reject a packet with an unknown version.
ptype (1 octet):
MUST be 0x53 (ASCII 'S') for SYNC.
seq (4 octets):
The sequence number of the last block in the dynamic window carried by this SYNC.
static_root (32 octets):
The sender's current static root, i.e., the root over all blocks the sender has acknowledged prior to this SYNC.
dynamic_len (4 octets):
The length in octets of dynamic_data.
dynamic_data (dynamic_len octets):
A zlib [RFC1950] compressed stream whose uncompressed content is the canonical JSON array [canonical(B_i), ...] of the pending context blocks, in ascending seq order. The array MUST be encoded as canonical JSON (sorted member names, no insignificant whitespace).
combined_hash (32 octets):
SHA256(static_root || dynamic_data), computed over the static_root field and the compressed dynamic_data octets exactly as they appear on the wire.

The fixed overhead of a SYNC packet is 74 octets (1 + 1 + 4 + 32 + 4 + 32), independent of the acknowledged-history depth. The total size is 74 octets plus dynamic_len.

5.2. ACK Packet

  0        1        2        3        4        5        6
  +--------+--------+--------------------------+
  |version | ptype  |          seq             |
  | 0x01   | 0x41   |     (uint32, 4 octets)   |
  +--------+--------+--------------------------+
  |                  new_root                  |
  |                 (32 octets)                |
  +--------------------------------------------+
version (1 octet):
MUST be 0x01.
ptype (1 octet):
MUST be 0x41 (ASCII 'A') for ACK.
seq (4 octets):
The seq of the SYNC being acknowledged, echoed unchanged.
new_root (32 octets):
The receiver's static root after folding in the acknowledged blocks (Section 6.3).

An ACK packet is exactly 38 octets.

6. Protocol Operation

6.1. Per-Role State

The SYNC/ACK exchange is asymmetric, and sender state MUST NOT be conflated with receiver state. For a given directional chain (Section 7) an endpoint maintains:

Receiver state:

  • local_root: the current static root;
  • last_seen_seq: the seq of the most recently accepted SYNC;
  • has_seen: a boolean, initially false, set true when the first SYNC is accepted;
  • ack_cache: the most recently sent ACK together with the pre-fold root over which it was computed.

Sender state:

  • static_root: the current static root;
  • dynamic: the ordered list of pending blocks;
  • next_seq: the seq to assign to the next added block;
  • pending_seq: the seq of the outstanding, unacknowledged SYNC, if any;
  • last_acked_seq: the seq of the most recently acknowledged SYNC;
  • a retransmit timer and a bounded retransmit counter.

6.2. Sending a SYNC

To transmit pending context, the sender:

  1. computes dynamic_data by canonical-JSON-encoding and zlib- compressing the pending blocks in ascending seq order;
  2. sets combined_hash = SHA256(static_root || dynamic_data);
  3. sets seq to the seq of the last pending block and records it as pending_seq;
  4. emits the SYNC packet and starts the retransmit timer.

The sender MUST NOT advance its static_root at send time; the root is advanced only upon receiving a valid ACK (Section 6.4).

6.3. Receiving a SYNC

On receiving a SYNC packet P, the receiver applies the following checks in order:

  1. Replay. If has_seen is true and P.seq < last_seen_seq, the SYNC is a replay of already-superseded state and MUST be silently discarded. (Note the strict "<": P.seq == last_seen_seq is handled by the idempotent-retransmit rule below, not discarded here.)
  2. Idempotent retransmit. If has_seen is true, P.seq == last_seen_seq, and P.static_root equals the pre-fold root recorded in ack_cache, the SYNC is a retransmission of an already-processed SYNC whose ACK was lost. The receiver MUST resend the cached ACK and MUST NOT fold again.
  3. Integrity. The receiver MUST recompute SHA256(P.static_root || P.dynamic_data) and reject the packet if it does not equal P.combined_hash.
  4. Alignment. The receiver MUST reject the packet if P.static_root does not equal its local_root. A mismatch indicates the two endpoints disagree on acknowledged history and requires re-establishment (Section 6.6).
  5. Ingest. The receiver decompresses P.dynamic_data, bounding the decompressed size (Section 11), parses the canonical JSON block array, and accepts the blocks as the new pending context.

On acceptance the receiver records pre_fold_root = local_root, folds each accepted block into local_root in order (Section 4), sets last_seen_seq = P.seq and has_seen = true, constructs an ACK with new_root = local_root, records (pre_fold_root, ACK) in ack_cache, and sends the ACK.

6.4. Receiving an ACK

On receiving an ACK packet A, the sender:

  1. ignores A if A.seq != pending_seq (a stale or duplicate ACK), or if A.seq <= last_acked_seq;
  2. otherwise folds each pending block into static_root in order, producing the sender's updated root;
  3. MUST reject the ACK if A.new_root does not equal the sender's updated root, indicating divergence (Section 6.6);
  4. on success clears the dynamic window, sets last_acked_seq = A.seq, clears pending_seq, and cancels the retransmit timer.

6.5. Retransmission and Loss

If the retransmit timer expires before an ACK is received, the sender MUST retransmit the SYNC bearing the unchanged seq and the unchanged pre-fold static_root, up to a bounded retransmit count. A single lost ACK MUST NOT cause session re-establishment or re-keying: the idempotent-retransmit rule (Section 6.3) ensures a retransmitted SYNC re-elicits the same ACK without double-folding. Only after the bounded retransmit count is exhausted without acknowledgment does the sender signal transport failure.

6.6. Error Handling and Re-establishment

A static-root mismatch (either endpoint) means the endpoints no longer share acknowledged history and cannot be reconciled within HCTP. Implementations MUST treat this as a fatal session error and re-establish a new session from genesis. HCTP does not define reconciliation of divergent chains; such divergence indicates loss, reordering that escaped detection at a lower layer, or tampering.

The seq field is a 32-bit unsigned integer. An endpoint MUST NOT allow seq to wrap; upon approaching 2^32 blocks in a session, both endpoints MUST re-key by starting a new session with a fresh genesis root. Sessions exceeding 2^32 blocks are not supported.

7. Bidirectional Synchronization

When both endpoints originate context concurrently, a single shared static root can diverge, because each endpoint would fold blocks in an order the other cannot reproduce. HCTP resolves this with per-direction chains: an endpoint pair maintains two independent rolling-root chains, one advanced solely by folding blocks originated by the first endpoint and acknowledged by the second, and the other advanced solely by folding blocks originated by the second endpoint and acknowledged by the first. A SYNC is associated with a direction, determined either from the role indicator or from the channel on which it is received. Because opposite-direction SYNCs fold into disjoint chains, they cannot diverge.

Alternatively, endpoints MAY serialize all folds onto a single shared chain using a single-writer turn-token: an endpoint transmits a SYNC only while holding the turn-token, and the token is conveyed to the peer upon transmission of an ACK. This yields a single agreed fold order at the cost of strictly alternating turns. An implementation MUST use exactly one of these two strategies for a given session and MUST NOT mix them.

8. Verifiable Summaries (Optional)

An endpoint that lacks the raw turn text MAY operate using the block's summary as its working context. If the endpoint later obtains the raw text, it can verify the block by recomputing content_hash from the raw text and comparing it to the block's content_hash field.

Implementations MAY additionally make the summary itself reproducible and thus verifiable, rather than merely asserted, by computing it with a fixed, pre-agreed deterministic summarization function -- for example a summarizer identified by name and version, applied with a fixed prompt or template and a deterministic decoding configuration -- such that any party holding the raw text and the agreed configuration recomputes the identical summary. This document does not mandate any particular summarization function; when determinism across heterogeneous implementations cannot be guaranteed, an endpoint MAY instead verify a summary under a pre-agreed equivalence predicate (for example a canonicalization or a distance threshold). Absent such a mechanism, the summary MUST be treated as unverified assertion.

9. Protocol Constants

PROTO_VERSION:
0x01
PTYPE_SYNC:
0x53
PTYPE_ACK:
0x41
GENESIS_SEED:
the ASCII octets "hctp-genesis-v1"
MAX_SUMMARY_LEN:
120 (Unicode code points)
Hash function:
SHA-256 [RFC6234]
Compression:
zlib [RFC1950] / DEFLATE [RFC1951]

10. Transport Considerations

HCTP is transport-agnostic; it defines only the octets of SYNC and ACK packets. A SYNC or ACK is a self-delimiting unit (its length is fully determined by its fixed fields plus dynamic_len) and MAY be carried as a single datagram, a length-framed record on a stream, a WebSocket [RFC6455] binary message, or the body of an HTTP request or response. HCTP performs no fragmentation of its own: if a dynamic payload exceeds the underlying channel's usable size, the sending application MUST split the pending blocks across multiple SYNC packets, each bearing the same static_root and an increasing seq, and reassemble on the receiver.

Because HCTP supplies no confidentiality or peer authentication, it MUST be carried over a transport that provides them where those properties are required (Section 11).

11. Security Considerations

HCTP is not a secure channel by itself. The static root, combined_hash, and content_hash are computed with an unkeyed hash function. They provide tamper-evidence against accidental corruption and against a party that cannot forge packets, but they provide no cryptographic authentication: an active attacker who can inject packets can recompute a consistent combined_hash and a consistent chain for any payload of the attacker's choosing. Authenticity, integrity against active attackers, and confidentiality MUST be provided by the underlying transport (for example TLS 1.3 [RFC8446] or QUIC [RFC9000]) or by an authenticated-encryption layer that wraps the HCTP packet. Absent such a layer, HCTP's integrity guarantees are meaningless.

No non-repudiation. Either endpoint can construct a valid-looking chain from genesis; the chain proves internal consistency, not origin. Binding context to a long-term identity requires signatures and is out of scope.

Replay. Within a session, the seq field and the strict ordering check reject replays of superseded SYNCs, and the static-root alignment check rejects out-of-context injection. Cross-session replay is prevented by starting each session from a fresh genesis and never reusing session state; endpoints SHOULD bind the session to transport-layer freshness (for example a fresh TLS session) to prevent whole-session replay.

Decompression resource exhaustion. dynamic_data is attacker-influenced compressed input. A receiver MUST bound the maximum accepted decompressed size and the maximum number of blocks per SYNC, and MUST abort decompression that exceeds those bounds, to avoid a decompression "bomb" denial of service. dynamic_len itself MUST be bounded before allocation.

Confidentiality of summaries. A block's summary may reveal the semantic content of a turn even though the raw text is not sent. Where the summary is sensitive, the transport MUST encrypt the packet. The content_hash likewise permits confirmation of a guessed plaintext; where raw texts are low-entropy or guessable, this MUST be considered.

Hash agility. This version fixes SHA-256 and a fixed genesis seed. Collision resistance of the chain rests entirely on SHA-256. A future version of the protocol (indicated by the version octet) may specify an alternative hash; endpoints MUST reject versions they do not implement.

12. IANA Considerations

12.1. HCTP Packet Type Registry

IANA is requested to create a new registry, "HCTP Packet Types", with a 1-octet unsigned integer identifier space, under a to-be-assigned HCTP-protocol registry group. The registration policy is "Specification Required" [RFC8126]. The initial contents are:

Table 1
Value Name Reference
0x41 ACK This document
0x53 SYNC This document
0x00, 0x02-0x40, 0x42-0x52, 0x54-0xFF Unassigned

12.2. HCTP Version Registry

IANA is requested to create a registry "HCTP Versions" with a 1-octet identifier space, registration policy "Specification Required", initially containing value 0x01 ("HCTP/1", this document).

12.3. Media Type Registration

IANA is requested to register the "application/hctp" media type for HCTP packets carried over media-type-aware transports, per the procedures of [RFC6838]. Type name: application; Subtype name: hctp; Required parameters: none; Optional parameters: none; Encoding considerations: binary; Security considerations: see Section 11 of this document.

13. Normative References

[RFC1950]
Deutsch, P. and J-L. Gailly, "ZLIB Compressed Data Format Specification version 3.3", RFC 1950, , <https://www.rfc-editor.org/info/rfc1950>.
[RFC1951]
Deutsch, P., "DEFLATE Compressed Data Format Specification version 1.3", RFC 1951, , <https://www.rfc-editor.org/info/rfc1951>.
[RFC2119]
Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, , <https://www.rfc-editor.org/info/rfc2119>.
[RFC6234]
Eastlake 3rd, D. and T. Hansen, "US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)", RFC 6234, , <https://www.rfc-editor.org/info/rfc6234>.
[RFC6838]
Freed, N., Klensin, J., and T. Hansen, "Media Type Specifications and Registration Procedures", BCP 13, RFC 6838, , <https://www.rfc-editor.org/info/rfc6838>.
[RFC8126]
Cotton, M., Leiba, B., and T. Narten, "Guidelines for Writing an IANA Considerations Section in RFCs", BCP 26, RFC 8126, , <https://www.rfc-editor.org/info/rfc8126>.
[RFC8174]
Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, , <https://www.rfc-editor.org/info/rfc8174>.

14. Informative References

[RFC6455]
Fette, I. and A. Melnikov, "The WebSocket Protocol", RFC 6455, , <https://www.rfc-editor.org/info/rfc6455>.
[RFC8446]
Rescorla, E., "The Transport Layer Security (TLS) Protocol Version 1.3", RFC 8446, , <https://www.rfc-editor.org/info/rfc8446>.
[RFC9000]
Iyengar, J., Ed. and M. Thomson, Ed., "QUIC: A UDP-Based Multiplexed and Secure Transport", RFC 9000, , <https://www.rfc-editor.org/info/rfc9000>.

Acknowledgments

The rolling-root delta-transfer design and the verifiable-summary block derive from an earlier reference implementation of the protocol.

Author's Address

Cristian Xavier Ruvalcaba
Saluca LLC