Add web GUI, docs, scripts, and 5G router config

- Web app (Flask): status, config, firewall, logs, users, restart
- Docs: AT commands, deploy, DNS, quickstart, web GUI
- Scripts: connect, deploy, diag, healthcheck, modem-status, speedtest, status, troubleshoot
- Init and iptables: 5g-router, 5g-webgui, rules.v4
- CHANGELOG, TODO, REVISION; config and README updates
This commit is contained in:
nearxos
2026-02-02 09:38:23 +02:00
parent 1136a332b5
commit 160ad641ce
46 changed files with 4320 additions and 40 deletions

View File

@@ -0,0 +1,311 @@
# Fibocom FM350-GL AT Commands Reference
Complete list of AT commands from **Fibocom FM350 AT Commands User Manual V2.10** (2023).
Source: `Fibocom_FM350_AT Commands User Manual_V2.10.pdf` in this repo.
**Tip:** To get the modems own list at runtime, use **`AT+CLAC`** (List of All Available AT Commands).
---
## Sending AT commands (FM350-GL)
```bash
cat /dev/ttyUSB1 & CAT_PID=$!
sleep 0.3
echo -e 'YOUR_COMMAND\r' > /dev/ttyUSB1
sleep 2
kill $CAT_PID 2>/dev/null
```
Use `/dev/ttyUSB1` when modem is in **USB Mode 40** (0e8d:7126). In Mode 41, AT commands do not work.
**If AT commands get no response:** run **`/usr/local/bin/diag-at-port.sh`** on the device to check serial ports, permissions, and which port responds to `AT`. See WEBGUI.md → Troubleshooting: No modem AT data.
---
## 1. Basic / test
| Command | Description |
|---------|-------------|
| `AT` | Test communication (expect `OK`) |
| `ATE0` / `ATE1` | Disable / enable command echo |
| `A/` | Repeat last command |
---
## 2. Modem information (Ch. 3)
| Command | Description |
|---------|-------------|
| `AT+CGMI` / `AT+GMI` | Manufacturer ID |
| `AT+CGMM` / `AT+GMM` | Model ID |
| `AT+CGMR` / `AT+GMR` | Revision / firmware version |
| `AT+CGSN` / `AT+GSN` | IMEI (product serial number) |
| `AT+CIMI` | IMSI |
| `AT+CNUM` | MSISDN(s) (phone number) |
| `AT+CCID` / `AT+ICCID` | SIM ICCID |
| `AT+EID` | eSIM EID |
| **`AT+CLAC`** | **List of all available AT commands** |
| `AT+SIMTYPE` | Current SIM type |
| `AT+CFSN` | Factory serial number |
| `AT+GTGATR` | Answer to Reset (ATR) info |
| `AT+GTAPPVER` | sAP firmware version |
| `AT+GTBASELINEVER` | Baseline version |
| `AT+GTPKGVER` | Firmware package version |
| `AT+GTMCFWVER` | Firmware original version |
| `AT+GTCUSTPACKVER` | Operator image version |
| `AT+GTCFGELEMVER` | Customized image version |
| `AT+GTCUSTDATAVER` | Device parameters image version |
| `AT+GTCURCAR` | Current carrier ID and name |
| `AT+GTLOCKCAR` | Carrier lock mode |
| `AT+GTESIMCFG` | eSIM function |
---
## 3. Modem control (Ch. 4)
| Command | Description |
|---------|-------------|
| `AT+CFUN` | Phone functionality (e.g. reset: `AT+CFUN=1,1`) |
| `AT+GTDUALSIM` | Dual SIM switch |
| `AT+CMEC` | Mobile termination control mode |
| `AT+CMER` | Mobile termination event reporting |
| `AT+EFUN` | Functionality for multiple SIM |
| `AT+MSMPD` | SIM hot plug enable/disable |
| `AT+CPWROFF` | Switch off MS |
---
## 4. Call control (Ch. 5)
| Command | Description |
|---------|-------------|
| `ATD` | Dial (data call) |
| `ATH` | Hang up |
| `AT+CUSD` | Unstructured Supplementary Service Data (USSD) |
---
## 5. Phone book & clock (Ch. 67)
| Command | Description |
|---------|-------------|
| `AT+CPBS` | Select phone book memory |
| `AT+CPBR` | Read phone book entries |
| `AT+CPBF` | Find phone book entries |
| `AT+CPBW` | Write phone book entry |
| `AT+CCLK` | Read/set system date and time |
---
## 6. SMS (Ch. 8)
| Command | Description |
|---------|-------------|
| `AT+CSCS` | Character set |
| `AT+CSMS` | Select message service |
| `AT+CPMS` | Preferred message storage |
| `AT+CMGF` | Message format (0=PDU, 1=text) |
| `AT+CSCA` | SMS centre address |
| `AT+CSMP` | Set SMS text mode parameters |
| `AT+CSDH` | Show SMS text mode parameters |
| `AT+CNMI` | New message indications |
| `AT+CNMA` | New message acknowledgement |
| `AT+CMGL` | List messages |
| `AT+CMGR` | Read message |
| `AT+CMSS` | Send message from storage |
| `AT+CMGW` | Write message to storage |
| `AT+CMGD` | Delete message |
| `AT+CGSMS` | Select service for MO SMS |
| `AT+CMGS` | Send message |
| `AT+CSAS` | Save SMS settings |
| `AT+CRES` | Restore SMS settings |
| `AT+CMMS` | More messages to send |
| `AT+CSCB` | Select cell broadcast messages |
---
## 7. SIM / security (Ch. 10)
| Command | Description |
|---------|-------------|
| `AT+CPIN` | PIN management (e.g. PIN enter) |
| `AT+CPWD` | Change password |
| `AT+CLCK` | Facility lock |
| `AT+CSIM` | Generic SIM access (transmit) |
| `AT+CRSM` | Restricted SIM access |
---
## 8. Network (Ch. 11)
| Command | Description |
|---------|-------------|
| `AT+CSQ` | Signal quality (RSSI) |
| `AT+CESQ` | Extended signal quality (5G: ss_rsrq, ss_rsrp, ss_sinr) |
| `AT+CREG` | Network registration status (2G/3G) |
| `AT+CGREG` | GPRS registration |
| `AT+CEREG` | EPS (LTE/5G) registration |
| `AT+COPS` | Operator selection |
| `AT+CPOL` | Preferred operator list |
| `AT+COPN` | Operator names |
| `AT+CEMODE` | RAT mode (e.g. LTE/5G preference) |
| `AT+PACSP` | RAT priority / URC |
| `AT+ERAT` | RAT (radio access technology) |
| `AT+EPBSEH` | (RAT / band related) |
| `AT+EPRATL` | (RAT list) |
| `AT+GTACT` | Active RAT / band info |
| `AT+GTCCINFO` | Cell / connection info |
| `AT+GTCAINFO` | Carrier aggregation info |
---
## 9. GPRS / PDP context (Ch. 12) data connection
| Command | Description |
|---------|-------------|
| `AT+CGDCONT` | Define PDP context (set APN, etc.) |
| `AT+CGATT` | Packet domain attach/detach |
| `AT+CGACT` | PDP context activate/deactivate |
| `AT+CGPADDR` | Show PDP addresses (assigned IP) |
| `AT+CGEQREQ` | QoS profile (requested) |
| `AT+CGCMOD` | PDP context modify |
| `AT+CGDATA` | Enter data state |
| `AT+CGDSCONT` | Define secondary PDP context |
| `AT+CGCONTRDP` | PDP context read dynamic parameters (IP, DNS, MTU) |
| `AT+CGSCONTRDP` | Secondary context read dynamic parameters |
| `AT+CGTFTRDP` | (Context read dynamic parameters) |
| `AT+CSCON` | Connection state (e.g. 5G NR state) |
| `AT+EIAAPN` | (APN / interworking) |
| `AT+E5GOPT` | 5G option |
| `AT+EAPNACT` | APN activation |
| `AT+GTDNS` | Get/set DNS for context |
---
## 10. Hardware / platform (Ch. 13)
| Command | Description |
|---------|-------------|
| **`AT+GTUSBMODE`** | **USB mode (40 = RNDIS, 41 = extended)** |
| `AT+GTFMODE` | (Firmware / feature mode) |
| `AT+GTDIPCMODE` | Dual IPC mode |
| `AT+GTREGWRITE` | Register write (platform) |
| `AT+GTRXPATHEN` | Rx path enable |
| `AT+GTTXPWR` | Max TX power |
| `AT+GTSAR3DBFB` | 3 dB per CC power setback (SAR) |
---
## 11. Body SAR (Ch. 14)
| Command | Description |
|---------|-------------|
| `AT+BODYSAREN` | Enable/disable body SAR |
| `AT+BODYSARMODE` | Control mode |
| `AT+BODYSARRULE` | Regulatory rule |
| `AT+BODYSARTRIGIDX` | SAR table trigger index |
| `AT+BODYSARON` | Trigger SAR (SW mode) |
| `AT+BODYSARPROFILE` | SAR table index to modify |
| `AT+BODYSARCFG` | Max TX power limit (antenna combination) |
| `AT+BODYSARVER` | SAR NVM version |
---
## 12. TA-SAR (Ch. 15)
| Command | Description |
|---------|-------------|
| `AT+GTTASEN` | Enable/disable TA-SAR |
| `AT+GTTASMODE` | Control mode |
| `AT+GTTASRULE` | Regulatory rule |
| `AT+GTTASCTRL` | TA-SAR control parameters |
| `AT+GTTASTRIGIDX` | SAR table trigger index |
| `AT+GTTASON` | Trigger TA-SAR (SW mode) |
| `AT+GTTASPROFILE` | SAR table index to modify |
| `AT+GTTASCFG` | Max TX power limit (antenna combination) |
| `AT+GTTASCLEAR` | Clear TA-SAR config |
| `AT+GTTASSTATE` | Current TA-SAR state |
| `AT+GTTASVER` | SAR NVM version |
| `AT+GTTASPLMNEN` | PLMN switch TA-SAR algorithm |
---
## 13. Tunable antenna (Ch. 16)
| Command | Description |
|---------|-------------|
| `AT+GTANTTUNINGEN` | Enable/disable tunable antenna |
| `AT+GTANTTUNEMODE` | GPO/MIPI tuning mode |
| `AT+GTANTCTRLMODE` | SW/HW tuning control |
| `AT+GTANTPROFILE` | Effective profile (SW mode) |
| `AT+GTANTGPOCFG` | GPO tuning values |
| `AT+GTANTTUNERCFG` | Tuner register value |
| `AT+GTANTMIPICFG` | MIPI tuning value |
---
## 14. FCC lock (Ch. 17)
| Command | Description |
|---------|-------------|
| `AT+GTFCCLOCKMODE` | FCC lock/unlock mode |
| `AT+GTFCCLOCKSTATE` | FCC lock/unlock state |
| `AT+GTFCCEFFSTATUS` | Current FCC status |
| `AT+GTFCCLOCKGEN` | Modem challenge (FCC lock) |
| `AT+GTFCCLOCKVER` | FCC lock verify challenge |
---
## 15. Thermal (Ch. 18)
| Command | Description |
|---------|-------------|
| `AT+GTTHERMAL` | Fibocom thermal management on/off |
| `AT+GTTHMLEN` | Actuator feature enable |
| `AT+GTSENRDTEMP` | Thermal sensor current |
| `AT+GTZONERDMAXTEMP` | Thermal zone max temperature |
| `AT+GTACTRDLEVEL` | Actuator current level |
| `AT+GTTHMLTIMER` | Thermal time interval |
| `AT+GTTHMLTIMES` | Thermal management running time |
| `AT+GTZONEWRTHD` | Write zone threshold |
| `AT+GTZONERDTHD` | Read zone threshold |
---
## 16. GNSS (Ch. 19)
| Command | Description |
|---------|-------------|
| `AT+GTGPSSWITCH` | GNSS enable/disable |
| `AT+GTGPSPOWER` | GNSS power control |
| `AT+GTGPSDELAID` | Start mode (e.g. A-GPS) |
---
## 17. Error reporting (Ch. 20)
| Command | Description |
|---------|-------------|
| `AT+CMEE` | Mobile equipment error report (0/1/2) |
| `AT+CEER` | Extended error report |
---
## Commands used in this project
| Command | Use |
|---------|-----|
| `AT` | Connectivity test |
| `AT+CSQ` | Signal strength (e.g. status/healthcheck) |
| `AT+CGDCONT=1,"IP","<APN>"` | Set APN |
| `AT+CGDCONT?` | Read PDP context (APN) |
| `AT+CGACT=1,1` | Activate PDP context 1 |
| `AT+CGPADDR=1` | Get modem IP |
| `AT+CGCONTRDP=1` | Get DNS, MTU, connection params |
| `AT+GTUSBMODE?` | Check USB mode |
| `AT+GTUSBMODE=?` | List supported USB modes |
| `AT+GTUSBMODE=40` | Set RNDIS (Mode 40) |
| `AT+CFUN=1,1` | Full reset modem |
For full syntax, parameters, and response codes see **Fibocom_FM350_AT Commands User Manual_V2.10.pdf**.

99
docs/DEPLOY.md Normal file
View File

@@ -0,0 +1,99 @@
# Deploying to a device (SSH / first-time setup)
## Update device (deploy script)
From your **local machine** (with the repo and SSH access to the device):
```bash
# Default device: root@10.130.60.121
./scripts/deploy.sh
# Or specify target
./scripts/deploy.sh root@192.168.1.100
# Or use env
TARGET=root@10.130.60.121 ./scripts/deploy.sh
```
The script:
1. Copies `etc/`, `scripts/`, `web/`, and `configure_fm350_5g.sh` to the device at `/tmp/Alpine_5G/`
2. Runs `./scripts/install.sh` on the device (installs/updates scripts, services, Web GUI)
3. Restarts `5g-router` and `5g-webgui` so changes take effect
Ensure SSH key or password auth works to the target before running.
---
## First-time SSH access
### 1. Get the device on the network
- Connect the device via Ethernet (eth0) so it gets an IP (DHCP) or set a static IP during Alpine setup.
- Note the device IP (e.g. from your routers DHCP list or Alpines console).
### 2. SSH key-based login (recommended)
On your **local machine** (not the device):
```bash
# Generate a key if you dont have one
ssh-keygen -t ed25519 -C "your@email" -f ~/.ssh/alpine_5g -N ""
# Copy the public key to the device (replace IP and user)
ssh-copy-id -i ~/.ssh/alpine_5g.pub root@10.130.60.121
```
Then connect with the key:
```bash
ssh -i ~/.ssh/alpine_5g root@10.130.60.121
```
To avoid specifying the key each time, add to `~/.ssh/config`:
```
Host alpine-5g
HostName 10.130.60.121
User root
IdentityFile ~/.ssh/alpine_5g
```
Then: `ssh alpine-5g`.
### 3. Password login
If you prefer password auth:
```bash
ssh root@10.130.60.121
```
Youll be prompted for the root password. Key-based auth is still recommended for scripts and automation.
### 4. Deploy the repo to the device
From your **local machine** (repo on your laptop):
```bash
# Copy entire repo
scp -r /path/to/Alpine_5G root@10.130.60.121:/tmp/
# Then on the device (SSH in and run):
ssh root@10.130.60.121 "cd /tmp/Alpine_5G && chmod +x scripts/install.sh && ./scripts/install.sh"
```
Or clone from git on the device if it has internet:
```bash
ssh root@10.130.60.121
apk add git
git clone https://github.com/YOUR_USER/Alpine_5G.git /tmp/Alpine_5G
cd /tmp/Alpine_5G && ./scripts/install.sh
```
## After deployment
- Edit `/etc/5g-router.conf` on the device if needed (APN, interfaces).
- Start 5G: `service 5g-router start` or `/usr/local/bin/connect-5g.sh`.
- Check status: `/usr/local/bin/status-5g.sh`.
- Health check for monitoring: `/usr/local/bin/healthcheck-5g.sh` (exit 0 = OK, 1 = down).

71
docs/DNS.md Normal file
View File

@@ -0,0 +1,71 @@
# DNS for Alpine 5G Router
The 5G modem gets DNS from the carrier (e.g. via `AT+CGCONTRDP=1`). The router and LAN clients need working DNS.
## Option 1: Use carrier DNS (automatic)
When 5G is up, you can put the carrier DNS in `/etc/resolv.conf` so the router itself uses it. CYTA example:
```
nameserver 195.14.130.220
nameserver 195.14.154.100
```
To automate: in `connect-5g.sh` or a post-up script, if `DNS_SERVERS` is set in `/etc/5g-router.conf`, write it to `/etc/resolv.conf` after configuring the interface.
Example in config:
```
DNS_SERVERS="195.14.130.220,195.14.154.100"
```
A one-liner to apply (run after 5G is up):
```sh
echo "nameserver 195.14.130.220" > /etc/resolv.conf
echo "nameserver 195.14.154.100" >> /etc/resolv.conf
```
## Option 2: dnsmasq (LAN DHCP + DNS)
dnsmasq can provide DHCP for LAN and act as DNS forwarder. Install and enable:
```bash
apk add dnsmasq
rc-update add dnsmasq default
```
Example `/etc/dnsmasq.conf`:
```
interface=eth0.100
dhcp-range=192.168.1.100,192.168.1.200,255.255.255.0,12h
dhcp-option=6,192.168.1.1
```
Then the router (192.168.1.1) must resolve DNS. Either:
- Set `/etc/resolv.conf` on the router to carrier or public DNS (8.8.8.8, 1.1.1.1), or
- Configure dnsmasq to use upstream servers: `server=195.14.130.220` and `server=195.14.154.100` (or 8.8.8.8).
So: **router** gets DNS from carrier or public; **LAN clients** get DHCP and DNS from dnsmasq (which forwards to those servers).
## Option 3: Public DNS only
Ignore carrier DNS and use public resolvers on the router:
```bash
echo "nameserver 8.8.8.8" > /etc/resolv.conf
echo "nameserver 1.1.1.1" >> /etc/resolv.conf
```
Ensure `ip_forward` and NAT are set so LAN clients use the router and thus the same DNS path.
## Summary
| Role | DNS source |
|------------|--------------------------------------|
| Router | `/etc/resolv.conf` (carrier or 8.8.8.8) |
| LAN clients| dnsmasq (option 6 = router) or router as gateway + same resolv |
After changing DNS, restart dnsmasq if used: `service dnsmasq restart`.

86
docs/QUICKSTART.md Normal file
View File

@@ -0,0 +1,86 @@
# Alpine 5G Router Quick Start
Get a new device from zero to 5G router in a few steps.
## Prerequisites
- Raspberry Pi 5 (or compatible) with Alpine Linux installed
- Fibocom FM350-GL modem connected via USB (Mode 40: 0e8d:7126)
- SIM card with data (e.g. CYTA Cyprus, APN `internet`)
## 1. Clone repo on the device (or copy files)
```bash
# If you have git on the device:
git clone https://github.com/YOUR_USER/Alpine_5G.git /tmp/Alpine_5G
cd /tmp/Alpine_5G
# Or: copy the repo (e.g. scp -r Alpine_5G root@device:/tmp/)
```
## 2. Install packages (on the device)
```bash
# Enable community repo if needed
sed -i 's|#.*community|http://mirrors.neterra.net/alpine/v3.23/community|' /etc/apk/repositories
apk update
apk add iptables libmbim-tools qmi-utils
# Optional: dnsmasq for LAN DHCP/DNS, speedtest-cli for speedtests
apk add dnsmasq speedtest-cli
```
## 3. Run install script
From the repo root:
```bash
cd /tmp/Alpine_5G
chmod +x scripts/install.sh
./scripts/install.sh
```
This installs:
- `/etc/5g-router.conf` (from example edit if needed)
- `/usr/local/bin/connect-5g.sh`, `status-5g.sh`, `healthcheck-5g.sh`, etc.
- `/etc/init.d/5g-router` (OpenRC service)
- `/etc/iptables/rules.v4`
- Enables `5g-router` at boot
## 4. Edit config (if needed)
```bash
vi /etc/5g-router.conf
```
Set at least:
- `APN` e.g. `internet` for CYTA
- `WAN_IF` / `LAN_IF` default `eth1` and `eth0.100` are usually correct
Optional: `FAILOVER_ENABLED=yes` and `FAILOVER_IF=eth0` to use ethernet when 5G is down.
## 5. Start 5G and test
```bash
service 5g-router start
# Or run once:
/usr/local/bin/connect-5g.sh
# Check status
/usr/local/bin/status-5g.sh
# Test connectivity
ping -c 3 8.8.8.8
```
## 6. Enable iptables restore at boot (if not already)
```bash
rc-update add iptables-restore default
```
## Done
The device will bring up 5G at boot. To restart 5G: `service 5g-router restart`.
For full docs see [README.md](../README.md) and [5G_MODEM_TROUBLESHOOTING.md](../5G_MODEM_TROUBLESHOOTING.md).

137
docs/WEBGUI.md Normal file
View File

@@ -0,0 +1,137 @@
# Alpine 5G Router Web GUI
Web interface with login and role-based access (admin and support). **One HTML page per function** (Status, Logs, Restart 5G, Config, Firewall, Routes, Users) with shared navigation.
## Access
- **URL:** `http://<device-ip>:5000` (e.g. `http://10.130.60.121:5000`)
- **Default users:**
- **admin** / **admin** full access (config, firewall, routes, users, logs, status, restart 5G)
- **support** / **support** view status, view logs, restart 5G only (no config/firewall/users)
**Change default passwords** after first login (admin: Users tab → set password).
## Permissions
| Feature | Admin | Support |
|--------------------|-------|--------|
| View status | ✓ | ✓ |
| View logs | ✓ | ✓ |
| Restart 5G | ✓ | ✓ |
| Edit config | ✓ | |
| Edit firewall | ✓ | |
| View routes | ✓ | |
| Manage users | ✓ | |
## Install and run
### On the device (after main install)
```bash
# Install Python and Flask (Alpine)
apk add python3 py3-flask
# If you used scripts/install.sh, Web GUI is already under /usr/local/share/5g-webgui
# Enable and start the service:
rc-update add 5g-webgui default
service 5g-webgui start
# Or run manually (foreground)
cd /usr/local/share/5g-webgui && ./run.sh
```
### From repo (development)
```bash
cd web
pip install -r requirements.txt # or: apk add py3-flask
python3 app.py
# Open http://localhost:5000
```
## Security
- Set **SECRET_KEY** in production: `export SECRET_KEY="your-random-secret"` before starting the app (or in the OpenRC service).
- Use HTTPS in production (put the app behind nginx/caddy with TLS).
- Change default admin and support passwords immediately.
## SQLite database
The Web GUI uses **SQLite** (`web/data/alpine5g.db`) for:
- **users** login accounts (admin/support); migrated from `users.json` on first run if that file existed.
- **iptables_rules** firewall rules (table, rule line, enabled, order). On first load, if the DB is empty, rules are imported from `/etc/iptables/rules.v4`.
- **static_routes** static routes (destination, gateway, dev, metric). Apply runs `ip route add` for each enabled route.
Firewall and Routes pages in the GUI list/add/edit/delete from the DB and provide an **Apply** button to write iptables and run `iptables-restore`, or run `ip route add` for routes.
## Files
| Path (on device) | Purpose |
|-------------------------------|----------------------------|
| `/usr/local/share/5g-webgui/` | App and static files |
| `/usr/local/share/5g-webgui/data/alpine5g.db` | SQLite DB (users, rules, routes) |
| `/etc/init.d/5g-webgui` | OpenRC service |
| `/var/log/5g-webgui.log` | Service log |
## Troubleshooting: Modem not up
If the modem/WAN is not coming up (Status shows WAN state DOWN, no IP, or “No modem AT data”):
**On the device**, run:
```bash
/usr/local/bin/diag-modem-up.sh
```
(or `./scripts/diag-modem-up.sh` from the repo). It reports:
- **5g-router service** status
- **Config** (WAN_IF, AT_PORT, APN)
- **Modem USB** (lsusb Fibocom; Mode 40 vs 41)
- **WAN interface** (exists, state, IP)
- **Default route** (via 5G or other)
- **AT port** (exists, AT response OK?)
- **Last log lines** from `/var/log/5g-router.log`
- **Ping** test and suggested fixes
Use this to see why connect-5g.sh failed (e.g. AT port not ready, wrong USB mode, no modem IP).
## Troubleshooting: No modem AT data
If the Status page shows **“No modem AT data (check AT port)”**, run the diagnostic **on the device** (SSH or console):
```bash
/usr/local/bin/diag-at-port.sh
```
(or `./scripts/diag-at-port.sh` from the repo). It reports:
- User and groups (whether youre in **dialout** for serial access)
- Serial devices (`/dev/ttyUSB*`, permissions)
- Modem in `lsusb` (Fibocom / 0e8d)
- Config `AT_PORT` and whether it exists
- Raw **AT** probe on each ttyUSB (which port returns **OK**)
- Result of `modem-status-at.sh`
**Typical fixes:** Add the web server user (e.g. the one running gunicorn) to group **dialout**; set `AT_PORT` in `/etc/5g-router.conf` to the port that responds (e.g. `/dev/ttyUSB0`); ensure modem is in USB mode 40 (RNDIS) so the AT port is present.
## Optional: run behind reverse proxy
Example with **nginx** (apk add nginx):
```nginx
server {
listen 80;
server_name router.local;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
Then access via `http://router.local` (port 80) instead of port 5000.