client2server Architecture
Complete system architecture for bidirectional event forwarding between OpenWrt routers and central server.
System Overview
flowchart TB
subgraph Router["OpenWrt Router"]
direction TB
Lua[("client2server-unified.lua")]
DHCP[("DHCP Monitor")]
WAN[("WAN Monitor")]
CMD[("Command Executor")]
Link[("Link Monitor")]
end
subgraph Cloud["Cloud"]
subgraph CaddyLB["Caddy Load Balancer :3843"]
WS[("WebSocket")]
API[("HTTP :3844")]
end
subgraph Server["Go Servers (x2)"]
S1[("server1")]
S2[("server2")]
end
subgraph Redpanda["Redpanda"]
Events[("router-events")]
Commands[("router-commands")]
end
subgraph Backend["LuIS Backend"]
API2[("API Server")]
DB[(Database)]
end
end
Router -->|WS| CaddyLB
CaddyLB --> WS
WS <-->|lb| S1
WS <-->|lb| S2
S1 -->|publish| Events
S2 -->|publish| Commands
Events --> Redpanda
Commands --> Redpanda
Redpanda --> API2
API2 --> DB
Data Flows
Event Flow (Router → Server)
sequenceDiagram
participant R as Router
participant C as Caddy
participant S as Go Server
participant K as Redpanda
participant L as LuIS
Note over R,L: DHCP Event Example
R->>C: WebSocket connect → :3843
activate C
C->>S: Proxy to server1
activate S
S->>K: Produce → router-events
activate K
K-->>S: ACK
deactivate K
S-->>C: ACK
deactivate S
C-->>R: Connected!
deactivate C
Note over R,L: Later - New DHCP Lease
R->>C: Event: dhcp_lease_new
C->>S: Forward event
S->>K: Publish event
S->>R: ACK
Note over R,L: LuIS Dashboard consumes
L->>K: Consume router-events
K-->>L: Event data
Command Flow (Server → Router)
sequenceDiagram
participant L as LuIS
participant S as Go Server
participant K as Redpanda
participant R as Router
Note over L,R: Server sends command to router
L->>S: POST /api/command
S->>K: Publish → router-commands
K-->>S: ACK
S-->>L: command_id
Note over R,R: Router receives via WebSocket
R->>R: Listen for commands
R->>R: Execute: uci set network.lan.ipaddr='192.168.1.1'
R->>R: Commit: uci commit network
R->>S: Return result
S->>K: Publish result (optional)
Component Architecture
flowchart LR
subgraph Router["Router (OpenWrt)"]
WSCli[("WebSocket Client")]
Buf[("Buffer File
/tmp/event_buffer")]
Events[("Event Sources")]
CMD[("Command Executor")]
Events -->|new| WSCli
WSCli -->|offline| Buf
Buf -->|flush| WSCli
end
subgraph Server["Go Server"]
WSHan[("WS Handler")]
RedProd[("Producer")]
RedCons[("Consumer")]
Routers[("Router State")]
CMDServ[("Command Service")]
end
WSCli -->|connect| WSHan
WSHan --> RedProd
RedProd -->|topics| Redpanda["Redpanda"]
Redpanda --> RedCons
RedCons --> CMDServ
CMDServ --> Routers
Port Mappings
| Service | Port | Protocol | Purpose |
|---------|------|----------|--------||
| Caddy WS | 3843 | WebSocket | Router connections |
| Caddy HTTP | 3844 | HTTP | REST API |
| Redpanda | 9092 | Kafka | Event storage |
| Redpanda REST | 8082 | HTTP | Schema registry |
Configuration
# docker-compose.yml
environment:
REDPANDA_BROKERS: redpanda:9092
TOKEN: ***
PORT: 3843
Build Targets
flowchart LR
A[("Source")] --> B[("Build")]
B --> C[("Package")]
B --> D[("Docker")]
B --> E[("IPK")]
C -->|Go| D[("server binary")]
C -->|Lua| E[("OpenWrt ipk")]
subgraph Build["Builds"]
direction TB
F[("docker-compose up")]
G[("./scripts/feeds")]
end
Topics (Redpanda)
| Topic |
Partitions |
Retention |
Purpose |
| router-events |
3 |
7 days |
All router events |
| router-commands |
3 |
1 hour |
Commands to routers |
Error Handling
flowchart TD
A[("Connection Lost")] --> B{Router Online?}
B -->|Yes| C[("Buffer Events")]
C --> D[("Retry Every 5s")]
D --> E{Connected?}
E -->|Yes| F[("Flush Buffer")]
E -->|No| D
B -->|No| G[("Wait")]
G --> H[("Timer Reset")]
H --> A
System Overview
flowchart TB
subgraph Router["OpenWrt Router"]
direction TB
Lua[("client2server-unified.lua")]
DHCP[("DHCP Monitor")]
WAN[("WAN Monitor")]
CMD[("Command Executor")]
end
subgraph CaddyLB["Caddy Load Balancer"]
WS["WebSocket :3843"]
API["HTTP API :3844"]
end
subgraph Server["Go Servers"]
S1["server1"]
S2["server2"]
end
subgraph Redpanda["Redpanda Cluster"]
Events[("router-events")]
Commands[("router-commands")]
end
subgraph Backend["LuIS Backend"]
API2[("API Server")]
DB[(Database)]
end
Router -->|WebSocket| CaddyLB
CaddyLB --> WS
WS --> S1
WS --> S2
S1 --> Events
S2 --> Commands
Events --> Redpanda
Commands --> Redpanda
Redpanda --> API2
API2 --> DB
Data Flow
sequenceDiagram
participant R as Router
participant C as Caddy
participant S as Go Server
participant K as Redpanda
participant L as LuIS
Note over R,L: Event Flow
R->>C: WebSocket connect :3843
C->>S: Proxy to server1
S->>K: Publish to router-events
Note over R,L: Command Flow
L->>K: Publish command
K->>S: Consume from router-commands
S->>R: WebSocket send command
R->>R: Execute (uci_set, shell, etc)
R->>S: Ack result
S->>K: Publish result
K->>L: Result stored
Component Details
flowchart LR
subgraph Lua["Lua Client"]
WS[("WebSocket")]
Buf[("Buffer")]
DHCP[("DHCP")]
WAN[("WAN")]
CMD[("Commands")]
end
subgraph GoServer["Go Server"]
Handler[("Handler")]
Redpanda[("Redpanda")]
RouterMgr[("Router State")]
end
WS -->|buffer when offline| Buf
Buf -->|flush on reconnect| Handler
DHCP -->|events| Handler
WAN -->|events| Handler
Handler --> Redpanda
Redpanda --> RouterMgr
Ports
| Service |
Port |
Protocol |
| WebSocket LB |
3843 |
WS |
| HTTP API |
3844 |
HTTP |
| Redpanda Kafka |
9092 |
Kafka |
| Redpanda REST |
8082 |
HTTP |
Config
# Environment
TOKEN: *** # Auth token
REDPANDA_BROKERS: redpanda:9092 # Redpanda address
PORT: 3843 # Listening port
Commands
| Command |
Example |
Response |
uci_set |
{"config":"network", "section":"lan", "option":"ipaddr", "value":"192.168.1.1"} |
Success |
shell |
{"command":"reboot"} |
Output |
reboot |
{} |
Scheduled |
wifi_restart |
{} |
Network restart |
status |
{} |
JSON status |
Events
| Event |
Source |
Payload |
dhcp_lease_new |
dnsmasq |
mac, ip, hostname |
dhcp_lease_expire |
dnsmasq |
mac, old_ip |
wan_link_up |
/sys/class/net/* |
device |
wan_link_down |
/sys/class/net/* |
device |
wan_dhcp_new |
ubus |
new_ip |
wan_dhcp_changed |
ubus |
old_ip, new_ip |
Docker Run
TOKEN=*** docker-compose up -d
IPK Build
# With OpenWrt SDK
make package/client2server-unified/compile
make package/client2server-unified/ipk