15 Commits
Author SHA1 Message Date
kazeyukiro 7bff84bcfa fix(cli): stop rejecting reset-config own flags as unknown arguments
main() 的全局参数扫描在遇到子命令后仍继续遍历,把 reset-config 的
--panel/--access/--public/--all/--yes 当成未知参数直接 log.Fatal,
导致 SSH 救援菜单(选项 16)必然失败并回滚重启:

    unknown argument: --panel

但 reset_config.go 本身是支持这些 flag 的(且 main.go 里的注释也
写了要交给子命令自己解析),只是扫描循环抢先拒绝了。

改为登记接受自有 flag 的子命令,扫描时放行其 flag。无子命令时的
未知参数(如 3m-ui --foo)仍照旧拒绝。
2026-10-05 14:13:44 +08:00
kazeyukiro a52ab85ab8 feat(logs): include panel runtime logs in UI log stream
Ring-buffer stdlib log output (ACME, bootstrap, errors) with [panel]
prefix and merge with Mihomo lines on GET /mihomo/logs, sorted by time.
2026-10-04 10:31:42 +00:00
風雪and風雪 7129cc346d perf(memory): keep install, update and runtime inside a small-RAM budget (#83)
* perf(memory): keep install, update and runtime inside a small-RAM budget
Small NAT/VPS targets have 128-512MB of RAM and no swap, so anything that
cannot be reclaimed is fatal. This tuning touches no swap and no mmap; it
works by not allocating in the first place.
Installer (covers install and update; update.sh only execs install.sh):
- Put the scratch directory under DATA_DIR instead of mktemp -d. /tmp is
tmpfs on many of these hosts, and without swap every byte there is pinned
in RAM: bundle, extracted release and a copy of the data directory were
held at once, exceeding total RAM of the machines being targeted.
- Hash assets on the way to disk instead of re-reading them. Download is a
single pass now; verification still completes before tar sees the bytes.
- Release the intermediate data.tar right after extraction rather than
holding the data directory twice for the rest of the run.
- Check scratch capacity before downloading, so we fail before the old
service is stopped rather than halfway through.
- Refresh Environment=GOMEMLIMIT/GOGC/THREE_M_UI_CORE_LOW_MEMORY and
MemoryMax on an existing unit. write_service() keeps an operator's unit,
which meant the values baked in at install time never tracked updates.
Panel:
- Bound the Go heap from the process itself via debug.SetMemoryLimit, so the
tuning reaches manual runs, containers and the installer's own init and
healthcheck instead of only a systemd unit. It acts in-process, so the
Mihomo core never inherits the panel's tighter GC target. An explicit
GOMEMLIMIT or GOGC always wins.
- Emit geodata-loader: memconservative for the core when low-memory mode is
on. The key is omitempty and pinned after user fragments merge, so nothing
changes for anyone who does not opt in, and no fragment can undo it.
No new calls to Mmap/Munmap/Madvise, no swap or mmap anywhere in the tree.
* fix(ci): keep the new scratch directory out of the installer's own paths
Two problems CI caught on the small-memory change, both from moving the
scratch directory from mktemp into DATA_DIR.
restore_files() rebuilds the data directory by deleting everything in it
except backups. The scratch directory now lives inside it, so a rollback
deleted the very directory it was reading its own rollback out of, and the
restore died looking for restore/ENABLED. Rollback therefore had to be
skipped, exempting the scratch directory from that purge; the cleanup trap
already removes it.
The test curl also modelled only one of curl's output modes. Writing to
stdout and answering -I are both normal curl behaviour, and both are now
covered, so the mock no longer pins the installer to a single invocation
shape. The distribution fake likewise emits its tampered asset on stdout
instead of through --output, keeping that test about checksum rejection
rather than about which flag was used.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
perf(memory): keep install, update and runtime inside a small-RAM budget (#83)
* perf(memory): keep install, update and runtime inside a small-RAM budget
Small NAT/VPS targets have 128-512MB of RAM and no swap, so anything that
cannot be reclaimed is fatal. This tuning touches no swap and no mmap; it
works by not allocating in the first place.
Installer (covers install and update; update.sh only execs install.sh):
- Put the scratch directory under DATA_DIR instead of mktemp -d. /tmp is
tmpfs on many of these hosts, and without swap every byte there is pinned
in RAM: bundle, extracted release and a copy of the data directory were
held at once, exceeding total RAM of the machines being targeted.
- Hash assets on the way to disk instead of re-reading them. Download is a
single pass now; verification still completes before tar sees the bytes.
- Release the intermediate data.tar right after extraction rather than
holding the data directory twice for the rest of the run.
- Check scratch capacity before downloading, so we fail before the old
service is stopped rather than halfway through.
- Refresh Environment=GOMEMLIMIT/GOGC/THREE_M_UI_CORE_LOW_MEMORY and
MemoryMax on an existing unit. write_service() keeps an operator's unit,
which meant the values baked in at install time never tracked updates.
Panel:
- Bound the Go heap from the process itself via debug.SetMemoryLimit, so the
tuning reaches manual runs, containers and the installer's own init and
healthcheck instead of only a systemd unit. It acts in-process, so the
Mihomo core never inherits the panel's tighter GC target. An explicit
GOMEMLIMIT or GOGC always wins.
- Emit geodata-loader: memconservative for the core when low-memory mode is
on. The key is omitempty and pinned after user fragments merge, so nothing
changes for anyone who does not opt in, and no fragment can undo it.
No new calls to Mmap/Munmap/Madvise, no swap or mmap anywhere in the tree.
* fix(ci): keep the new scratch directory out of the installer's own paths
Two problems CI caught on the small-memory change, both from moving the
scratch directory from mktemp into DATA_DIR.
restore_files() rebuilds the data directory by deleting everything in it
except backups. The scratch directory now lives inside it, so a rollback
deleted the very directory it was reading its own rollback out of, and the
restore died looking for restore/ENABLED. Rollback therefore had to be
skipped, exempting the scratch directory from that purge; the cleanup trap
already removes it.
The test curl also modelled only one of curl's output modes. Writing to
stdout and answering -I are both normal curl behaviour, and both are now
covered, so the mock no longer pins the installer to a single invocation
shape. The distribution fake likewise emits its tampered asset on stdout
instead of through --output, keeping that test about checksum rejection
rather than about which flag was used.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
perf(memory): keep install, update and runtime inside a small-RAM budget (#83)
* perf(memory): keep install, update and runtime inside a small-RAM budget
Small NAT/VPS targets have 128-512MB of RAM and no swap, so anything that
cannot be reclaimed is fatal. This tuning touches no swap and no mmap; it
works by not allocating in the first place.
Installer (covers install and update; update.sh only execs install.sh):
- Put the scratch directory under DATA_DIR instead of mktemp -d. /tmp is
tmpfs on many of these hosts, and without swap every byte there is pinned
in RAM: bundle, extracted release and a copy of the data directory were
held at once, exceeding total RAM of the machines being targeted.
- Hash assets on the way to disk instead of re-reading them. Download is a
single pass now; verification still completes before tar sees the bytes.
- Release the intermediate data.tar right after extraction rather than
holding the data directory twice for the rest of the run.
- Check scratch capacity before downloading, so we fail before the old
service is stopped rather than halfway through.
- Refresh Environment=GOMEMLIMIT/GOGC/THREE_M_UI_CORE_LOW_MEMORY and
MemoryMax on an existing unit. write_service() keeps an operator's unit,
which meant the values baked in at install time never tracked updates.
Panel:
- Bound the Go heap from the process itself via debug.SetMemoryLimit, so the
tuning reaches manual runs, containers and the installer's own init and
healthcheck instead of only a systemd unit. It acts in-process, so the
Mihomo core never inherits the panel's tighter GC target. An explicit
GOMEMLIMIT or GOGC always wins.
- Emit geodata-loader: memconservative for the core when low-memory mode is
on. The key is omitempty and pinned after user fragments merge, so nothing
changes for anyone who does not opt in, and no fragment can undo it.
No new calls to Mmap/Munmap/Madvise, no swap or mmap anywhere in the tree.
* fix(ci): keep the new scratch directory out of the installer's own paths
Two problems CI caught on the small-memory change, both from moving the
scratch directory from mktemp into DATA_DIR.
restore_files() rebuilds the data directory by deleting everything in it
except backups. The scratch directory now lives inside it, so a rollback
deleted the very directory it was reading its own rollback out of, and the
restore died looking for restore/ENABLED. Rollback therefore had to be
skipped, exempting the scratch directory from that purge; the cleanup trap
already removes it.
The test curl also modelled only one of curl's output modes. Writing to
stdout and answering -I are both normal curl behaviour, and both are now
covered, so the mock no longer pins the installer to a single invocation
shape. The distribution fake likewise emits its tampered asset on stdout
instead of through --output, keeping that test about checksum rejection
rather than about which flag was used.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
perf(memory): keep install, update and runtime inside a small-RAM budget (#83)
* perf(memory): keep install, update and runtime inside a small-RAM budget
Small NAT/VPS targets have 128-512MB of RAM and no swap, so anything that
cannot be reclaimed is fatal. This tuning touches no swap and no mmap; it
works by not allocating in the first place.
Installer (covers install and update; update.sh only execs install.sh):
- Put the scratch directory under DATA_DIR instead of mktemp -d. /tmp is
tmpfs on many of these hosts, and without swap every byte there is pinned
in RAM: bundle, extracted release and a copy of the data directory were
held at once, exceeding total RAM of the machines being targeted.
- Hash assets on the way to disk instead of re-reading them. Download is a
single pass now; verification still completes before tar sees the bytes.
- Release the intermediate data.tar right after extraction rather than
holding the data directory twice for the rest of the run.
- Check scratch capacity before downloading, so we fail before the old
service is stopped rather than halfway through.
- Refresh Environment=GOMEMLIMIT/GOGC/THREE_M_UI_CORE_LOW_MEMORY and
MemoryMax on an existing unit. write_service() keeps an operator's unit,
which meant the values baked in at install time never tracked updates.
Panel:
- Bound the Go heap from the process itself via debug.SetMemoryLimit, so the
tuning reaches manual runs, containers and the installer's own init and
healthcheck instead of only a systemd unit. It acts in-process, so the
Mihomo core never inherits the panel's tighter GC target. An explicit
GOMEMLIMIT or GOGC always wins.
- Emit geodata-loader: memconservative for the core when low-memory mode is
on. The key is omitempty and pinned after user fragments merge, so nothing
changes for anyone who does not opt in, and no fragment can undo it.
No new calls to Mmap/Munmap/Madvise, no swap or mmap anywhere in the tree.
* fix(ci): keep the new scratch directory out of the installer's own paths
Two problems CI caught on the small-memory change, both from moving the
scratch directory from mktemp into DATA_DIR.
restore_files() rebuilds the data directory by deleting everything in it
except backups. The scratch directory now lives inside it, so a rollback
deleted the very directory it was reading its own rollback out of, and the
restore died looking for restore/ENABLED. Rollback therefore had to be
skipped, exempting the scratch directory from that purge; the cleanup trap
already removes it.
The test curl also modelled only one of curl's output modes. Writing to
stdout and answering -I are both normal curl behaviour, and both are now
covered, so the mock no longer pins the installer to a single invocation
shape. The distribution fake likewise emits its tampered asset on stdout
instead of through --output, keeping that test about checksum rejection
rather than about which flag was used.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
perf(memory): keep install, update and runtime inside a small-RAM budget (#83)
* perf(memory): keep install, update and runtime inside a small-RAM budget
Small NAT/VPS targets have 128-512MB of RAM and no swap, so anything that
cannot be reclaimed is fatal. This tuning touches no swap and no mmap; it
works by not allocating in the first place.
Installer (covers install and update; update.sh only execs install.sh):
- Put the scratch directory under DATA_DIR instead of mktemp -d. /tmp is
tmpfs on many of these hosts, and without swap every byte there is pinned
in RAM: bundle, extracted release and a copy of the data directory were
held at once, exceeding total RAM of the machines being targeted.
- Hash assets on the way to disk instead of re-reading them. Download is a
single pass now; verification still completes before tar sees the bytes.
- Release the intermediate data.tar right after extraction rather than
holding the data directory twice for the rest of the run.
- Check scratch capacity before downloading, so we fail before the old
service is stopped rather than halfway through.
- Refresh Environment=GOMEMLIMIT/GOGC/THREE_M_UI_CORE_LOW_MEMORY and
MemoryMax on an existing unit. write_service() keeps an operator's unit,
which meant the values baked in at install time never tracked updates.
Panel:
- Bound the Go heap from the process itself via debug.SetMemoryLimit, so the
tuning reaches manual runs, containers and the installer's own init and
healthcheck instead of only a systemd unit. It acts in-process, so the
Mihomo core never inherits the panel's tighter GC target. An explicit
GOMEMLIMIT or GOGC always wins.
- Emit geodata-loader: memconservative for the core when low-memory mode is
on. The key is omitempty and pinned after user fragments merge, so nothing
changes for anyone who does not opt in, and no fragment can undo it.
No new calls to Mmap/Munmap/Madvise, no swap or mmap anywhere in the tree.
* fix(ci): keep the new scratch directory out of the installer's own paths
Two problems CI caught on the small-memory change, both from moving the
scratch directory from mktemp into DATA_DIR.
restore_files() rebuilds the data directory by deleting everything in it
except backups. The scratch directory now lives inside it, so a rollback
deleted the very directory it was reading its own rollback out of, and the
restore died looking for restore/ENABLED. Rollback therefore had to be
skipped, exempting the scratch directory from that purge; the cleanup trap
already removes it.
The test curl also modelled only one of curl's output modes. Writing to
stdout and answering -I are both normal curl behaviour, and both are now
covered, so the mock no longer pins the installer to a single invocation
shape. The distribution fake likewise emits its tampered asset on stdout
instead of through --output, keeping that test about checksum rejection
rather than about which flag was used.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
perf(memory): keep install, update and runtime inside a small-RAM budget (#83)
* perf(memory): keep install, update and runtime inside a small-RAM budget
Small NAT/VPS targets have 128-512MB of RAM and no swap, so anything that
cannot be reclaimed is fatal. This tuning touches no swap and no mmap; it
works by not allocating in the first place.
Installer (covers install and update; update.sh only execs install.sh):
- Put the scratch directory under DATA_DIR instead of mktemp -d. /tmp is
tmpfs on many of these hosts, and without swap every byte there is pinned
in RAM: bundle, extracted release and a copy of the data directory were
held at once, exceeding total RAM of the machines being targeted.
- Hash assets on the way to disk instead of re-reading them. Download is a
single pass now; verification still completes before tar sees the bytes.
- Release the intermediate data.tar right after extraction rather than
holding the data directory twice for the rest of the run.
- Check scratch capacity before downloading, so we fail before the old
service is stopped rather than halfway through.
- Refresh Environment=GOMEMLIMIT/GOGC/THREE_M_UI_CORE_LOW_MEMORY and
MemoryMax on an existing unit. write_service() keeps an operator's unit,
which meant the values baked in at install time never tracked updates.
Panel:
- Bound the Go heap from the process itself via debug.SetMemoryLimit, so the
tuning reaches manual runs, containers and the installer's own init and
healthcheck instead of only a systemd unit. It acts in-process, so the
Mihomo core never inherits the panel's tighter GC target. An explicit
GOMEMLIMIT or GOGC always wins.
- Emit geodata-loader: memconservative for the core when low-memory mode is
on. The key is omitempty and pinned after user fragments merge, so nothing
changes for anyone who does not opt in, and no fragment can undo it.
No new calls to Mmap/Munmap/Madvise, no swap or mmap anywhere in the tree.
* fix(ci): keep the new scratch directory out of the installer's own paths
Two problems CI caught on the small-memory change, both from moving the
scratch directory from mktemp into DATA_DIR.
restore_files() rebuilds the data directory by deleting everything in it
except backups. The scratch directory now lives inside it, so a rollback
deleted the very directory it was reading its own rollback out of, and the
restore died looking for restore/ENABLED. Rollback therefore had to be
skipped, exempting the scratch directory from that purge; the cleanup trap
already removes it.
The test curl also modelled only one of curl's output modes. Writing to
stdout and answering -I are both normal curl behaviour, and both are now
covered, so the mock no longer pins the installer to a single invocation
shape. The distribution fake likewise emits its tampered asset on stdout
instead of through --output, keeping that test about checksum rejection
rather than about which flag was used.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
perf(memory): keep install, update and runtime inside a small-RAM budget (#83)
* perf(memory): keep install, update and runtime inside a small-RAM budget
Small NAT/VPS targets have 128-512MB of RAM and no swap, so anything that
cannot be reclaimed is fatal. This tuning touches no swap and no mmap; it
works by not allocating in the first place.
Installer (covers install and update; update.sh only execs install.sh):
- Put the scratch directory under DATA_DIR instead of mktemp -d. /tmp is
tmpfs on many of these hosts, and without swap every byte there is pinned
in RAM: bundle, extracted release and a copy of the data directory were
held at once, exceeding total RAM of the machines being targeted.
- Hash assets on the way to disk instead of re-reading them. Download is a
single pass now; verification still completes before tar sees the bytes.
- Release the intermediate data.tar right after extraction rather than
holding the data directory twice for the rest of the run.
- Check scratch capacity before downloading, so we fail before the old
service is stopped rather than halfway through.
- Refresh Environment=GOMEMLIMIT/GOGC/THREE_M_UI_CORE_LOW_MEMORY and
MemoryMax on an existing unit. write_service() keeps an operator's unit,
which meant the values baked in at install time never tracked updates.
Panel:
- Bound the Go heap from the process itself via debug.SetMemoryLimit, so the
tuning reaches manual runs, containers and the installer's own init and
healthcheck instead of only a systemd unit. It acts in-process, so the
Mihomo core never inherits the panel's tighter GC target. An explicit
GOMEMLIMIT or GOGC always wins.
- Emit geodata-loader: memconservative for the core when low-memory mode is
on. The key is omitempty and pinned after user fragments merge, so nothing
changes for anyone who does not opt in, and no fragment can undo it.
No new calls to Mmap/Munmap/Madvise, no swap or mmap anywhere in the tree.
* fix(ci): keep the new scratch directory out of the installer's own paths
Two problems CI caught on the small-memory change, both from moving the
scratch directory from mktemp into DATA_DIR.
restore_files() rebuilds the data directory by deleting everything in it
except backups. The scratch directory now lives inside it, so a rollback
deleted the very directory it was reading its own rollback out of, and the
restore died looking for restore/ENABLED. Rollback therefore had to be
skipped, exempting the scratch directory from that purge; the cleanup trap
already removes it.
The test curl also modelled only one of curl's output modes. Writing to
stdout and answering -I are both normal curl behaviour, and both are now
covered, so the mock no longer pins the installer to a single invocation
shape. The distribution fake likewise emits its tampered asset on stdout
instead of through --output, keeping that test about checksum rejection
rather than about which flag was used.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
perf(memory): keep install, update and runtime inside a small-RAM budget (#83)
* perf(memory): keep install, update and runtime inside a small-RAM budget
Small NAT/VPS targets have 128-512MB of RAM and no swap, so anything that
cannot be reclaimed is fatal. This tuning touches no swap and no mmap; it
works by not allocating in the first place.
Installer (covers install and update; update.sh only execs install.sh):
- Put the scratch directory under DATA_DIR instead of mktemp -d. /tmp is
tmpfs on many of these hosts, and without swap every byte there is pinned
in RAM: bundle, extracted release and a copy of the data directory were
held at once, exceeding total RAM of the machines being targeted.
- Hash assets on the way to disk instead of re-reading them. Download is a
single pass now; verification still completes before tar sees the bytes.
- Release the intermediate data.tar right after extraction rather than
holding the data directory twice for the rest of the run.
- Check scratch capacity before downloading, so we fail before the old
service is stopped rather than halfway through.
- Refresh Environment=GOMEMLIMIT/GOGC/THREE_M_UI_CORE_LOW_MEMORY and
MemoryMax on an existing unit. write_service() keeps an operator's unit,
which meant the values baked in at install time never tracked updates.
Panel:
- Bound the Go heap from the process itself via debug.SetMemoryLimit, so the
tuning reaches manual runs, containers and the installer's own init and
healthcheck instead of only a systemd unit. It acts in-process, so the
Mihomo core never inherits the panel's tighter GC target. An explicit
GOMEMLIMIT or GOGC always wins.
- Emit geodata-loader: memconservative for the core when low-memory mode is
on. The key is omitempty and pinned after user fragments merge, so nothing
changes for anyone who does not opt in, and no fragment can undo it.
No new calls to Mmap/Munmap/Madvise, no swap or mmap anywhere in the tree.
* fix(ci): keep the new scratch directory out of the installer's own paths
Two problems CI caught on the small-memory change, both from moving the
scratch directory from mktemp into DATA_DIR.
restore_files() rebuilds the data directory by deleting everything in it
except backups. The scratch directory now lives inside it, so a rollback
deleted the very directory it was reading its own rollback out of, and the
restore died looking for restore/ENABLED. Rollback therefore had to be
skipped, exempting the scratch directory from that purge; the cleanup trap
already removes it.
The test curl also modelled only one of curl's output modes. Writing to
stdout and answering -I are both normal curl behaviour, and both are now
covered, so the mock no longer pins the installer to a single invocation
shape. The distribution fake likewise emits its tampered asset on stdout
instead of through --output, keeping that test about checksum rejection
rather than about which flag was used.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
perf(memory): keep install, update and runtime inside a small-RAM budget (#83)
* perf(memory): keep install, update and runtime inside a small-RAM budget
Small NAT/VPS targets have 128-512MB of RAM and no swap, so anything that
cannot be reclaimed is fatal. This tuning touches no swap and no mmap; it
works by not allocating in the first place.
Installer (covers install and update; update.sh only execs install.sh):
- Put the scratch directory under DATA_DIR instead of mktemp -d. /tmp is
tmpfs on many of these hosts, and without swap every byte there is pinned
in RAM: bundle, extracted release and a copy of the data directory were
held at once, exceeding total RAM of the machines being targeted.
- Hash assets on the way to disk instead of re-reading them. Download is a
single pass now; verification still completes before tar sees the bytes.
- Release the intermediate data.tar right after extraction rather than
holding the data directory twice for the rest of the run.
- Check scratch capacity before downloading, so we fail before the old
service is stopped rather than halfway through.
- Refresh Environment=GOMEMLIMIT/GOGC/THREE_M_UI_CORE_LOW_MEMORY and
MemoryMax on an existing unit. write_service() keeps an operator's unit,
which meant the values baked in at install time never tracked updates.
Panel:
- Bound the Go heap from the process itself via debug.SetMemoryLimit, so the
tuning reaches manual runs, containers and the installer's own init and
healthcheck instead of only a systemd unit. It acts in-process, so the
Mihomo core never inherits the panel's tighter GC target. An explicit
GOMEMLIMIT or GOGC always wins.
- Emit geodata-loader: memconservative for the core when low-memory mode is
on. The key is omitempty and pinned after user fragments merge, so nothing
changes for anyone who does not opt in, and no fragment can undo it.
No new calls to Mmap/Munmap/Madvise, no swap or mmap anywhere in the tree.
* fix(ci): keep the new scratch directory out of the installer's own paths
Two problems CI caught on the small-memory change, both from moving the
scratch directory from mktemp into DATA_DIR.
restore_files() rebuilds the data directory by deleting everything in it
except backups. The scratch directory now lives inside it, so a rollback
deleted the very directory it was reading its own rollback out of, and the
restore died looking for restore/ENABLED. Rollback therefore had to be
skipped, exempting the scratch directory from that purge; the cleanup trap
already removes it.
The test curl also modelled only one of curl's output modes. Writing to
stdout and answering -I are both normal curl behaviour, and both are now
covered, so the mock no longer pins the installer to a single invocation
shape. The distribution fake likewise emits its tampered asset on stdout
instead of through --output, keeping that test about checksum rejection
rather than about which flag was used.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
perf(memory): keep install, update and runtime inside a small-RAM budget (#83)
* perf(memory): keep install, update and runtime inside a small-RAM budget
Small NAT/VPS targets have 128-512MB of RAM and no swap, so anything that
cannot be reclaimed is fatal. This tuning touches no swap and no mmap; it
works by not allocating in the first place.
Installer (covers install and update; update.sh only execs install.sh):
- Put the scratch directory under DATA_DIR instead of mktemp -d. /tmp is
tmpfs on many of these hosts, and without swap every byte there is pinned
in RAM: bundle, extracted release and a copy of the data directory were
held at once, exceeding total RAM of the machines being targeted.
- Hash assets on the way to disk instead of re-reading them. Download is a
single pass now; verification still completes before tar sees the bytes.
- Release the intermediate data.tar right after extraction rather than
holding the data directory twice for the rest of the run.
- Check scratch capacity before downloading, so we fail before the old
service is stopped rather than halfway through.
- Refresh Environment=GOMEMLIMIT/GOGC/THREE_M_UI_CORE_LOW_MEMORY and
MemoryMax on an existing unit. write_service() keeps an operator's unit,
which meant the values baked in at install time never tracked updates.
Panel:
- Bound the Go heap from the process itself via debug.SetMemoryLimit, so the
tuning reaches manual runs, containers and the installer's own init and
healthcheck instead of only a systemd unit. It acts in-process, so the
Mihomo core never inherits the panel's tighter GC target. An explicit
GOMEMLIMIT or GOGC always wins.
- Emit geodata-loader: memconservative for the core when low-memory mode is
on. The key is omitempty and pinned after user fragments merge, so nothing
changes for anyone who does not opt in, and no fragment can undo it.
No new calls to Mmap/Munmap/Madvise, no swap or mmap anywhere in the tree.
* fix(ci): keep the new scratch directory out of the installer's own paths
Two problems CI caught on the small-memory change, both from moving the
scratch directory from mktemp into DATA_DIR.
restore_files() rebuilds the data directory by deleting everything in it
except backups. The scratch directory now lives inside it, so a rollback
deleted the very directory it was reading its own rollback out of, and the
restore died looking for restore/ENABLED. Rollback therefore had to be
skipped, exempting the scratch directory from that purge; the cleanup trap
already removes it.
The test curl also modelled only one of curl's output modes. Writing to
stdout and answering -I are both normal curl behaviour, and both are now
covered, so the mock no longer pins the installer to a single invocation
shape. The distribution fake likewise emits its tampered asset on stdout
instead of through --output, keeping that test about checksum rejection
rather than about which flag was used.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
perf(memory): keep install, update and runtime inside a small-RAM budget (#83)
* perf(memory): keep install, update and runtime inside a small-RAM budget
Small NAT/VPS targets have 128-512MB of RAM and no swap, so anything that
cannot be reclaimed is fatal. This tuning touches no swap and no mmap; it
works by not allocating in the first place.
Installer (covers install and update; update.sh only execs install.sh):
- Put the scratch directory under DATA_DIR instead of mktemp -d. /tmp is
tmpfs on many of these hosts, and without swap every byte there is pinned
in RAM: bundle, extracted release and a copy of the data directory were
held at once, exceeding total RAM of the machines being targeted.
- Hash assets on the way to disk instead of re-reading them. Download is a
single pass now; verification still completes before tar sees the bytes.
- Release the intermediate data.tar right after extraction rather than
holding the data directory twice for the rest of the run.
- Check scratch capacity before downloading, so we fail before the old
service is stopped rather than halfway through.
- Refresh Environment=GOMEMLIMIT/GOGC/THREE_M_UI_CORE_LOW_MEMORY and
MemoryMax on an existing unit. write_service() keeps an operator's unit,
which meant the values baked in at install time never tracked updates.
Panel:
- Bound the Go heap from the process itself via debug.SetMemoryLimit, so the
tuning reaches manual runs, containers and the installer's own init and
healthcheck instead of only a systemd unit. It acts in-process, so the
Mihomo core never inherits the panel's tighter GC target. An explicit
GOMEMLIMIT or GOGC always wins.
- Emit geodata-loader: memconservative for the core when low-memory mode is
on. The key is omitempty and pinned after user fragments merge, so nothing
changes for anyone who does not opt in, and no fragment can undo it.
No new calls to Mmap/Munmap/Madvise, no swap or mmap anywhere in the tree.
* fix(ci): keep the new scratch directory out of the installer's own paths
Two problems CI caught on the small-memory change, both from moving the
scratch directory from mktemp into DATA_DIR.
restore_files() rebuilds the data directory by deleting everything in it
except backups. The scratch directory now lives inside it, so a rollback
deleted the very directory it was reading its own rollback out of, and the
restore died looking for restore/ENABLED. Rollback therefore had to be
skipped, exempting the scratch directory from that purge; the cleanup trap
already removes it.
The test curl also modelled only one of curl's output modes. Writing to
stdout and answering -I are both normal curl behaviour, and both are now
covered, so the mock no longer pins the installer to a single invocation
shape. The distribution fake likewise emits its tampered asset on stdout
instead of through --output, keeping that test about checksum rejection
rather than about which flag was used.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
perf(memory): keep install, update and runtime inside a small-RAM budget (#83)
* perf(memory): keep install, update and runtime inside a small-RAM budget
Small NAT/VPS targets have 128-512MB of RAM and no swap, so anything that
cannot be reclaimed is fatal. This tuning touches no swap and no mmap; it
works by not allocating in the first place.
Installer (covers install and update; update.sh only execs install.sh):
- Put the scratch directory under DATA_DIR instead of mktemp -d. /tmp is
tmpfs on many of these hosts, and without swap every byte there is pinned
in RAM: bundle, extracted release and a copy of the data directory were
held at once, exceeding total RAM of the machines being targeted.
- Hash assets on the way to disk instead of re-reading them. Download is a
single pass now; verification still completes before tar sees the bytes.
- Release the intermediate data.tar right after extraction rather than
holding the data directory twice for the rest of the run.
- Check scratch capacity before downloading, so we fail before the old
service is stopped rather than halfway through.
- Refresh Environment=GOMEMLIMIT/GOGC/THREE_M_UI_CORE_LOW_MEMORY and
MemoryMax on an existing unit. write_service() keeps an operator's unit,
which meant the values baked in at install time never tracked updates.
Panel:
- Bound the Go heap from the process itself via debug.SetMemoryLimit, so the
tuning reaches manual runs, containers and the installer's own init and
healthcheck instead of only a systemd unit. It acts in-process, so the
Mihomo core never inherits the panel's tighter GC target. An explicit
GOMEMLIMIT or GOGC always wins.
- Emit geodata-loader: memconservative for the core when low-memory mode is
on. The key is omitempty and pinned after user fragments merge, so nothing
changes for anyone who does not opt in, and no fragment can undo it.
No new calls to Mmap/Munmap/Madvise, no swap or mmap anywhere in the tree.
* fix(ci): keep the new scratch directory out of the installer's own paths
Two problems CI caught on the small-memory change, both from moving the
scratch directory from mktemp into DATA_DIR.
restore_files() rebuilds the data directory by deleting everything in it
except backups. The scratch directory now lives inside it, so a rollback
deleted the very directory it was reading its own rollback out of, and the
restore died looking for restore/ENABLED. Rollback therefore had to be
skipped, exempting the scratch directory from that purge; the cleanup trap
already removes it.
The test curl also modelled only one of curl's output modes. Writing to
stdout and answering -I are both normal curl behaviour, and both are now
covered, so the mock no longer pins the installer to a single invocation
shape. The distribution fake likewise emits its tampered asset on stdout
instead of through --output, keeping that test about checksum rejection
rather than about which flag was used.
---------
Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
2026-09-27 09:27:35 +08:00
Z User 42557d4384 feat(cli): reset-config subcommand for SSH rescue
Adds a new '3m-ui reset-config' CLI subcommand that lets operators undo
panel configuration changes via SSH when the web UI is unreachable
(e.g. panel bound to 127.0.0.1, wrong port, broken SSL config).
USAGE
3m-ui reset-config [--panel] [--access] [--public] [--all] [--yes]
SCOPE FLAGS (default: --panel if none given)
--panel    Clear server.listen (= ':<port>') + disable SSL/ACME
→ panel boots plain HTTP on next restart. Port preserved.
--access   Clear listener.public_host / access_sni / access_alpn
→ nodes fall back to their bind address.
--public   Clear server.public_url
→ subscription links fall back to request host.
--all      Apply all three scopes above.
--yes / -y Skip interactive y/N confirmation (for rescue scripts).
AFTER RESET
sudo systemctl restart 3m-ui
Open http://<server-ip>:<port>  (HTTP, not HTTPS)
Re-apply settings from the web UI.
SAFETY
- Never deletes users, listeners, traffic records, or credentials.
- Preserves server.port (so SSH rescue doesn't require re-reading YAML).
- SSL disabled by flipping Enabled flag only — Domain / cert paths kept
so operator can re-enable from UI after fixing underlying issue.
- Interactive y/N prompt by default; --yes for scripted rescue.
FILES ADDED
backend/cmd/server/reset_config.go      (~210 LOC)
backend/cmd/server/reset_config_test.go (~110 LOC, 5 tests)
FILES MODIFIED
backend/cmd/server/main.go             (wire reset-config subcommand)
backend/internal/config/config.go      (+ClearListenInFile +ClearPublicURLInFile helpers)
Tests: 23/23 backend packages PASS.
feat(cli): reset-config subcommand for SSH rescue
Adds a new '3m-ui reset-config' CLI subcommand that lets operators undo
panel configuration changes via SSH when the web UI is unreachable
(e.g. panel bound to 127.0.0.1, wrong port, broken SSL config).
USAGE
3m-ui reset-config [--panel] [--access] [--public] [--all] [--yes]
SCOPE FLAGS (default: --panel if none given)
--panel    Clear server.listen (= ':<port>') + disable SSL/ACME
→ panel boots plain HTTP on next restart. Port preserved.
--access   Clear listener.public_host / access_sni / access_alpn
→ nodes fall back to their bind address.
--public   Clear server.public_url
→ subscription links fall back to request host.
--all      Apply all three scopes above.
--yes / -y Skip interactive y/N confirmation (for rescue scripts).
AFTER RESET
sudo systemctl restart 3m-ui
Open http://<server-ip>:<port>  (HTTP, not HTTPS)
Re-apply settings from the web UI.
SAFETY
- Never deletes users, listeners, traffic records, or credentials.
- Preserves server.port (so SSH rescue doesn't require re-reading YAML).
- SSL disabled by flipping Enabled flag only — Domain / cert paths kept
so operator can re-enable from UI after fixing underlying issue.
- Interactive y/N prompt by default; --yes for scripted rescue.
FILES ADDED
backend/cmd/server/reset_config.go      (~210 LOC)
backend/cmd/server/reset_config_test.go (~110 LOC, 5 tests)
FILES MODIFIED
backend/cmd/server/main.go             (wire reset-config subcommand)
backend/internal/config/config.go      (+ClearListenInFile +ClearPublicURLInFile helpers)
Tests: 23/23 backend packages PASS.
feat(cli): reset-config subcommand for SSH rescue
Adds a new '3m-ui reset-config' CLI subcommand that lets operators undo
panel configuration changes via SSH when the web UI is unreachable
(e.g. panel bound to 127.0.0.1, wrong port, broken SSL config).
USAGE
3m-ui reset-config [--panel] [--access] [--public] [--all] [--yes]
SCOPE FLAGS (default: --panel if none given)
--panel    Clear server.listen (= ':<port>') + disable SSL/ACME
→ panel boots plain HTTP on next restart. Port preserved.
--access   Clear listener.public_host / access_sni / access_alpn
→ nodes fall back to their bind address.
--public   Clear server.public_url
→ subscription links fall back to request host.
--all      Apply all three scopes above.
--yes / -y Skip interactive y/N confirmation (for rescue scripts).
AFTER RESET
sudo systemctl restart 3m-ui
Open http://<server-ip>:<port>  (HTTP, not HTTPS)
Re-apply settings from the web UI.
SAFETY
- Never deletes users, listeners, traffic records, or credentials.
- Preserves server.port (so SSH rescue doesn't require re-reading YAML).
- SSL disabled by flipping Enabled flag only — Domain / cert paths kept
so operator can re-enable from UI after fixing underlying issue.
- Interactive y/N prompt by default; --yes for scripted rescue.
FILES ADDED
backend/cmd/server/reset_config.go      (~210 LOC)
backend/cmd/server/reset_config_test.go (~110 LOC, 5 tests)
FILES MODIFIED
backend/cmd/server/main.go             (wire reset-config subcommand)
backend/internal/config/config.go      (+ClearListenInFile +ClearPublicURLInFile helpers)
Tests: 23/23 backend packages PASS.
feat(cli): reset-config subcommand for SSH rescue
Adds a new '3m-ui reset-config' CLI subcommand that lets operators undo
panel configuration changes via SSH when the web UI is unreachable
(e.g. panel bound to 127.0.0.1, wrong port, broken SSL config).
USAGE
3m-ui reset-config [--panel] [--access] [--public] [--all] [--yes]
SCOPE FLAGS (default: --panel if none given)
--panel    Clear server.listen (= ':<port>') + disable SSL/ACME
→ panel boots plain HTTP on next restart. Port preserved.
--access   Clear listener.public_host / access_sni / access_alpn
→ nodes fall back to their bind address.
--public   Clear server.public_url
→ subscription links fall back to request host.
--all      Apply all three scopes above.
--yes / -y Skip interactive y/N confirmation (for rescue scripts).
AFTER RESET
sudo systemctl restart 3m-ui
Open http://<server-ip>:<port>  (HTTP, not HTTPS)
Re-apply settings from the web UI.
SAFETY
- Never deletes users, listeners, traffic records, or credentials.
- Preserves server.port (so SSH rescue doesn't require re-reading YAML).
- SSL disabled by flipping Enabled flag only — Domain / cert paths kept
so operator can re-enable from UI after fixing underlying issue.
- Interactive y/N prompt by default; --yes for scripted rescue.
FILES ADDED
backend/cmd/server/reset_config.go      (~210 LOC)
backend/cmd/server/reset_config_test.go (~110 LOC, 5 tests)
FILES MODIFIED
backend/cmd/server/main.go             (wire reset-config subcommand)
backend/internal/config/config.go      (+ClearListenInFile +ClearPublicURLInFile helpers)
Tests: 23/23 backend packages PASS.
feat(cli): reset-config subcommand for SSH rescue
Adds a new '3m-ui reset-config' CLI subcommand that lets operators undo
panel configuration changes via SSH when the web UI is unreachable
(e.g. panel bound to 127.0.0.1, wrong port, broken SSL config).
USAGE
3m-ui reset-config [--panel] [--access] [--public] [--all] [--yes]
SCOPE FLAGS (default: --panel if none given)
--panel    Clear server.listen (= ':<port>') + disable SSL/ACME
→ panel boots plain HTTP on next restart. Port preserved.
--access   Clear listener.public_host / access_sni / access_alpn
→ nodes fall back to their bind address.
--public   Clear server.public_url
→ subscription links fall back to request host.
--all      Apply all three scopes above.
--yes / -y Skip interactive y/N confirmation (for rescue scripts).
AFTER RESET
sudo systemctl restart 3m-ui
Open http://<server-ip>:<port>  (HTTP, not HTTPS)
Re-apply settings from the web UI.
SAFETY
- Never deletes users, listeners, traffic records, or credentials.
- Preserves server.port (so SSH rescue doesn't require re-reading YAML).
- SSL disabled by flipping Enabled flag only — Domain / cert paths kept
so operator can re-enable from UI after fixing underlying issue.
- Interactive y/N prompt by default; --yes for scripted rescue.
FILES ADDED
backend/cmd/server/reset_config.go      (~210 LOC)
backend/cmd/server/reset_config_test.go (~110 LOC, 5 tests)
FILES MODIFIED
backend/cmd/server/main.go             (wire reset-config subcommand)
backend/internal/config/config.go      (+ClearListenInFile +ClearPublicURLInFile helpers)
Tests: 23/23 backend packages PASS.
feat(cli): reset-config subcommand for SSH rescue
Adds a new '3m-ui reset-config' CLI subcommand that lets operators undo
panel configuration changes via SSH when the web UI is unreachable
(e.g. panel bound to 127.0.0.1, wrong port, broken SSL config).
USAGE
3m-ui reset-config [--panel] [--access] [--public] [--all] [--yes]
SCOPE FLAGS (default: --panel if none given)
--panel    Clear server.listen (= ':<port>') + disable SSL/ACME
→ panel boots plain HTTP on next restart. Port preserved.
--access   Clear listener.public_host / access_sni / access_alpn
→ nodes fall back to their bind address.
--public   Clear server.public_url
→ subscription links fall back to request host.
--all      Apply all three scopes above.
--yes / -y Skip interactive y/N confirmation (for rescue scripts).
AFTER RESET
sudo systemctl restart 3m-ui
Open http://<server-ip>:<port>  (HTTP, not HTTPS)
Re-apply settings from the web UI.
SAFETY
- Never deletes users, listeners, traffic records, or credentials.
- Preserves server.port (so SSH rescue doesn't require re-reading YAML).
- SSL disabled by flipping Enabled flag only — Domain / cert paths kept
so operator can re-enable from UI after fixing underlying issue.
- Interactive y/N prompt by default; --yes for scripted rescue.
FILES ADDED
backend/cmd/server/reset_config.go      (~210 LOC)
backend/cmd/server/reset_config_test.go (~110 LOC, 5 tests)
FILES MODIFIED
backend/cmd/server/main.go             (wire reset-config subcommand)
backend/internal/config/config.go      (+ClearListenInFile +ClearPublicURLInFile helpers)
Tests: 23/23 backend packages PASS.
feat(cli): reset-config subcommand for SSH rescue
Adds a new '3m-ui reset-config' CLI subcommand that lets operators undo
panel configuration changes via SSH when the web UI is unreachable
(e.g. panel bound to 127.0.0.1, wrong port, broken SSL config).
USAGE
3m-ui reset-config [--panel] [--access] [--public] [--all] [--yes]
SCOPE FLAGS (default: --panel if none given)
--panel    Clear server.listen (= ':<port>') + disable SSL/ACME
→ panel boots plain HTTP on next restart. Port preserved.
--access   Clear listener.public_host / access_sni / access_alpn
→ nodes fall back to their bind address.
--public   Clear server.public_url
→ subscription links fall back to request host.
--all      Apply all three scopes above.
--yes / -y Skip interactive y/N confirmation (for rescue scripts).
AFTER RESET
sudo systemctl restart 3m-ui
Open http://<server-ip>:<port>  (HTTP, not HTTPS)
Re-apply settings from the web UI.
SAFETY
- Never deletes users, listeners, traffic records, or credentials.
- Preserves server.port (so SSH rescue doesn't require re-reading YAML).
- SSL disabled by flipping Enabled flag only — Domain / cert paths kept
so operator can re-enable from UI after fixing underlying issue.
- Interactive y/N prompt by default; --yes for scripted rescue.
FILES ADDED
backend/cmd/server/reset_config.go      (~210 LOC)
backend/cmd/server/reset_config_test.go (~110 LOC, 5 tests)
FILES MODIFIED
backend/cmd/server/main.go             (wire reset-config subcommand)
backend/internal/config/config.go      (+ClearListenInFile +ClearPublicURLInFile helpers)
Tests: 23/23 backend packages PASS.
feat(cli): reset-config subcommand for SSH rescue
Adds a new '3m-ui reset-config' CLI subcommand that lets operators undo
panel configuration changes via SSH when the web UI is unreachable
(e.g. panel bound to 127.0.0.1, wrong port, broken SSL config).
USAGE
3m-ui reset-config [--panel] [--access] [--public] [--all] [--yes]
SCOPE FLAGS (default: --panel if none given)
--panel    Clear server.listen (= ':<port>') + disable SSL/ACME
→ panel boots plain HTTP on next restart. Port preserved.
--access   Clear listener.public_host / access_sni / access_alpn
→ nodes fall back to their bind address.
--public   Clear server.public_url
→ subscription links fall back to request host.
--all      Apply all three scopes above.
--yes / -y Skip interactive y/N confirmation (for rescue scripts).
AFTER RESET
sudo systemctl restart 3m-ui
Open http://<server-ip>:<port>  (HTTP, not HTTPS)
Re-apply settings from the web UI.
SAFETY
- Never deletes users, listeners, traffic records, or credentials.
- Preserves server.port (so SSH rescue doesn't require re-reading YAML).
- SSL disabled by flipping Enabled flag only — Domain / cert paths kept
so operator can re-enable from UI after fixing underlying issue.
- Interactive y/N prompt by default; --yes for scripted rescue.
FILES ADDED
backend/cmd/server/reset_config.go      (~210 LOC)
backend/cmd/server/reset_config_test.go (~110 LOC, 5 tests)
FILES MODIFIED
backend/cmd/server/main.go             (wire reset-config subcommand)
backend/internal/config/config.go      (+ClearListenInFile +ClearPublicURLInFile helpers)
Tests: 23/23 backend packages PASS.
feat(cli): reset-config subcommand for SSH rescue
Adds a new '3m-ui reset-config' CLI subcommand that lets operators undo
panel configuration changes via SSH when the web UI is unreachable
(e.g. panel bound to 127.0.0.1, wrong port, broken SSL config).
USAGE
3m-ui reset-config [--panel] [--access] [--public] [--all] [--yes]
SCOPE FLAGS (default: --panel if none given)
--panel    Clear server.listen (= ':<port>') + disable SSL/ACME
→ panel boots plain HTTP on next restart. Port preserved.
--access   Clear listener.public_host / access_sni / access_alpn
→ nodes fall back to their bind address.
--public   Clear server.public_url
→ subscription links fall back to request host.
--all      Apply all three scopes above.
--yes / -y Skip interactive y/N confirmation (for rescue scripts).
AFTER RESET
sudo systemctl restart 3m-ui
Open http://<server-ip>:<port>  (HTTP, not HTTPS)
Re-apply settings from the web UI.
SAFETY
- Never deletes users, listeners, traffic records, or credentials.
- Preserves server.port (so SSH rescue doesn't require re-reading YAML).
- SSL disabled by flipping Enabled flag only — Domain / cert paths kept
so operator can re-enable from UI after fixing underlying issue.
- Interactive y/N prompt by default; --yes for scripted rescue.
FILES ADDED
backend/cmd/server/reset_config.go      (~210 LOC)
backend/cmd/server/reset_config_test.go (~110 LOC, 5 tests)
FILES MODIFIED
backend/cmd/server/main.go             (wire reset-config subcommand)
backend/internal/config/config.go      (+ClearListenInFile +ClearPublicURLInFile helpers)
Tests: 23/23 backend packages PASS.
feat(cli): reset-config subcommand for SSH rescue
Adds a new '3m-ui reset-config' CLI subcommand that lets operators undo
panel configuration changes via SSH when the web UI is unreachable
(e.g. panel bound to 127.0.0.1, wrong port, broken SSL config).
USAGE
3m-ui reset-config [--panel] [--access] [--public] [--all] [--yes]
SCOPE FLAGS (default: --panel if none given)
--panel    Clear server.listen (= ':<port>') + disable SSL/ACME
→ panel boots plain HTTP on next restart. Port preserved.
--access   Clear listener.public_host / access_sni / access_alpn
→ nodes fall back to their bind address.
--public   Clear server.public_url
→ subscription links fall back to request host.
--all      Apply all three scopes above.
--yes / -y Skip interactive y/N confirmation (for rescue scripts).
AFTER RESET
sudo systemctl restart 3m-ui
Open http://<server-ip>:<port>  (HTTP, not HTTPS)
Re-apply settings from the web UI.
SAFETY
- Never deletes users, listeners, traffic records, or credentials.
- Preserves server.port (so SSH rescue doesn't require re-reading YAML).
- SSL disabled by flipping Enabled flag only — Domain / cert paths kept
so operator can re-enable from UI after fixing underlying issue.
- Interactive y/N prompt by default; --yes for scripted rescue.
FILES ADDED
backend/cmd/server/reset_config.go      (~210 LOC)
backend/cmd/server/reset_config_test.go (~110 LOC, 5 tests)
FILES MODIFIED
backend/cmd/server/main.go             (wire reset-config subcommand)
backend/internal/config/config.go      (+ClearListenInFile +ClearPublicURLInFile helpers)
Tests: 23/23 backend packages PASS.
feat(cli): reset-config subcommand for SSH rescue
Adds a new '3m-ui reset-config' CLI subcommand that lets operators undo
panel configuration changes via SSH when the web UI is unreachable
(e.g. panel bound to 127.0.0.1, wrong port, broken SSL config).
USAGE
3m-ui reset-config [--panel] [--access] [--public] [--all] [--yes]
SCOPE FLAGS (default: --panel if none given)
--panel    Clear server.listen (= ':<port>') + disable SSL/ACME
→ panel boots plain HTTP on next restart. Port preserved.
--access   Clear listener.public_host / access_sni / access_alpn
→ nodes fall back to their bind address.
--public   Clear server.public_url
→ subscription links fall back to request host.
--all      Apply all three scopes above.
--yes / -y Skip interactive y/N confirmation (for rescue scripts).
AFTER RESET
sudo systemctl restart 3m-ui
Open http://<server-ip>:<port>  (HTTP, not HTTPS)
Re-apply settings from the web UI.
SAFETY
- Never deletes users, listeners, traffic records, or credentials.
- Preserves server.port (so SSH rescue doesn't require re-reading YAML).
- SSL disabled by flipping Enabled flag only — Domain / cert paths kept
so operator can re-enable from UI after fixing underlying issue.
- Interactive y/N prompt by default; --yes for scripted rescue.
FILES ADDED
backend/cmd/server/reset_config.go      (~210 LOC)
backend/cmd/server/reset_config_test.go (~110 LOC, 5 tests)
FILES MODIFIED
backend/cmd/server/main.go             (wire reset-config subcommand)
backend/internal/config/config.go      (+ClearListenInFile +ClearPublicURLInFile helpers)
Tests: 23/23 backend packages PASS.
feat(cli): reset-config subcommand for SSH rescue
Adds a new '3m-ui reset-config' CLI subcommand that lets operators undo
panel configuration changes via SSH when the web UI is unreachable
(e.g. panel bound to 127.0.0.1, wrong port, broken SSL config).
USAGE
3m-ui reset-config [--panel] [--access] [--public] [--all] [--yes]
SCOPE FLAGS (default: --panel if none given)
--panel    Clear server.listen (= ':<port>') + disable SSL/ACME
→ panel boots plain HTTP on next restart. Port preserved.
--access   Clear listener.public_host / access_sni / access_alpn
→ nodes fall back to their bind address.
--public   Clear server.public_url
→ subscription links fall back to request host.
--all      Apply all three scopes above.
--yes / -y Skip interactive y/N confirmation (for rescue scripts).
AFTER RESET
sudo systemctl restart 3m-ui
Open http://<server-ip>:<port>  (HTTP, not HTTPS)
Re-apply settings from the web UI.
SAFETY
- Never deletes users, listeners, traffic records, or credentials.
- Preserves server.port (so SSH rescue doesn't require re-reading YAML).
- SSL disabled by flipping Enabled flag only — Domain / cert paths kept
so operator can re-enable from UI after fixing underlying issue.
- Interactive y/N prompt by default; --yes for scripted rescue.
FILES ADDED
backend/cmd/server/reset_config.go      (~210 LOC)
backend/cmd/server/reset_config_test.go (~110 LOC, 5 tests)
FILES MODIFIED
backend/cmd/server/main.go             (wire reset-config subcommand)
backend/internal/config/config.go      (+ClearListenInFile +ClearPublicURLInFile helpers)
Tests: 23/23 backend packages PASS.
2026-09-25 06:10:45 +00:00
Z User 71119d59d6 feat(ui): 18 panel languages, login theme/lang, live about version
- Settings appearance language select uses full LOCALE_OPTIONS (18 locales)
- Login page: theme + language switches (persisted via existing stores)
- About shows version from /api/v1/health (buildinfo linked at release time)
- ldflags write Version/GitCommit/BuildTime into internal/buildinfo
feat(ui): 18 panel languages, login theme/lang, live about version
- Settings appearance language select uses full LOCALE_OPTIONS (18 locales)
- Login page: theme + language switches (persisted via existing stores)
- About shows version from /api/v1/health (buildinfo linked at release time)
- ldflags write Version/GitCommit/BuildTime into internal/buildinfo
feat(ui): 18 panel languages, login theme/lang, live about version
- Settings appearance language select uses full LOCALE_OPTIONS (18 locales)
- Login page: theme + language switches (persisted via existing stores)
- About shows version from /api/v1/health (buildinfo linked at release time)
- ldflags write Version/GitCommit/BuildTime into internal/buildinfo
feat(ui): 18 panel languages, login theme/lang, live about version
- Settings appearance language select uses full LOCALE_OPTIONS (18 locales)
- Login page: theme + language switches (persisted via existing stores)
- About shows version from /api/v1/health (buildinfo linked at release time)
- ldflags write Version/GitCommit/BuildTime into internal/buildinfo
feat(ui): 18 panel languages, login theme/lang, live about version
- Settings appearance language select uses full LOCALE_OPTIONS (18 locales)
- Login page: theme + language switches (persisted via existing stores)
- About shows version from /api/v1/health (buildinfo linked at release time)
- ldflags write Version/GitCommit/BuildTime into internal/buildinfo
feat(ui): 18 panel languages, login theme/lang, live about version
- Settings appearance language select uses full LOCALE_OPTIONS (18 locales)
- Login page: theme + language switches (persisted via existing stores)
- About shows version from /api/v1/health (buildinfo linked at release time)
- ldflags write Version/GitCommit/BuildTime into internal/buildinfo
feat(ui): 18 panel languages, login theme/lang, live about version
- Settings appearance language select uses full LOCALE_OPTIONS (18 locales)
- Login page: theme + language switches (persisted via existing stores)
- About shows version from /api/v1/health (buildinfo linked at release time)
- ldflags write Version/GitCommit/BuildTime into internal/buildinfo
feat(ui): 18 panel languages, login theme/lang, live about version
- Settings appearance language select uses full LOCALE_OPTIONS (18 locales)
- Login page: theme + language switches (persisted via existing stores)
- About shows version from /api/v1/health (buildinfo linked at release time)
- ldflags write Version/GitCommit/BuildTime into internal/buildinfo
feat(ui): 18 panel languages, login theme/lang, live about version
- Settings appearance language select uses full LOCALE_OPTIONS (18 locales)
- Login page: theme + language switches (persisted via existing stores)
- About shows version from /api/v1/health (buildinfo linked at release time)
- ldflags write Version/GitCommit/BuildTime into internal/buildinfo
feat(ui): 18 panel languages, login theme/lang, live about version
- Settings appearance language select uses full LOCALE_OPTIONS (18 locales)
- Login page: theme + language switches (persisted via existing stores)
- About shows version from /api/v1/health (buildinfo linked at release time)
- ldflags write Version/GitCommit/BuildTime into internal/buildinfo
feat(ui): 18 panel languages, login theme/lang, live about version
- Settings appearance language select uses full LOCALE_OPTIONS (18 locales)
- Login page: theme + language switches (persisted via existing stores)
- About shows version from /api/v1/health (buildinfo linked at release time)
- ldflags write Version/GitCommit/BuildTime into internal/buildinfo
feat(ui): 18 panel languages, login theme/lang, live about version
- Settings appearance language select uses full LOCALE_OPTIONS (18 locales)
- Login page: theme + language switches (persisted via existing stores)
- About shows version from /api/v1/health (buildinfo linked at release time)
- ldflags write Version/GitCommit/BuildTime into internal/buildinfo
2026-09-10 16:10:17 +00:00
freephilx 2e6256fb6a feat(distribution): add complete installation and recovery workflows
Ship stable-only Linux bundles and multiarch Docker images with a pinned Mihomo core. Share secure first-run initialization across native and container installs.
Validate downloads before downtime, snapshot persistent state before updates, and restore the previous installation after a failed upgrade. Add backup, restore, healthcheck, and randomized administrator reset commands.
Add release verification, lifecycle regression tests, and installation documentation.
feat(distribution): add complete installation and recovery workflows
Ship stable-only Linux bundles and multiarch Docker images with a pinned Mihomo core. Share secure first-run initialization across native and container installs.
Validate downloads before downtime, snapshot persistent state before updates, and restore the previous installation after a failed upgrade. Add backup, restore, healthcheck, and randomized administrator reset commands.
Add release verification, lifecycle regression tests, and installation documentation.
feat(distribution): add complete installation and recovery workflows
Ship stable-only Linux bundles and multiarch Docker images with a pinned Mihomo core. Share secure first-run initialization across native and container installs.
Validate downloads before downtime, snapshot persistent state before updates, and restore the previous installation after a failed upgrade. Add backup, restore, healthcheck, and randomized administrator reset commands.
Add release verification, lifecycle regression tests, and installation documentation.
feat(distribution): add complete installation and recovery workflows
Ship stable-only Linux bundles and multiarch Docker images with a pinned Mihomo core. Share secure first-run initialization across native and container installs.
Validate downloads before downtime, snapshot persistent state before updates, and restore the previous installation after a failed upgrade. Add backup, restore, healthcheck, and randomized administrator reset commands.
Add release verification, lifecycle regression tests, and installation documentation.
feat(distribution): add complete installation and recovery workflows
Ship stable-only Linux bundles and multiarch Docker images with a pinned Mihomo core. Share secure first-run initialization across native and container installs.
Validate downloads before downtime, snapshot persistent state before updates, and restore the previous installation after a failed upgrade. Add backup, restore, healthcheck, and randomized administrator reset commands.
Add release verification, lifecycle regression tests, and installation documentation.
feat(distribution): add complete installation and recovery workflows
Ship stable-only Linux bundles and multiarch Docker images with a pinned Mihomo core. Share secure first-run initialization across native and container installs.
Validate downloads before downtime, snapshot persistent state before updates, and restore the previous installation after a failed upgrade. Add backup, restore, healthcheck, and randomized administrator reset commands.
Add release verification, lifecycle regression tests, and installation documentation.
feat(distribution): add complete installation and recovery workflows
Ship stable-only Linux bundles and multiarch Docker images with a pinned Mihomo core. Share secure first-run initialization across native and container installs.
Validate downloads before downtime, snapshot persistent state before updates, and restore the previous installation after a failed upgrade. Add backup, restore, healthcheck, and randomized administrator reset commands.
Add release verification, lifecycle regression tests, and installation documentation.
feat(distribution): add complete installation and recovery workflows
Ship stable-only Linux bundles and multiarch Docker images with a pinned Mihomo core. Share secure first-run initialization across native and container installs.
Validate downloads before downtime, snapshot persistent state before updates, and restore the previous installation after a failed upgrade. Add backup, restore, healthcheck, and randomized administrator reset commands.
Add release verification, lifecycle regression tests, and installation documentation.
feat(distribution): add complete installation and recovery workflows
Ship stable-only Linux bundles and multiarch Docker images with a pinned Mihomo core. Share secure first-run initialization across native and container installs.
Validate downloads before downtime, snapshot persistent state before updates, and restore the previous installation after a failed upgrade. Add backup, restore, healthcheck, and randomized administrator reset commands.
Add release verification, lifecycle regression tests, and installation documentation.
feat(distribution): add complete installation and recovery workflows
Ship stable-only Linux bundles and multiarch Docker images with a pinned Mihomo core. Share secure first-run initialization across native and container installs.
Validate downloads before downtime, snapshot persistent state before updates, and restore the previous installation after a failed upgrade. Add backup, restore, healthcheck, and randomized administrator reset commands.
Add release verification, lifecycle regression tests, and installation documentation.
feat(distribution): add complete installation and recovery workflows
Ship stable-only Linux bundles and multiarch Docker images with a pinned Mihomo core. Share secure first-run initialization across native and container installs.
Validate downloads before downtime, snapshot persistent state before updates, and restore the previous installation after a failed upgrade. Add backup, restore, healthcheck, and randomized administrator reset commands.
Add release verification, lifecycle regression tests, and installation documentation.
feat(distribution): add complete installation and recovery workflows
Ship stable-only Linux bundles and multiarch Docker images with a pinned Mihomo core. Share secure first-run initialization across native and container installs.
Validate downloads before downtime, snapshot persistent state before updates, and restore the previous installation after a failed upgrade. Add backup, restore, healthcheck, and randomized administrator reset commands.
Add release verification, lifecycle regression tests, and installation documentation.
2026-09-08 22:47:35 +08:00
Z User 286873b128 feat(ops+sub): Phase A/B install menu, admin reset CLI, UA/sub UX
- Keep initial admin password as admin (no random password)
- Install summary prints local/public panel URLs
- Management menu: show URL, reset-admin to admin, BBR, geo update, public_url
- Binary: reset-admin command
- Expand subscription User-Agent auto-detect; frontend hints + user status filters
feat(ops+sub): Phase A/B install menu, admin reset CLI, UA/sub UX
- Keep initial admin password as admin (no random password)
- Install summary prints local/public panel URLs
- Management menu: show URL, reset-admin to admin, BBR, geo update, public_url
- Binary: reset-admin command
- Expand subscription User-Agent auto-detect; frontend hints + user status filters
feat(ops+sub): Phase A/B install menu, admin reset CLI, UA/sub UX
- Keep initial admin password as admin (no random password)
- Install summary prints local/public panel URLs
- Management menu: show URL, reset-admin to admin, BBR, geo update, public_url
- Binary: reset-admin command
- Expand subscription User-Agent auto-detect; frontend hints + user status filters
feat(ops+sub): Phase A/B install menu, admin reset CLI, UA/sub UX
- Keep initial admin password as admin (no random password)
- Install summary prints local/public panel URLs
- Management menu: show URL, reset-admin to admin, BBR, geo update, public_url
- Binary: reset-admin command
- Expand subscription User-Agent auto-detect; frontend hints + user status filters
feat(ops+sub): Phase A/B install menu, admin reset CLI, UA/sub UX
- Keep initial admin password as admin (no random password)
- Install summary prints local/public panel URLs
- Management menu: show URL, reset-admin to admin, BBR, geo update, public_url
- Binary: reset-admin command
- Expand subscription User-Agent auto-detect; frontend hints + user status filters
feat(ops+sub): Phase A/B install menu, admin reset CLI, UA/sub UX
- Keep initial admin password as admin (no random password)
- Install summary prints local/public panel URLs
- Management menu: show URL, reset-admin to admin, BBR, geo update, public_url
- Binary: reset-admin command
- Expand subscription User-Agent auto-detect; frontend hints + user status filters
feat(ops+sub): Phase A/B install menu, admin reset CLI, UA/sub UX
- Keep initial admin password as admin (no random password)
- Install summary prints local/public panel URLs
- Management menu: show URL, reset-admin to admin, BBR, geo update, public_url
- Binary: reset-admin command
- Expand subscription User-Agent auto-detect; frontend hints + user status filters
feat(ops+sub): Phase A/B install menu, admin reset CLI, UA/sub UX
- Keep initial admin password as admin (no random password)
- Install summary prints local/public panel URLs
- Management menu: show URL, reset-admin to admin, BBR, geo update, public_url
- Binary: reset-admin command
- Expand subscription User-Agent auto-detect; frontend hints + user status filters
feat(ops+sub): Phase A/B install menu, admin reset CLI, UA/sub UX
- Keep initial admin password as admin (no random password)
- Install summary prints local/public panel URLs
- Management menu: show URL, reset-admin to admin, BBR, geo update, public_url
- Binary: reset-admin command
- Expand subscription User-Agent auto-detect; frontend hints + user status filters
feat(ops+sub): Phase A/B install menu, admin reset CLI, UA/sub UX
- Keep initial admin password as admin (no random password)
- Install summary prints local/public panel URLs
- Management menu: show URL, reset-admin to admin, BBR, geo update, public_url
- Binary: reset-admin command
- Expand subscription User-Agent auto-detect; frontend hints + user status filters
feat(ops+sub): Phase A/B install menu, admin reset CLI, UA/sub UX
- Keep initial admin password as admin (no random password)
- Install summary prints local/public panel URLs
- Management menu: show URL, reset-admin to admin, BBR, geo update, public_url
- Binary: reset-admin command
- Expand subscription User-Agent auto-detect; frontend hints + user status filters
feat(ops+sub): Phase A/B install menu, admin reset CLI, UA/sub UX
- Keep initial admin password as admin (no random password)
- Install summary prints local/public panel URLs
- Management menu: show URL, reset-admin to admin, BBR, geo update, public_url
- Binary: reset-admin command
- Expand subscription User-Agent auto-detect; frontend hints + user status filters
2026-09-04 14:53:52 +00:00
Z User d6e64ab313 chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
chore(release): set version to v1.0.0
Align Makefile, embedded default, frontend package, and script help
text with the first stable release tag.
2026-08-31 06:21:47 +00:00
風雪 6bb63ac161 fix: add missing embed.go 2026-08-16 14:08:21 +08:00
dzx941 6ac6b31a39 update 2026-08-16 11:16:41 +08:00
dzx941 28f4c14057 fix: align listener generation with mihomo official spec
- Port now supports mihomo ports format (single/range/list)
- Add explicit RoutingMark field support
- Auto-propagate UDP flag for vmess/vless/trojan/anytls/trusttunnel
- Update validator to accept port ranges
- Sync visual config and client converter port types
- Rename module from dzx941 to kazeyukiro
- Fix missing golang-jwt dependency
- Fix frontend tsconfig and add missing axios/zustand dependencies
2026-08-16 09:55:37 +08:00
風雪 ab6347d32f Create index.html 2026-08-16 09:36:22 +08:00
風雪 050dd95054 Delete backend/cmd/server/1 2026-08-16 09:35:28 +08:00
風雪 e257eecc66 Add files via upload 2026-08-16 09:35:03 +08:00
風雪 900dccc6fe Create 1 2026-08-16 09:34:18 +08:00