Skip to content

Commit 3c24d31

Browse files
committed
docs: rewrite the README around PiTun being a router
The README still described a proxy that drops in next to your router. It had not been touched since 1.4, and 1.6.0 made the sentence wrong: PiTun can now be the router. Both languages, rewritten rather than patched: The tagline and "What it is" lead with the two shapes and a table that answers the only question a reader has at that point — who hands out addresses, who does NAT, what hardware each needs. A new **Router mode** section covers the uplink (DHCP/IPoE, static, PPPoE, VLAN, MAC clone), the LAN and its bridge, the WiFi and its capability gate, and the firewall posture, with the commit-confirm warning stated plainly rather than buried. Architecture now has a second diagram: the same proxy engine with a router wrapped around it. The comparison against router-based packages was rewritten too. Saying PiTun is "for when you want server-side orchestration" while those tools run *on* a router reads differently now that PiTun can be one. Then everything else that had drifted since 1.4. There was no Updating section at all, which is how a box ends up on a year-old version; there is one now, and it explains why the panel's button needs a host-side agent. Added: whole-box backup and restore, connecting a panel PiTun didn't install, the panel travelling with its server, the Xray connection-lifetime policy. Fixed: the SNI scanner was still documented as living in the node form, where it hasn't been since 1.5.1. The dashboard screenshot is behind a spoiler with the rest, so the page opens on text.
1 parent 3457557 commit 3c24d31

2 files changed

Lines changed: 435 additions & 30 deletions

File tree

README.md

Lines changed: 216 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
**🌐 Languages:** **English** · [Русский](README.ru.md)
44

5-
> Self-hosted transparent proxy manager for Raspberry Pi 4/5 (and any
6-
> other Linux box). Drops in next to your router, intercepts LAN
7-
> traffic via nftables TPROXY, and routes it through xray-core based
8-
> on your rules — domain, GeoIP, GeoSite, MAC, port, protocol — with a
9-
> web UI.
5+
> Self-hosted router and transparent proxy manager for Raspberry Pi 4/5
6+
> (and any other Linux box). Sit it **beside** your router, or let it
7+
> **be** the router — taking the ISP uplink, handing out addresses,
8+
> doing NAT and serving the WiFi. Either way it intercepts LAN traffic
9+
> via nftables TPROXY and routes it through xray-core by your rules —
10+
> domain, GeoIP, GeoSite, MAC, port, protocol — from a web UI.
1011
1112
[![CI](https://img.shields.io/github/actions/workflow/status/DaveBugg/PiTun/ci.yml?branch=master&label=CI)](#)
1213
[![License](https://img.shields.io/badge/license-BSD--3--Clause-blue)](LICENSE)
@@ -19,13 +20,15 @@
1920
## Table of contents
2021

2122
- [What it is](#what-it-is)
23+
- [Router mode](#router-mode)
2224
- [Why PiTun?](#why-pitun)
2325
- [Screenshots](#screenshots)
2426
- [Architecture](#architecture)
2527
- [Features](#features)
2628
- [Supported protocols](#supported-protocols)
2729
- [Quick start](#quick-start)
2830
- [Server-side proxy install](#server-side-proxy-install)
31+
- [Updating](#updating)
2932
- [Configuration](#configuration)
3033
- [Troubleshooting](#troubleshooting)
3134
- [Uninstall](#uninstall)
@@ -40,12 +43,28 @@
4043
## What it is
4144

4245
PiTun turns a small Linux box into a **transparent proxy gateway** for
43-
your home network. Devices that use the box as their default gateway
44-
have their outbound traffic intercepted at the kernel level, routed
46+
your home network — and, since v1.6.0, into **the router itself** if you
47+
want it to be. Traffic is intercepted at the kernel level, routed
4548
through one of several supported VPN protocols, and either tunnelled,
4649
sent direct, or dropped — all according to rules you set up in the
4750
web UI.
4851

52+
**Two shapes, one box.** Which one you run is a single choice in the
53+
panel, and gateway is the default:
54+
55+
| | **Gateway** — beside your router | **Router** — instead of it |
56+
|---|---|---|
57+
| Who hands out addresses | your existing router | PiTun |
58+
| Who does NAT | your existing router | PiTun |
59+
| How devices are covered | point their gateway at PiTun | just by being on the network |
60+
| Hardware needed | any box, one port is enough | two or more ports |
61+
| WiFi | your router's | PiTun's, if the adapter can serve it |
62+
63+
The gateway shape is the gentle one: nothing about your network
64+
changes, and only the devices you point at PiTun go through it. The
65+
router shape covers everything on the LAN by construction — including
66+
the devices that have no proxy settings to speak of.
67+
4968
It was built for and primarily tested on the **Raspberry Pi 4 / 5**
5069
(64-bit Raspberry Pi OS), but the project ships **linux/amd64** images
5170
too, so any Intel/AMD mini-PC, NUC, old laptop or x86_64 server that
@@ -66,6 +85,55 @@ routing rule set:**
6685
| SOCKS5 | `1080` | Explicit proxy for browsers and apps |
6786
| HTTP | `8080` | For apps without SOCKS5 support |
6887

88+
## Router mode
89+
90+
**Since v1.6.0 PiTun is a complete router.** Not a proxy that happens to
91+
forward — it takes the ISP line, runs the DHCP server, does the NAT, is
92+
the firewall, and puts the WiFi on the air. Everything the proxy already
93+
did keeps working on top: rules, node rotation, chains, the DNS log.
94+
95+
Turn it on in **Router → Operating mode**. It is offered only on hardware
96+
with two or more physical ports, and never switched on by itself.
97+
98+
**The uplink**, in the shapes an ISP line actually comes in:
99+
100+
| Mode | What it means |
101+
|---|---|
102+
| DHCP | Address from the provider. What most providers call **IPoE** |
103+
| Static | Address, gateway and DNS you were given |
104+
| PPPoE | Login and password, the session dialled by PiTun |
105+
| VLAN tag | 802.1Q id on the uplink port, on its own or with any of the above |
106+
| MAC clone | Present the provider a specific MAC — for lines bound to one |
107+
108+
**The network below.** DHCP with your own pool and lease time, and
109+
per-device reserved addresses assigned from the Devices page. PiTun
110+
announces itself as the resolver, so routing rules and the DNS query log
111+
cover devices that never opted in to anything. A LAN can be several
112+
ports — sockets and the radio bridged into one segment, so a laptop on
113+
the cable and a phone on the WiFi share a subnet and see each other.
114+
115+
**The WiFi.** WPA2 or WPA2/WPA3-transitional, band and channel, hidden if
116+
you like, country code obeyed. PiTun checks the adapter can serve an
117+
access point before it tries: plenty of cards can only join networks, and
118+
finding that out when hostapd refuses to start means the working setup is
119+
already dismantled.
120+
121+
**The firewall.** The uplink accepts nothing new from the internet — one
122+
blanket rule rather than a list of ports to remember. The panel, SSH and
123+
xray's own listeners all bind `0.0.0.0`: they stay reachable from the LAN
124+
and invisible from outside. Two exceptions keep the line working — DHCP
125+
replies, which arrive as NEW rather than RELATED, and the ICMP that path-
126+
MTU discovery needs. If PiTun sits behind another router, its "WAN" is
127+
your own network, and you can publish the panel and SSH there on purpose;
128+
that switch refuses outright if the uplink address turns out to be public.
129+
130+
> **Switching modes changes the network under you.** Router mode has no
131+
> fallback — PiTun *is* the router — so a bad apply would leave nobody
132+
> able to undo it. PiTun therefore reverts to gateway unless you confirm
133+
> the network still works, and an unconfirmed change never survives a
134+
> reboot. Do the first switch where you can reach the box, not over SSH
135+
> from the network it is about to rebuild.
136+
69137
## Why PiTun?
70138

71139
A bunch of self-hosted proxy managers already exist:
@@ -115,22 +183,37 @@ router-bound options:
115183
the whole LAN (TPROXY), SOCKS5 for apps that want an explicit proxy,
116184
HTTP for legacy clients. Most tools force you to pick one.
117185

186+
- **It can replace the router entirely.** Those packages run *on* a
187+
router because they need one. PiTun can be it: ISP uplink (DHCP,
188+
static, PPPoE, VLAN, MAC clone), DHCP server, NAT, firewall and WiFi
189+
— see [Router mode](#router-mode). One box instead of two, and the
190+
proxy policy applies to everything on the network by construction,
191+
not only to the devices you remembered to point at it.
192+
118193
vs. **router-based packages** (podkop, passwall, passwall2,
119194
homeproxy, xKeen): they're brilliant at "all my devices, minimal
120-
setup" on a $30 router with 64 MB RAM. PiTun is for the case where
195+
setup" on a $30 router with 64 MB RAM, and if that is your whole
196+
requirement they are the lighter answer. PiTun is for the case where
121197
you want **server-side orchestration** (deploy & manage your own
122198
VPS), **multi-tier traffic policy** (different rules per device
123-
group), and **a modern web UI with a mobile-friendly layout — tweak
124-
a rule from your phone, even from the bathroom if you must** — at
125-
the cost of needing an RPi 4/5 (or any small Linux box) with 64 GB+
126-
disk.
199+
group), **a box that can be the router rather than ride on one**, and
200+
**a modern web UI with a mobile-friendly layout — tweak a rule from
201+
your phone, even from the bathroom if you must** — at the cost of
202+
needing an RPi 4/5 (or any small Linux box) with 64 GB+ disk.
127203

128204
## Screenshots
129205

206+
<details>
207+
<summary><strong>Dashboard</strong> — click to expand · 1 screenshot</summary>
208+
209+
<br>
210+
130211
<a href="docs/screenshots/dashboard.jpg">
131212
<img src="docs/screenshots/dashboard.jpg" alt="Dashboard" width="800">
132213
</a>
133214

215+
</details>
216+
134217
<details>
135218
<summary><strong>VPS provisioning & x-ui orchestration</strong> (since v1.3.0) — click to expand · 6 screenshots</summary>
136219

@@ -245,6 +328,9 @@ disk.
245328

246329
## Architecture
247330

331+
**Gateway mode** — PiTun sits beside the router and proxies the devices
332+
pointed at it:
333+
248334
```
249335
┌──────────────────────────────────────────────┐
250336
Devices ────► │ PiTun host (RPi / mini-PC) │
@@ -265,12 +351,58 @@ disk.
265351
└──────────────────────────────────────────────┘
266352
```
267353

354+
**Router mode** — the same engine, with PiTun holding both ends of the
355+
network. The proxy layer above is unchanged; what is added is everything
356+
a router does:
357+
358+
```
359+
┌──────────────────────────────────────────────┐
360+
ISP ────────►│ WAN dhcp / static / pppoe / vlan / mac-clone│
361+
│ │ │
362+
│ ├─ nftables: NAT (masquerade) + firewall │
363+
│ │ uplink accepts nothing new inbound │
364+
│ │ TCP MSS clamped to the path MTU │
365+
│ │ │
366+
│ ▼ │
367+
│ ┌────────── the proxy engine above ────────┐ │
368+
│ └──────────────────────────────────────────┘ │
369+
│ │ │
370+
│ ├─ dnsmasq: DHCP + reserved addresses │
371+
│ ├─ hostapd: WiFi access point │
372+
│ │ │
373+
Devices ◄─────┤ LAN one or more ports, bridged: br-lan │
374+
(wired+WiFi) │ cable and radio in one subnet │
375+
└──────────────────────────────────────────────┘
376+
commit-confirm watchdog guards every change
377+
```
378+
268379
Web UI talks to a FastAPI backend that owns the xray-core process,
269380
the nftables ruleset, and a SQLite database with all configuration.
270381
Frontend is a single-page React app served by nginx.
271382

272383
## Features
273384

385+
**Router** (v1.6.0 — see [Router mode](#router-mode))
386+
- Two operating modes: **gateway** beside your router, or **router**
387+
instead of it. Explicit choice, gateway by default
388+
- Uplink: DHCP (IPoE), static, **PPPoE**, 802.1Q VLAN tag, MAC clone —
389+
with NAT, the firewall and the counters following the interface the
390+
traffic actually leaves by, ppp link or tagged sub-interface included
391+
- DHCP server with pool, lease time and per-device reserved addresses
392+
- **A LAN of several ports** — sockets and the radio bridged into one
393+
segment, one subnet, one DHCP scope
394+
- **WiFi access point** — WPA2 or WPA2/WPA3-transitional, band, channel,
395+
hidden SSID, regulatory domain; gated on an AP-capability probe of the
396+
adapter
397+
- A WAN that accepts nothing new from the internet, with the panel and
398+
SSH publishable on the uplink on purpose when PiTun sits behind another
399+
router — refused if that address turns out to be public
400+
- **Commit-confirm watchdog** — a change that breaks the network reverts
401+
itself unless a human confirms, and never survives a reboot unconfirmed
402+
- Uplink diagnosis on nftables counters, for the failures that are
403+
otherwise silent: no DHCP replies, no ICMP coming back, traffic leaving
404+
by a port that isn't the uplink
405+
274406
**Core**
275407
- Transparent proxy via TPROXY + nftables, no per-device client
276408
- SOCKS5 / HTTP proxies on the LAN
@@ -359,7 +491,9 @@ Frontend is a single-page React app served by nginx.
359491
- **Host network controls** — change the box's own gateway / DNS from
360492
Settings → Network with an auto-backup + one-click rollback; warns on
361493
a routing self-loop (gateway pointing at the box itself) or a
362-
double-hop through another PiTun
494+
double-hop through another PiTun. (In router mode PiTun owns its own
495+
uplink, so these are configured on the **Router** page instead and
496+
this section refuses to fight it)
363497

364498
**Servers & deployments**
365499
- Inventory of remote VPS hosts (host, SSH credentials, tags) separate
@@ -378,8 +512,24 @@ Frontend is a single-page React app served by nginx.
378512
healthcheck (panel API, xray, nginx, UFW, TLS cert, disk, mem),
379513
cache↔panel sync for hand-added clients, random / custom
380514
fakesite rotation
381-
- **SNI / REALITY-dest scanner** in the node form — probe a candidate
382-
host for TLS 1.3 + HTTP/2 before saving it as the masquerade target
515+
516+
- **Connect a panel PiTun didn't install.** Import a server that already
517+
runs x-ui and the X-ui page used to stay empty — a panel became one only
518+
by being deployed through PiTun. Paste the `xui://` line from the
519+
install, or just your panel login: the API token is fetched for you,
520+
reusing an existing one rather than minting a new one per attempt
521+
- **The panel travels with its server** — a server exported with secrets
522+
carries its panel registration (bundle envelope v3), so restoring it
523+
elsewhere doesn't leave x-ui installed and unlisted
524+
- **REALITY dest / SNI scanner** where the inbound is created — probe a
525+
candidate IP or domain *through the active node* for TLS 1.3 + HTTP/2 and
526+
see what actually answers, instead of guessing a masquerade target from a
527+
hardcoded list
528+
- **Connection-lifetime policy for Xray** — one set of timeouts for the
529+
box's own instance and every registered panel. Xray's default kills an
530+
idle *pooled* connection after five minutes, which is the "works, then
531+
it doesn't" that SDK and agent clients hit; panels are patched rather
532+
than overwritten, and one button pushes a change to the whole fleet
383533

384534
**Operations**
385535
- One-click GeoIP / GeoSite refresh — three switchable upstream
@@ -395,6 +545,12 @@ Frontend is a single-page React app served by nginx.
395545
your node list with any v2rayN-compatible client; symmetric
396546
`Import` button auto-detects URI list vs JSON bundle. Single-node URI
397547
export straight from a node card too
548+
- **Whole-box configuration backup** — Settings → Backup & Restore
549+
exports settings, subscriptions, nodes, routing sets and rules, DNS
550+
rules, balancers, circles, devices and UA templates as one file, and
551+
restores it onto a fresh box. Secrets are opt-in, so the file you share
552+
for debugging carries no credentials; restore previews per-section
553+
add/update/delete counts before it writes anything
398554
- **Login lockout** — five consecutive failed logins lock an account
399555
for 15 minutes (HTTP 429 + `Retry-After`); the primary LAN-only
400556
brute-force guard
@@ -426,6 +582,7 @@ Frontend is a single-page React app served by nginx.
426582
| **RAM** | 1 GB | 2 GB+ (helps with naive sidecars and large geo data refresh) |
427583
| **Disk** | 4 GB free | 8 GB+ (docker images + DB growth + DNS query log) |
428584
| **Network** | 1 LAN interface, static IP, wired preferred | 1× wired GbE for LAN |
585+
| **Network — [router mode](#router-mode)** | 2 physical ports (one for the ISP, one for the LAN) | 2× wired GbE, plus an AP-capable WiFi adapter to serve wireless |
429586
| **OS** | Any modern 64-bit Linux with kernel ≥ 5.4 (TPROXY support) | Raspberry Pi OS 64-bit, Debian 12+, Ubuntu 22.04+ |
430587
| **Architectures** | `linux/arm64` *(RPi 4/5)* · `linux/amd64` *(Intel/AMD mini-PC, NUC, x86_64 server)* ||
431588

@@ -435,6 +592,10 @@ Frontend is a single-page React app served by nginx.
435592
- Docker + Docker Compose v2
436593
- Root access on the host (nftables + raw socket binding)
437594
- A static LAN IP for the host
595+
- For [router mode](#router-mode): two or more physical ports, and — if
596+
PiTun is to serve the WiFi — an adapter that can run an access point.
597+
PiTun checks the adapter for you and says so before anything is torn
598+
down
438599

439600
### Install — one-liner
440601

@@ -738,6 +899,46 @@ rotation — random pick from the bundled archive or upload a custom
738899
`.zip`. Chain orchestration (see **Proxy Chains** below) glues two
739900
registered panels into a two-hop VLESS+Reality tunnel.
740901
902+
## Updating
903+
904+
**From the panel.** Settings → Updates checks GitHub — through the active
905+
node, so a throttled direct route isn't a blocker — shows what changed, and
906+
applies it with live progress.
907+
908+
The button does not update the box itself: it writes a request that a
909+
host-side agent carries out, because the update replaces the very container
910+
the panel is served from. The installer sets that agent up. On a box
911+
installed before v1.6.2 it isn't there, and the button will sit at 0%
912+
forever — install it once:
913+
914+
```bash
915+
bash /opt/pitun/scripts/pitun-update.sh --install-agent
916+
```
917+
918+
This does **not** enable unattended updates. The agent only acts on a
919+
request you made.
920+
921+
**From the command line.** Re-run the installer with a version; it detects
922+
an existing install, snapshots the database first, and refuses to go
923+
backwards:
924+
925+
```bash
926+
curl -fsSL https://raw.githubusercontent.com/DaveBugg/PiTun/master/install.sh -o /tmp/pitun-install.sh
927+
sudo bash /tmp/pitun-install.sh --version v1.6.2
928+
```
929+
930+
**On a schedule**, opt-in and separate from the button — a daily systemd
931+
timer that only reports unless you ask it to act:
932+
933+
```bash
934+
bash /opt/pitun/scripts/pitun-update.sh --install-timer # check and report
935+
bash /opt/pitun/scripts/pitun-update.sh --install-timer --apply # check and update
936+
```
937+
938+
Every path takes a SQLite snapshot to
939+
`/opt/pitun/data-backup-pre-vX.Y.Z-*.db` before touching anything, and
940+
router mode comes back on its own after the containers are recreated.
941+
741942
## Configuration
742943
743944
All runtime config goes through the web UI. The only settings that

0 commit comments

Comments
 (0)