Files
sing-box-extended/service/admin_panel

admin_panel

A TypeScript (Vite + React + Material UI, dark theme) admin panel that talks to the manager_api/http server.

The compiled SPA is embedded into the Go binary via //go:embed — the post-processed dist/ directory is checked into the repo, so go build -tags with_admin_panel ./cmd/sing-box produces a fully self-contained binary with no Node.js required at compile time.

The web/ directory holds the original TypeScript sources, which are only needed when the panel itself is being modified. make admin_panel_regen rebuilds dist/ from those sources via npm run build followed by an in-place post-processing step (cmd/internal/admin_panel_pack).

Layout

service/admin_panel/
├── service.go         # Go service (build tag: with_admin_panel, //go:embed dist)
├── service_stub.go    # Stub when the tag is missing
├── service_test.go    # Tests for the SPA handler
├── dist/              # Embedded SPA bytes (committed to git)
│   ├── index.html
│   ├── index.html.gz
│   └── assets/
│       ├── index-*.js
│       ├── index-*.js.gz
│       ├── index-*.css
│       ├── index-*.css.gz
│       └── inter-*.woff2
└── web/               # Vite + React TypeScript source (only needed to regen)
    ├── package.json
    ├── vite.config.ts
    ├── tsconfig.json
    ├── index.html
    └── src/
        ├── main.tsx
        ├── App.tsx
        ├── theme.ts
        ├── api/{client,types}.ts
        ├── auth/AuthContext.tsx
        ├── components/{Layout,CrudPage}.tsx
        └── pages/*.tsx

cmd/internal/admin_panel_pack/
└── main.go            # Post-processor: drops .woff, rewrites CSS, pre-gzips text

Building sing-box with the panel

dist/ is committed, so any developer or CI machine can build a self-contained binary with no Node.js / npm available:

go build -tags with_admin_panel ./cmd/sing-box
# or
make build_admin_panel

If the tag is omitted the service registers a stub that errors out on start.

Regenerating dist/

Whenever the SPA source under web/ is changed, run:

make admin_panel_regen

That target performs two steps:

  1. make admin_panel_webnpm install + npm run build in web/, producing service/admin_panel/dist/.
  2. make admin_panel_pack — runs the cmd/internal/admin_panel_pack post-processor, which:
    • deletes the legacy *.woff (WOFF 1.0) fallback fonts; every browser since 2014 reads WOFF2 natively, and shipping both formats roughly doubles the embedded font payload;
    • rewrites the bundled *.css to drop ,url(...).woff) format("woff") references so the @font-face rules don't point at files that aren't shipped;
    • drops a gzip-compressed *.gz companion next to every text-like asset (.html, .css, .js, .svg, .json) at gzip.BestCompression. The runtime serves those bytes verbatim with Content-Encoding: gzip when the client advertises gzip support, and falls back to the raw file otherwise.

After the post-processing pass, commit the resulting dist/ directory along with your source changes.

Configuring sing-box

Add a service entry of type admin-panel:

{
  "services": [
    {
      "type": "admin-panel",
      "tag": "admin",
      "listen": "127.0.0.1",
      "listen_port": 8081
    }
  ]
}

The panel itself does not require any back-end auth: at sign-in the user enters the URL and bearer key of a manager-api instance. Both are stored only in the browser's localStorage.