client diagrams

This commit is contained in:
Evgeny @ SimpleX Chat
2026-03-14 11:24:12 +00:00
parent abcc6da9a0
commit 4df501efe4
5 changed files with 547 additions and 16 deletions
+4 -16
View File
@@ -2,11 +2,11 @@
SimpleX client libraries provide low-level protocol access to SimpleX routers. They implement the wire protocols ([SMP](../protocol/simplex-messaging.md), [XFTP](../protocol/xftp.md), [NTF](../protocol/push-notifications.md)) and handle connection lifecycle, but leave encryption, identity management, and connection orchestration to the application.
This is **Layer 2** of the [SimpleX Network architecture](../protocol/overview-tjr.md). Layer 1 is the routers themselves; Layer 3 is the [Agent](AGENT.md), which builds duplex encrypted connections on top of these libraries.
This is **Layer 2** of the [SimpleX Network architecture](../protocol/overview-tjr.md). Layer 1 is the routers themselves; Layer 3 is the [Agent](AGENT.md), which builds duplex encrypted connections on top of these libraries. For internal architecture diagrams (thread topology, command processing flows), see [`spec/clients.md`](../spec/clients.md).
## SMP Client
**Source**: [`Simplex.Messaging.Client`](../src/Simplex/Messaging/Client.hs)**Module spec**: [`spec/modules/Simplex/Messaging/Client.md`](../spec/modules/Simplex/Messaging/Client.md)
**Source**: [`Simplex.Messaging.Client`](../src/Simplex/Messaging/Client.hs). For architecture and module specs, see [SMP Client](../spec/clients.md#smp-client-protocolclient).
The SMP client connects to SMP routers and manages simplex messaging queues — the fundamental addressing primitive of the SimpleX Network. Each simplex queue is a unidirectional, ordered sequence of fixed-size packets (16,384 bytes) with separate cryptographic credentials for sending and receiving. The queue model and command set are defined in the [SMP protocol](../protocol/simplex-messaging.md).
@@ -33,7 +33,7 @@ Routers are identified by the SHA-256 hash of their CA certificate fingerprint,
## XFTP Client
**Source**: [`Simplex.FileTransfer.Client`](../src/Simplex/FileTransfer/Client.hs)**Module spec**: [`spec/modules/Simplex/FileTransfer/Client.md`](../spec/modules/Simplex/FileTransfer/Client.md)
**Source**: [`Simplex.FileTransfer.Client`](../src/Simplex/FileTransfer/Client.hs). For architecture and module specs, see [XFTP Client](../spec/clients.md#xftp-client).
The XFTP client connects to XFTP routers and manages data packets — individually addressed blocks used for larger payload delivery. Data packets come in fixed sizes (64KB, 256KB, 1MB, 4MB), hiding the actual payload size. The XFTP protocol runs over HTTP/2, simplifying browser integration. The data packet lifecycle and command set are defined in the [XFTP protocol](../protocol/xftp.md).
@@ -46,7 +46,7 @@ The XFTP client connects to XFTP routers and manages data packets — individual
## NTF Client
**Source**: [`Simplex.Messaging.Notifications.Client`](../src/Simplex/Messaging/Notifications/Client.hs)**Module spec**: [`spec/modules/Simplex/Messaging/Notifications/Client.md`](../spec/modules/Simplex/Messaging/Notifications/Client.md)
**Source**: [`Simplex.Messaging.Notifications.Client`](../src/Simplex/Messaging/Notifications/Client.hs). For architecture and module specs, see [NTF Client](../spec/clients.md#ntf-client).
The NTF client connects to NTF (notification) routers and manages push notification tokens and subscriptions. It implements the [Push Notifications protocol](../protocol/push-notifications.md).
@@ -81,15 +81,3 @@ The following capabilities require the [Agent](AGENT.md) (Layer 3):
- [SimpleX Messaging Protocol](../protocol/simplex-messaging.md) — SMP wire format, commands, and security properties
- [XFTP Protocol](../protocol/xftp.md) — XFTP wire format, data packet lifecycle
- [SimpleX Network overview](../protocol/overview-tjr.md) — architecture, trust model, and design rationale
## Module specs
- [SMP Client](../spec/modules/Simplex/Messaging/Client.md) — proxy forwarding, batching, connection lifecycle, keepalive
- [XFTP Client](../spec/modules/Simplex/FileTransfer/Client.md) — handshake, data packet operations, forward secrecy
- [NTF Client](../spec/modules/Simplex/Messaging/Notifications/Client.md) — token and subscription operations, batch commands
- [SMP Protocol types](../spec/modules/Simplex/Messaging/Protocol.md) — command types, queue addresses, message encoding
- [XFTP Protocol types](../spec/modules/Simplex/FileTransfer/Protocol.md) — data packet types, XFTP commands
- [NTF Protocol types](../spec/modules/Simplex/Messaging/Notifications/Protocol.md) — notification commands, token/subscription types
- [Transport](../spec/modules/Simplex/Messaging/Transport.md) — TLS transport, session handshake
- [HTTP/2 Client](../spec/modules/Simplex/Messaging/Transport/HTTP2/Client.md) — HTTP/2 transport layer
- [Crypto](../spec/modules/Simplex/Messaging/Crypto.md) — cryptographic primitives used by clients
+165
View File
@@ -0,0 +1,165 @@
# Client Architecture
SimpleX clients are the Layer 2 libraries that connect to routers. This document shows their internal architecture: component topology and command processing flows.
For deployment and usage, see [docs/CLIENT.md](../docs/CLIENT.md). For protocol specifications, see [SMP](../protocol/simplex-messaging.md), [XFTP](../protocol/xftp.md), [Push Notifications](../protocol/push-notifications.md).
---
## SMP Client (ProtocolClient)
**Module specs**: [Client](modules/Simplex/Messaging/Client.md) · [Protocol](modules/Simplex/Messaging/Protocol.md) · [Transport](modules/Simplex/Messaging/Transport.md) · [Crypto](modules/Simplex/Messaging/Crypto.md)
Generic protocol client used for both SMP and NTF connections. Manages a single TLS connection with multiplexed command/response matching via correlation IDs.
### Component topology
![SMP Client — Component Topology](diagrams/smp-client.svg)
### Command/response flow
```mermaid
sequenceDiagram
participant C as Caller<br>(Agent / router)
box ProtocolClient
participant SC as sentCommands<br>(TMap CorrId Request)
participant SQ as sndQ
participant S as send thread
participant R as receive thread
participant RQ as rcvQ
participant P as process thread
end
participant Router as SMP Router
C->>SC: mkTransmission (generate CorrId, create Request with empty responseVar)
C->>SQ: write (Request, encoded command)
S->>SQ: read
S-->>S: check pending flag (drop if timed out)
S->>Router: tPutLog (transmit bytes)
Router->>R: tGetClient (receive batch)
R->>RQ: write transmissions
P->>RQ: read
P->>SC: lookup CorrId
alt command response (CorrId matches, pending)
P->>SC: remove CorrId + fill responseVar (TMVar)
else expired response (CorrId matches, already timed out)
P->>C: write to msgQ (STResponse)
else server event (empty CorrId)
P->>C: write to msgQ (STEvent)
end
Note over C: getResponse: takeTMVar with timeout
```
---
## SMPClientAgent
**Module specs**: [Client Agent](modules/Simplex/Messaging/Client/Agent.md)
Connection manager that multiplexes multiple ProtocolClient connections. Tracks subscriptions, handles reconnection with backoff, and forwards server messages and connection events upward. Used by SMP router (proxying) and NTF router (subscriptions).
### Component topology
![SMPClientAgent — Component Topology](diagrams/smp-client-agent.svg)
### Connection lifecycle
```mermaid
sequenceDiagram
participant C as Consumer<br>(router / app)
participant A as SMPClientAgent
participant PC as ProtocolClient
participant Router as SMP Router
C->>A: getSMPServerClient'' (server)
alt client exists in smpClients
A->>C: return existing client
else no client
A->>PC: connectClient (create new ProtocolClient)
PC->>Router: TLS handshake
A->>A: register disconnect handler
A->>C: return new client
end
C->>A: subscribeQueuesNtfs (queueIds)
A->>A: add to pendingQueueSubs
A->>PC: sendProtocolCommands (SUB batch)
PC->>Router: SUB commands
Router->>PC: OK responses
A->>A: move pending → activeQueueSubs
A->>C: CASubscribed (via agentQ)
Note over Router: connection drops
PC->>A: disconnect handler fires
A->>A: filter by SessionId (only remove subs matching disconnected session)
A->>A: move active → pending (queue subs + service subs)
A->>C: CAServiceDisconnected (via agentQ, if service sub existed)
A->>C: CADisconnected (via agentQ, if queue subs existed)
A->>A: spawn smpSubWorker (retry with backoff)
A->>PC: reconnect + resubscribe pending subs
A->>C: CAConnected + CASubscribed (via agentQ)
```
---
## XFTP Client
**Module specs**: [Client](modules/Simplex/FileTransfer/Client.md) · [Protocol](modules/Simplex/FileTransfer/Protocol.md) · [HTTP/2 Client](modules/Simplex/Messaging/Transport/HTTP2/Client.md)
Stateless wrapper around HTTP2Client. XFTPClient adds no threads of its own — each operation is a synchronous HTTP/2 request/response. Serialization and multiplexing happen inside HTTP2Client's internal request queue and process thread.
### Component topology
![XFTP Client — Component Topology](diagrams/xftp-client.svg)
### Upload/download flow
```mermaid
sequenceDiagram
participant C as Caller<br>(Agent / app)
participant X as XFTPClient
participant H as HTTP2Client
participant Router as XFTP Router
C->>X: createXFTPChunk (FNEW)
X->>H: HTTP/2 POST (encoded command)
H->>Router: request
Router->>H: response (sender ID + recipient IDs)
H->>X: decode response
X->>C: return IDs
C->>X: uploadXFTPChunk (FPUT + file data)
X->>H: HTTP/2 POST (streaming body)
H->>Router: request with file stream
Router->>H: OK
H->>X: OK
X->>C: return OK
C->>X: downloadXFTPChunk (FGET + ephemeral DH key)
X->>H: HTTP/2 POST (command)
H->>Router: request
Router->>H: streaming response (server DH key + nonce + encrypted data)
H->>X: streaming body
X->>X: compute DH secret, decrypt + save to file
X->>C: return ()
```
---
## NTF Client
**Module specs**: [Client](modules/Simplex/Messaging/Notifications/Client.md) · [Protocol](modules/Simplex/Messaging/Notifications/Protocol.md)
Type alias for ProtocolClient — same architecture as SMP Client:
```haskell
type NtfClient = ProtocolClient NTFVersion ErrorType NtfResponse
```
Same threads (send, receive, process, monitor), same queues (sndQ, rcvQ, sentCommands, msgQ), same command/response flow. Different command types: TNEW, TVFY, TCHK, TRPL, TDEL, TCRN, SNEW, SCHK, SDEL, PING.
+146
View File
@@ -0,0 +1,146 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 750 480" font-family="monospace" font-size="12">
<defs>
<marker id="arr" viewBox="0 0 10 10" refX="9" refY="5"
markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#333" />
</marker>
<marker id="arr-g" viewBox="0 0 10 10" refX="9" refY="5"
markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#999" />
</marker>
</defs>
<!-- Title -->
<text x="375" y="22" text-anchor="middle" font-size="15" font-weight="bold">SMPClientAgent -- Component Topology</text>
<!-- ===== CONSUMER (top) ===== -->
<text x="15" y="55" font-size="9" fill="#999">consumer</text>
<text x="15" y="68" font-size="9" fill="#999">(NTF router /</text>
<text x="15" y="81" font-size="9" fill="#999"> SMP proxy /</text>
<text x="15" y="94" font-size="9" fill="#999"> application)</text>
<!-- msgQ -->
<rect x="105" y="45" width="200" height="38" rx="4"
fill="#fef7e0" stroke="#f9ab00" />
<text x="205" y="63" text-anchor="middle" font-weight="bold">msgQ</text>
<text x="205" y="77" text-anchor="middle" font-size="10">(TBQueue, server messages)</text>
<!-- consumer <- msgQ -->
<line x1="105" y1="64" x2="80" y2="64"
stroke="#999" marker-end="url(#arr-g)" />
<!-- agentQ -->
<rect x="340" y="45" width="230" height="38" rx="4"
fill="#fef7e0" stroke="#f9ab00" />
<text x="455" y="63" text-anchor="middle" font-weight="bold">agentQ</text>
<text x="455" y="77" text-anchor="middle" font-size="10">(TBQueue SMPClientAgentEvent)</text>
<!-- consumer <- agentQ -->
<line x1="340" y1="55" x2="80" y2="55"
stroke="#999" marker-end="url(#arr-g)" />
<!-- agentQ events note -->
<text x="580" y="50" font-size="9" fill="#666">CAConnected</text>
<text x="580" y="61" font-size="9" fill="#666">CADisconnected</text>
<text x="580" y="72" font-size="9" fill="#666">CASubscribed / CASubError</text>
<text x="580" y="83" font-size="9" fill="#666">CAServiceDisconnected</text>
<text x="580" y="94" font-size="9" fill="#666">CAServiceSubscribed / SubError</text>
<!-- ===== AGENT CORE ===== -->
<rect x="25" y="105" width="700" height="175" rx="6"
fill="none" stroke="#888" stroke-dasharray="6,3" />
<text x="35" y="120" fill="#888" font-size="10">SMPClientAgent (connection manager)</text>
<!-- smpClients -->
<rect x="45" y="135" width="220" height="48" rx="4"
fill="#fef7e0" stroke="#f9ab00" />
<text x="155" y="156" text-anchor="middle" font-weight="bold">smpClients</text>
<text x="155" y="174" text-anchor="middle" font-size="10">(TMap SMPServer SMPClientVar)</text>
<!-- subscription tracking -->
<rect x="290" y="135" width="250" height="56" rx="4"
fill="#fef7e0" stroke="#f9ab00" />
<text x="415" y="149" text-anchor="middle" font-size="10">activeQueueSubs / pendingQueueSubs</text>
<text x="415" y="161" text-anchor="middle" font-size="9" fill="#666">(TMap SMPServer (TMap QueueId ...))</text>
<text x="415" y="174" text-anchor="middle" font-size="10">activeServiceSubs / pendingServiceSubs</text>
<text x="415" y="186" text-anchor="middle" font-size="9" fill="#666">(TMap SMPServer (TVar (Maybe ...)))</text>
<!-- smpClients -> msgQ (server messages forwarded up) -->
<line x1="155" y1="135" x2="155" y2="85"
stroke="#333" marker-end="url(#arr)" />
<!-- subscription state -> agentQ (connection events forwarded up) -->
<line x1="415" y1="135" x2="415" y2="85"
stroke="#333" marker-end="url(#arr)" />
<!-- reconnect workers -->
<rect x="565" y="135" width="140" height="48" rx="4"
fill="#e6f4ea" stroke="#34a853" />
<text x="635" y="156" text-anchor="middle" font-size="11">smpSubWorkers</text>
<text x="635" y="174" text-anchor="middle" font-size="9" fill="#666">(one per server)</text>
<!-- workers -> smpClients (reconnect) -->
<line x1="565" y1="159" x2="265" y2="159"
stroke="#555" stroke-dasharray="3,2" marker-end="url(#arr)" />
<text x="390" y="155" font-size="9" fill="#555">reconnect + resubscribe</text>
<!-- getSMPServerClient'' label -->
<text x="50" y="210" font-size="10" fill="#666">getSMPServerClient'': get or create client</text>
<text x="50" y="224" font-size="10" fill="#666">connectClient: create ProtocolClient, register disconnect handler</text>
<text x="50" y="238" font-size="10" fill="#666">on disconnect: filter by SessionId, move active → pending, notify agentQ, spawn worker</text>
<text x="50" y="252" font-size="10" fill="#666">worker: retry connect with backoff, resubscribe pending subs</text>
<text x="50" y="266" font-size="10" fill="#666">subscribeQueuesNtfs / subscribeServiceNtfs: subscribe + track state</text>
<!-- ===== PROTOCOL CLIENTS (bottom) ===== -->
<rect x="25" y="300" width="700" height="95" rx="6"
fill="none" stroke="#888" stroke-dasharray="6,3" />
<text x="35" y="315" fill="#888" font-size="10">ProtocolClient connections (one per SMP Router)</text>
<!-- Client 1 -->
<rect x="45" y="330" width="150" height="48" rx="4"
fill="#e8f0fe" stroke="#4285f4" />
<text x="120" y="351" text-anchor="middle">ProtocolClient</text>
<text x="120" y="369" text-anchor="middle" font-size="10">→ SMP Router A</text>
<!-- Client 2 -->
<rect x="220" y="330" width="150" height="48" rx="4"
fill="#e8f0fe" stroke="#4285f4" />
<text x="295" y="351" text-anchor="middle">ProtocolClient</text>
<text x="295" y="369" text-anchor="middle" font-size="10">→ SMP Router B</text>
<!-- Client N -->
<rect x="395" y="330" width="150" height="48" rx="4"
fill="#e8f0fe" stroke="#4285f4" />
<text x="470" y="351" text-anchor="middle">ProtocolClient</text>
<text x="470" y="369" text-anchor="middle" font-size="10">→ SMP Router N</text>
<!-- dots -->
<text x="560" y="360" font-size="14" fill="#888">...</text>
<!-- smpClients -> clients -->
<line x1="100" y1="183" x2="100" y2="328"
stroke="#333" marker-end="url(#arr)" />
<line x1="200" y1="183" x2="295" y2="328"
stroke="#333" marker-end="url(#arr)" />
<!-- ===== LEGEND ===== -->
<rect x="25" y="415" width="700" height="50" rx="4"
fill="none" stroke="#ddd" />
<rect x="40" y="428" width="14" height="11" rx="2"
fill="#e8f0fe" stroke="#4285f4" />
<text x="60" y="438" font-size="10">ProtocolClient</text>
<rect x="185" y="428" width="14" height="11" rx="2"
fill="#fef7e0" stroke="#f9ab00" />
<text x="205" y="438" font-size="10">state / queue</text>
<rect x="310" y="428" width="14" height="11" rx="2"
fill="#e6f4ea" stroke="#34a853" />
<text x="330" y="438" font-size="10">background worker</text>
<text x="40" y="456" font-size="10" fill="#666">
Solid arrows: TBQueue flow. Dashed: reconnection / resubscription.
</text>
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

+149
View File
@@ -0,0 +1,149 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 420" font-family="monospace" font-size="12">
<defs>
<marker id="arr" viewBox="0 0 10 10" refX="9" refY="5"
markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#333" />
</marker>
<marker id="arr-g" viewBox="0 0 10 10" refX="9" refY="5"
markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#999" />
</marker>
</defs>
<!-- Title -->
<text x="360" y="22" text-anchor="middle" font-size="15" font-weight="bold">SMP Client (ProtocolClient) -- Component Topology</text>
<!-- ===== PER-CONNECTION GROUP ===== -->
<rect x="120" y="38" width="480" height="240" rx="6"
fill="none" stroke="#888" stroke-dasharray="6,3" />
<text x="130" y="53" fill="#888" font-size="10">per connection (raceAny_ -- any thread exit tears down connection)</text>
<!-- caller label + arrow -->
<text x="15" y="105" font-size="9" fill="#999">caller</text>
<text x="10" y="118" font-size="9" fill="#999">(Agent/</text>
<text x="10" y="131" font-size="9" fill="#999"> router)</text>
<!-- caller -> sndQ (sendProtocolCommand) -->
<line x1="55" y1="115" x2="138" y2="115"
stroke="#999" marker-end="url(#arr-g)" />
<text x="73" y="109" font-size="9" fill="#999">commands</text>
<!-- sndQ -->
<rect x="140" y="90" width="120" height="48" rx="4"
fill="#fef7e0" stroke="#f9ab00" />
<text x="200" y="111" text-anchor="middle" font-weight="bold">sndQ</text>
<text x="200" y="129" text-anchor="middle" font-size="10">(TBQueue, 64)</text>
<!-- sndQ -> send -->
<line x1="260" y1="114" x2="298" y2="114"
stroke="#333" marker-end="url(#arr)" />
<!-- send thread -->
<rect x="300" y="68" width="100" height="34" rx="4"
fill="#e8f0fe" stroke="#4285f4" />
<text x="350" y="90" text-anchor="middle">send</text>
<!-- send -> network -->
<line x1="400" y1="85" x2="498" y2="85"
stroke="#999" marker-end="url(#arr-g)" />
<!-- receive thread -->
<rect x="300" y="118" width="100" height="34" rx="4"
fill="#e8f0fe" stroke="#4285f4" />
<text x="350" y="140" text-anchor="middle">receive</text>
<!-- network -> receive -->
<line x1="498" y1="135" x2="400" y2="135"
stroke="#999" marker-end="url(#arr-g)" />
<!-- network label -->
<text x="505" y="113" font-size="9" fill="#999">SMP</text>
<text x="505" y="126" font-size="9" fill="#999">Router</text>
<text x="505" y="139" font-size="9" fill="#999">(TLS)</text>
<!-- receive -> rcvQ -->
<line x1="350" y1="152" x2="350" y2="168"
stroke="#333" marker-end="url(#arr)" />
<!-- rcvQ -->
<rect x="290" y="170" width="120" height="48" rx="4"
fill="#fef7e0" stroke="#f9ab00" />
<text x="350" y="191" text-anchor="middle" font-weight="bold">rcvQ</text>
<text x="350" y="209" text-anchor="middle" font-size="10">(TBQueue, 64)</text>
<!-- rcvQ -> process -->
<line x1="350" y1="218" x2="350" y2="234"
stroke="#333" marker-end="url(#arr)" />
<!-- process thread -->
<rect x="300" y="236" width="100" height="34" rx="4"
fill="#e8f0fe" stroke="#4285f4" />
<text x="350" y="258" text-anchor="middle">process</text>
<!-- sentCommands -->
<rect x="140" y="170" width="130" height="48" rx="4"
fill="#fef7e0" stroke="#f9ab00" />
<text x="205" y="191" text-anchor="middle" font-weight="bold">sentCommands</text>
<text x="205" y="209" text-anchor="middle" font-size="10">(TMap CorrId Request)</text>
<!-- process -> sentCommands (match CorrId) -->
<line x1="300" y1="253" x2="270" y2="220"
stroke="#555" stroke-dasharray="3,2" marker-end="url(#arr)" />
<text x="268" y="244" font-size="9" fill="#555">match</text>
<!-- process -> responseVar (back to caller) -->
<line x1="300" y1="260" x2="60" y2="145"
stroke="#555" stroke-dasharray="3,2" marker-end="url(#arr)" />
<text x="130" y="220" font-size="9" fill="#555">responseVar</text>
<text x="130" y="232" font-size="9" fill="#555">(TMVar)</text>
<!-- monitor thread (optional) -->
<rect x="430" y="190" width="100" height="34" rx="4"
fill="#f5f5f5" stroke="#bbb" />
<text x="480" y="212" text-anchor="middle" font-size="11">monitor</text>
<!-- monitor -> sndQ (sends PING) -->
<line x1="430" y1="207" x2="260" y2="130"
stroke="#555" stroke-dasharray="3,2" marker-end="url(#arr)" />
<text x="370" y="180" font-size="9" fill="#555">PING</text>
<!-- monitor optional label -->
<text x="540" y="210" font-size="9" fill="#bbb">optional</text>
<!-- ===== msgQ (server events to agent) ===== -->
<rect x="250" y="310" width="200" height="38" rx="4"
fill="#fef7e0" stroke="#f9ab00" />
<text x="350" y="327" text-anchor="middle" font-weight="bold">msgQ (optional)</text>
<text x="350" y="341" text-anchor="middle" font-size="10">(TBQueue, server events)</text>
<!-- process -> msgQ (events with empty CorrId) -->
<line x1="340" y1="270" x2="340" y2="308"
stroke="#333" marker-end="url(#arr)" />
<text x="280" y="293" font-size="9" fill="#555">events (empty CorrId)</text>
<!-- msgQ -> consumer -->
<line x1="250" y1="329" x2="60" y2="329"
stroke="#999" marker-end="url(#arr-g)" />
<text x="100" y="323" font-size="9" fill="#999">to Agent / SMPClientAgent</text>
<!-- ===== LEGEND ===== -->
<rect x="25" y="365" width="670" height="45" rx="4"
fill="none" stroke="#ddd" />
<rect x="40" y="378" width="14" height="11" rx="2"
fill="#e8f0fe" stroke="#4285f4" />
<text x="60" y="388" font-size="10">thread</text>
<rect x="130" y="378" width="14" height="11" rx="2"
fill="#fef7e0" stroke="#f9ab00" />
<text x="150" y="388" font-size="10">queue / state</text>
<rect x="275" y="378" width="14" height="11" rx="2"
fill="#f5f5f5" stroke="#bbb" />
<text x="295" y="388" font-size="10">optional</text>
<text x="40" y="404" font-size="10" fill="#666">
Solid arrows: TBQueue flow. Dashed: STM lookups / TMVar responses.
</text>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

+83
View File
@@ -0,0 +1,83 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 620 280" font-family="monospace" font-size="12">
<defs>
<marker id="arr" viewBox="0 0 10 10" refX="9" refY="5"
markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#333" />
</marker>
<marker id="arr-g" viewBox="0 0 10 10" refX="9" refY="5"
markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#999" />
</marker>
</defs>
<!-- Title -->
<text x="310" y="22" text-anchor="middle" font-size="15" font-weight="bold">XFTP Client -- Component Topology</text>
<!-- ===== CLIENT WRAPPER ===== -->
<rect x="120" y="40" width="380" height="130" rx="6"
fill="none" stroke="#888" stroke-dasharray="6,3" />
<text x="130" y="55" fill="#888" font-size="10">per connection (XFTPClient adds no threads; serialization in HTTP2Client)</text>
<!-- caller label -->
<text x="15" y="95" font-size="9" fill="#999">caller</text>
<text x="10" y="108" font-size="9" fill="#999">(Agent/</text>
<text x="10" y="121" font-size="9" fill="#999"> router)</text>
<!-- caller -> XFTPClient -->
<line x1="55" y1="100" x2="138" y2="100"
stroke="#999" marker-end="url(#arr-g)" />
<!-- XFTPClient -->
<rect x="140" y="68" width="150" height="48" rx="4"
fill="#e8f0fe" stroke="#4285f4" />
<text x="215" y="89" text-anchor="middle" font-weight="bold">XFTPClient</text>
<text x="215" y="107" text-anchor="middle" font-size="10">sendXFTPCommand</text>
<!-- XFTPClient -> HTTP2Client -->
<line x1="290" y1="92" x2="328" y2="92"
stroke="#333" marker-end="url(#arr)" />
<!-- HTTP2Client -->
<rect x="330" y="68" width="150" height="48" rx="4"
fill="#fce8e6" stroke="#ea4335" />
<text x="405" y="89" text-anchor="middle" font-weight="bold">HTTP2Client</text>
<text x="405" y="107" text-anchor="middle" font-size="10">(TLS + HTTP/2 streams)</text>
<!-- HTTP2Client -> network -->
<line x1="480" y1="92" x2="518" y2="92"
stroke="#999" marker-end="url(#arr-g)" />
<text x="525" y="88" font-size="9" fill="#999">XFTP</text>
<text x="525" y="101" font-size="9" fill="#999">Router</text>
<text x="525" y="114" font-size="9" fill="#999">(HTTP/2)</text>
<!-- Handshake state -->
<rect x="140" y="130" width="150" height="32" rx="4"
fill="#fef7e0" stroke="#f9ab00" />
<text x="215" y="151" text-anchor="middle" font-size="10">thParams (negotiated)</text>
<!-- upload/download note -->
<text x="330" y="143" font-size="10" fill="#666">uploads: streaming request body</text>
<text x="330" y="157" font-size="10" fill="#666">downloads: ephemeral DH + streaming</text>
<text x="330" y="171" font-size="10" fill="#666"> response body (per-chunk forward secrecy)</text>
<!-- ===== LEGEND ===== -->
<rect x="25" y="195" width="570" height="70" rx="4"
fill="none" stroke="#ddd" />
<rect x="40" y="210" width="14" height="11" rx="2"
fill="#e8f0fe" stroke="#4285f4" />
<text x="60" y="220" font-size="10">client wrapper</text>
<rect x="195" y="210" width="14" height="11" rx="2"
fill="#fce8e6" stroke="#ea4335" />
<text x="215" y="220" font-size="10">external connection</text>
<rect x="385" y="210" width="14" height="11" rx="2"
fill="#fef7e0" stroke="#f9ab00" />
<text x="405" y="220" font-size="10">state</text>
<text x="40" y="248" font-size="10" fill="#666">
XFTPClient adds no threads. HTTP2Client has internal reqQ + process thread.
</text>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB