Architecture
One binary runs four things over one SQLite file. This page is the short version; the README carries the same material with sequence and ER diagrams.
What runs on one server
| Part | Listens on | Does |
|---|---|---|
| Panel | :2053 /app/ (and :2054 /dl/ for resellers) | Sessions, API, reseller scoping. Same frontend for both, the session decides what is visible. |
| Subscription server | :2056 /sub/ | Three formats, landing page, client-download page, temporary sharing, the "subscription unavailable" page. |
| Data plane | line ports | Embedded sing-box: inbounds per line, outbounds per upstream, traffic and connection tracking, per-user limits. |
| Background | — | Stats every 10 s, quota judgment every minute, upstream probes (each server checks only what it uses), log cleanup hourly, certificate renewal, backups, master/node sync every 5 s. |
All four share m-ui.db (SQLite in WAL mode). The panel writes, the data plane and subscription server read, so a click in the panel is visible everywhere immediately. There is no message queue, cache server or second database to run.
What a save does
- Field validation and port checks — against other lines, the panel ports and anything else already listening on the server.
- A write transaction (
BEGIN IMMEDIATE) writes the change. - The full configuration is rendered from the database and handed to sing-box's own parser. If it does not parse, the transaction rolls back and the request fails with the reason. Nothing in production moved.
- On success the change is applied by tier: user → swap the inbound user table (nobody else drops); upstream → hot-swap the outbound; line → restart the data plane, and if the new config fails to start (a port grabbed by another process, say) restore the previous working one.
A subscription request
GET /sub/<key>: the key is matched as a user name, a random subscription token, or a temporary share token (which serves the same user with a second credential set). Disabled users, expired resellers and unknown keys get 404 — a short explanation page for browsers, a plain 404 for clients. For a live user, a browser gets the landing page; a client gets universal links, Clash YAML or a sing-box config depending on ?format=. Nodes are the user's assigned lines × the servers each line is deployed on, plus external nodes and subscriptions.
Master and nodes
Every 5 seconds the master snapshots lines, upstreams, users, credentials, resellers and synced settings, hashes them into a revision, pushes to all nodes concurrently and pulls each node's report (traffic since a cursor, online IPs, status). Nodes never judge quota; users the master disables simply disappear from the next snapshot. Details on the multi-server page.
Data model
| Table | Holds | Relations |
|---|---|---|
| lines | protocol, port, TLS, transport, options, which servers | → upstream (exit); ↔ users via user_lines; ↔ nodes |
| users | credentials per protocol, quota and usage, expiry, device and speed limits, subscription key, share token | ↔ lines; → reseller (optional); ← plans on create |
| upstreams | outbound type and options | ← lines |
| nodes | name, domain / address, API URL and token, ratio | ↔ lines |
| resellers | budgets (traffic, bandwidth, devices), expiry, own landing copy, granted lines | → users, plans |
| stats, agent_counters, traffic_cursors | time series and cross-server accounting | |
| settings, admins, sub_logs, audit | everything else |
Background cadence
| Every | What |
|---|---|
| 5 s | Master → nodes snapshot push and report pull |
| 10 s | Read traffic and connections from the data plane, write per user / line / upstream stats |
| 10 s | Evaluate limit rules (schedule / burst); active states ride the next snapshot to nodes |
| 1 min | Quota, expiry and periodic resets; data-plane watchdog |
| 10 min | WAL checkpoint so the live database is always safe to copy |
| 6 h | Version check against GitHub Releases (check only) |
| 1 h | Prune series and logs by retention; fold minute samples older than 48 h into hourly buckets |
| 12 h | Certificate renewal check (sing-box swaps the renewed certificate itself, no restart) |
| daily | Daily report |
Code layout
| Directory | Responsibility |
|---|---|
web/ | Panel and API, reseller panel, external API, embedded frontend (zero-build ES modules) |
sub/ | Subscription formats, landing page, client downloads, sharing |
render/ core/ | Lines → sing-box config and its dry run; the embedded data plane and its tracking |
hub/ | Snapshot push, traffic reclaim, online aggregation |
runner/ | Process orchestration, three-tier reload and rollback, certificates, backups |
jobs/ monitor/ | Stats, quota enforcement, probes, alerts |
database/ | Models, SQLite open and migration |
selfupdate/ deploy/ | Version check and in-place update, installer |
Diagrams: README → Architecture. Deeper dives: why the core is embedded · how hot reload works · how sync works.