1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! SimpleX Chat channel: connects to an already-running `simplex-chat` CLI
//! process over its local WebSocket control API (via the `simploxide-client`
//! crate, `websocket` feature only — no `ffi`, no bundled CLI binary; see
//! `docs/simplex-aegis-comms-assessment.md` §4/§8 for the full rationale).
//!
//! Architecture (outbound-only, like Feishu `mode = "ws"`):
//! SimpleX user <--> SMP relay network <--> simplex-chat CLI (local)
//! ^
//! | ws://127.0.0.1:PORT
//! v
//! Aegis gateway
//!
//! Aegis never listens on a public port for this channel: the CLI process
//! dials *out* to the SMP relay servers, and Aegis dials the CLI's *local*
//! control WebSocket. The CLI is a separately managed process (installed and
//! started by the operator, not spawned or bundled by Aegis) — this keeps
//! the integration on the Apache-2.0/MIT side of `simploxide-client`'s
//! conditional license (see assessment doc §8.2/§8.4).
//!
//! Security: the CLI's WebSocket control API has **no authentication and no
//! transport encryption** by design (upstream docs). It MUST stay bound to
//! loopback. [`GatewaySimplexConfig`] (in `aegis-core::config`) enforces this
//! at the config-validation layer.
//!
//! `simploxide_client::id::ChatId` (SimpleX's real chat identity — a direct
//! contact, group, or local note-to-self) does not implement `Serialize`, so
//! it cannot round-trip through the generic [`OutboundMessage::chat_id`]
//! `String` field used by the rest of the gateway. [`ChatIdRegistry`] below
//! is the bridge: the gateway's `serve_simplex` event loop registers each
//! inbound chat's real `ChatId` under a stable string key (used as the
//! session's `chat_id`), and [`SimplexChannel::send`] looks it back up when
//! replying. The registry is process-local, in-memory only — SimpleX chat
//! identities are re-derived from the CLI's own state on each inbound event,
//! so nothing durable is lost if the gateway restarts.
use ;
use async_trait;
use ChatId;
use HashMap;
use ;
use crate;
/// Stable string key <-> real [`ChatId`] bridge (see module docs).
/// Outbound-send half of the SimpleX channel. Wraps a [`simploxide_client`]
/// `Bot` handle plus a [`ChatIdRegistry`] so replies can be routed back to
/// the correct contact/group through the same control connection that the
/// gateway's event loop is reading from.
///
/// `recv()` is intentionally unimplemented (returns an error): SimpleX's
/// control API is event-stream based (`EventStream`), not poll-based, so the
/// gateway drives inbound messages directly from `serve_simplex`'s event
/// loop rather than through this trait's `recv()`. This struct exists so the
/// *outbound* path can reuse the generic [`OutboundMessage`] shape, matching
/// how the other channels' `send()` call sites are structured.