Files
thefeed/README.md
T
2026-03-26 00:19:03 +03:30

250 lines
8.3 KiB
Markdown

# thefeed
DNS-based feed reader for Telegram channels. Designed for environments where only DNS queries work.
## How It Works
```
┌──────────────┐ DNS TXT Query ┌──────────────┐ MTProto ┌──────────┐
│ Client │ ──────────────────────▸ │ Server │ ──────────────▸ │ Telegram │
│ (Web UI) │ ◂────────────────────── │ (DNS auth) │ ◂────────────── │ API │
└──────────────┘ Encrypted TXT └──────────────┘ └──────────┘
```
**Server** (runs outside censored network):
- Connects to Telegram, reads messages from configured channels
- Serves feed data as encrypted DNS TXT responses
- Random padding on responses to vary size (anti-DPI)
- Session persistence — login once, run forever
- All data stored in a single directory
**Client** (runs inside censored network):
- Browser-based web UI with RTL/Farsi support (VazirMatn font)
- Configure via the web UI — no CLI flags needed
- Sends encrypted DNS TXT queries via available resolvers
- Single-label base32 encoding (stealthier) or double-label hex
- Rate limiting to respect resolver limits
- Live DNS query log in the browser
- All data (config, cache) stored next to the binary
## Anti-DPI Features
- **Variable response size**: Random padding (0-32 bytes) on each DNS response prevents fingerprinting by fixed packet size
- **Single-label queries**: Base32 encoded subdomain in one DNS label (`abc123def.t.example.com`) instead of the more detectable two-label hex pattern
- **Resolver shuffling**: Queries are distributed across resolvers randomly
- **Rate limiting**: Configurable query rate to blend with normal DNS traffic
- **Concurrency limiting**: Max 3 concurrent block fetches to avoid DNS bursts
- **Random query padding**: 4 random bytes in each query payload
## Protocol
**Block size**: 180 bytes payload (fits in 512-byte UDP DNS with padding + encryption overhead)
**Query format** (single-label, default): `[base32_encrypted].t.example.com`
**Query format** (double-label): `[hex_part1].[hex_part2].t.example.com`
- Payload: 4 random bytes + 2 channel + 2 block = 8 bytes, AES-256-GCM encrypted
**Response**: `[2-byte length][data][random padding]` → AES-256-GCM encrypted → Base64
**Encryption**: AES-256-GCM with HKDF-derived keys from shared passphrase
## Quick Install (Server)
One-line install (downloads latest release from GitHub)
```bash
bash <(curl -Ls https://raw.githubusercontent.com/sartoopjj/thefeed/main/scripts/install.sh)
```
Or manually:
```bash
# On your server (Linux with systemd)
curl -Ls https://raw.githubusercontent.com/sartoopjj/thefeed/main/scripts/install.sh -o install.sh
sudo bash install.sh
```
The script will:
1. Download the latest release binary from GitHub
2. Ask for your domain, passphrase, Telegram credentials, channels
3. Login to Telegram interactively (one-time)
4. Set up a systemd service
Update: `sudo bash install.sh` (detects existing config, only updates binary)
Re-login: `sudo bash install.sh --login`
Uninstall: `sudo bash install.sh --uninstall`
## Manual Setup
### Prerequisites
- Go 1.26+
- Telegram API credentials from https://my.telegram.org
- A domain with NS records pointing to your server
### Server
```bash
# Build
make build-server
# First run: login to Telegram and save session
./build/thefeed-server \
--login-only \
--data-dir ./data \
--domain t.example.com \
--key "your-secret-passphrase" \
--api-id 12345 \
--api-hash "your-api-hash" \
--phone "+1234567890"
# Normal run (uses saved session from data directory)
./build/thefeed-server \
--data-dir ./data \
--domain t.example.com \
--key "your-secret-passphrase" \
--api-id 12345 \
--api-hash "your-api-hash" \
--phone "+1234567890" \
--listen ":5300"
```
All data files (session, channels) are stored in the `--data-dir` directory (default: `./data`).
Environment variables: `THEFEED_DOMAIN`, `THEFEED_KEY`, `TELEGRAM_API_ID`, `TELEGRAM_API_HASH`, `TELEGRAM_PHONE`, `TELEGRAM_PASSWORD`
#### Server Flags
| Flag | Default | Description |
|------|---------|-------------|
| `--data-dir` | `./data` | Data directory for channels, session, config |
| `--domain` | | DNS domain (required) |
| `--key` | | Encryption passphrase (required) |
| `--channels` | `{data-dir}/channels.txt` | Path to channels file |
| `--api-id` | | Telegram API ID (required) |
| `--api-hash` | | Telegram API Hash (required) |
| `--phone` | | Telegram phone number (required) |
| `--session` | `{data-dir}/session.json` | Path to Telegram session file |
| `--login-only` | `false` | Authenticate to Telegram, save session, exit |
| `--listen` | `:5300` | DNS listen address |
| `--padding` | `32` | Max random padding bytes (0=disabled) |
| `--version` | | Show version and exit |
### Client
```bash
# Build
make build-client
# Run (opens web UI in browser)
./build/thefeed-client
# Custom data directory and port
./build/thefeed-client --data-dir ./mydata --port 9090
```
On first run, the client creates a `./thefeeddata/` directory next to where you run it. Open `http://127.0.0.1:8080` in your browser and configure your domain, passphrase, and resolvers through the Settings page.
All configuration, cache, and data files are stored in the data directory.
#### Client Flags
| Flag | Default | Description |
|------|---------|-------------|
| `--data-dir` | `./thefeeddata` | Data directory for config, cache |
| `--port` | `8080` | Web UI port |
| `--version` | | Show version and exit |
### Web UI
The browser-based UI has:
- **Channels sidebar** (left): channel list with selection
- **Messages panel** (right): messages with native RTL/Farsi rendering (VazirMatn font)
- **Log panel** (bottom): live DNS query log
- **Settings modal**: configure domain, passphrase, resolvers, query mode, rate limit
## Development
```bash
make test # Run tests
make build # Build both binaries
make build-all # Cross-compile all platforms
make vet # Go vet
make fmt # Format code
make clean # Remove build artifacts
```
## DNS Records Setup
You need **two DNS records** on your domain. Suppose your server IP is `203.0.113.10` and you want to use `example.com`:
### 1. A Record for the NS server
| Type | Name | Value |
|------|------|-------|
| A | `ns.example.com` | `203.0.113.10` |
This points a hostname to your server IP.
### 2. NS Record for the tunnel subdomain
| Type | Name | Value |
|------|------|-------|
| NS | `t.example.com` | `ns.example.com` |
This delegates all DNS queries for `t.example.com` (and its subdomains) to your server.
> **Note:** The server needs to receive packets on external port 53. Running on `:53` directly requires root. It's better to listen on an unprivileged port (`:5300`) and port-forward 53 to it.
>
> Replace `eth0` with your actual network interface name (check with `ip a`):
> ```bash
> sudo iptables -I INPUT -p udp --dport 5300 -j ACCEPT
> sudo iptables -t nat -I PREROUTING -i eth0 -p udp --dport 53 -j REDIRECT --to-ports 5300
> sudo ip6tables -I INPUT -p udp --dport 5300 -j ACCEPT
> sudo ip6tables -t nat -I PREROUTING -i eth0 -p udp --dport 53 -j REDIRECT --to-ports 5300
> ```
>
> To make these rules persistent across reboots:
> ```bash
> sudo apt install iptables-persistent # Debian/Ubuntu
> sudo netfilter-persistent save
> ```
## channels.txt Format
```
# Comments start with #
@VahidOnline
```
## Security
- All queries and responses are encrypted with AES-256-GCM
- Separate HKDF-derived keys for queries and responses
- Random padding in queries prevents caching and replay
- Random padding in responses prevents DPI size fingerprinting
- No session state — each query is independent
- Pre-shared passphrase required for both client and server
- Telegram 2FA password is prompted interactively (not stored in CLI args)
- Session file stored with 0600 permissions
## Service Management
```bash
# After install.sh
systemctl status thefeed-server
systemctl restart thefeed-server
journalctl -u thefeed-server -f
# Update channels
sudo vi /opt/thefeed/data/channels.txt
sudo systemctl restart thefeed-server
# Update binary
sudo bash scripts/install.sh
```
## License
MIT