npm package: @simplified-org/tunnel
Description: CLI to expose a local port via simplified-org.com with one command
Latest version: 1.1.0

## simplified-tunnel

Portable tunneling stack built on FRP, consisting of:
- **FRP server** (`frps`): terminates tunnels and routes subdomains to clients.
- **Tunnel Manager API + admin UI** (`src/`): hands out subdomains, tracks activity, exposes WebSocket updates, and serves a localhost-only dashboard.
- **CLI** (`simplified`): one-command client that requests a tunnel and runs `frpc` for you.

Use this README to self-host the full stack (Docker or bare metal), understand the architecture, and see operational quirks.

---

## How the system behaves
1) A client (via CLI or HTTP) asks the Tunnel Manager for a subdomain. If none is provided, the manager generates a memorable adjective-noun (optionally with numeric suffix).  
2) The manager returns FRP connection info plus the public URL (`https://<subdomain>.<FRP_DOMAIN>`).  
3) The client starts `frpc` to connect to the FRP server using the shared token.  
4) FRP server (`frps`) accepts the tunnel and vhosts HTTP traffic for that subdomain to the client’s local port.  
5) All requests are logged; admin dashboard/WebSocket streams live stats and logs (localhost-only by default).

---

## Repo layout
- `Dockerfile`, `start.sh`, `frps.ini.template`, `docker-compose.yml` – FRP server container + compose with API.  
- `bin/simplified.js`, `lib/*` – CLI entrypoint, tunnel API client, frpc downloader/runner.  
- `src/` – Tunnel Manager API + admin UI.  
  - `server/index.js` HTTP + WebSocket server, middleware, admin gating.  
  - `server/api.js` REST routes.  
  - `server/tunnelManager.js` in-memory + file-backed tunnel state, history, WebSocket broadcasting.  
  - `server/logger.js` request/tunnel logging to file + buffer + WS.  
  - `server/randomWords.js` subdomain generation/validation.  
  - `admin/` static dashboard (served locally).  
  - `data/` runtime state/logs (gitignored).  
- `test/webapp` – simple sample webapp + FRP binaries for local testing.

---

## Prerequisites for self-hosting
- A domain; add a wildcard DNS record `*.your-domain` pointing to the host running `frps` (DNS-only/proxied off).  
- Open ports: `80` (HTTP vhost) and `7000` (FRP control).  
- A shared FRP token (`FRP_TOKEN`).  
- Node.js 18+ if running the API/CLI directly; Docker if containerizing.  
- TLS termination handled externally (e.g., Cloudflare “Full” mode) — this stack serves HTTP only.

---

## Option A: Docker Compose (FRP server + Tunnel Manager)
```bash
cp docker-compose.yml docker-compose.local.yml
# edit env values in the file or export FRP_TOKEN/FRP_SERVER_ADDR etc.
docker compose -f docker-compose.local.yml up --build
```
Services:
- `frps` (ports 80, 7000) built from the Dockerfile; requires `FRP_DOMAIN`, `FRP_BIND_PORT`, `FRP_HTTP_PORT`, `FRP_TOKEN`, optional `FRP_MAX_POOL`.
- `api` (port 4000) runs `/src` with `npm install && npm start`; set `ADMIN_HOST=0.0.0.0` only if you intend to expose the dashboard and secure it yourself (defaults to localhost when run bare).

Recommended extras:
- Mount `./src/data` as a volume to keep tunnel/log/history data across restarts.
- Set `FRP_SERVER_ADDR` to the public IP/DNS of the `frps` host (defaults to `34.227.103.55` in compose; change this).

---

## Option B: Docker only (FRP server)
Build and run the FRP server container:
```bash
docker build -t simplified-tunnel-frps .
docker run --rm -p 80:80 -p 7000:7000 \
  -e FRP_DOMAIN=your-domain.com \
  -e FRP_BIND_PORT=7000 \
  -e FRP_HTTP_PORT=80 \
  -e FRP_TOKEN=your-token \
  -e FRP_MAX_POOL=200 \
  simplified-tunnel-frps
```
Then run the Tunnel Manager separately (Node or another container) pointed at this FRP endpoint.

---

## Option C: Bare metal
**FRP server**
```bash
export FRP_DOMAIN=your-domain.com
export FRP_BIND_PORT=7000
export FRP_HTTP_PORT=80
export FRP_TOKEN=your-token
# place a compatible frps binary in PATH or alongside start.sh
./start.sh
```

**Tunnel Manager API + admin**
```bash
cd src
npm install
TUNNEL_MANAGER_PORT=4000 \
ADMIN_HOST=127.0.0.1 \
FRP_DOMAIN=your-domain.com \
FRP_SERVER_ADDR=<frps-host> \
FRP_BIND_PORT=7000 \
FRP_TOKEN=your-token \
npm start
```
Admin dashboard lives at `http://127.0.0.1:4000/admin` (localhost-only). APIs under `/api`.

---

## CLI (client) usage against your deployment
```bash
npm install -g simplified
SIMPLIFIED_API_BASE=http://<api-host>:4000 simplified --port 3000
# optional: --subdomain my-site
```
- Downloads `frpc` from GitHub releases (cached at `~/.simplified/frpc`).
- Supported platforms: macOS (arm64/amd64), Linux (arm64/amd64), Windows (amd64/arm64).

---

## Configuration reference
**FRP server (`start.sh` / Dockerfile / compose)**
- Required: `FRP_DOMAIN`, `FRP_BIND_PORT`, `FRP_HTTP_PORT`, `FRP_TOKEN`
- Optional: `FRP_MAX_POOL` (default 200), `FRPS_BIN` (custom binary path)

**Tunnel Manager (`src/config.js`)**
- `TUNNEL_MANAGER_PORT` (default 4000)
- `ADMIN_HOST` (default `127.0.0.1` — set to `0.0.0.0` only when you secure access)
- `FRP_DOMAIN` (default `simplified-org.com`)
- `FRP_SERVER_ADDR` (default `localhost`)
- `FRP_BIND_PORT` (default `7000`)
- `FRP_TOKEN` (default `supersecret`)
- `MAX_TUNNELS_PER_USER` (default 5)
- `TUNNEL_TIMEOUT` (default 24h, ms)
- Data files live in `src/data`: `tunnels.json`, `history.json`, `requests.log` (gitignored).

**CLI**
- `SIMPLIFIED_API_BASE` overrides the API URL (defaults to `https://api.simplified-org.com`).

DNS/TLS:
- Create wildcard A record `*.<FRP_DOMAIN>` to the `frps` host.
- TLS terminates at your DNS/CDN; `frps` serves HTTP only.

---

## Code architecture and flow
- **CLI**  
  - `bin/simplified.js`: parses flags, calls tunnel API, ensures `frpc`, spawns it.  
  - `lib/api.js`: POST `/api/tunnels`, handles errors.  
  - `lib/downloader.js`: downloads/extracts `frpc` (v0.61.0) per OS/arch into `~/.simplified`.  
  - `lib/tunnel.js`: spawns `frpc http -s <addr> -P <port> -l <local> --sd <subdomain> -t <token>`.
- **Tunnel Manager (src/server)**  
  - `index.js`: Express + WS server; logs every request; gates admin routes to localhost; serves `/admin`; health/root endpoints.  
  - `api.js`: REST routes for tunnel CRUD, availability checks, suggestions, stats, admin logs/history/config.  
  - `tunnelManager.js`: in-memory map + JSON persistence; random subdomains; conflict resolution; TTL cleanup; WebSocket broadcasts of tunnels/stats; modes (`production` vs `local`).  
  - `logger.js`: request/tunnel log levels, file append, in-memory buffer, WS fanout, log filtering/cleanup APIs.  
  - `randomWords.js`: adjective/noun lists, suffixes, validation and alternative generation.  
  - `config.js`: env parsing and defaults.  
  - `admin/`: static dashboard (dark theme) consuming `/ws` for live updates.  
- **FRP server**  
  - `start.sh`: validates env, renders `frps.ini` from template via `envsubst`, execs `frps`.  
  - `Dockerfile`: alpine base, downloads FRP `frps` (default v0.52.3) for detected arch, runs as non-root.

---

## Quirks and operational notes
- Admin access is localhost-only in code. In `docker-compose.yml`, `ADMIN_HOST` is set to `0.0.0.0`; you must secure that port yourself (firewall, auth, VPN).  
- Data persistence: `src/data` is not volume-mounted by default in compose—mount it to avoid losing tunnels/logs/history.  
- FRP versions differ: container pulls `frps` v0.52.3 while the CLI fetches `frpc` v0.61.0; align versions if you see protocol warnings.  
- Stats persistence is throttled (writes every 10 requests) to reduce disk I/O.  
- Tunnel expiry defaults to 24h; expired tunnels are removed and added to history.  
- Subdomain suggestions reserve names like `admin`, `api`, `localhost`; conflicts return alternatives.  
- HTTPS termination is out of scope; keep DNS/CDN in “DNS only” (no orange cloud) unless you handle TLS separately.  
- CLI cache location: `~/.simplified/frpc`; removal is manual.  
- WebSocket and admin endpoints reject non-local IPs; log entries include IP/User-Agent for auditing.  
- Compose uses a hardcoded example IP `34.227.103.55` for `FRP_SERVER_ADDR`; change it to your host.

---

## How to propose improvements
Ideas based on current code paths:
- Align `frps`/`frpc` versions and make the version configurable in both Dockerfile and CLI.  
- Add auth to the admin dashboard and optionally allow remote access with proper protection.  
- Persist data to a real store (SQLite/Postgres) and run migrations instead of JSON files.  
- Add rate limits / per-IP quotas in `requestTunnel` to prevent abuse.  
- Expose metrics (Prometheus) and health for `frps` alongside the API health check.  
- Support HTTPS termination (LetsEncrypt/ACME) or behind-proxy documentation.  
- Provide official images for the API (not just compose-on-the-fly).  
- Add integration tests that spin up `frps` + API + sample `frpc` to validate end-to-end.

---

## Testing the sample webapp locally
```bash
cd test/webapp/backend
npm install
npm start   # serves http://localhost:3000
```
Use the CLI with `--port 3000` against your running manager + `frps` to publish it.

---

## Uninstalling the CLI
```bash
npm uninstall -g simplified
rm -rf ~/.simplified/frpc
```