**********************************************************************
TITH                                              THIS ISN'T THAT HARD
**********************************************************************

Publication:  TTS‐0005
Revision:     1
Title:        Bundle Format
Author(s):    Stephen Hurd
Date:         2025‐11‐15
══════════════════════════════════════════════════════════════════════

Status of this document
───────────────────────

  This document is a TITH Technical Standard (TTS) — it specifies
  the current technical requirements and recommendations for TITH
  software developers, coordinators and sysops of the Fidonet network
  and other networks using FTN technology.

  This document is released to the public domain, and may be used,
  copied or modified for any purpose whatever.

Abstract
────────

  This document describes how assets are placing into a bundle for
  transfer to another system.

Contents
────────

  1.  Introduction
  2.  Encoding
  3.  Bundle Types
  4.  Ancillary Types
  5.  Error Handling
  6.  Reply Bundles

  A. References
  B. History

══════════════════════════════════════════════════════════════════════


1. Introduction
───────────────

  One of the core capabilities needed for an FTN network is the
  ability to transfer data from one node to another.  This standard
  defines the format to be used for that transfer.


2. Encoding
───────────

  Data to be transfered between two nodes is collected into a single
  bundle for the transfer.  The entire bundle is TLV encoded as
  defined in TTS‑0002.

  The bundle as a whole is a sequence of TLV values as described in
  TTS‑0003.

  The first item MUST be a Destination as defined in TTS‑0003.

  The second item MUST be an Origin as defined in TTS‑0003.

  The third item MUST be a Timestamp as defined in TTS‑0003.  This
  timestamp indicates the time when the bundling process started.  In
  cases where an additional item is added to an existing bundle, the
  Timestamp is not updated (though the Length will be).

  After this is a sequence of SignedTLV values containing sequences of
  the types defined in section 3, or one of the experimental reserved
  values 32 to 63. These SignedTLV values SHOULD NOT contain an Origin
  value.

  Types 32 to 63 are reserved for experimental extensions to this
  standard, and may be used by explicit agreement between the
  operators of two nodes.  If the use is useful to other operators, it
  is RECOMMENDED that it be added to this document with a new number,
  and the software using it modified to use the new value.  Basically,
  as soon as three node operators agree to use the same value, it
  should be added to this document and the experimental value
  abandoned.

  It is RECOMMENDED that there be no more than two SignedTLV values in
  each bundle, with the first containing all FileRequest,
  PollMessages, PollFiles, and PollFileRequests values, and the next
  containing everything else.  If there are no FileRequest or Poll
  values, a single SignedTLV SHOULD be used.  The reason for this is
  to allow the destination node to validate the SignedTLV containing
  the FileRequests and start sending them while any Files or Messages
  are being sent from the Origin to the Destination.


3. Types
────────

  The following types may be included in a bundle.

  64. Message
  ───────────
  A Message value has a large number of manditory and optional values.
  The order is specified here as a list, with M indicating Manditory,
  O indicating Optional, N indicating Manditory for NetMail, and E
  indicating Manditory for EchoMail.  If a + is listed after the
  manditory/optional chracter, indicates that multiple values of that
  type may be included.

  M  Origin (TTS-0003)
  M  Destination (TTS-0003)
  M  Timestamp (TTS-0003)
  M  ToUserName (Section 4)
  M  FromUserName (Section 4)
  M  Subject (Section 4)
  M  MessageText (Section 4)
  E  Area (Section 4)
  O+ File (Section 3)
  O  LegacyAttributes (Section 4)
  O  TimestampOffset (Section 4)
  O  TearLine (Section 4)
  O  OriginLine (Section 4)
  O  MessageID (Section 4)
  O  ReplyTo (Section 4)
  O  OriginalCharacterSet (Section 4)
  O  Signature (TTS-0003)
  M  RequestIdentifier (Section 4)
  M+ Via (Section 4)
  O  SeenBy (Section 4)
  O+ AdditionalKludgeLine (Section 4)

  Note that everything before the Signature value MUST NOT be modified
  in transit, or the Signature will fail to validate. If the signature
  does not validate, the software SHOULD send a message to that effect
  to the original user and when imported to a BBS, the message SHOULD
  have an indication that it was modified in transit.

  65. File
  ────────
  A File value has a large number of manditory and optional values.
  The order is specified here as a list, with M indicating Manditory,
  O indicating Optional, A indicating manditory for a File inside of a
  Message, T indicating manditory for a File not inside of a Message,
  directly inside of a SignedTLV, and F file a file that is part of
  a distribution network.
  O  Filename (Section 4)
  O  Timestamp (TTS-0003)
  T  RequestIdentifier (Section 4)
  M  Contents (Section 4)
  F  Area (Section 4)
  F  Origin (TTS-0003)
  O  ShortDescription (Section 4)
  O+ LongDescriptionLine (Section 4)
  O  TearLine (Section 4)
  O  MagicWord (Section 4)
  O  Replaces (Section 4)
  O  Signature (TTS-0003)
  F+ Via (Section 4)
  F+ SeenBy (Section 4)

  If a timestamp is present, it represents the time of last
  modification of the file.

  The file values were chosen to allow replacing tick files.

  66. FileRequest
  ───────────────
  A Request value which contains a manditory Filename as defined in
  section 4, optionally followed by a Timestamp as defined in
  TTS‑0003, and followed by a manditory RequestIdentifier.  If a
  Timestamp is present, the request is conditional on the last
  modified time of Filename being newer than the Timestamp.

  Existing FidoNet requests types (Bark and WaZOO) have an optional
  password, but it is expected that having public key authentication
  should be both more secure and easier to administer, making password
  controlled file requests obsolete.

  The WaZOO request can also request files the same age or older than
  the timestamp.  The author can't come up with a reasonable use for
  this, so is not including that functionality initially.

  Finally, WaZOO requests allow wildcards, which means the requester
  does not know the name of the file they're requesting, or how many
  files they are requesting.  However, since the Filename value is
  unrestricted, it's not like I can stop you can allowing wildcards
  in there.  I think it's a terrible idea though, and the processors
  written as part of the TITH project won't support them.

  67. Rejected
  ────────────
  A Rejected value which contains a manditory RequestIdentifier as
  defined in section 4, followed by a manditory TLVHash as defined in
  section 4, followed by an optional Timestamp as defined by TTS‑0003,
  followed by a manditory number encoded in the same manner as Type
  and Length, optionally followed by a UTF‑8 string containing a
  human‐readable description of why there is no file.

  This is included because a Message, File, or FileRequest from
  Destination to Origin was not accepted.

  The RequestIdentifier is the the one that was in the original
  Message, File, or FileRequest.

  If a Timestamp is present, indicates an expected time at which the
  same request would not be Rejected.

  The number may have one of the following values:

  1 – Indicates that in the current configuration, the TLV value would
      always be Rejected.  Including it in a future bundle would also
      be rejected.
  2 – Indicates that the TLV was conditional, and the condition was
      not met.  For example, if a Timestamp was included in a
      FileRequest, but the last modified time of the requested file
      matches the value of the Timestamp.  If a Timestamp is included
      in the NoFile value, this is a time at which it is expected that
      TLV value would not be Rejected.
  3 – Indicates that the node is not willing to accept the TLV value
      at this time.  If a Timestamp is included in the NoFile value,
      this is a time at which the node expects to be willing to accept
      the TLV value.

  68. Accepted
  ────────────
  An Accepted value which contains a manditory RequestIdentifier as
  defined in section 4, followed by a manditory TLVHash as defined in
  section 4.

  69. PollMessages
  ────────────────
  A PollMessages value contains a manditory RequestIdentifier.  If
  this is present, requests that the Destination include any held
  Messages for the Origin in the response.

  70. PollFiles
  ─────────────
  A PollFiles value  contains a manditory RequestIdentifier.  If this
  is present, requests that the Destination include any held Messages
  for the Origin in the response.

  71. PollFileRequests
  ────────────────────
  A PollFileRequests value  contains a manditory RequestIdentifier.
  If this is present, requests that the Destination include any held
  Messages for the Origin in the response.


4. Ancillary Types
──────────────────

  The following types are defined for use by types in section 3.

  96. Filename
  ────────────
  The value is a UTF‑8 string containing the name of the file.  No
  restrictions are placed on the value of this string, but the name
  MUST NOT contain additional path components.  Software SHOULD NOT
  use values that are not allowed by common operating systems.
  Commonly this means no control characters (U+00 to U+1F, and U+7F),
  and none of U+22 ('"'), U+2A ('*'), U+2F ('/'), U+3A (':'),
  U+3C ('<'), U+3D ('>'), U+3F ('?'), U+5C ('\'), or U+7C ('|').

  97. Contents
  ────────────
  An arbitrary sequence of bytes with no restrictions on contents or
  length.

  98. RequestIdentifier
  ─────────────────────
  Contains a number encoded using the same method as the Type and
  Length values as defined by TTS‑0002.  This number MUST be unique
  for each SignedTLV from the Origin to the Destination, and is used
  to correlate a status or file from the Destination with a specific
  request.

  99. TLVHash
  ───────────
  Contains a 32-byte hash as generated by the libhydrogen generic hash
  functions on a TLV object using a context that is the C string
  "HashTLV" (with trailing NUL).  Thirty-two bytes was chosen because
  that's the size of the signature public key.  This is used so that a
  specific SignedTLV must be accepted by the destination system and to
  prevent replay attacks.

  100. FileAttached
  ─────────────────
  Contains a RequestIdentifier of a file that is also in the bundle.
  The indicated file is attached to the message.

  101. LegacyAttributes
  ─────────────────────
  Contains a number encoded the same as the Type and Length values.
  The number has the same meaning as the AttributeWord in FTS-0001.
  If anything in here is actually useful, it should have a separate
  value.

  102. TimestampOffset
  ────────────────────
  Contains a number encoded the same as the Type and Length values.
  If this number is added to the Timestamp, the result will be the
  local time/date the message was written.

  103. ToUserName
  ───────────────
  Contains a UTF-8 string which is a user name on the destination
  system.

  104. FromUserName
  ─────────────────
  Contains a UTF-8 string which is a user name on the origin system.

  105. Subject
  ────────────
  Contains a UTF-8 string which is the subject of the message.

  106. MessageText
  ────────────────
  Contains a UTF-8 string which is the contents of the message.

  107. Area
  ─────────
  Contains an AreaName value optionally followed by an AreaDescription
  value.

  108. AreaName
  ─────────────
  Contains a UTF-8 value which specifies which area the message is in.
  This likely has a lot of legacy restrictions that I can't find in
  any official document when used with legacy FTN software.

  109. AreaDescription
  ────────────────────
  Contains a UTF-8 value which provides a description of the area for
  use when automatically adding areas.

  110. TearLine
  ─────────────
  Contains a UTF-8 value which is "an optional program specifier".
  There is no "---" prefix in this value.

  111. OriginLine
  ───────────────
  Contains a small amount of information about the system where the
  message originated. The string SHOULD end with the node address in
  parenthesis.  The " * Origin: " part is not included.

  112. SeenBy
  ───────────
  Optional and only barely useful. Contains a Trimmed Collection of
  addresses as specified in TTS-0004.  This collection is every system
  that is expected to have received this message.

  113. Via
  ────────
  A Via Value contains a required Address followed by a required
  Timestamp value followed by a UTF-8 string which MAY contain the
  program name and version separated by a space.

  114. MessageID
  ──────────────
  Contains a UTF-8 string that uniquely identifies the message and the
  Origin system.

  115. ReplyTo
  ────────────
  Contains a required Address followed by a required UTF-8 string that
  was copied from the MessageID of a message that this one is a reply
  to.

  116. OriginalCharacterSet
  ─────────────────────────
  Contains a UTF-8 string that indicates a previous value of the CHRS
  control paragraph.  The Message will have been converted from this
  to UTF-8 before it was placed in a bundle.

  117. AdditionalKludgeLine
  ─────────────────────────
  Contains a UTF-8 string.  This string represents a control paragraph
  and would be added to a legacy message. The CTRL-A character is not
  present.

  118. ShortDescription
  ─────────────────────
  Contains a UTF-8 string which describes the File. This SHOULD be no
  more than 45 characters, and MUST NOT contain newlines.

  119. LongDescriptionLine
  ────────────────────────
  Contains a UTF-8 string for each line of a multi-line description.
  Each line SHOULD be no more than 45 characters, and MUST NOT contain
  newlines.

  120. MagicWord
  ──────────────
  Contains a UTF-8 string indicating a potential alias for requesting
  the file with.

  121. Replaces
  ─────────────
  Contains a UTF-8 string with wildcard characters '*' and '?' to
  select files this replaces. It is expected that configuration around
  sources and signatures be available.


5. Error Handling
─────────────────
  When processing a bundle, errors can fall into two categories,
  framing and data.

  A framing error occurs in the TLV parsing where the Type or Length
  is not properly encoded, or the Value is not long enough.  When a
  framing error occurs, the software MUST NOT attempt to process past
  the offset in the bundle where the error occured, and if it occurs
  inside a SignedTLV, the entire SignedTLV must be rejected.
  Processing should be the same as if the SignedTLV (if any) and
  everything after it was not present in the bundle.

  A data error occures because a Value field contains an error.  If
  this occurs before the first SignedTLV value, it MUST be handled as
  a framing error.  If this occurs in a SignedTLV value, the
  processing MUST be identical to processing the same Bundle without
  the SignedTLV value that contains the error.  Missing required
  values, and having values in the wrong order are data errors.

  Note that an unknown type is not an error.  While it's encouraged to
  log a message indicating that an unknown type is used, any unknown
  type in any position MUST be ignored and retained.


6.  Reply Bundles
─────────────────

  Reply bundles are generated in response to other bundles.  For each
  value in the bundle of the types Message, File, FileRequest,
  PollMessages, PollFiles, and PollRequests, an Accepted or Rejected
  value is included in the same order as in the original bundle.  The
  polled types may also be included.  An Accepted value for a Poll
  type SHOULD be followed by the types polled if any.


A. References
─────────────

  [FTA‑1006] "Keywords to indicate requirement levels" FTSC,
  2000‑01‑17.

  [TTS‑5000] "The Distribution Nodelist", TITH, 2025‑11‑14


B. History
──────────

   Rev.1, 2025‑11‑15: Initial Release.

**********************************************************************
