An open API service indexing awesome lists of open source software.

https://github.com/coo1white/cool-tunnel-server

Self-hosted PHP control plane
https://github.com/coo1white/cool-tunnel-server

filamentphp frankenphp self-hosted

Last synced: 11 days ago
JSON representation

Self-hosted PHP control plane

Awesome Lists containing this project

README

          

# cool-tunnel-server

[![License: AGPL-3.0-only](https://img.shields.io/badge/license-AGPL--3.0--only-1c5cdc)](./LICENSE)
[![LTSC-Heng Draft](https://img.shields.io/badge/license--draft-LTSC--Heng-111111)](./LTSC-HENG-LICENSE-DRAFT.md)
[![Latest release](https://img.shields.io/badge/release-v0.22.2-1c5cdc)](https://github.com/coo1white/cool-tunnel-server/releases/tag/v0.22.2)
[![CI](https://github.com/coo1white/cool-tunnel-server/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/coo1white/cool-tunnel-server/actions/workflows/ci.yml)
[![Audit](https://github.com/coo1white/cool-tunnel-server/actions/workflows/audit.yml/badge.svg?branch=main)](https://github.com/coo1white/cool-tunnel-server/actions/workflows/audit.yml)

Open-source, self-hosted proxy server for a Debian VPS.

Cool Tunnel Server runs Caddy, sing-box, VLESS + Reality, a Next.js
admin web app, and a Bun/Hono API with Better Auth and SQLite in Docker
Compose. You point a domain at your VPS, install the stack, create user
accounts in the admin UI, and connect devices through per-user
subscription URLs.

It is a VPS-hosted VPN alternative for people who want to own and audit
their server. It is not a managed VPN service: you are responsible for
the VPS, domain, updates, backups, provider terms, and local law.

> **šŸ‘‹ New here? Start gentle.**
> - **I want to run my own server** (but I'm not fluent in SSH/Docker/DNS) →
> [**Beginner's guide**](./docs/beginners-guide.md) explains what you need,
> what it costs, and what each step does, then hands you to the exact
> commands.
> - **I want to work on the code** → [**Your first contribution**](./docs/first-contribution.md)
> takes you from a fresh clone to an open pull request.
>
> Comfortable already? The [Quickstart](#quickstart) below is the fast path.

## What You Get

- **Next.js admin UI** for accounts, settings, health, audit history,
and subscription URLs.
- **Bun/Hono admin API** with Better Auth, RBAC, and SQLite storage.
- **Private VLESS + Reality endpoint** generated from admin state — the
live sing-box config re-renders automatically on every account change,
with a grace window on UUID rotation so clients aren't dropped mid-rotate.
- **`ct` operator CLI** for install, update, doctor, doctor auto-fix, backup,
restore, rollback, support bundles, and config validation/preview.
- **Docker Compose runtime** with Caddy SNI routing, sing-box,
`admin-api`, `admin-web`, and an allowlist-only `docker-proxy` that
keeps the Docker socket out of the panel process.
- **Release-pinned Docker image bundles** (one per architecture) with
`SHA256SUMS` verification.
- **No local runtime builds on the VPS** during normal install/update:
the server downloads verified release images and loads them with
Docker.
- **Privacy-first diagnostics**: project health checks, support bundles, audit
exports, and key-center metadata redact secrets and must not log per-user
destinations or track users.
- **Per-account data metering** — each account's traffic is measured, its
data balance counts down as it's used, and the admin panel shows usage +
"last seen". Track usage per account without spreadsheets.
- **Fast on bad long-haul links** — the install bakes in BBR + high-throughput
kernel tuning, and an optional second transport (**Hysteria2 / QUIC over UDP**)
that your client auto-selects by latency, so traffic slides to whichever path
is faster right now. (Distance still sets the floor — put a node near your
users; multiple nodes are supported.)
- **Self-healing + self-diagnosing for non-experts** — the panel never
blank-screens (it shows a clear "what to do" message instead), common host
issues fix themselves on restart, and **`ct doctor`** checks everything and
tells you in plain English what to fix — no log-reading required.
- **Operational recovery surfaces** — rollback previews, config previews,
redacted support bundles, node health trends, and the admin **System** page
make risky VPS operations inspectable before they change state.
- **Operator experience polish** — the admin dashboard includes a guided
onboarding checklist, in-app operational notifications, clearer node/traffic
hints, consistent admin primitives, and safer security/support-bundle
guidance.

## Requirements

| Need | Notes |
| --- | --- |
| Debian VPS | Debian 12 or newer; Debian 12 is the primary target |
| Root SSH or sudo | Required for Docker, firewall, and service setup |
| Domain name | Point an `A` record at the VPS public IPv4 |
| Open ports | `80/tcp` for ACME and `443/tcp` for panel/proxy traffic |
| Small VPS | Designed for about 1 vCPU / 1 GB RAM deployments |
| Disk | ~25 GB free recommended — the ~420 MB image bundle, the loaded Docker images, and update headroom can push a smaller disk to "tight" in `ct doctor` |

New to VPS, ACME, or Docker terms? See the
[glossary](./docs/glossary.md).

## Quickstart

SSH to a fresh Debian VPS as root:

```sh
ssh root@your.vps.public.ip
```

Install base tools and open the firewall:

```sh
apt update && apt -y upgrade
apt install -y ca-certificates curl git gnupg ufw dnsutils chrony fail2ban unattended-upgrades

ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable
```

Bootstrap the latest release:

```sh
LATEST="$(curl -fsSLI -o /dev/null -w '%{url_effective}' https://github.com/coo1white/cool-tunnel-server/releases/latest | sed 's#.*/##')"
BRANCH="${LATEST}" /bin/bash -c "$(curl -fsSL "https://raw.githubusercontent.com/coo1white/cool-tunnel-server/${LATEST}/scripts/bootstrap.sh")"
```

The bootstrap auto-generates `BETTER_AUTH_SECRET` **and** the
`REALITY_PRIVATE_KEY` / `REALITY_PUBLIC_KEY` pair into `.env`, so a fresh install
needs no manual key step. (To rotate the Reality keys later, run
`ct reality-keygen` and paste the new pair into `.env`.)

Configure and install — set your domains + ACME email in `.env`, then install:

```sh
cd /opt/cool-tunnel-server
nano .env
./ct install
./ct doctor
```

Release installs download the verified Docker image bundle for the VPS
CPU architecture and load it in one step. The VPS uses `docker load`;
it does not build Rust, Bun, Go, Node/Next, or Docker images
during `ct install` or `ct update`.

Set at least these `.env` values before running `./ct install`:

| Key | Meaning |
| --- | --- |
| `DOMAIN` | Proxy/base domain |
| `PANEL_DOMAIN` | Admin panel hostname, usually `panel.` |
| `ACME_EMAIL` | Email for certificate renewal notices |
| `REALITY_PRIVATE_KEY` | `private_key` from `ct reality-keygen` (43-char base64url) |
| `REALITY_PUBLIC_KEY` | Matching `public_key` from the same command |

For the full install walkthrough, expected output, DNS checks, and
recovery hints, read [GETTING_STARTED.md](./GETTING_STARTED.md).

## Panel Login and Account Setup

Open the admin UI:

```text
https:///login
```

Create the first owner from the VPS. The token is one-time only and
expires:

```sh
cd /opt/cool-tunnel-server
ct admin bootstrap
```

Open the root-only setup URL from the generated file once; the API
stores the one-time token in an HttpOnly cookie and immediately
redirects to a clean `/setup` page. Create the owner account, then log
in at `/login`. Public signup is disabled by default. After that, create
a proxy account:

```text
Users -> New proxy account -> Save
```

After the account is created, open the account row's **Subscription
URL** action and copy the **Import URL** into the Cool Tunnel client.
That URL contains the per-account subscription token, so treat it like a
password. If you lose the URL, open the same action again; if you rotate
the UUID, copy the fresh URL after rotation.

If you need to recover access:

```sh
cd /opt/cool-tunnel-server
printf '%s\n' '' | ct admin create-owner --email you@example.com --username you --password-stdin
printf '%s\n' '' | ct admin users reset-password --id --password-stdin
```

## Daily Operation

Most VPS operation should stay inside the `ct` command:

```sh
cd /opt/cool-tunnel-server

ct doctor # health dashboard with PASS / WARN / FAIL remediation
ct doctor --fix --dry-run # preview conservative auto-repairs
ct backup # snapshot DB + .env + ACME certs
ct update # update to the current release and restart safely
ct rollback status # inspect update rollback state
ct support-bundle --dry-run --json
```

### Copy-Paste VPS Update

SSH into an already-installed server and paste this — it backs up, updates to the
latest release, health-checks, and prints your admin URL:

```sh
sudo bash -lc 'set -euo pipefail; cd /opt/cool-tunnel-server; test -f .env || { echo "ERROR: .env is missing. This looks like a fresh or unfinished install, not an update."; echo "Run: cd /opt/cool-tunnel-server && cp .env.example .env && nano .env && ./ct install"; exit 1; }; ./ct backup; ./ct update; ./ct doctor; echo; echo "Admin URL:"; . ./.env; echo "https://${PANEL_DOMAIN}/login"; echo; echo "If first-owner setup is still needed, run: ct admin bootstrap"'
```

Healthy when `ct doctor` ends in **`0 FAIL`**. After first login, open
`/system` to walk the onboarding checklist: owner account, panel domain,
TLS/ACME, runtime services, sing-box render, Reality keys, access accounts,
redeem codes, rollback, support bundle, and key-center readiness. The admin
pages use the same compact header, stat, badge, table, empty/error, and risk
hint patterns so Dashboard, System, Nodes, Users, Settings, Security, Audit, and
Notifications scan the same way. For the step-by-step beginner walkthrough, the
normal-looking log lines, version-jump notes, turning on Hysteria2, rollback,
and recovering a lost `.env`, see the
**[Update guide (wiki)](https://github.com/coo1white/cool-tunnel-server/wiki/Updating-and-backups)**.

## Speed and Metering

On by default for new installs; existing servers opt in via `.env` + `ct update`.
Run `ct doctor` any time to see their status in plain English.

### Per-account usage metering

Each account's traffic is measured and its **data balance** counts down as it's
used; the panel shows **Usage** and **Last seen**. On by default
(`CT_TRAFFIC_STATS=true`). Verify with:

```sh
ct doctor # look for: Traffic metering: ON — N account(s) metered
```

Push some traffic through a key, wait ~30s, and the numbers move. When an
account's balance hits 0, its key pauses automatically until you add more data
balance or the user redeems an access code. The customer portal uses neutral
Access / Entitlement / Quota / Redeem / Expires wording; there is no public
pricing, checkout, or payment flow.

### Make it faster on a long route (BBR + Hysteria2)

Two speed-ups for a far-away server (e.g. crossing continents):

- **BBR kernel tuning** is baked into the installer — nothing to do; `ct doctor`
shows `Congestion ctl: bbr`.
- **Hysteria2** adds a second transport (QUIC over UDP). Your client measures
both and uses whichever is faster *right now*. On by default for new installs.
- **Brutal congestion control** (v0.19.14+, optional) — tell Hysteria2 your real
bandwidth and it switches from BBR to a fixed-rate, loss-tolerant sender.
Measured benefit is **conditional**: on a long-haul, lossy path (ā‰ˆ300 ms RTT,
5–20 % loss) Brutal delivered **~1.2–1.4Ɨ BBR's throughput** in our lab; on a
clean or short-hop link BBR is already as good, and a cap set *below* your real
capacity will *reduce* speed. Only turn it on if your users are far and the
path is lossy. Step 5 below.

#### Open Hysteria2 on an existing server (one time, ~2 min)

1. **Get the new code** (the QUIC sing-box + the UDP/443 port map):
```sh
cd /opt/cool-tunnel-server && ./ct backup && ./ct update
```
2. **Generate the cert + enable it** — run this block **once** (it appends 4 lines to `.env`):
```sh
d=$(mktemp -d)
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes \
-keyout "$d/k.pem" -out "$d/c.pem" -days 3650 -subj "/CN=cool-tunnel.local" \
-addext "subjectAltName=DNS:cool-tunnel.local" 2>/dev/null # SAN is required
{ echo "CT_HYSTERIA_ENABLED=true"; echo "CT_HYSTERIA_PORT=443"; \
echo "HYSTERIA_CERT_B64=$(base64 -w0 <"$d/c.pem")"; \
echo "HYSTERIA_KEY_B64=$(base64 -w0 <"$d/k.pem")"; } >> .env
rm -rf "$d"
```
3. **Apply it** (re-renders the proxy config; sing-box hot-reloads — no restart needed):
```sh
./ct render singbox && ./ct doctor # confirm: Hysteria2: on (QUIC/UDP :443)
```
4. **Open UDP/443 in your firewall.** Easy to miss — TCP/443 is already open, but
Hysteria2 is **UDP**:
```sh
sudo ufw allow 443/udp # only if you run ufw
```
Also allow **UDP 443** in your VPS provider's cloud firewall / security group
if it has one (Oracle, AWS, GCP…). Many providers leave all ports open — then
there's nothing to do.
5. **(Optional, v0.19.14+) Hold throughput for 4K.** Set your plan's **real**
bandwidth and Hysteria2 uses the Brutal controller instead of BBR — it keeps
the pipe full on a lossy path so a 4K stream buffers ahead. Both-or-neither;
over-stating capacity causes loss, not speed, so keep these at/below what the
server actually delivers:
```sh
{ echo "CT_HYSTERIA_UP_MBPS=50"; echo "CT_HYSTERIA_DOWN_MBPS=200"; } >> .env # ← your real Mbps
./ct render singbox && ./ct doctor
```
Leave them unset to keep adaptive BBR (the safe default).

**Use it from a client:** take your account's **Subscription URL** (panel →
**Users** → the account → **Copy**) and **add `/singbox.json`** to the end:

```
https://panel.yourdomain.com/api/v1/subscription//singbox.json
```

Import that into a **sing-box** app (SFA on Android, SFI on iOS, SFM on macOS, or
the `sing-box` CLI) — it carries *both* transports and auto-selects the faster
one. The plain VLESS subscription keeps working unchanged; this just adds the
faster UDP option. **Safe if UDP is blocked** anywhere — the client just stays on
VLESS/TCP, nothing breaks.

> **Latency vs. throughput:** these tune *throughput*. The biggest cut to
> *latency* (ping) is a server closer to your users — cool-tunnel supports
> multiple nodes, so add one in the right region on the **Nodes** page. The
> **System** page summarizes service health, node probe trends, config state,
> rollback/support-bundle reminders, and recent audit activity.

Active node probes record bounded history and currently check TCP reachability
and latency to the node endpoint. UDP/protocol-specific probes are surfaced as
not checked until a future transport-aware probe lands.

## What Runs

| Service | Role |
| --- | --- |
| `caddy` | Public `:443` front door, ACME, TLS, and SNI routing |
| `singbox` | VLESS + Reality proxy service |
| `admin-api` | Hono/Bun API, Better Auth, SQLite store, subscription endpoint, and render actions |
| `admin-web` | Next.js admin dashboard |
| `docker-proxy` | Allowlist-only Docker-socket forwarder — the **only** service mounting the socket (read-only). It permits just container health reads and restarts, so `admin-api` never holds socket access that could reach the host daemon |
| `redis` | Internal BullMQ backend for scheduled admin jobs such as audit/traffic retention; startup failure is logged without taking unrelated API routes down |

The control plane is the Better-T-Stack monorepo: `apps/web`,
`apps/api`, `packages/shared`, `packages/db`, `packages/security`,
`packages/config`, the TypeScript operator CLI, `singbox-core`, and the
shared Rust `ct-protocol` crate. See [docs/architecture.md](./docs/architecture.md)
for diagrams and design rationale.

Operational boundaries are explicit:

- `ct` owns host operations: install, update, rollback, backup, restore,
doctor, support bundles, host config staging, Docker Compose orchestration,
and release/bundle validation.
- `admin-api` owns control-plane operations: auth/session/RBAC, users,
accounts, nodes, portal, settings, audit, status summaries, safe render
requests, and non-destructive diagnostics.
- `admin-web` owns UI only. It calls `admin-api`, parses shared API contracts,
never shells out, never assumes host paths, and never receives raw secrets
except one-time values intentionally returned for setup or node enrollment.

## Security

Defense-in-depth for protecting admin and proxy-user data:

- **Docker-socket isolation.** Only the minimal `docker-proxy` holds the Docker
socket (read-only) and forwards just container health reads and restarts, so a
panel compromise cannot reach the host daemon to escape the container.
- **RBAC with peer-admin limits.** Owner / admin / operator / viewer roles are
enforced in the API and re-checked in the data layer; admins manage only
operator/viewer (never a peer admin or owner), and the last active owner
cannot be removed.
- **Auth hardening.** Argon2id password hashing, secure-by-default forced
rotation for admin-created accounts, per-IP **and** per-account login
throttling against brute-force/spray, session-bound CSRF tokens, and
HSTS + a strict CSP on the panel.
- **Secrets at rest.** `.env` and the SQLite database (with its WAL/SHM
sidecars) are mode `0600`; subscription tokens are unforgeable HMACs; audit
entries and logs redact secret values.
- **Security operations.** The admin **Security** page can export redacted audit
records and preview key rotations. Node sync secrets can be rotated with an
audit trail; session and Reality key material stay manual-only because they
can invalidate active users or client profiles.
- **Verified transport TLS.** Reality borrows a real cover site's certificate,
so there's no server cert to leak or mis-issue; the optional Hysteria2
transport pins a self-signed cert, and config load refuses a cert with no
SubjectAltName (a CN-only cert modern clients silently reject) instead of
failing closed with no visible cause.

Found something? See [SECURITY.md](./SECURITY.md).

## Project Rule

The operator experience should stay simple:

```text
install simple -> update simple -> doctor simple -> fix simple
```

That means `ct install`, `ct update`, and `ct doctor` are the normal
surface, and diagnostics should name the next command to run when
something fails.

## Release

Latest stable server release: `v0.22.2`.

Server releases own the runtime assets used by clients:

- server package/source release;
- `SHA256SUMS`;
- `ct-operator-linux-x64` and `ct-operator-linux-arm64`;
- per-architecture `cool-tunnel-server-images-linux-*.tar.gz` image bundle
for VPS `ct install` and `ct update` — the sing-box engine and `singbox-core`
ship baked inside it (no separate `singbox-core-linux-*` download).

The macOS client runtime (`sing-box-*-darwin-universal` + `cool-tunnel-core-v*`)
is a stable pin: published only on the release named in
[`manifests/client-runtime.upstream.json`](manifests/client-runtime.upstream.json),
not on every release. The client bundles its own copies and updates from that
pinned release, so clients and server stay on compatible parts.

## Documentation

| Goal | Read |
| --- | --- |
| New to all this — deploy gently | [docs/beginners-guide.md](./docs/beginners-guide.md) |
| Install for the first time | [GETTING_STARTED.md](./GETTING_STARTED.md) |
| Debian VPS install reference | [docs/installation-debian.md](./docs/installation-debian.md) |
| Update, backup, rotate, debug | [docs/operations.md](./docs/operations.md) |
| Go faster + meter usage | [Speed & metering (wiki)](https://github.com/coo1white/cool-tunnel-server/wiki/Speed-and-metering) |
| Troubleshoot install/update/doctor | [docs/operator-runbook.md](./docs/operator-runbook.md) |
| Smoke-test a release | [docs/test-vps.md](./docs/test-vps.md) |
| Understand the architecture | [docs/architecture.md](./docs/architecture.md) |
| Look up terms | [docs/glossary.md](./docs/glossary.md) |
| Make your first code change | [docs/first-contribution.md](./docs/first-contribution.md) |
| Contribute (full conventions) | [CONTRIBUTING.md](./CONTRIBUTING.md) |
| Report a security issue | [SECURITY.md](./SECURITY.md) |

The operator CLI also includes built-in help:

```sh
ct help
```

## License + Posture

- Active license: [AGPL-3.0-only](./LICENSE).
- Stricter LTSC-Heng draft:
[LTSC-HENG-LICENSE-DRAFT.md](./LTSC-HENG-LICENSE-DRAFT.md).
- No user tracking. Internal health metrics are allowed; per-user
destination logging is forbidden.
- Read [Disclaimer.md](./Disclaimer.md) before production use.

Bundled upstream components keep their own licenses. See
[NOTICE](./NOTICE) and
[THIRD_PARTY_LICENSES.md](./THIRD_PARTY_LICENSES.md).

Jurisdiction: Wyoming, USA. Steward: coolwhite LLC.