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>
This commit is contained in:
bluebluesodaandCommandCodeBot committed 2026-09-11 06:41:06 +00:00
1 parent 770b763d2a
commit 44c3727ad3
4 files changed
+74 -9

No files matched your search

+2 -1
View File
@@ -10,7 +10,8 @@ A lightweight Incus container hosting panel for small VPS instances and low-memo
- Admin panel: users, quotas, domains, IPv6 pool, SSH public keys, and audit logs; supports logging in as a user
- User panel: power control, self-service Debian 13/AlmaLinux 9/openSUSE Leap 16/Arch Linux reinstall, domain and SSH key management, encrypted sticky notes (exportable), and a time machine (checkpoint snapshots; rolling back discards the later checkpoints)
- The admin can assign each user a dedicated accent color (click the username in the user list): that user's "log in" button and user-panel background use it, so impersonating operators can tell users apart at a glance
- Multi-container users: group accounts as `name` + `name-1`, `name-2`, … to share one login and password across several containers, with a switcher and per-group accent color
- The admin can assign each user a dedicated accent color (click the username in the user list): that user's "log in" button and user-panel background use it, so impersonating operators can tell users apart at a glance. A user group shares one color
- CPU, memory, and disk oversubscription; quota changes apply live without a container restart
- Strict container network isolation; prefix mode assigns each container a dedicated `/112` IPv6 block that it can subdivide
- Traffic is counted in both directions; over-quota containers are rate-limited to 1 Mbps both ways
+2 -1
View File
@@ -10,7 +10,8 @@
- 管理员面板:用户、配额、域名、IPv6 地址池、SSH 公钥与审计日志;支持"以用户身份登录"接入用户面板
- 用户面板:电源控制、自助重装 Debian 13/AlmaLinux 9/openSUSE Leap 16/Arch Linux、域名与 SSH 密钥管理、加密便签(可导出)、时光机(检查点快照,回滚即丢弃此后的检查点)
- 管理员可为每个用户分配专属颜色(在用户列表中点击用户名):该用户的「登录面板」按钮采用此颜色,管理员代客登录时其用户面板也以此颜色渲染,方便区分不同用户;用户自行登录始终显示默认主题
- 多容器用户组:相邻账号按 `用户名` + `用户名-1`、`用户名-2`… 归为一组,共用同一登录与密码管理多个容器,支持容器切换与组内统一配色
- 管理员可为每个用户分配专属颜色(在用户列表中点击用户名):该用户的「登录面板」按钮采用此颜色,管理员代客登录时其用户面板也以此颜色渲染,方便区分不同用户;用户自行登录始终显示默认主题;同一用户组共用一个颜色
- CPU、内存、磁盘支持超售;配额修改实时生效,无需重启容器
- 容器网络严格隔离;IPv6 前缀模式下每个容器获得独立 `/112`,可继续划分子网
- 流量按上下行合计,超额后双向限速至 1 Mbps
+1 -1
View File
@@ -4,7 +4,7 @@ Technical documentation for vpsmgr. All docs are in English.
| Doc | What it covers |
|---|---|
| [architecture.md](architecture.md) | System design: components, storage, network, security, traffic, image, install/uninstall lifecycle |
| [architecture.md](architecture.md) | System design: components, storage, network, security, traffic, users and user groups, image, install/uninstall lifecycle |
| [configuration.md](configuration.md) | Full reference for `/etc/vpsmgr/config.yaml` |
| [ipv6.md](ipv6.md) | IPv6 pass-through design: deterministic addresses, prefix support, routing, installer flow |
| [ipv6-ndp-responder.md](ipv6-ndp-responder.md) | Why prefix mode replaced ndppd with an in-tree NDP responder, design hardening, and what to test at scale |
+69 -6
View File
@@ -1,9 +1,9 @@
# Architecture
vpsmgr is a lightweight LXC hosting panel: one Debian 13 container per user,
managed from a web panel and a CLI. Everything ships as a single Go binary
(`vps` = CLI + embedded web panel); Incus, nftables and Traefik provide the
plumbing.
vpsmgr is a lightweight LXC hosting panel: one Debian 13 container per user
account, managed from a web panel and a CLI. Everything ships as a single Go
binary (`vps` = CLI + embedded web panel); Incus, nftables and Traefik provide
the plumbing.
## Design goals
@@ -193,7 +193,7 @@ The panel daemon runs as the dedicated unprivileged `vps` system user
`/var/log/vpsmgr-init.log` there. Shebangs are honored; a hanging script
cannot block the reinstall because it is backgrounded. Delivery failure only
warns — the reinstall still succeeds.
- **Per-user accent color**: each user can carry an accent color
- **Accent color (group-wide)**: each user can carry an accent color
(`users.color`, a hex string from the admin panel's **fixed 10-color palette**
in `admin` — black/white/gray are excluded; the palette is the allowlist, so
a free-form hex can never be stored). The entry point is deliberately low-key:
@@ -208,7 +208,9 @@ The panel daemon runs as the dedicated unprivileged `vps` system user
user-panel tint renders **only for operator impersonation sessions**, so a
user logging in with their own password always sees the default theme. Only
the admin can set it; users cannot. Setting/clearing is audited as
`color.update` under `000+<user>`.
`color.update` under `000+<user>`. The color is **shared across a user group**
(see “User groups” below): writing it updates every member, and a new member
inherits the group's existing color.
- **Snapshots ("时光机" / time machine)**: each user can keep up to
`snapshots.limit` disk-only checkpoints (named `snap-<UTC>...`) of their
container and create / delete / restore them from the panel. The panel calls
@@ -237,6 +239,67 @@ The panel daemon runs as the dedicated unprivileged `vps` system user
container deletes every checkpoint, which the reinstall dialog warns about
with the exact count.
### User groups (multi-container users)
One login can manage several containers by grouping panel accounts under a
naming convention. There is **no group table** — the group is derived from the
account name at read time, so adding or removing a member needs no migration:
- An account `parent` and every account named `parent-<digits>` are one
**user group**. `alice` is the parent (labelled machine **A**); `alice-1`,
`alice-09` are its children (labelled by their digit suffix, leading zeros
preserved).
- The parent part must start and end with a letter. Nesting is rejected:
`alice-1-2` is not a child, and a `-<digits>` suffix whose prefix is not a
valid parent (`alice1-2`, `a-1`) makes the name invalid. Names stay ≤ 31
characters. `mgr.ParseUserGroup` / `UserGroupName` are the single definitions,
covered by `group_test.go`.
- **Only the admin panel can create a child** (`ValidateAddName(..., true)`).
The CLI / `vps add` path passes `AllowChild=false` and rejects child names.
The admin create form mirrors the rule client-side and previews the group and
its size before submitting.
State that is **shared across a group**:
- **Panel password.** A brand-new group gets a fresh 20-char password; adding a
member to an existing group reuses the group's bcrypt hash, and the one-time
credential block prints “same as the existing user-group password” instead of
a password. This holds on the CLI too: `vps add` of a parent name whose group
already has members inherits the group hash, and its output says the password
was inherited rather than printing one. Changing it (`ChangePanelPassword`,
the path behind both the panel's own change flow and the admin reset) hashes
once and rewrites **every** member in a single transaction, dropping each
member's other sessions — the change flow keeps the caller's current token, an
admin reset passes an empty token and drops all
(`db.UpdateUsersPasswordAndDeleteSessions`). The **container root password is
not shared**: every container gets its own, and for an inherited group member
that value is generated but deliberately not displayed, so the root password
must be reset from the panel.
- **Accent color.** `SetGroupColor` writes one color to all members
(`db.UpdateUsersColor`, one transaction), and `inheritGroupColorLocked` gives a
newly added member the first color found in its group.
- **A container switcher.** With more than one member the user panel shows a
`<select>` in the header. Each option is labelled
`<label>-<cpu>c<mem>g<disk>g` — the machine label (`A` for the base account,
else the numeric suffix) plus the three quota values, e.g. `2-4c8g40g`;
the group-name prefix is dropped because every option shares it. The list is
ordered base-first then by numeric suffix (so `A, 1, 2, … 10`, not lexical).
`POST /switch-user` accepts only a name in the caller's own group, mints a
session for the target, drops the old one and audits `session.switch.<target>`.
A warning banner (repeated inside the reinstall and snapshot dialogs) names the
current machine label so a destructive action is not aimed at the wrong
sibling, and the sticky-notes card title carries the same label (`Machine 2`)
when the group has more than one container — notes themselves stay
per-container, so each machine keeps its own encrypted set.
All of it is process-safe: the add path and the password/color changes hold the
manager's operation mutex, and the multi-row writes are single transactions.
Deleting one member never touches its siblings, and because the group is name-
derived it survives the parent's deletion: a leftover child keeps the group
name (and its shared credentials) with no `A` machine, and re-adding the parent
name later simply rejoins the same group and inherits its password — the
convention working as designed, not a fresh tenant.
## Storage
Three backends, all selected by setting `VPSMGR_STORAGE` when running