LinkQuota is a Netlify Edge relay with a VPS-side VLESS subscription manager. It is designed for operators who want a small, auditable stack for routing traffic through a Netlify domain while managing per-customer VLESS accounts, subscription links, quota, expiry, and enable/disable state on their own VPS.
Use this project only on infrastructure that you own or have permission to administer.
- Netlify Edge Function relay for forwarding client traffic to your upstream service.
- Route selection for regular traffic, managed VLESS traffic, and subscription requests.
- VPS-side customer manager CLI for add, list, show, update, enable, disable, delete, and enforce.
- Plain text subscription server with
Subscription-Userinfoheaders. - Managed Xray config generation for active customers.
- Quota and expiry enforcement with a systemd timer.
- Nginx reverse proxy config for
/s/<token>,/m/<token>,/managed, and/health. - Optional private Telegram bot for admin/customer operations.
This public repository intentionally does not include:
- Private SSH keys.
- BotFather tokens.
- Telegram admin IDs.
- Real customer tokens or UUIDs.
- Generated subscription files.
- Production VPS IPs, domains, Netlify site IDs, or provider account details.
- Local legacy deployment notes.
Any local v2ray-server-setup/, sshKEY/, .env, customers.json, xray-managed.json, and generated subscription files are ignored by git.
.
├── netlify.toml
├── netlify/
│ └── edge-functions/
│ └── relay.js
├── public/
│ └── index.html
├── server/
│ ├── vless-sub-manager/
│ │ ├── manager.py
│ │ ├── sub_server.py
│ │ ├── vless-sub-manager.env.example
│ │ ├── nginx-vless-sub-manager.conf
│ │ ├── nginx-vless-log-format.conf
│ │ ├── vless-managed-xray.service
│ │ ├── vless-subscription.service
│ │ ├── vless-quota-enforcer.service
│ │ └── vless-quota-enforcer.timer
│ └── vless-manager-bot/
│ ├── bot.py
│ ├── config.py
│ ├── manager_client.py
│ ├── keyboards.py
│ ├── formatters.py
│ ├── requirements.txt
│ ├── .env.example
│ └── vless-manager-bot.service
├── VPS_SETUP_MAP.md
├── CONTRIBUTING.md
├── SECURITY.md
├── LICENSE
├── package.json
└── README.mdflowchart LR
Client["VLESS client"]
Netlify["Netlify Edge relay"]
Upstream["Existing upstream service"]
Nginx["VPS Nginx"]
SubServer["Subscription server"]
ManagedXray["Managed Xray"]
Manager["manager.py"]
Bot["Optional Telegram bot"]
DB["customers.json"]
Client --> Netlify
Netlify -->|default paths| Upstream
Netlify -->|/s/token| Nginx --> SubServer --> DB
Netlify -->|/m/token or /managed| Nginx --> ManagedXray
Manager --> DB
Manager --> ManagedXray
Bot --> Manager
The relay is configured in netlify.toml:
[build]
command = "npm run build"
publish = "public"
[[edge_functions]]
function = "relay"
path = "/*"The Edge Function reads these environment variables:
| Variable | Required | Purpose |
|---|---|---|
TARGET_DOMAIN |
Yes | Default upstream for all paths that are not subscription or managed paths. |
MANAGED_TARGET_DOMAIN |
No | Upstream for /m/<token> and /managed. Usually your VPS Nginx HTTP endpoint. |
SUBSCRIPTION_BASE |
No | Upstream for /s/<token>. Usually your VPS Nginx HTTP endpoint. |
Routing behavior:
| Client-facing path | Upstream selected by relay |
|---|---|
/s/<token> |
SUBSCRIPTION_BASE, when set |
/m/<token> |
MANAGED_TARGET_DOMAIN, when set |
/managed |
MANAGED_TARGET_DOMAIN, when set |
| Anything else | TARGET_DOMAIN |
The relay strips hop-by-hop and Netlify-specific headers, preserves the request method, streams request bodies for non-GET/non-HEAD requests, and returns upstream status, headers, and body.
Install dependencies:
npm ciRun the no-op build check:
npm run buildRun locally with Netlify CLI:
npm run devConfigure Netlify environment variables for your own deployment:
npx netlify env:set TARGET_DOMAIN "https://your-default-upstream.example.com:443"
npx netlify env:set MANAGED_TARGET_DOMAIN "http://your-vps-domain.example.com:80"
npx netlify env:set SUBSCRIPTION_BASE "http://your-vps-domain.example.com:80"Deploy:
npx netlify deploy --prodFor the VPS side, follow VPS_SETUP_MAP.md.
The manager reads /etc/default/vless-sub-manager, stores customer state in /opt/vless-sub-manager/customers.json, and generates /opt/vless-sub-manager/xray-managed.json.
Common commands:
sudo /usr/bin/python3 /opt/vless-sub-manager/manager.py init
sudo /usr/bin/python3 /opt/vless-sub-manager/manager.py add ali --title "Ali 1GB" --quota 1gb --days 30
sudo /usr/bin/python3 /opt/vless-sub-manager/manager.py list
sudo /usr/bin/python3 /opt/vless-sub-manager/manager.py show ali
sudo /usr/bin/python3 /opt/vless-sub-manager/manager.py update ali --quota 2gb
sudo /usr/bin/python3 /opt/vless-sub-manager/manager.py update ali --days 15
sudo /usr/bin/python3 /opt/vless-sub-manager/manager.py disable ali
sudo /usr/bin/python3 /opt/vless-sub-manager/manager.py enable ali
sudo /usr/bin/python3 /opt/vless-sub-manager/manager.py delete ali
sudo /usr/bin/python3 /opt/vless-sub-manager/manager.py enforceSupported quota units are b, kb, mb, gb, and tb. Use --days 0 for no expiry.
The bot is a private admin UI around manager.py. It requires a BotFather token and one or more Telegram numeric admin IDs.
See server/vless-manager-bot/README.md.
Run these checks before making your fork public:
git status --ignored -s
rg -n "BOT_TOKEN|PRIVATE KEY|BEGIN .* KEY|ADMIN_USER_IDS|vless://|ssh -i|netlify.app|duckdns|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+"Expected sensitive local files should be ignored, not staged.
Also verify:
.envfiles contain only examples when tracked.customers.jsonis not tracked.- Real subscription URLs are not in docs.
- SSH private keys are not in the repository.
- The local legacy
v2ray-server-setup/directory is not tracked.
Contributions are welcome. Read CONTRIBUTING.md before opening an issue or pull request.
MIT. See LICENSE.