m-uising-box panel

Deep dive · 2026-09-05

Why m-ui embeds sing-box instead of managing an external process

Most panels write a JSON file and restart a core they do not control. m-ui links sing-box as a Go library and runs it inside the panel process. This is the single decision most of the rest of the design follows from.

What "embedded" means here

sing-box is written in Go and exposes its box, inbound and outbound managers as packages. m-ui's core/ package constructs a box from a rendered configuration (NewBox), starts it, and keeps handles to the inbound and outbound managers. When the panel needs to change something it calls those managers directly — AddInbound, AddOutbound, swap a user table — instead of writing a file and sending a signal. The data plane is a set of goroutines in the same process as the HTTP handlers and the SQLite connection.

Four things this makes possible

1. Users change without a restart

sing-box inbounds keep a user table keyed by credential. Because the panel holds the inbound object, it can replace that table in place when a user is added, disabled or re-keyed. Existing connections of other users are not touched. A panel driving an external process can only restart it or use whatever partial reload API the core offers over a socket.

2. Exact traffic and connection data

Per-user, per-line and per-upstream counters are read from the core's own trackers every 10 seconds — no parsing of stats APIs, no log scraping. Online source IPs come from the connection tracker, which is what makes a cross-server device limit possible at all.

3. The same parser validates every save

Before a line, user or upstream is committed, m-ui renders the complete configuration and asks sing-box's own option parser to load it (core/validate.go). Whatever sing-box would reject at startup is rejected at save time, inside the database transaction, with the core's own error message. There is no second, panel-side validator that can drift from the real one.

4. One binary to install, back up and update

There is no core version to keep in step with the panel version. The installer downloads one file; the updater replaces one file; a backup is the database plus certificates. The Docker-less install path is a direct consequence.

What it costs

Embedding also means the panel and the data plane share a process: if the data plane must restart, so does its host. Two things in m-ui exist specifically to pay this cost down.

A third consequence is memory: the panel's footprint includes sing-box's. On a 1 GB VPS this is not a problem, and the panel does not need a second machine or container for the core.

What it deliberately does not do

m-ui does not expose sing-box's raw JSON as the primary interface. Lines, upstreams and users are the model; the JSON is rendered from them for every server. That keeps a multi-server deployment consistent by construction — the same record produces the same inbound on every node — at the price of not supporting every sing-box option. A per-line "advanced" field accepts extra inbound options for the cases the form does not cover.

Related: how hot reload works · architecture overview · core/ on GitHub