Fast, safe, and payload-transparent routing for DNS and TLS transports.
ΩΨ§Ψ±Ψ³Ϋ Β· CottenMax beta Β· Install Β· Usage Β· Commands Β· Docker Β· Upgrade Β· Uninstall Β· Credits
CottenRouter is a lightweight front router that lets multiple DNS-tunnel backends share one server, one IP address, and public port 53. It reads the first DNS question, selects the longest matching configured suffix, and forwards the packet to the correct private backend.
It can also share public DoT and HTTPS ports by reading TLS SNI and passing each encrypted stream through unchanged. CottenRouter does not terminate TLS, add DNS labels, or add protocol bytes, so it does not reduce tunnel MTU.
Key features:
- Concurrent routing for several DNS-tunnel projects on one public endpoint
- UDP DNS, DNS-over-TCP, DoT, DoH, NaiveProxy, and StunTLS passthrough
- Guided terminal UI for installation, configuration, monitoring, and removal
- Transactional host installation with snapshots, health checks, and rollback
- Fixed queues, rate limits, timeouts, connection caps, and resource controls
- Loopback-only administration API and a dashboard refreshed every two seconds
- Rootless, read-only, shell-free Docker image
- Checksum-verified
linux/amd64andlinux/arm64release binaries
flowchart LR
C[DNS / TLS clients] -->|UDP/TCP 53, DoT, HTTPS| R[CottenRouter]
R -->|Longest DNS suffix| D1[CottenDNS :5301]
R -->|Longest DNS suffix| D2[MasterDnsVPN :5302]
R -->|Longest DNS suffix| D3[StormDNS :5303]
R -->|Longest DNS suffix| D4[thefeed :5304]
R -->|Longest DNS suffix, UDP| D6[Soar :5305]
R -->|Longest DNS suffix, UDP| D7[Soar 2.2 :5306]
R -->|Longest DNS suffix, UDP/TCP| D8[WhiteDNS Panel :5307]
R -->|DNS route or TLS SNI| D5[SlipGate services]
R -.->|Loopback only| A[Admin API :9088]
| Host installation | Docker | |
|---|---|---|
| Routing core | β | β |
| Guided backend installers | β | β |
| Interactive control deck | β | β |
| systemd and firewall integration | β | β |
| Rootless runtime | β | β |
| Best for | A fresh server managed end to end | Existing container infrastructure |
WhiteDNS Panel is a separate Easy Install entry alongside Soar and Soar 2.2. The default installer resolves the newest published stable panel-v* release independently of the Router version, selects the Linux AMD64 or ARM64 bundle for the host, and verifies the archive and embedded runtime checksums. The current integration is tested with Panel v0.14.4.
sudo cottenrouter install --project whitedns-panel --domain p.example.com --tcp
sudo cottenrouter update --project whitedns-panelLeave the TUI's Legacy beta archive field blank for the current stable release. The pinned Panel 0.5.1 CI archive remains available only through explicit --panel-beta-bundle selection; ordinary installs and updates do not choose it. Soar 2.2 remains the separate older standalone Lantern v0.2.2 option.
Use a separate, short delegated base zone for the Panel. Router forwards its account subdomains to the private backend. Current Panel accounts provide Soar, Cotten and MasterDNS with native per-engine settings. The standalone CottenDNS entry provides Normal and CottenMax with their existing shared domain/key.
Router imports the Panel's current serving zones on install, update, Configure and Advanced saves. After changing tunnel zones in the Panel, save its Router Configure form to refresh the routes. Preserve the existing identity base zone and private DNS port; serving zones may change without replacing account identities.
The first Panel install prompts for an administrator password; automated installs can pass --panel-password-file /path/to/protected-file (0600). Updates preserve the database, account keys, usage and chosen session limits. Router verifies archive/runtime checksums, the selected version and supported CLI contract, checks readiness, and restores the previous service and database if an upgrade fails.
Administration binds to loopback, normally 127.0.0.1:2054; occupied ports are skipped. Run sudo cottenrouter keys --project whitedns-panel to find the address, then forward it, for example ssh -L 2054:127.0.0.1:2054 root@SERVER. Router owns maintenance for this installation. Account management and backup downloads remain in the Panel; its standalone privileged updater and public HTTPS wizard are unavailable in Router mode.
For CottenDNS, use sudo cottenrouter update --project cottendns. Every operation resolves the latest published compatible release independently of Router's version, with no silent stale-version fallback.
Published standalone CottenDNS and CottenMax have no hidden eight-session cap: defaults are 2,048 sessions and expanded capacity raises them to at least 4,096. Panel uses the per-engine account limits and defaults provided by its selected release; Router preserves explicit administrator settings. These are session ceilings, not guaranteed user counts.
CottenMax is available for opt-in beta testing inside CottenDNS. It reuses the CottenDNS service, delegated domain, shared key and existing Router route. Select Normal / CottenMax beta in a compatible client; an updated CottenDNS server accepts both modes. No extra Router project, route or shared key is needed.
| Component | Beta availability |
|---|---|
| Router and CottenDNS source | Changes are on main: TaJirax/CottenRouter, WhiteDNS/CottenDNS and TaJirax/cottenDNS |
| Desktop client | Windows debug build, with Normal / CottenMax selection |
| Android client | Debug APKs, with profile mode selection |
| Published installers | Install the current CottenDNS release, supporting Normal and Max together |
These CI artifacts are temporary and may require a GitHub sign-in. No new Desktop or Android release was published. Keep Normal mode available as a fallback while testing real devices, mobile networks and long-running connections.
The updated Max clients support UDP and TCP DNS for live tunnel traffic. Auto probes both paths independently during resolver discovery and authenticated MTU scanning, then carries traffic over usable paths. The existing local device proxy and Router route stay the same; see the UDP/TCP transport and scan guide.
For more session headroom, choose expanded in the CottenDNS form's Capacity field, or pass --expanded-capacity to its install/configure command. This opts in to at least 4096 server sessions, 1024 normal streams/session, and 512 Max streams/session; higher custom values are preserved. Max's higher cap requires updated server and clients. keep preserves current values. Rate and byte limits stay in force. These are configured ceilings, not a guaranteed user count; see capacity and limits.
Use the current Router release for the updated TUI and managed installer checks. Install or update the CottenDNS backend from the TUI or run sudo cottenrouter update --project cottendns; the operation independently resolves the latest published CottenDNS version. The app scanner shows live UDP/TCP paths, and Max connection state waits for authenticated transfer proof.
For an existing Router-managed backend, preserve its service working directory, configuration, key and private loopback listener ports when replacing its server binary. CottenRouter must retain public port 53; do not move the backend to that port. Reuse the existing CottenDNS route, then select Max in the client and reconnect. Very long legacy domains can exceed Max's domain limits.
The Router Guide tab and CottenDNS install form explain the shared domain/key and compatible-server requirement. See terminal setup and mode switching and optional Max transports and tuning. DoH over HTTP/3 and DoQ are client-to-resolver options; they do not add QUIC passthrough or authoritative QUIC listeners to CottenRouter.
Soar and Soar 2.2 remain separate backends with their own zones and keys. CottenMax is the mode inside CottenDNS; Soar 2.2 remains the older Lantern v0.2.2 option described below.
For a host installation you need:
- Linux with
systemd 245+ rootorsudoaccess- A public IP and at least one unique domain or subdomain for each backend
- Ubuntu 20.04+, Debian 11+, or RHEL/Rocky/AlmaLinux 9+ is recommended
amd64orarm64for a prebuilt binary; other architectures build from source
WSL is supported for development and testing only. Use a native Linux server for production.
curl -fsSL https://raw.githubusercontent.com/TaJirax/CottenRouter/main/scripts/install.sh | sudo bash
sudo cottenrouter tuiThe installer downloads the latest official release, verifies its checksum, checks required tools, systemd, disk space, and listeners, and waits for a successful health check. A failed installation restores the previous state. Running the command again performs an in-place upgrade.
To install the current checkout, including local changes and all seven backends, run these commands from the repository root on Linux or inside WSL:
sudo bash scripts/install.sh --local-source="$PWD"
sudo cottenrouter tuiOpen Install, select projects with Space, and press i for the guided queue. Tab moves fields; Enter on the last field installs. Each project needs its own delegated domain. Thefeed asks for at least one source in its native setup; SlipGate opens its native multi-tunnel wizard.
Install a specific release:
curl -fsSL https://raw.githubusercontent.com/TaJirax/CottenRouter/main/scripts/install.sh \
| sudo bash -s -- --version=v1.2.26Install the current development branch:
curl -fsSL https://raw.githubusercontent.com/TaJirax/CottenRouter/main/scripts/install.sh \
| sudo bash -s -- --channel=edgeBuild from source during installation:
curl -fsSL https://raw.githubusercontent.com/TaJirax/CottenRouter/main/scripts/install.sh \
| sudo bash -s -- --build-from-source| Installer option | Purpose |
|---|---|
--version=vX.Y.Z |
Install an exact tagged release |
--channel=stable |
Install the latest release; this is the default |
--channel=edge |
Build the latest default-branch commit for testing |
--build-from-source |
Compile locally instead of using a release binary |
--no-swap |
Skip the installer-owned emergency swap safeguard |
sudo cottenrouter tui| Key | Action |
|---|---|
Tab / Shift+Tab |
Switch Overview, Install, Ports and Guide tabs |
Space |
Select or deselect a project |
i |
Run guided installation for selected projects |
Enter / e |
Edit domains, private ports, TCP, DoT, and DoH |
a |
Open the complete native project configuration |
s |
Restart a project |
U |
Update an installed backend from its published channel |
u |
Detach a project while preserving its files |
x |
Permanently remove a project after confirmation |
v |
Show credential paths and public values |
V |
Reveal secrets after explicit confirmation |
Give every backend a unique delegated domain, for example cotten.example.com, master.example.com, storm.example.com, feed.example.com, and soar.example.com. Exact duplicate suffixes are rejected. Nested suffixes are supported and the longest match wins.
Select Soar in the project manager, or install it directly:
sudo cottenrouter install --project=soar --domain=soar.example.com
sudo cottenrouter keys --project=soarSoar builds the latest resolved official Lantern commit, provisions a compatible Go toolchain when needed, and creates soar-server on UDP 127.0.0.1:5305. Source builds need network access for Go and module downloads plus disk space for the compiler and build cache.
Soar 2.2 is a separate option using WhiteDNS's standalone setup 0.1.3 bundle with the older Lantern engine v0.2.2. It downloads a checksum-verified prebuilt amd64 binary and runs soar22-server on UDP 127.0.0.1:5306:
sudo cottenrouter install --project=soar22 --domain=soar22.example.com
sudo cottenrouter keys --project=soar22Both options can run together with separate delegated zones, ports, and keys. CottenRouter manages their services; WhiteDNS's native wizard, manager, and web panel are not run. Each option uses one delegated zone; backend TCP, DoT, DoH, extra domains, and chat domains are unavailable. Share each displayed public key with its clients; private seeds stay on disk and survive reinstallation. See Soar setup for configuration and verification.
sudo systemctl status cottenrouter
sudo journalctl -u cottenrouter -f
sudo cottenrouter healthz -config /etc/cottenrouter/config.json
curl -fsS http://127.0.0.1:9088/v1/statusValidate or run a configuration manually:
cottenrouter check -config cottenrouter.json
cottenrouter serve -config cottenrouter.jsonStart with cottenrouter.example.json, or use cottenrouter.docker.json for containers. Unknown configuration fields are rejected.
Import an existing SlipGate configuration:
cottenrouter slipgate-import \
--input /etc/slipgate/config.json \
--output cottenrouter.jsonSlipGate DNS transports, including Soar, support both UDP and TCP ingress;
imported routes use tcp_backend: "udp" to reach their native UDP listeners.
Feed's native main, alias, messenger and MTProto domains are automatically
routed together on install, update and Advanced save. See
backend integration for native paths and settings.
Inspect current upstream projects and installers:
cottenrouter catalog
cottenrouter catalog --offlineThe online catalog resolves and verifies each project's current default branch. The offline output is bundled fallback metadata and is not recommended for installation.
cottenrouter tui Install, manage, and monitor projects
cottenrouter serve Run the router with a configuration
cottenrouter check Validate a configuration without serving
cottenrouter healthz Probe router health
cottenrouter install Install a backend from the CLI
cottenrouter update Update an installed backend independently
cottenrouter configure Edit stable backend settings
cottenrouter advanced Edit advanced native backend settings
cottenrouter service Start, stop, or restart a project
cottenrouter remove Detach or purge a project
cottenrouter uninstall Alias for the project removal operation
cottenrouter keys Display project credentials
cottenrouter catalog List current projects and installers
cottenrouter slipgate-import Convert an existing SlipGate configuration
cottenrouter version Print the installed version
Use cottenrouter <command> --help for command-specific options.
Manage a service directly:
sudo cottenrouter service --project=cottendns --action=restart
sudo cottenrouter service --project=stormdns --action=stop
sudo cottenrouter service --project=stormdns --action=startDetach one backend while preserving its files:
sudo cottenrouter remove --project=cottendnsPermanently remove that backend's managed data:
sudo cottenrouter remove --project=cottendns --purge --confirm=cottendnsThe container runs the routing core only. You remain responsible for the lifecycle of backend containers.
curl -fsSLO https://raw.githubusercontent.com/TaJirax/CottenRouter/main/docker-compose.yml
curl -fsSLO https://raw.githubusercontent.com/TaJirax/CottenRouter/main/cottenrouter.docker.json
# Replace the example domain and backend in cottenrouter.docker.json
docker compose up -dCheck health:
docker compose exec cottenrouter \
/usr/local/bin/cottenrouter healthz \
-config /etc/cottenrouter/config.jsonUpdate or remove the container:
docker compose pull
docker compose up -d
docker compose downThe official ghcr.io/tajirax/cottenrouter image supports linux/amd64 and linux/arm64. It listens on unprivileged port 5353 inside the container while Docker publishes host port 53. It runs as UID 65532, drops all capabilities, uses a read-only root filesystem, and includes no shell. See the Docker guide.
The installer upgrades CottenRouter in place. It snapshots the active configuration, replaces the binary, validates the configuration, restarts the service, and waits for the health probe. If any step fails, it restores the previous installation automatically.
flowchart LR
A[Current installation] --> B[Snapshot config and service state]
B --> C[Download and verify release]
C --> D[Validate and restart]
D -->|Health check passes| E[Upgrade complete]
D -->|Health check fails| F[Automatic rollback]
curl -fsSL https://raw.githubusercontent.com/TaJirax/CottenRouter/main/scripts/install.sh | sudo bashPinning a version is useful for reproducible deployments or returning to a known release:
curl -fsSL https://raw.githubusercontent.com/TaJirax/CottenRouter/main/scripts/install.sh \
| sudo bash -s -- --version=v1.2.26Available versions are listed on the Releases page.
Use this only when you intentionally want the latest default-branch commit:
curl -fsSL https://raw.githubusercontent.com/TaJirax/CottenRouter/main/scripts/install.sh \
| sudo bash -s -- --channel=edgedocker compose pull
docker compose up -dThe configuration is a read-only bind mount, so replacing the container does not replace your local config file.
Backend updates are independent of CottenRouter updates:
sudo cottenrouter update --project cottendnsIn the TUI, select an installed project and press U. CottenDNS resolves the
latest published WhiteDNS release on every operation and checks the installed
binary version. Domains, keys and routing settings are preserved.
This is the published-release path. If you installed the CottenMax source beta, use its beta update workflow to retain Max support; a backend update can replace a beta binary with the latest published release.
cottenrouter version
sudo systemctl --no-pager --full status cottenrouter
sudo cottenrouter healthz -config /etc/cottenrouter/config.jsonProduction tip: use the stable channel or an exact version. Take a server snapshot before upgrading upstream backend projects, because their native installers run as root and are maintained independently.
Safely remove CottenRouter while preserving configuration, upstream projects, panels, pre-existing firewall rules, and swap:
sudo cottenrouter-uninstallAlso delete the router configuration:
sudo cottenrouter-uninstall --purge --confirm CottenRouterDelete CottenRouter, every managed backend, and their managed data:
sudo cottenrouter-uninstall \
--purge \
--purge-backends \
--confirm CottenRouterRemove only swap created and marked as owned by the installer:
sudo cottenrouter-uninstall --remove-swap --confirm CottenRouterPurge operations cannot be undone. CottenRouter removes only firewall rules, accounts, and swap carrying its ownership markers; it does not remove pre-existing state.
- Parse the first DNS question and drop malformed packets.
- Select the longest configured domain suffix.
- Allocate a transaction ID on a connected backend socket.
- Forward the packet unchanged apart from that temporary ID.
- Accept a reply only when its question name, type, and class match exactly.
- Restore the client's original ID and return the reply.
IDs are never reused in one socket generation. After all 65,536 IDs have been consumed, CottenRouter rotates to a new source port so a late response cannot enter the new generation.
Remote backends are rejected by default, both UDP and TCP messages are capped at 16 KiB, and the admin API must remain on loopback. Transaction-ID and question matching is not cryptographic authentication; configured backends are trusted. Host backend installers run as root, so snapshot the server before running an upstream release you have not reviewed.
Read more in Security, Installer and control deck, and Backend integration.
git clone https://github.com/TaJirax/CottenRouter.git
cd CottenRouter
go build -o cottenrouter ./cmd/cottenrouter
go test ./...
go test -race ./...
go vet ./...
gofmt -w cmd internalCI also checks backend isolation under load, full 16 KiB packets, throughput, concurrent DoT/DoH and NaiveProxy/StunTLS routing, formatting, and the rootless Docker image.
Use Issues for bugs and feature requests, and Pull Requests for code contributions.
| Guide | Contents |
|---|---|
| Docker | Rootless container layout, networking, images, and updates |
| Installer and control deck | TUI controls, ports, panels, safeguards, and removal |
| Backend integration | Domains, listeners, feature boundaries, and project notes |
| Soar setup | Official Soar and WhiteDNS's older Soar 2.2 option, routing, keys, and verification |
| CottenMax beta | Client debug builds, compatible server requirement, shared credentials and release-channel status |
| Security | Flood controls, resource limits, systemd hardening, and swap |
| Configuration example | Complete annotated-style configuration starting point |
| Release notes v1.2.13 | What changed since v1.2.10, upgrade steps, and load-test results |
CottenRouter is designed to interoperate with these independent open-source projects. We sincerely thank their authors and contributors:
| Project | Role in the ecosystem | Link |
|---|---|---|
| CottenDNS | DNS tunnel with UDP/TCP, DoT, DoH, ARQ, MTU discovery and the optional CottenMax beta mode | Repository |
| MasterDnsVPN | DNS tunneling backend | Repository |
| StormDNS | DNS tunneling backend | Repository |
| thefeed | Feed, chat, MTProto, media, and relay over DNS | Repository |
| Soar | Encrypted TCP streams through recursive DNS resolvers | Repository |
| Soar 2.2 / WhiteDNS | Standalone bundle of Lantern Soar v0.2.2 | Repository |
| SlipGate | DNS transports including Soar, SlipNet, NaiveProxy, and StunTLS | Repository |
The terminal UI is built with Bubble Tea, Bubbles, and Lip Gloss from Charm. The Docker runtime uses scratch with the static router and trusted CA certificates. Complete dependency lists are available in go.mod and go.sum.
No upstream source tree or binary is vendored in this repository. Every upstream project retains its own copyright and license, and its installer is fetched directly from its repository only when requested by the operator. See NOTICE.md.
CottenRouter is released under the MIT License. Upstream integrations remain subject to their respective project licenses.
Built for cleaner, safer, and more manageable DNS-tunnel deployments.