27 Commits
Author SHA1 Message Date
bluebluesodaandCommandCodeBot 02e590c974 fix(install): make oneclick.sh work via bash <(curl ...)
When piped through /dev/fd the script's $0 is not a filesystem path, so
sourcing scripts/lib-download.sh failed ("... /dev/fd/scripts/lib-download.sh:
No such file or directory"). Inline dl_content() instead so the gitless
bootstrap can still retry the repo tarball download.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-09-24 13:21:26 +00:00
bluebluesodaandCommandCodeBot 4c93ae4be3 feat(haproxy): replace Traefik with a pinned HAProxy domain proxy
The panel now fronts user domains with HAProxy 3.4 LTS (official performance
packages) instead of Traefik: about 100 MB of RSS and 200 MB of disk saved for
a feature set that never used more than Host routing on :80 and SNI passthrough
on :443.

- hpx: publish the proxy configuration as immutable GENERATIONS. Every domain
  mutation renders the whole set from the DB, validates it with the pinned
  `haproxy -c`, and only then repoints the `current` symlink, so a rejected
  candidate leaves the running proxy untouched and a stale domain can never
  survive into a later generation. Routing is carried by two map files
  (Host -> backend, SNI -> backend): per-domain frontend rules cannot work from
  a second file because HAProxy rejects duplicate section names since 3.3 and
  attaches bare directives to the section opened last.
- mgr: republish and reload (SIGUSR2, i.e. `systemctl reload`) after every
  domain change, so a change takes effect without dropping established HTTP or
  TLS connections. Rollback semantics (DB first, then publish) are unchanged.
- cfg,db: `net.traefik` is renamed to `net.haproxy` with identical values. A
  pre-rename config.yaml key and the legacy VPSMGR_TRAEFIK env var are still
  read (new name wins), and schema migration v18 moves the settings mirror from
  `traefik` to `haproxy`, keeping a newer value when both rows exist.
- 30-haproxy.sh: install the pinned branch from the vendor repository and
  migrate a Traefik host in one run — install and validate HAProxy first, then
  remove Traefik's binary, unit, service account and dynamic rules (only its
  config file is kept, inert). No fallback keeps both proxies alive.
- upgrade-haproxy.sh: follow the pinned branch to its newest patch, with a
  configuration check and a graceful reload; `install.sh --update` runs it.
- tests: generation publish/rollback, map rendering, config-key and settings
  migrations, plus real-binary tests (skipped unless
  VPSMGR_TEST_HAPROXY_BIN is set) that run the generated configuration through
  `haproxy -c` and start it in a network namespace.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-09-16 12:59:13 +00:00
bluebluesodaandCommandCodeBot 44c3727ad3 docs: document multi-container user groups
- add a "User groups" section: name-derived grouping, name validation, the
  shared panel password and accent color, and the container switcher
- describe the switcher labels and the per-container sticky-notes tag
- note the accent color is group-wide in the color section
- list multi-container users in both READMEs

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-09-11 06:41:06 +00:00
bluebluesodaandCommandCodeBot b46bd0a901 fix(panel): theme color only on admin impersonation; thin borders back to 1px
- Per-user accent color now tints the user panel only for operator
  impersonation sessions (handleOverview clears ThemeColor otherwise);
  users logging in with their own password see the default theme
- Restore 1px borders on user-panel cards, buttons, inputs, quick stats,
  modals and spinner, and on the login card/input (was 3px)
- Update docs and tests; add HTTP-level test covering both session kinds

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-09-06 04:52:37 +00:00
bluebluesoda 3f5fffc961 Update README.md 2026-09-02 11:22:47 +08:00
bluebluesoda f79ccb09f0 feat(install): add btrfs storage backend (native subvolume or loop file)
- VPSMGR_STORAGE now accepts btrfs alongside zfs and dir. On a btrfs root the
  pool is created as a native subvolume at /var/lib/incus/storage-pools/vpsmgr
  (Incus only allows that source path and requires it NOT to pre-exist); on any
  other host it becomes a sparse loop-file pool sized exactly like ZFS.
- install.sh asks for an explicit beta confirmation up front when the btrfs
  backend is selected (default yes — on a btrfs host there is no ZFS fallback).
  Non-interactive installs proceed; non-btrfs/default-zfs installs are
  unchanged.
- btrfs swap: on a btrfs root the swapfile is created with
  `btrfs filesystem mkswapfile` (btrfs-progs >= 6.1), falling back to
  truncate + chattr +C + dd for older tools — the generic fallocate+mkswap path
  fails on btrfs (swap must be NODATACOW and preallocated). Non-btrfs hosts
  keep the existing fallocate path.
- 00-check.sh installs btrfs-progs and skips the ZFS DKMS/kernel-header
  toolchain for btrfs and dir alike; 10-incus.sh verifies the final driver.
- Panel Go code is untouched: container management is storage-driver-
  transparent, and lx.EnsureZFSRemoveSnapshots is a no-op off ZFS.
- Docs (architecture, configuration, READMEs, AGENTS) updated for the new
  backend, including snapshot-restore semantics (btrfs keeps later snapshots).

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
2026-08-31 17:00:03 +08:00
bluebluesoda 6fcc97b81b docs: document the snapshot "time machine" semantics
- architecture.md: new bullet under Users and resources explaining that
  restoring to an older checkpoint discards the later ones, done as a single
  Incus call via zfs.remove_snapshots on the ZFS root volume (recursive
  rollback), applied at add/reinstall and refreshed on every `vps install`
  (incl. --update), with the delete-and-retry fallback and the reinstall
  checkpoint-count warning.
- configuration.md: snapshots.limit row now notes that restoring to an older
  checkpoint auto-deletes the newer ones.
- README / README.en: list the time-machine (checkpoint) feature in the user
  panel feature lists.

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
2026-08-31 06:58:11 +00:00
bluebluesoda 840dc52aac Update README.md 2026-08-30 19:46:17 +08:00
bluebluesoda fa151eaee4 feat(install): configurable slot range + auto-off Traefik on 80/443 conflict
Add net.slot_range, a shrinkable container slot range (read as the IPv4
last octet a new container may take; idx = octet-1) that bounds how many
containers can be created and, consequently, how much of the user-port
block 10000-29999 a host reserves. Default 2-201 = 200 slots; must always
be a sub-range of 2-201 (only-shrink rule). Affects ONLY new containers —
existing ones keep their slot/ports.

- cfg: add net.slot_range with strict ParseSlotRange validation
  (exactly one '-', both ends integers in 2..201, lo<=hi), SlotCount/
  SlotIdxBounds helpers, and VPSMGR_SLOT_RANGE env handling in FillAuto.
  Register net.slot_range as operator / next-add in the config registry.
- db/mgr: NextFreeIdx now picks from a caller-supplied [idxMin,idxMax]
  window instead of the fixed 1..cfg.MaxUsers; mgr.Add feeds it from the
  configured range. Existing users outside a narrowed range keep their
  slots.
- admin panel: capacity readout follows SlotCount() instead of a fixed
  200 (honestly shows over-capacity when pre-existing users fall outside
  a shrunken range).
- installer: 00-checklist asks the slot range right after the subnet
  octet (env/adoption/non-interactive branches, validated). The
  port-occupancy check now scans only the range's user-port span rather
  than the whole 10000-29999.
- 80/443 handling: if either is already bound by a non-vpsmgr process,
  the install no longer fails — it proceeds with Traefik installed but
  DISABLED (net.traefik false, no autostart). 00-check writes a marker
  that install.sh turns into VPSMGR_TRAEFIK=0 for 30-traefik and the
  panel. A belt-and-suspenders guard skips the force-off when our own
  traefik.service is already active (update/adoption). SSH 30000-31999
  and the user-port span remain hard failures.
- traefik: bump pinned version to 3.7.12 and rewrite 30-traefik's
  enable/disable decision to respect the effective net.traefik (forced
  env > adopted config > default), so a disabled traefik is never
  briefly started.
- docs: README(s), config example, architecture and configuration docs
  updated; tests added for slot-range parsing and the FillAuto env vars.

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
2026-08-30 09:36:29 +00:00
bluebluesoda 1158eb96da feat(config): add independent Traefik toggle
- add runtime net.traefik switch with live DB state\n- stop and disable Traefik when IPv4 forwarding is off\n- block new domains while Traefik is disabled
2026-08-29 14:34:47 +00:00
bluebluesoda 97396589f5 feat(install): support disabling IPv4 forwarding
- add --disable-v4forward with explicit confirmation\n- skip reserved-port checks and keep Traefik stopped for IPv6-only installs\n- document dedicated-host requirements and sync config references
2026-08-28 15:07:24 +00:00
bluebluesoda 29db57355b Update README.md 2026-08-28 17:35:38 +08:00
bluebluesoda 7c69a533f2 Update README.md 2026-08-28 16:37:57 +08:00
bluebluesoda 705b5d5b64 feat(admin): per-user accent color for login button and user panel
- DB v12: users.color column (hex from a fixed 8-color palette, allowlist)
- Admin: click the username to open a swatch picker + clear; colored text/dot
- Login button defaults to neutral black/white; themed when a color is set
- User panel: tint background and accents via CSS variables in both modes
- Document the hidden entry point in READMEs and docs/architecture.md
2026-08-26 18:15:18 +00:00
bluebluesoda cb3dbe19bc feat(panel): split CPU display window — admin 1h, user 5m; doc updates
- Admin overview table and CLI vps list/show now show the one-hour CPU
  average; the user panel keeps the five-minute (near-live) average.
- Relabel the admin column to 1小时CPU / 1h CPU.
- Unify the two READMEs with Chinese as the source of truth and add the
  features shipped since v1.3.5 (SSH key management, login-as, encrypted
  notes, Debian dev + Arch images).
2026-08-25 05:58:29 +00:00
bluebluesoda bd55fc4a77 Update README.md 2026-08-25 03:48:26 +08:00
bluebluesoda 49d34f3748 feat(images): optional Arch Linux rolling image builder
- scripts/90-arch-image.sh: builds vpsmgr/arch-sshd from images:archlinux/
  current; unlike other image scripts it always deletes and rebuilds (rolling),
  records Archlinux<YYMM> in the image description, keeps the image slim and
  deletes the base afterwards
- Register Arch Linux label + rolling-release blurb and a gray triangle icon
  in the user panel reinstall dialog
- Docs (README, architecture, development, AGENTS) and image tests updated
2026-08-24 17:48:12 +00:00
bluebluesoda f9114c86f3 Update README.md 2026-08-22 00:33:05 +08:00
bluebluesodaandCommandCodeBot dcc2f767ff feat(images): optional openSUSE Leap 16 image builder
Add scripts/70-opensuse-image.sh, modeled on 60-rhel-image.sh: pulls
images:opensuse/16.0, installs sshd + universal tooling via zypper,
cleans caches, and publishes vpsmgr/opensuse-sshd (base deleted).

- Register the alias in imageLabels so the reinstall dialog shows
  "openSUSE Leap 16"; extend collectManagedImages tests
- Leap 16 ships systemd-networkd, so panel IPv6 config takes the
  existing Debian networkd branch — no runtime code changes needed
- Optional: not part of install.sh, keeping small hosts lean
- Document the new image in README (zh/en) and docs/architecture.md

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-08-21 06:20:23 +00:00
bluebluesodaandCommandCodeBot 7344b8ef3c feat(swap): auto-apply swap ratio on install, drop swap-reapply CLI
The swap-reapply command is internal bookkeeping users should never
see: containers get the configured incus.swap_ratio automatically, so
remove it from the CLI surface.

- cmdInstall now calls ApplySwapToAll alongside the other upgrade
  self-heal steps, so containers created before swap support get their
  allowance after upgrading and running `vps install`
- vps config set incus.swap_ratio keeps applying immediately to all
  containers (unchanged)
- drop the swap-reapply dispatch, cmdSwapReapply and help entry
- docs updated accordingly

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-08-21 05:03:41 +00:00
bluebluesodaandCommandCodeBot 7b31f2d4ec feat(swap): configurable per-container swap allowance
Incus 7 on cgroup v2 writes memory.swap.max=0 for every container
unless limits.memory.swap carries an explicit byte amount (true/false
both end up as 0), so containers previously got no host swap at all
and `free` inside showed 0. Add incus.swap_ratio (default 0.5) so each
container gets limits.memory.swap = limits.memory x ratio, set at
create/reinstall and on quota changes.

- config: incus.swap_ratio field + registry entry (non-negative float,
  0 disables swap; ApplyImmediate)
- lx: Client carries swapRatio; SetMem keeps swap proportional,
  SetSwap refreshes only the swap key
- mgr: ApplySwapToAll refreshes every container from the DB memory
  quota (idempotent, no restart)
- cli: `vps config set incus.swap_ratio N` re-applies to all
  containers automatically; `vps swap-reapply` is the manual/repair
  path for containers created before swap support
- docs: configuration/architecture/README updated

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-08-20 21:01:44 +00:00
bluebluesoda 9da5624560 docs: raise minimum disk recommendation 2026-08-17 11:51:34 +00:00
bluebluesoda ee975021ba docs: refine feature summaries
- condense Chinese and English feature details\n- document quotas, networking, traffic, storage, and proxy behavior
2026-08-17 11:49:19 +00:00
bluebluesoda 50496f9367 docs: refresh bilingual readmes
- make Chinese the default README\n- streamline installation and usage guidance\n- align platform, IPv6, storage, and CLI documentation
2026-08-17 11:36:20 +00:00
bluebluesodaandCommandCodeBot 2980e3a9ce fix(traefik): v1.0.0 binary with world-readable dynamic dir fallback
Keep the v1.0.0 codebase unchanged (main.go untouched). Two installer
script fixes on top:

- scripts/30-traefik.sh: make /etc/traefik and the dynamic dir 0755
  (world-readable) instead of relying on the vps<->traefik group
  cross-link, so traefik can always read the dynamic dir regardless of
  install order or group membership. vps still owns the dir and writes
  as the owner.
- scripts/40-panel.sh: fix the prebuilt-download EXIT trap to not
  reference a function-local variable under `set -u` (unbound variable
  skipped cleanup).

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-08-14 19:11:20 +00:00
bluebluesodaandCommandCodeBot 1663563020 docs: reflect the Incus runtime, vps.* services and privilege drop
- architecture.md: Incus components, unprivileged panel section
  (incus-admin group + sudoers whitelist), vps.* unit names, atomic
  user creation transaction.
- configuration.md: incus.* config keys, /var/lib/incus socket, vps
  service restart.
- development.md: Incus dev-loop instructions, updated layout.
- README(.zh-CN).md: Debian 13/Ubuntu requirements, Incus mention.
- AGENTS.md: Incus invariants (socket, websocket exec, EnsureImage
  before Launch, sudoers whitelist, vps.service naming).

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-08-14 03:33:37 +00:00
bluebluesodaandCommandCodeBot ebc2b1478f feat(incus): port the container runtime from LXD to Incus 7 LTS
The panel now talks to Incus over its Unix socket REST API
(/var/lib/incus/unix.socket, group incus-admin) exclusively:

- internal/lx: exec moved from the `lxc` CLI to the API websocket
  transport (Exec/ExecSH/RunInitScript); EnsureImage pulls a
  remote-qualified fallback (images:debian/13) before Launch since the
  API cannot auto-fetch it inside the create call; per-container df
  probe in mgr/batch.go uses the same API path.
- cfg: config key renamed lxd.* -> incus.* (image, image_fallback,
  pool, bridge, socket); DefaultSocket is the Debian-package path.
- mgr: container disk probe and image handling updated; comment sweep
  from LXD to Incus.
- deps: add gorilla/websocket for the exec transport.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-08-14 03:33:15 +00:00