# client2server

> **Lightweight bi-directional event forwarder for OpenWrt routers**

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

## Overview

```
┌─────────────────────────────────────────────────────────────┐
│                    client2server Architecture               │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   OpenWrt Routers                                             │
│       │                                                      │
│       ▼ WebSocket :3843                                      │
│   ┌──────────────────────────────────────────┐               │
│   │   Caddy Load Balancer                    │               │
│   │   - Health checks                       │               │
│   │   - Auto-failover                       │               │
│   └──────────────────┬───────────────────┘               │
│                      │                                    │
│          ┌───────────┴───────────┐                      │
│          ▼                     ▼                      │
│   ┌─────────────┐       ┌─────────────┐               │
│   │  server1   │       │  server2   │               │
│   │  (Go)      │       │  (Go)      │               │
│   └─────┬───────┘       └─────┬───────┘               │
│         │                  │                        │
│         └────────┬─────────┘                        │
│                  │                                 │
│                  ▼                                 │
│         ┌─────────────────┐                       │
│         │   Redpanda     │  Events stored          │
│         │   (Kafka)      │  Persisted             │
│         └───────────────┘                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

## Features

- ✅ **WebSocket connection** with auto-reconnect
- ✅ **Local buffer** (store-and-forward while offline)
- ✅ **DHCP lease events** (new/expire)
- ✅ **WAN link state** (up/down monitoring)
- ✅ **WAN IP changes** (new ISP detection)
- ✅ **Bidirectional** (receive commands from server)
- ✅ **Command executor** (uci_set, shell, reboot, status)
- ✅ **Load balancer** ready (Caddy)
- ✅ **Event persistence** (Redpanda)

## Quick Start

### Option 1: Docker Compose (Recommended)

```bash
# Clone and run
git clone https://git3.techno-world.net/lrosales/client2server.git
cd client2server

# Start all services
TOKEN=your-secret-token docker-compose up -d

# Check status
docker-compose ps
```

### Option 2: Manual (Development)

```bash
# Go server
cd server
go build -o server .
REDPANDA_BROKERS=localhost:9092 TOKEN=*** ./server

# On OpenWrt router (copy all files)
scp package/src/client2server-unified.lua root@router:/usr/sbin/
scp package/files/etc/init.d/client2server root@router:/etc/init.d/
scp package/files/etc/config/client2server root@router:/etc/config/
scp package/hotplug/01-wifi root@router:/etc/hotplug.d/wireless/
scp package/hotplug/02-dhcp root@router:/etc/hotplug.d/dhcp/
ssh root@router "chmod +x /usr/sbin/client2server-unified.lua /etc/init.d/client2server /etc/hotplug.d/wireless/01-wifi /etc/hotplug.d/dhcp/02-dhcp"
ssh root@router "/etc/init.d/client2server enable && /etc/init.d/client2server start"
```

## Ports

| Service | Port | Protocol |
|----------|------|----------|
| WebSocket + HTTP | 3843 | WS + HTTP (unified) |
| Redpanda | 9092 | Kafka (Redpanda) |

## Configuration

### Environment Variables

```bash
TOKEN=***                # Authentication token
REDPANDA_BROKERS=redpanda:9092   # Redpanda address
PORT=3843               # Server port
```

### UCI Config (on Router)

```bash
# /etc/config/client2server
config client2server 'general'
    option enabled '1'

config server
    option url 'wss://your-server.com:3843'
    option token '***'

config wan
    option interface 'wan'
    option device 'eth0'
```

## Commands

Send from server to router via WebSocket or HTTP API:

```bash
# UCI set
curl -X POST http://localhost:3843/api/command \
  -H "Authorization: Bearer TOKEN" \
  -d '{"router_id":"router1","command":"uci_set","args":{"config":"network","section":"lan","option":"ipaddr","value":"192.168.1.1"}}'

# Shell
curl -X POST http://localhost:3843/api/command \
  -H "Authorization: Bearer TOKEN" \
  -d '{"router_id":"router1","command":"shell","args":{"command":"reboot"}}'

# Reboot
curl -X POST http://localhost:3843/api/command \
  -H "Authorization: Bearer TOKEN" \
  -d '{"router_id":"router1","command":"reboot"}'
```

### Available Commands

| Command | Description | Arguments |
|---------|-------------|-----------|
| `uci_set` | Set UCI config value | `config`, `section`, `option`, `value` |
| `shell` | Run shell command | `command` |
| `reboot` | Reboot router | - |
| `wifi_restart` | Restart WiFi | - |
| `status` | Get router status | - |

## Events

Router sends these events to server:

| Event | Source | Payload |
|-------|--------|---------|
| `dhcp_lease_new` | dnsmasq (luv + hotplug) | `mac`, `ip`, `hostname` |
| `dhcp_lease_expire` | dnsmasq (luv + hotplug) | `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` |
| `wifi_connected` | hostapd (hotplug) | `mac`, `interface` |
| `wifi_disconnected` | hostapd (hotplug) | `mac`, `interface` |

## Project Structure

```
client2server/
├── ARCHITECTURE.md         # Architecture docs
├── README.md             # This file
├── MEMORY.md             # Project memory (AI/agent context)
├── Caddyfile            # Caddy load balancer
├── docker-compose.yml   # Full stack
├── package/
│   ├── Makefile         # IPK build
│   ├── src/
│   │   └── client2server-unified.lua  # Router script (canonical)
│   ├── files/
│   │   ├── etc/init.d/client2server
│   │   └── etc/config/client2server
│   └── hotplug/
│       ├── 01-wifi      # /etc/hotplug.d/wireless/ - instant WiFi events
│       └── 02-dhcp      # /etc/hotplug.d/dhcp/     - instant DHCP events
└── server/
    ├── main.go
    ├── go.mod
    └── Dockerfile
```

## Building IPK

```bash
# With OpenWrt SDK
./scripts/feeds update -a
./scripts/feeds install client2server-unified
make package/client2server-unified/compile
make package/client2server-unified/ipk

# Output
# bin/packages/*/client2server-unified_*.ipk
```

## Monitoring

```bash
# Server logs
docker-compose logs -f server1

# Router logs (on OpenWrt)
logread -f -e client2server

# Redpanda
docker-compose logs -f redpanda

# API
curl http://localhost:3843/api/routers
curl http://localhost:3843/api/events
```

## Security

- Token-based auth on both WS and HTTP
- Use TLS (wss://) in production
- Firewalls: Only allow port 3843 from router network
- Redpanda: Enable auth for production

## License

MIT - Luis Rosales 2026