Rigel — no-KYC SOCKS5 Proxy Shop

Self-hosted SOCKS5 proxy storefront. Sells access to datacenter / residential / mobile proxies. Bitcoin via BTCPay. No KYC — username + email + password + arithmetic captcha, nothing else.

Status: LIVE — end-to-end verified 2026-09-10. Real BTC payment → NBXplorer match → BTCPay webhook → subscription activated → per-user SOCKS5 credentials issued → authenticated proxy access proven from an external network (off-LAN vantage, correct country exit).

Stack

Piece Detail
Storefront Flask SPA — /opt/rigel/app.py (SQLite, no-KYC auth, BTCPay invoicing)
Proxy frontend /opt/rigel/proxy_server.py — authenticated SOCKS5 on :1081
Container CT 158 rigel @ 10.30.20.116 — Debian 12, nginx :80 → Flask :5000
Services rigel.service (:5000), rigel-proxy.service (:1081)
Payments BTCPay (store id + keys in Teable → API_Credentials) — webhook → POST /api/btcpay/webhook
Public web https://rigel.thetempleofdoom.com (fleet tunnel 1aeb1ac0, remote-managed)
Public SOCKS5 76.146.4.178:1081 — WAN port-forward (see below)

No secrets in this repo. Store IDs, API keys and webhook secrets live in the rigel.service environment and in Teable → API_Credentials.

Inventory

Location Type Endpoint
Tokyo datacenter (Nord exit) CT680 10.30.20.154:1080
London datacenter (Nord exit) CT681 10.30.20.71:1080
Sydney datacenter (Nord exit) CT682 10.30.20.189:1080
Residential rotating / sticky IPRoyal geo.iproyal.com:12321
Mobile 4G rotating IPRoyal 4g.iproyal.com

proxy_server.py derives the upstream list from app.py's LOCATIONS (single source of truth) so the shop and the proxy can never disagree about inventory.

API

Route Body Purpose
GET /api/captcha — arithmetic challenge
POST /api/register {username, email, password, captcha_id, captcha} no-KYC signup
POST /api/login {username, password} session
POST /api/buy {plan_id} creates BTCPay invoice + pending subscription
GET /api/subscriptions — user's active proxies + issued credentials
POST /api/btcpay/webhook BTCPay payload activates subscription + issues creds

Plans: day (8,000 sats) · week (45,000 sats) · month (150,000 sats).

Public SOCKS5 access — the part that isn't obvious

A Cloudflare tunnel cannot carry SOCKS5 (HTTP-only), and Tailscale Funnel's raw-TCP mode does not actually forward publicly (verified, then reverted). Customers therefore connect to the home WAN IP directly: the router DNATs tcp 1081 → 10.30.20.116:1081.

⚠️ If SOCKS5 ever stops being reachable from the internet, check in this order:

  1. Router — iptables -t nat -L VSERVER -n | grep 1081
  2. Hook present? — /jffs/scripts/firewall-start must exist and be executable (setting the vts_rulelist nvram var alone does not emit the DNAT on this firmware)
  3. Service — systemctl is-active rigel-proxy inside CT158
  4. Never trust a LAN test — NAT loopback can pass while the world can't reach it. Verify from an external host.

Auth model

proxy_server.py validates every SOCKS5 username/password against rigel.db: the subscription must exist, be active, and not expired. The subscription's location selects the upstream exit. Unknown user, wrong password, expired sub, or wrong auth method → rejected; no traffic leaves. All time comparisons are UTC.

Raw upstream egress lock

The Nord :1080 exits are LAN-only "dumb relays" with no auth by design. To stop tailnet nodes (100.64/10), the VPN tunnel (tun0), and other LAN hosts from using them for free, each Nord CT (680/681/682) restricts :1080 to the Rigel frontend (CT158, .116) + loopback. Rule lives in /etc/network/if-up.d/rigel-egress-lock (persists across reboot, same hook mechanism as the Nord killswitch). Re-apply or inspect with:

sh /etc/network/if-up.d/rigel-egress-lock
iptables -S INPUT | grep 1080

Deploy

tar czf rigel.tar.gz app.py proxy_server.py
scp rigel.tar.gz root@10.30.20.85:/tmp/
ssh root@10.30.20.85 "pct push 158 /tmp/rigel.tar.gz /tmp/rigel.tar.gz && pct exec 158 -- bash -c '
  cd /opt/rigel &&
  cp app.py app.py.bak-\$(date +%s) &&
  cp proxy_server.py proxy_server.py.bak-\$(date +%s) &&
  tar xzf /tmp/rigel.tar.gz &&
  systemctl restart rigel rigel-proxy'"

Always back up the running file before overwriting it. CT158 does not accept the standard fleet root password over SSH — deploy through the Proxmox host with pct exec.

Gotchas

  • Fleet tunnel is REMOTE-MANAGED — edit ingress via the Cloudflare API, not the local config-fleet.yml. Local edits are silently ignored.
  • checkoutLink comes back with the LAN host (it's derived from the API call's Host header) — app.py rewrites the prefix onto BTCPAY_PUBLIC_URL.
  • SOCKS5 CONNECT to a domain target must length-prefix the domain (atyp=3). Omit the prefix and the upstream reads the first character as a length and hangs.
  • rigel.db is gitignored — never commit the live database.
  • BTCPay NetworkFeeMode = Always makes the exact amount land a few sats short (PaidPartial rather than Settled); activation still fires. Not settable via the Greenfield API.
Description
Rigel — no-KYC SOCKS5 proxy shop (Nord exits + IPRoyal residential/mobile, BTCPay)
Readme 50 KiB
Languages
Python 100%