Chapter 6 Data Transmission
6.1 Bidirectional Data Flow
A DTP implementation MUST support the following two independent data flow directions:
| Direction | Name | Sender | Receiver |
|---|---|---|---|
| Terminal → Fay | Data Collection | Slave | Master |
| Fay → Terminal | Data Injection | Master | Slave |
Both directions MUST satisfy:
- Use the same LogicalFrame format and processing flow.
- Maintain independent sequence number spaces.
- Maintain independent resume state (unacknowledged Fragment cache, highest acknowledged sequence number, etc.).
- Be mutually non-interfering: state changes in one direction MUST NOT affect the other direction.
6.2 Data Collection Workflow (Terminal → Fay)
Data collection MUST follow this workflow:
6.2.1 Sender (DTP_Slave) Workflow
- Receive the data submitted by the terminal application.
- Build the Fragment:
a. Generate a new Fragment_ID (UUID v4).
b. Set
agreementIdto the Agreement_ID of the current active Agreement. c. SetoriginTimestampto the UTC millisecond timestamp at which the data was actually produced. MUST NOT use the current instant. d. Attach structured ContextMetadata (see Section 4.11). e. AttachdagDependencies(MAY be an empty array). - Validate DAG dependencies:
a. Invoke the DAG Manager to verify that no cycle would be formed (see Section 6.7).
b. If a cycle is detected, MUST reject the Fragment and return the
DAG_CYCLE_DETECTEDerror (4001). - Build the LogicalFrame:
a. Set
frameType = "data". b. Apply Agreement_ID compression rules (see Section 4.5). c. Assign a monotonically increasingsequenceNumber(data collection direction). d. Set the encryption metadata. - Encrypt the Payload: encrypt the Fragment's
datafield using the key pre-negotiated by CAP. - Serialize the LogicalFrame.
- Invoke the Transport_Adapter to send the binary data.
- Add the Fragment to the unacknowledged cache (see Section 8.2).
6.2.2 Receiver (DTP_Master) Workflow
- Receive the binary data delivered by the Transport_Adapter.
- Deserialize into a LogicalFrame. If it fails, the frame MUST be discarded and the
FRAME_DESERIALIZATION_FAILEDerror (1001) MUST be returned. - Validate the protocol version (see Chapter 10).
- Parse the Agreement_ID (apply the compression rules in Section 4.5). If associated with an unknown Agreement, the frame MUST be discarded and the
AGREEMENT_NOT_FOUNDerror (3001) MUST be returned. - Decrypt the Payload. If it fails, the frame MUST be discarded and the
DECRYPTION_FAILEDerror (2001) MUST be returned. - Deserialize into a Fragment.
- Validate DAG dependencies:
a. If all target dependencies already exist, MUST accept and mark as
accepted. b. If some target dependencies have not yet arrived, MUST mark aspendingand cache (see Section 6.7). c. If a cycle is detected, MUST reject and return theDAG_CYCLE_DETECTEDerror (4001). - Update the receive state: set the Fragment's sequenceNumber as the highest received sequence number for that direction (if it advances).
- Send an acknowledgment (see Chapter 8).
- Persist the Fragment into the Personal Data Heap.
6.3 Data Injection Workflow (Fay → Terminal)
Data injection MUST follow this workflow:
6.3.1 Sender (DTP_Master) Workflow
- Query the Personal Data Heap and filter the data per the Agreement's
dataRangeto produce a minimized data set. - Build the Fragment (same as steps 2-3 of Section 6.2.1).
- Build the LogicalFrame, encrypt the Payload, serialize, and send (same as steps 4-8 of Section 6.2.1), but use the data injection direction's sequence number space.
6.3.2 Receiver (DTP_Slave) Workflow
- Deserialize, validate version, parse Agreement_ID, decrypt, deserialize Fragment, validate DAG (same as steps 1-7 of Section 6.2.2).
- Update the receive state and send an acknowledgment (same as steps 8-9 of Section 6.2.2).
- Deliver the Fragment to the terminal application.
6.4 Agreement_ID Compression in Transmission
An implementation MUST strictly follow the Agreement_ID compression rules defined in Section 4.5.
6.4.1 Compression Example
The following is a transmission sequence that complies with the specification:
Fragment 1: agreementId = "abc-123" (new Agreement, full ID)
Fragment 2: agreementId = null (reuse "abc-123")
Fragment 3: agreementId = null (reuse "abc-123")
Fragment 4: agreementId = "def-456" (switch to a new Agreement, full ID)
Fragment 5: agreementId = null (reuse "def-456")
6.4.2 Compression Constraints
An implementation MUST satisfy:
- MAY choose not to compress (i.e. all Fragments carry the full Agreement_ID).
- If compression is chosen, MUST strictly follow Section 4.5.
- The receiver MUST support both compressed and uncompressed modes.
- The context Agreement_ID MUST be reset to null after a Session is suspended and resumed (i.e. the first Fragment after resumption MUST carry the full ID).
6.5 Sequence Number Management
6.5.1 Monotonic Increase
The sequenceNumber of every Fragment MUST satisfy:
- Be monotonically increasing within a single Session.
- SHOULD be strictly contiguous (i.e. increment by 1 each time).
- MUST NOT repeat or regress.
6.5.2 Per-Direction Independence
The data collection direction and the data injection direction MUST maintain independent sequence number spaces:
Data collection direction (collection): seq 1, 2, 3, 4, 5, ...
Data injection direction (injection): seq 1, 2, 3, 4, 5, ...
An implementation MUST NOT share the sequence number space between the two directions.
6.5.3 Restart and Sessions
Sequence numbers MUST satisfy:
- The sequence number MUST be reset when a new Session begins (SHOULD start from 0 or 1).
- When a Session is restored from
Suspended, the sequence number MUST retain its value before suspension and MUST NOT be reset. - If the sequence number approaches the implementation-defined maximum, the sender MUST proactively establish a new Session to avoid overflow.
6.6 Origin Timestamp Preservation
An implementation MUST ensure that the Origin_Timestamp remains unchanged during transmission:
- The sender MUST record the instant at which the data was actually produced as Origin_Timestamp.
- Serialization, encryption, transmission, decryption, and deserialization MUST NOT modify the Origin_Timestamp.
- The receiver MUST NOT modify the received Origin_Timestamp.
- When persisting, the receiver MUST retain the Origin_Timestamp.
An implementation SHOULD maintain an independent "transmission timestamp" field outside the frame header (implementation-defined), but MUST NOT confuse it with Origin_Timestamp.
6.7 DAG Dependency Handling
6.7.1 Adding Fragments to the DAG
When the receiver receives a Fragment, it MUST process dependencies through the DAG Manager:
- Extract the Fragment's
dagDependencies. - For each dependency: a. Check whether the target Fragment_ID is already in the DAG. b. Check whether adding this edge would form a cycle.
- Return one of the following three results based on the check outcome:
| Result | Meaning | Subsequent Action |
|---|---|---|
accepted | All dependencies are resolved; no cycle | MUST add the Fragment to the DAG |
pending | Some dependencies are unresolved (target Fragments have not arrived); no cycle | MUST cache the Fragment and wait for dependency resolution |
rejected | A cycle is detected | MUST reject the Fragment and return the DAG_CYCLE_DETECTED error (4001) |
6.7.2 Deferred Resolution
When a Fragment is in the pending state:
- The implementation MUST continue to wait for dependencies in the cache for that Fragment.
- When the target Fragments of dependencies arrive, MUST re-evaluate all related
pendingFragments in the cache. - MAY set a maximum cache time (implementation-defined). After timeout, SHOULD return the
DAG_DEPENDENCY_UNRESOLVEDerror (4002) and discard.
6.7.3 Cycle Detection Algorithm
An implementation MUST detect cycles before adding a new Fragment. The detection algorithm SHOULD use DFS (depth-first search) or Tarjan's algorithm.
Concretely: from each dependency target of the new Fragment, perform DFS. If the new Fragment itself is reachable from a dependency target, a cycle is formed.
6.8 Multi-Agreement Interleaved Transmission
When multiple active Agreements coexist, the sender MUST:
- Associate each Fragment with the correct Agreement via the Agreement_ID in the frame header.
- When switching Agreements (i.e. the next Fragment belongs to a different Agreement), MUST carry the full Agreement_ID in that Fragment (i.e. null compression cannot be used).
- SHOULD schedule the sending order according to each Agreement's
priority.
