<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="rfc7991bis.rnc"?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<rfc
  xmlns:xi="http://www.w3.org/2001/XInclude"
  category="exp"
  docName="draft-baker-jtp-00"
  ipr="trust200902"
  obsoletes=""
  updates=""
  submissionType="independent"
  xml:lang="en"
  tocInclude="true"
  tocDepth="4"
  symRefs="true"
  sortRefs="true"
  version="3">

  <front>
    <title abbrev="Jason Transfer Protocol">Jason Transfer Protocol (JTP)</title>

    <seriesInfo name="Internet-Draft" value="draft-baker-jtp-00"/>

    <author fullname="Matthew Baker" initials="M." surname="Baker">
      <organization>Independent</organization>
      <address>
        <postal>
          <country>Canada</country>
        </postal>
        <email>hey@mattt.space</email>
        <uri>https://github.com/punctuations/jtp</uri>
      </address>
    </author>

    <date year="2026" month="March" day="31"/>

    <area>Applications and Real-Time</area>
    <workgroup>Independent Submission</workgroup>

    <keyword>binary protocol</keyword>
    <keyword>image transfer</keyword>
    <keyword>delta sync</keyword>
    <keyword>content addressing</keyword>
    <keyword>TCP</keyword>
    <keyword>TLS</keyword>

    <abstract>
      <t>
        This document specifies the Jason Transfer Protocol (JTP), a compact
        binary protocol for listing and transferring images over a reliable
        ordered byte stream. JTP is designed to be simple to implement and
        efficient to parse. Images are addressed by a content-derived 64-bit
        identifier computed using xxHash64. The protocol supports catalog
        enumeration, point retrieval by identifier, delta synchronization, and
        connection reuse via a keep-alive mechanism. Transport security may be
        provided by TLS with an ALPN protocol identifier of "jtp/1".
      </t>
      <t>
        This document specifies the on-wire format of JTP version 1. It does
        not specify any particular implementation.
      </t>
    </abstract>

    <note title="Discussion Venues" removeInRFC="true">
      <t>
        Source for this draft and an issue tracker can be found at
        <xref target="JTP-REPO"/>.
      </t>
    </note>
  </front>

  <middle>

    <!-- ================================================================ -->
    <section anchor="intro" numbered="true" toc="default">
      <name>Introduction</name>
      <t>
        The Jason Transfer Protocol (JTP) is a request/response binary protocol
        intended for efficient enumeration and retrieval of image files over a
        reliable, ordered byte stream such as TCP. JTP addresses images by a
        content-derived 64-bit hash (xxHash64), enabling content integrity
        checks and delta synchronization without additional metadata exchange.
      </t>
      <t>
        JTP is motivated by use cases in which a client must efficiently
        synchronize a local image store against a remote server, acquiring only
        the subset of images it does not already possess (delta sync). The
        protocol is deliberately minimal: it provides catalog listing, targeted
        retrieval, batch delta sync, and a combined single-round-trip operation.
      </t>
      <t>
        This specification defines:
      </t>
      <ul spacing="normal">
        <li>The on-wire encoding of all request and response message types.</li>
        <li>The data types, flag fields, and identifier derivation rules used
            by the protocol.</li>
        <li>Transport requirements and optional TLS integration.</li>
        <li>Error signaling, resource limits, and extensibility mechanisms.</li>
      </ul>

      <section anchor="requirements-language" numbered="true" toc="default">
        <name>Requirements Language</name>
        <t>
          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 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and
          only when, they appear in all capitals, as shown here.
        </t>
      </section>

      <section anchor="terminology" numbered="true" toc="default">
        <name>Terminology</name>
        <dl newline="false" spacing="normal">
          <dt>Client</dt>
          <dd>
            An endpoint that initiates connections to a server and sends JTP
            requests.
          </dd>
          <dt>Server</dt>
          <dd>
            An endpoint that accepts connections from clients and serves JTP
            responses.
          </dd>
          <dt>ImageID</dt>
          <dd>
            A 64-bit content identifier derived from the raw bytes of an image
            file, computed as xxHash64(image_bytes, seed=0).
          </dd>
          <dt>Varint</dt>
          <dd>
            An unsigned LEB128 (Little-Endian Base 128) encoding of a 32-bit
            unsigned integer, as defined in <xref target="sec-varint"/>.
          </dd>
          <dt>Catalog</dt>
          <dd>
            The set of (ImageID, flags, filename, size) tuples returned by a
            LIST response.
          </dd>
          <dt>Image Packet</dt>
          <dd>
            The wire encoding of a single image, comprising flags, length,
            ImageID, and raw data bytes.
          </dd>
          <dt>Delta Sync</dt>
          <dd>
            The BATCH operation in which a client sends its set of known
            ImageIDs and the server returns only the images the client does not
            have.
          </dd>
        </dl>
      </section>
    </section>

    <!-- ================================================================ -->
    <section anchor="overview" numbered="true" toc="default">
      <name>Protocol Overview</name>
      <t>
        JTP is a request/response protocol. A single connection carries one or
        more request/response exchanges. A typical interaction proceeds as
        follows:
      </t>
      <ol spacing="normal">
        <li>
          The client opens a connection (TCP or TLS-wrapped TCP) to the server.
        </li>
        <li>
          The client transmits a LIST request (ReqType = 1). The server replies
          with a catalog frame containing (ImageID, Flags, Filename, Size)
          entries for every image it serves.
        </li>
        <li>
          <t>The client requests images using one of:</t>
          <ul spacing="normal">
            <li>
              GET_BY_ID (ReqType = 0): explicit retrieval of up to 255 images
              by their ImageIDs.
            </li>
            <li>
              BATCH (ReqType = 2): delta sync in which the client provides all
              ImageIDs it already holds; the server returns only the missing
              images.
            </li>
            <li>
              LIST_AND_GET (ReqType = 5): a combined single-round-trip operation
              that returns all available images together with their metadata.
            </li>
          </ul>
        </li>
        <li>
          The server transmits zero or more image packets, each containing
          flags, byte length, ImageID, and raw (possibly compressed) image
          data.
        </li>
      </ol>

      <section anchor="keepalive" numbered="true" toc="default">
        <name>Connection Reuse (Keep-Alive)</name>
        <t>
          JTP supports connection reuse through a keep-alive mechanism. When
          enabled, multiple request/response exchanges may be performed over a
          single connection, amortizing connection establishment and TLS
          handshake costs.
        </t>
        <ul spacing="normal">
          <li>
            If the keep-alive flag (bit 0 of RequestFlags) is set in a request,
            the server SHOULD keep the connection open after completing the
            response and await the next request.
          </li>
          <li>
            If the keep-alive flag is not set, the server SHOULD close the
            connection after the response has been fully sent.
          </li>
          <li>
            Servers MAY implement idle timeouts and close stale keep-alive
            connections unilaterally.
          </li>
          <li>
            Clients SHOULD NOT assume keep-alive is honored. Clients MUST
            handle server-initiated connection closure gracefully at any point
            after a complete response has been received.
          </li>
        </ul>
        <t>
          Legacy deployments MAY use a one-request-per-connection mode: the
          client opens a connection, sends exactly one request, reads the
          complete response, and then the connection is closed by either party.
        </t>
      </section>
    </section>

    <!-- ================================================================ -->
    <section anchor="transport" numbered="true" toc="default">
      <name>Transport</name>
      <t>
        JTP requires an ordered, reliable, full-duplex byte stream. The default
        and RECOMMENDED transport is TCP. JTP MAY be wrapped in TLS
        <xref target="RFC8446"/> to provide confidentiality and integrity for
        both request and response data.
      </t>
      <t>
        No default TCP port is assigned by this specification. Deployments
        SHOULD document the port(s) they use. The reference implementation
        defaults to port 8443.
      </t>

      <section anchor="tls-alpn" numbered="true" toc="default">
        <name>TLS and Application-Layer Protocol Negotiation</name>
        <t>
          When TLS is used, servers MAY advertise the following ALPN
          <xref target="RFC7301"/> protocol identifier:
        </t>
        <ul spacing="compact">
          <li><tt>jtp/1</tt></li>
        </ul>
        <t>
          Clients that support ALPN SHOULD offer <tt>jtp/1</tt> during the TLS
          handshake. A server that does not recognize the offered ALPN
          identifier MAY proceed without ALPN selection or abort the handshake.
        </t>
        <t>
          JTP does not define certificate distribution. Deployments MAY use
          self-signed certificates, a locally trusted certificate authority, or
          a certificate issued by a public PKI. Clients SHOULD verify the
          server certificate according to the applicable PKI policy.
        </t>
      </section>
    </section>

    <!-- ================================================================ -->
    <section anchor="data-types" numbered="true" toc="default">
      <name>Data Types</name>

      <section anchor="sec-integers" numbered="true" toc="default">
        <name>Unsigned Integers: u8, u16, u32, u64</name>
        <t>
          JTP uses unsigned integers of 8, 16, 32, and 64 bits. Unless
          otherwise specified, all multi-byte fixed-width integers are encoded
          in network byte order (big-endian).
        </t>
      </section>

      <section anchor="sec-varint" numbered="true" toc="default">
        <name>Variable-Length Integer: varint(u32)</name>
        <t>
          The varint(u32) type uses unsigned LEB128 (Little-Endian Base 128)
          encoding. LEB128 is described in the DWARF debugging standard and is
          in common use in binary protocols (e.g., WebAssembly, Protocol
          Buffers). The following properties apply to the JTP varint(u32) type:
        </t>
        <ul spacing="normal">
          <li>
            Encodable range: 0 through 4,294,967,295 (0x00000000 through
            0xFFFFFFFF), inclusive.
          </li>
          <li>
            Encoded length: 1 to 5 bytes.
          </li>
          <li>
            Each byte stores 7 data bits in bits 0 through 6. Bit 7 (0x80) is
            the continuation bit; a value of 1 indicates that additional bytes
            follow.
          </li>
        </ul>
        <t>
          Canonical encoding: Implementations SHOULD produce the minimal
          (canonical) encoding, i.e., no unnecessary high-order zero groups.
          Receivers MAY reject non-canonical encodings as malformed.
        </t>
        <t>
          Example: the value 4660 (0x00001234) encodes as the two-byte
          sequence 0xB4 0x24. Derivation: 4660 in binary is
          0001 0010 0011 0100. Split into 7-bit groups from least significant:
          0110100 (0x34) and 0100100 (0x24 without the continuation bit,
          giving 0x24 with bit 7 clear for the final byte). The first byte has
          bit 7 set: 0xB4. The second byte is 0x24.
        </t>
      </section>

      <section anchor="sec-utf8" numbered="true" toc="default">
        <name>UTF-8 Strings</name>
        <t>
          Filenames in the catalog are encoded as UTF-8 <xref target="RFC3629"/>
          byte sequences. The protocol transmits an explicit byte-length prefix;
          no null terminator is used. Receivers SHOULD validate that filename
          bytes constitute well-formed UTF-8.
        </t>
      </section>
    </section>

    <!-- ================================================================ -->
    <section anchor="sec-identifiers" numbered="true" toc="default">
      <name>Identifiers</name>

      <section anchor="imageid" numbered="true" toc="default">
        <name>ImageID</name>
        <t>
          An ImageID is a 64-bit unsigned integer computed from the raw bytes
          of an image file using the xxHash64 non-cryptographic hash function
          with a seed value of zero:
        </t>
        <artwork align="center"><![CDATA[
  ImageID = xxHash64(image_bytes, seed = 0)
        ]]></artwork>
        <t>
          The xxHash64 algorithm is defined in the xxHash specification
          <xref target="xxHash"/>.
          Implementors MUST use seed value 0.
        </t>
        <t>
          On the wire, ImageID is transmitted as a u64 in big-endian byte
          order. When rendered in human-readable form (e.g., log output), the
          RECOMMENDED representation is the 16-character lowercase hexadecimal
          encoding of the 8 big-endian bytes.
        </t>
        <t>
          ImageID provides content integrity verification but is NOT a
          cryptographic message authentication code (MAC). An adversary with
          the ability to modify transmitted data can also forge an ImageID.
          Cryptographic integrity of image content requires TLS or an
          equivalent channel security mechanism (see
          <xref target="security-considerations"/>).
        </t>
      </section>
    </section>

    <!-- ================================================================ -->
    <section anchor="flags" numbered="true" toc="default">
      <name>Flags Field</name>
      <t>
        Both catalog entries and image packets carry a one-octet Flags field.
        The bit assignments are:
      </t>
      <table align="center">
        <thead>
          <tr>
            <th>Bits</th>
            <th>Mask</th>
            <th>Name</th>
            <th>Description</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>0-2</td>
            <td>0x07</td>
            <td>FileType</td>
            <td>Image file type code (see <xref target="file-type-codes"/>)</td>
          </tr>
          <tr>
            <td>3</td>
            <td>0x08</td>
            <td>Compressed</td>
            <td>1 = image data is Zstd compressed</td>
          </tr>
          <tr>
            <td>4</td>
            <td>0x10</td>
            <td>Encrypted</td>
            <td>Reserved for future encryption support; MUST be 0</td>
          </tr>
          <tr>
            <td>5-7</td>
            <td>0xE0</td>
            <td>(reserved)</td>
            <td>MUST be 0 unless defined by a future extension</td>
          </tr>
        </tbody>
      </table>

      <section anchor="file-type-codes" numbered="true" toc="default">
        <name>File Type Codes</name>
        <t>
          The three-bit FileType sub-field (bits 0-2) encodes the image format:
        </t>
        <table align="center">
          <thead>
            <tr>
              <th>Code</th>
              <th>Format</th>
            </tr>
          </thead>
          <tbody>
            <tr><td>0</td><td>PNG</td></tr>
            <tr><td>1</td><td>JPEG (JFIF / Exif)</td></tr>
            <tr><td>2</td><td>WebP</td></tr>
            <tr><td>3</td><td>BMP</td></tr>
            <tr><td>4</td><td>GIF</td></tr>
            <tr><td>5</td><td>Reserved</td></tr>
            <tr><td>6</td><td>Reserved</td></tr>
            <tr><td>7</td><td>Unknown / Other</td></tr>
          </tbody>
        </table>
        <t>
          If the file type cannot be determined, senders SHOULD use code 7
          (Unknown / Other).
        </t>
      </section>

      <section anchor="flag-compression" numbered="true" toc="default">
        <name>Compression (Bit 3)</name>
        <t>
          When bit 3 of the Flags field is set, the image data in the
          enclosing image packet (see <xref target="image-packet"/>) is
          compressed using Zstandard (Zstd)
          <xref target="RFC8878"/> at an implementation-defined compression
          level. Receivers MUST decompress the data before use or integrity
          verification.
        </t>
        <t>
          Integrity verification via xxHash64 (see <xref target="imageid"/>)
          MUST be performed against the decompressed data.
        </t>
        <t>
          If a receiver does not support Zstd decompression and the Compressed
          bit is set, the receiver SHOULD treat the packet as an error and
          SHOULD close the connection or send an UnsupportedFeature ERROR
          response (see <xref target="error-codes"/>).
        </t>
      </section>

      <section anchor="flag-encryption" numbered="true" toc="default">
        <name>Encryption (Bit 4)</name>
        <t>
          Bit 4 is reserved for future encryption support. Senders MUST set
          this bit to 0 in the current version of the protocol. Receivers that
          encounter this bit set SHOULD treat the packet as an error.
        </t>
      </section>

      <section anchor="flag-reserved" numbered="true" toc="default">
        <name>Reserved Bits (Bits 5-7)</name>
        <t>
          Bits 5 through 7 are reserved. Senders MUST set reserved bits to 0.
          Receivers MAY reject messages containing non-zero reserved bits as
          malformed.
        </t>
      </section>
    </section>

    <!-- ================================================================ -->
    <section anchor="requests" numbered="true" toc="default">
      <name>Requests</name>
      <t>
        Every JTP request begins with a one-octet ReqType field that identifies
        the request type. Requests that support connection reuse carry a second
        one-octet RequestFlags field immediately following ReqType.
      </t>

      <section anchor="request-flags" numbered="true" toc="default">
        <name>RequestFlags</name>
        <table align="center">
          <thead>
            <tr>
              <th>Bit</th>
              <th>Name</th>
              <th>Description</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>0</td>
              <td>keep-alive</td>
              <td>1 = request connection keep-alive after response</td>
            </tr>
            <tr>
              <td>1-7</td>
              <td>(reserved)</td>
              <td>MUST be 0 unless defined by a future extension</td>
            </tr>
          </tbody>
        </table>
        <t>
          Servers MUST reject requests that have any reserved RequestFlags bit
          set to 1 by either closing the connection or transmitting an
          InvalidRequest ERROR response (see <xref target="error-codes"/>).
        </t>
      </section>

      <section anchor="req-list" numbered="true" toc="default">
        <name>LIST Request (ReqType = 1)</name>
        <t>
          The LIST request asks the server to return a complete catalog of the
          images it currently serves. It carries no additional payload beyond
          the fixed two-octet header.
        </t>
        <table align="center">
          <thead>
            <tr><th>Field</th><th>Type</th><th>Size (octets)</th><th>Description</th></tr>
          </thead>
          <tbody>
            <tr><td>ReqType</td><td>u8</td><td>1</td><td>Value: 1</td></tr>
            <tr><td>RequestFlags</td><td>u8</td><td>1</td><td>Bit 0 = keep-alive</td></tr>
          </tbody>
        </table>
        <t>
          The server responds with a LIST response as defined in
          <xref target="resp-list"/>.
        </t>
      </section>

      <section anchor="req-get-by-id" numbered="true" toc="default">
        <name>GET_BY_ID Request (ReqType = 0)</name>
        <t>
          The GET_BY_ID request asks the server to return the image data for a
          specified set of ImageIDs.
        </t>
        <table align="center">
          <thead>
            <tr><th>Field</th><th>Type</th><th>Size (octets)</th><th>Description</th></tr>
          </thead>
          <tbody>
            <tr><td>ReqType</td><td>u8</td><td>1</td><td>Value: 0</td></tr>
            <tr><td>RequestFlags</td><td>u8</td><td>1</td><td>Bit 0 = keep-alive</td></tr>
            <tr><td>Count</td><td>u8</td><td>1</td><td>Number of ImageIDs (N)</td></tr>
            <tr><td>ImageID[0..N-1]</td><td>u64</td><td>8 x N</td><td>Requested IDs, big-endian</td></tr>
          </tbody>
        </table>
        <t>Semantics:</t>
        <ul spacing="normal">
          <li>N MAY be zero, in which case no ImageID fields are present and
              the server returns zero image packets.</li>
          <li>N MUST NOT exceed 255 (the maximum value of a u8).</li>
          <li>Servers MAY silently ignore ImageIDs that are not present in
              the catalog.</li>
        </ul>
        <t>
          Response framing: The GET_BY_ID response does not include an explicit
          top-level count. Clients SHOULD read exactly N image packets
          (as defined in <xref target="image-packet"/>). Servers that silently
          skip unknown IDs will therefore produce fewer than N image packets;
          clients MUST be prepared for this. If the keep-alive flag is set, the
          connection remains open after the last image packet.
        </t>
      </section>

      <section anchor="req-batch" numbered="true" toc="default">
        <name>BATCH Request (ReqType = 2)</name>
        <t>
          The BATCH request implements delta synchronization. The client
          provides the complete set of ImageIDs it already holds; the server
          responds with only those images the client does not have.
        </t>
        <table align="center">
          <thead>
            <tr><th>Field</th><th>Type</th><th>Size (octets)</th><th>Description</th></tr>
          </thead>
          <tbody>
            <tr><td>ReqType</td><td>u8</td><td>1</td><td>Value: 2</td></tr>
            <tr><td>RequestFlags</td><td>u8</td><td>1</td><td>Bit 0 = keep-alive</td></tr>
            <tr><td>HaveCount</td><td>varint(u32)</td><td>1-5</td><td>Number of ImageIDs (N)</td></tr>
            <tr><td>ImageID[0..N-1]</td><td>u64</td><td>8 x N</td><td>IDs the client already has</td></tr>
          </tbody>
        </table>
        <t>Semantics:</t>
        <ul spacing="normal">
          <li>The server computes the set difference (server catalog) minus
              (client's HaveSet) and returns exactly those images.</li>
          <li>Servers SHOULD reject BATCH requests in which HaveCount exceeds
              1,000,000, responding with an InvalidRequest ERROR.</li>
        </ul>
        <t>
          The server responds with a BATCH response as defined in
          <xref target="resp-batch"/>.
        </t>
      </section>

      <section anchor="req-list-and-get" numbered="true" toc="default">
        <name>LIST_AND_GET Request (ReqType = 5)</name>
        <t>
          The LIST_AND_GET request retrieves all available images in a single
          round trip, without a prior LIST exchange. The server returns all
          images together with their ImageIDs; no separate catalog is needed.
        </t>
        <table align="center">
          <thead>
            <tr><th>Field</th><th>Type</th><th>Size (octets)</th><th>Description</th></tr>
          </thead>
          <tbody>
            <tr><td>ReqType</td><td>u8</td><td>1</td><td>Value: 5</td></tr>
            <tr><td>RequestFlags</td><td>u8</td><td>1</td><td>Bit 0 = keep-alive</td></tr>
          </tbody>
        </table>
        <t>
          No additional payload. The server responds with a LIST_AND_GET
          response as defined in <xref target="resp-list-and-get"/>.
        </t>
      </section>
    </section>

    <!-- ================================================================ -->
    <section anchor="responses" numbered="true" toc="default">
      <name>Responses</name>
      <t>
        Each response type begins with a four-octet ASCII magic header that
        allows receivers to identify the response type and detect framing
        errors.
      </t>

      <section anchor="resp-list" numbered="true" toc="default">
        <name>LIST Response</name>
        <t>
          The LIST response carries the server's image catalog. It begins with
          the fixed frame:
        </t>
        <table align="center">
          <thead>
            <tr><th>Field</th><th>Type</th><th>Size (octets)</th><th>Description</th></tr>
          </thead>
          <tbody>
            <tr><td>Header</td><td>bytes</td><td>4</td><td>ASCII "JTPL" (0x4A 0x54 0x50 0x4C)</td></tr>
            <tr><td>Count</td><td>u16</td><td>2</td><td>Number of catalog entries (N)</td></tr>
            <tr><td>Entry[0..N-1]</td><td>-</td><td>variable</td><td>Repeated N times (see below)</td></tr>
          </tbody>
        </table>
        <t>Each catalog entry has the following structure:</t>
        <table align="center">
          <thead>
            <tr><th>Field</th><th>Type</th><th>Size (octets)</th><th>Description</th></tr>
          </thead>
          <tbody>
            <tr><td>ImageID</td><td>u64</td><td>8</td><td>Content identifier, big-endian</td></tr>
            <tr><td>Flags</td><td>u8</td><td>1</td><td>File type and feature flags (see <xref target="flags"/>)</td></tr>
            <tr><td>NameLen</td><td>u16</td><td>2</td><td>Byte length of Filename</td></tr>
            <tr><td>Filename</td><td>bytes</td><td>NameLen</td><td>UTF-8 basename of the image file</td></tr>
            <tr><td>Size</td><td>varint(u32)</td><td>1-5</td><td>Byte length of image data in the corresponding image packet</td></tr>
          </tbody>
        </table>
        <t>Notes:</t>
        <ul spacing="normal">
          <li>
            Size is the byte count of the data field that will appear in an
            image packet for this ImageID. If the Compressed flag is set, Size
            reflects the compressed data length.
          </li>
          <li>
            Filenames are informational only. Clients SHOULD NOT trust or use
            path components from filenames. Clients SHOULD sanitize filenames
            before using them as local filesystem paths.
          </li>
        </ul>
      </section>

      <section anchor="image-packet" numbered="true" toc="default">
        <name>Image Packet</name>
        <t>
          Image packets are the common unit of image delivery used by the
          GET_BY_ID, BATCH, and LIST_AND_GET responses.
        </t>
        <table align="center">
          <thead>
            <tr><th>Field</th><th>Type</th><th>Size (octets)</th><th>Description</th></tr>
          </thead>
          <tbody>
            <tr><td>Flags</td><td>u8</td><td>1</td><td>File type and feature flags (see <xref target="flags"/>)</td></tr>
            <tr><td>Length</td><td>varint(u32)</td><td>1-5</td><td>Byte length of Data</td></tr>
            <tr><td>ImageID</td><td>u64</td><td>8</td><td>Content identifier, big-endian</td></tr>
            <tr><td>Data</td><td>bytes</td><td>Length</td><td>Raw (possibly compressed) image bytes</td></tr>
          </tbody>
        </table>
        <t>
          Integrity verification: After receiving an image packet (and
          decompressing if the Compressed flag is set), receivers SHOULD verify:
        </t>
        <artwork align="center"><![CDATA[
  ImageID == xxHash64(Data, seed = 0)
        ]]></artwork>
        <t>
          If verification fails, receivers SHOULD discard the data and SHOULD
          treat the condition as a transmission error. Continuing to process
          corrupt data is NOT RECOMMENDED.
        </t>
      </section>

      <section anchor="resp-batch" numbered="true" toc="default">
        <name>BATCH Response</name>
        <table align="center">
          <thead>
            <tr><th>Field</th><th>Type</th><th>Size (octets)</th><th>Description</th></tr>
          </thead>
          <tbody>
            <tr><td>Header</td><td>bytes</td><td>4</td><td>ASCII "JTPB" (0x4A 0x54 0x50 0x42)</td></tr>
            <tr><td>MissingCount</td><td>varint(u32)</td><td>1-5</td><td>Number of missing images (M)</td></tr>
            <tr><td>Packet[0..M-1]</td><td>-</td><td>variable</td><td>M image packets (see <xref target="image-packet"/>)</td></tr>
          </tbody>
        </table>
        <t>
          The client reads exactly M image packets following the header.
        </t>
      </section>

      <section anchor="resp-list-and-get" numbered="true" toc="default">
        <name>LIST_AND_GET Response</name>
        <table align="center">
          <thead>
            <tr><th>Field</th><th>Type</th><th>Size (octets)</th><th>Description</th></tr>
          </thead>
          <tbody>
            <tr><td>Header</td><td>bytes</td><td>4</td><td>ASCII "JTPG" (0x4A 0x54 0x50 0x47)</td></tr>
            <tr><td>Count</td><td>u16</td><td>2</td><td>Number of images (N)</td></tr>
            <tr><td>Packet[0..N-1]</td><td>-</td><td>variable</td><td>N image packets (see <xref target="image-packet"/>)</td></tr>
          </tbody>
        </table>
        <t>
          The client reads exactly N image packets. Because each image packet
          contains the ImageID, no separate catalog is required.
        </t>
      </section>
    </section>

    <!-- ================================================================ -->
    <section anchor="error-handling" numbered="true" toc="default">
      <name>Error Handling</name>

      <section anchor="error-response" numbered="true" toc="default">
        <name>Structured ERROR Response</name>
        <t>
          Servers that wish to provide machine-readable error information MAY
          transmit a structured ERROR response in place of the expected response
          frame:
        </t>
        <table align="center">
          <thead>
            <tr><th>Field</th><th>Type</th><th>Size (octets)</th><th>Description</th></tr>
          </thead>
          <tbody>
            <tr><td>Header</td><td>bytes</td><td>4</td><td>ASCII "JTPE" (0x4A 0x54 0x50 0x45)</td></tr>
            <tr><td>ErrorCode</td><td>u8</td><td>1</td><td>Numeric error code (see <xref target="error-codes"/>)</td></tr>
            <tr><td>MessageLen</td><td>u16</td><td>2</td><td>Byte length of Message</td></tr>
            <tr><td>Message</td><td>bytes</td><td>MessageLen</td><td>UTF-8 human-readable error description</td></tr>
          </tbody>
        </table>
      </section>

      <section anchor="error-codes" numbered="true" toc="default">
        <name>Error Codes</name>
        <table align="center">
          <thead>
            <tr><th>Code</th><th>Name</th><th>Description</th></tr>
          </thead>
          <tbody>
            <tr><td>1</td><td>NotFound</td><td>One or more requested resources were not found</td></tr>
            <tr><td>2</td><td>InvalidRequest</td><td>The request was malformed or violated a protocol constraint</td></tr>
            <tr><td>3</td><td>ServerError</td><td>An internal server error occurred</td></tr>
            <tr><td>4</td><td>UnsupportedFeature</td><td>A requested feature is not supported by this server</td></tr>
            <tr><td>5</td><td>RateLimited</td><td>The request was refused because a rate limit was exceeded</td></tr>
          </tbody>
        </table>
      </section>

      <section anchor="legacy-errors" numbered="true" toc="default">
        <name>Legacy Error Signaling</name>
        <t>
          Servers MAY signal errors by closing the TCP connection or terminating
          the TLS session without sending a structured ERROR response. Clients
          MUST handle the following conditions as request failure:
        </t>
        <ul spacing="normal">
          <li>Unexpected TCP or TLS connection closure (EOF) before the
              response has been fully received.</li>
          <li>A response magic header that does not match any known value
              ("JTPL", "JTPB", "JTPG", "JTPE").</li>
          <li>A reserved Flags or RequestFlags bit set to 1.</li>
          <li>A varint that is non-canonical or encodes a value exceeding
              0xFFFFFFFF.</li>
          <li>Any other decoding error that prevents the message from being
              parsed as specified in this document.</li>
        </ul>
      </section>
    </section>

    <!-- ================================================================ -->
    <section anchor="limits" numbered="true" toc="default">
      <name>Limits and Resource Considerations</name>
      <t>
        JTP implementations SHOULD defend against resource exhaustion attacks
        arising from large field values. In particular:
      </t>
      <ul spacing="normal">
        <li>
          varint(u32) values that, when used as allocation sizes, would exhaust
          available memory SHOULD be rejected. Implementations MAY impose
          per-field upper bounds lower than 4,294,967,295.
        </li>
        <li>
          Oversized NameLen values (e.g., values that would require reading
          more bytes than remain in the anticipated message) SHOULD be rejected.
        </li>
        <li>
          Large Count or HaveCount values SHOULD be checked before allocating
          memory proportional to them.
        </li>
      </ul>
      <t>
        Because the Length and Size fields are encoded as varint(u32), the
        maximum single image data payload supported by this framing is
        4,294,967,295 octets (2^32 - 1, approximately 4 GiB). Implementations
        MAY enforce stricter per-image size limits appropriate to their
        deployment context.
      </t>
      <t>
        Servers SHOULD reject BATCH requests in which HaveCount exceeds
        1,000,000 by transmitting an InvalidRequest ERROR response.
      </t>
    </section>

    <!-- ================================================================ -->
    <section anchor="extensibility" numbered="true" toc="default">
      <name>Extensibility</name>
      <t>
        JTP version 1 is designed to accommodate future evolution without
        breaking existing implementations:
      </t>
      <ul spacing="normal">
        <li>
          Unassigned ReqType values (currently: 3, 4, and values 6-255) are
          reserved. Future documents MAY define their semantics. Servers
          receiving an unrecognized ReqType SHOULD respond with an
          UnsupportedFeature ERROR and close the connection.
        </li>
        <li>
          Reserved Flags bits (bits 5-7) and reserved RequestFlags bits (bits
          1-7) MUST remain 0 in version 1 messages. Future extensions MAY
          assign meaning to these bits via a new specification. Receivers MUST
          be able to handle (or reject) messages with previously undefined bits
          set.
        </li>
        <li>
          The Encrypted flag (Flags bit 4) is reserved for a future encryption
          layer definition. This specification does not define the encryption
          scheme; a future document will do so.
        </li>
      </ul>
      <t>
        A future versioning scheme for the protocol as a whole MAY be
        introduced through one or more of the following mechanisms:
      </t>
      <ul spacing="normal">
        <li>
          A new ALPN protocol identifier (e.g., <tt>jtp/2</tt>) negotiated
          during TLS handshake.
        </li>
        <li>
          A new ReqType value designated for capability negotiation.
        </li>
        <li>
          Explicit version magic fields in a revised framing layer.
        </li>
      </ul>
    </section>

    <!-- ================================================================ -->
    <section anchor="security-considerations" numbered="true" toc="default">
      <name>Security Considerations</name>

      <section anchor="sec-transport" numbered="true" toc="default">
        <name>Transport Security</name>
        <t>
          Deployments SHOULD use TLS <xref target="RFC8446"/> to protect JTP
          connections against passive eavesdropping and active tampering.
          Without TLS, both image content and ImageIDs are transmitted in the
          clear, and an on-path attacker may modify image data or inject
          fabricated responses.
        </t>
      </section>

      <section anchor="sec-integrity" numbered="true" toc="default">
        <name>Content Integrity</name>
        <t>
          The xxHash64-derived ImageID provides a non-cryptographic integrity
          check against accidental corruption (e.g., transmission bit errors).
          It does NOT provide security against a malicious actor, who can compute
          a valid xxHash64 over any chosen payload. Applications that require
          cryptographic integrity assurance MUST rely on TLS record-layer
          integrity or a separate authenticated mechanism.
        </t>
      </section>

      <section anchor="sec-dos" numbered="true" toc="default">
        <name>Denial of Service</name>
        <t>
          Servers SHOULD validate and bound all count and size fields before
          allocating memory or performing I/O proportional to those values.
          Specific recommendations:
        </t>
        <ul spacing="normal">
          <li>
            Reject BATCH requests with HaveCount exceeding 1,000,000.
          </li>
          <li>
            Enforce maximum image sizes appropriate to the deployment.
          </li>
          <li>
            Enforce keep-alive idle timeouts to reclaim connection resources
            from inactive clients.
          </li>
          <li>
            Apply request rate limiting (e.g., using the RateLimited error
            code) to mitigate abusive clients.
          </li>
        </ul>
      </section>

      <section anchor="sec-filename" numbered="true" toc="default">
        <name>Filename Safety</name>
        <t>
          Filenames in the catalog are informational. Clients MUST NOT use
          catalog filenames as filesystem paths without first sanitizing them.
          In particular, clients MUST strip or reject path separators, relative
          path components (e.g., ".." sequences), and any characters not valid
          in the target filesystem. Failure to do so may allow a malicious
          server to write files to unintended locations (path traversal).
        </t>
      </section>

      <section anchor="sec-certificates" numbered="true" toc="default">
        <name>Certificate Validation</name>
        <t>
          When TLS is used, clients SHOULD validate the server's certificate
          against a trusted trust anchor appropriate to the deployment. The use
          of self-signed certificates or a local CA is acceptable in controlled
          environments but introduces risks if the trust anchor is compromised
          or mis-deployed.
        </t>
      </section>
    </section>

    <!-- ================================================================ -->
    <section anchor="iana" numbered="true" toc="default">
      <name>IANA Considerations</name>
      <t>
        This document has no IANA actions at this time. A future revision of
        this specification MAY request registration of the following:
      </t>
      <ul spacing="normal">
        <li>
          The ALPN Protocol ID <tt>jtp/1</tt> in the "TLS Application-Layer
          Protocol Negotiation (ALPN) Protocol IDs" registry
          <xref target="RFC7301"/>.
        </li>
        <li>
          A TCP port number for JTP in the "Service Name and Transport Protocol
          Port Number Registry".
        </li>
        <li>
          A registry for JTP ReqType values.
        </li>
        <li>
          A registry for JTP Error Codes.
        </li>
      </ul>
    </section>

  </middle>

  <back>
    <references>
      <name>References</name>

      <references>
        <name>Normative References</name>

        <reference anchor="RFC2119" target="https://www.rfc-editor.org/rfc/rfc2119">
          <front>
            <title>Key words for use in RFCs to Indicate Requirement Levels</title>
            <author fullname="S. Bradner" initials="S." surname="Bradner"/>
            <date month="March" year="1997"/>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="2119"/>
          <refcontent>also known as BCP 14</refcontent>
        </reference>

        <reference anchor="RFC8174" target="https://www.rfc-editor.org/rfc/rfc8174">
          <front>
            <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
            <author fullname="B. Leiba" initials="B." surname="Leiba"/>
            <date month="May" year="2017"/>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="8174"/>
        </reference>

        <reference anchor="RFC3629" target="https://www.rfc-editor.org/rfc/rfc3629">
          <front>
            <title>UTF-8, a transformation format of ISO 10646</title>
            <author fullname="F. Yergeau" initials="F." surname="Yergeau"/>
            <date month="November" year="2003"/>
          </front>
          <seriesInfo name="STD" value="63"/>
          <seriesInfo name="RFC" value="3629"/>
        </reference>

        <reference anchor="RFC8446" target="https://www.rfc-editor.org/rfc/rfc8446">
          <front>
            <title>The Transport Layer Security (TLS) Protocol Version 1.3</title>
            <author fullname="E. Rescorla" initials="E." surname="Rescorla"/>
            <date month="August" year="2018"/>
          </front>
          <seriesInfo name="RFC" value="8446"/>
        </reference>

        <reference anchor="RFC7301" target="https://www.rfc-editor.org/rfc/rfc7301">
          <front>
            <title>Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension</title>
            <author fullname="S. Friedl" initials="S." surname="Friedl"/>
            <author fullname="A. Popov" initials="A." surname="Popov"/>
            <author fullname="A. Langley" initials="A." surname="Langley"/>
            <author fullname="E. Stephan" initials="E." surname="Stephan"/>
            <date month="July" year="2014"/>
          </front>
          <seriesInfo name="RFC" value="7301"/>
        </reference>

        <reference anchor="RFC8878" target="https://www.rfc-editor.org/rfc/rfc8878">
          <front>
            <title>Zstandard Compression and the 'application/zstd' Media Type</title>
            <author fullname="Y. Collet" initials="Y." surname="Collet"/>
            <author fullname="M. Kucherawy" initials="M." surname="Kucherawy" role="editor"/>
            <date month="February" year="2021"/>
          </front>
          <seriesInfo name="RFC" value="8878"/>
        </reference>

      </references>

      <references>
        <name>Informative References</name>

        <reference anchor="xxHash" target="https://cyan4973.github.io/xxHash/">
          <front>
            <title>xxHash - Extremely Fast Non-Cryptographic Hash Algorithm</title>
            <author fullname="Yann Collet" initials="Y." surname="Collet"/>
            <date year="2023"/>
          </front>
        </reference>

        <reference anchor="JTP-REPO" target="https://github.com/punctuations/jtp">
          <front>
            <title>punctuations/jtp: High-performance binary protocol for efficient image transfer over TCP</title>
            <author fullname="Matt" surname="Matt"/>
            <date year="2026"/>
          </front>
        </reference>

      </references>
    </references>

    <!-- ================================================================ -->
    <section anchor="appendix-varint" numbered="true" toc="default">
      <name>Varint Encoding Example</name>
      <t>
        This appendix illustrates the unsigned LEB128 (varint(u32)) encoding
        used by JTP for the value 4660 (0x00001234).
      </t>
      <t>
        Step 1: Represent the value in binary (14 significant bits):
      </t>
      <artwork align="left"><![CDATA[
  4660 = 0001 0010 0011 0100 (binary)
      ]]></artwork>
      <t>
        Step 2: Group into 7-bit chunks from least significant to most
        significant (padding to a multiple of 7 if necessary):
      </t>
      <artwork align="left"><![CDATA[
  Chunk 0 (least significant): 011 0100  = 0x34
  Chunk 1 (most significant):  010 0100  = 0x24
      ]]></artwork>
      <t>
        Step 3: Set the continuation bit (bit 7 = 0x80) on all but the final
        chunk:
      </t>
      <artwork align="left"><![CDATA[
  Byte 0: 0x34 | 0x80 = 0xB4  (more bytes follow)
  Byte 1: 0x24        = 0x24  (final byte, continuation bit clear)
      ]]></artwork>
      <t>
        Encoded result: <tt>0xB4 0x24</tt> (2 bytes).
      </t>
      <t>
        Decoding: multiply Chunk 1 by 2^7 (128) and add Chunk 0:
        36 * 128 + 52 = 4608 + 52 = 4660.
      </t>
    </section>

    <!-- ================================================================ -->
    <section anchor="appendix-wire-summary" numbered="true" toc="default">
      <name>Wire Format Summary</name>
      <t>
        The following diagrams provide a compact summary of each message format.
        All fields are transmitted left-to-right, top-to-bottom as written.
        Fields labelled "var" have variable length as described in the
        corresponding section.
      </t>

      <section anchor="app-req-formats" numbered="true" toc="default">
        <name>Request Formats</name>
        <artwork align="left"><![CDATA[
LIST (ReqType = 1):
  +----------+----------+
  | ReqType  | ReqFlags |
  |  (0x01)  |  (u8)    |
  +----------+----------+

GET_BY_ID (ReqType = 0):
  +----------+----------+-------+-------- - - --------+
  | ReqType  | ReqFlags | Count |  ImageID[0..N-1]    |
  |  (0x00)  |  (u8)    | (u8)  |  N x u64 big-endian |
  +----------+----------+-------+-------- - - --------+

BATCH (ReqType = 2):
  +----------+----------+-------------+-------- - - --------+
  | ReqType  | ReqFlags |  HaveCount  |  ImageID[0..N-1]    |
  |  (0x02)  |  (u8)    | varint(u32) |  N x u64 big-endian |
  +----------+----------+-------------+-------- - - --------+

LIST_AND_GET (ReqType = 5):
  +----------+----------+
  | ReqType  | ReqFlags |
  |  (0x05)  |  (u8)    |
  +----------+----------+
        ]]></artwork>
      </section>

      <section anchor="app-resp-formats" numbered="true" toc="default">
        <name>Response Formats</name>
        <artwork align="left"><![CDATA[
LIST response ("JTPL"):
  +--------+--------+-------+------ - - ------+
  | "JTPL" | Count  |       Entries            |
  | 4 bytes| (u16)  |  N x catalog entry (var) |
  +--------+--------+-------+------ - - ------+

  Catalog entry:
  +---------+-------+---------+----------+------+
  | ImageID | Flags | NameLen | Filename | Size |
  | (u64)   | (u8)  | (u16)   | NameLen  | var  |
  +---------+-------+---------+----------+------+

Image packet (used in GET_BY_ID, BATCH, LIST_AND_GET responses):
  +-------+--------+---------+------ - ------+
  | Flags | Length | ImageID |     Data       |
  | (u8)  | (var)  | (u64)   | Length bytes   |
  +-------+--------+---------+------ - ------+

BATCH response ("JTPB"):
  +--------+--------------+------ - - ------+
  | "JTPB" | MissingCount |    Images       |
  | 4 bytes| varint(u32)  | M x image pkt   |
  +--------+--------------+------ - - ------+

LIST_AND_GET response ("JTPG"):
  +--------+-------+------ - - ------+
  | "JTPG" | Count |    Images       |
  | 4 bytes| (u16) | N x image pkt   |
  +--------+-------+------ - - ------+

ERROR response ("JTPE"):
  +--------+-----------+------------+-- - - --+
  | "JTPE" | ErrorCode | MessageLen | Message |
  | 4 bytes|   (u8)    |   (u16)    |  var    |
  +--------+-----------+------------+-- - - --+
        ]]></artwork>
      </section>
    </section>

  </back>
</rfc>
