m-uising-box panel

Deep dive · 2026-09-23

What a Line means in m-ui

Every proxy panel has to answer "what do I edit?". Most answer with the core's own vocabulary: inbounds, outbounds, routing rules. m-ui answers with one record — a line — and renders the rest.

Definition

A line is: inbound protocol + port → where traffic exits, plus the options that protocol needs (TLS mode, transport, protocol-specific fields) and the list of servers it is deployed to. That is the whole record. Users are attached to lines through an assignment table; upstreams are referenced by id as the exit.

FieldMeaning
protocol, portThe listener sing-box will open — Hysteria2 on UDP 24443, VLESS on TCP 443, and so on.
tlsnone, certificate (the server's own), or Reality with its keys and handshake target.
transporttcp, WebSocket, gRPC, HTTPUpgrade, HTTP, with their paths and hosts.
optionsProtocol extras: Hysteria2 bandwidth hints, obfuscation and port hopping; Shadowsocks method; VLESS Vision flow.
upstreamDirect, or an upstream record (another proxy, WARP). This becomes the outbound and the routing rule.
serversEmpty = every server; otherwise the subset of nodes that should run this listener.

What rendering does

When m-ui needs a configuration for a server — on save, on restart, or when pushing a snapshot to a node — it walks the lines deployed to that server and produces, for each: one sing-box inbound with the users assigned to it (credentials from the user records), the server's own certificate paths if TLS is "certificate", and a routing rule that sends traffic entering that inbound to the line's exit. Exits are rendered once each as outbounds; direct and block are built in. The result is a complete sing-box JSON that the embedded core loads, or that its parser checks before a save is committed.

Because rendering is a pure function of the database, two servers with the same lines produce the same inbounds, and a line changed once changes everywhere on the next 5-second snapshot.

What is deliberately not in a line

Why not just expose the JSON?

Three reasons, in order of how often they bite people running panels:

  1. Consistency across servers. Hand-maintained inbounds drift; a rendered line cannot. Adding a node means "deploy this line there", not "copy this block and fix the certificate paths".
  2. Validation. A form with a fixed shape can be checked before it reaches the core; m-ui additionally hands the full render to sing-box's parser inside the save transaction, so what the core would reject at startup is rejected at save time.
  3. Reload tiers. Knowing that a change touched only users, only an upstream, or the line itself is what lets m-ui pick a hot swap over a restart. A free-form JSON edit gives no such information.

Practical consequences