Files
bluebluesodaandCommandCodeBot 39fd66ff85 docs(agents): never use a real IP address in the repo
- add the rule to the conventions with the documentation ranges to use
  (RFC 5737 for IPv4, RFC 3849 for IPv6) and note that fixtures, docs and
  commit messages are all covered
- record why: a real prefix names an operator's live allocation, the block
  arithmetic is length-driven so a doc-range prefix substitutes unchanged, and
  scrubbing a leak later means rewriting published history (force-pushed tags,
  broken forks/clones). Host addresses, credentials and provider/ASN names are
  called out as the same class of leak

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-09-27 15:04:29 +00:00

5.6 KiB

AGENTS.md

Guide for AI coding agents working in this repo.

Project

vpsmgr — a lightweight LXC hosting panel. Debian 13/Ubuntu host + Incus containers, one Debian 13 container per user, managed via a web panel and a CLI (single Go binary). Hosting-panel concepts, not an ordinary web app: installers, containers, networking, storage.

Commands (always set CGO_ENABLED=0)

The project is pure Go — no cgo, never enable it. net (stdlib) pulls in runtime/cgo whenever a C toolchain is present, which fails on machines with broken gcc.

cd src
CGO_ENABLED=0 go build ./...
CGO_ENABLED=0 go test ./...
CGO_ENABLED=0 go vet ./...

Shell scripts: check syntax with bash -n scripts/*.sh. Note: install.sh and 00-ip-ask.sh (the install-time network asks — IPv6 prefix + container subnet octet) are sourced, so they must use return, not exit.

Incus (not LXD)

The container runtime is Incus 7 LTS, installed from the Zabbly package repo (10-incus.sh) — never snap, never the lxc CLI.

  • Daemon socket: /var/lib/incus/unix.socket (group incus-admin). The panel daemon (vps.service) runs as the unprivileged vps user, which is a member of incus-admin; all Incus access is via the REST API over that socket.
  • internal/lx is the ONLY package that talks to Incus. Exec is done over the API websocket transport (Exec/ExecSH/RunInitScript) — there must be no incus/lxc CLI calls in the panel runtime path.
  • Storage pool vpsmgr (ZFS by default, bridge incusbr0). btrfs is supported as an alternative backend (on a btrfs host it becomes a native subvolume; elsewhere a loop file) — VPSMGR_STORAGE=btrfs. A dir backend exists only as an explicit VPSMGR_STORAGE=dir opt-in for test boxes — never a fallback; a failed pool (ZFS/btrfs) must abort, not downgrade. Container management is storage-driver-transparent in the Go panel; the only driver- aware code is lx.EnsureZFSRemoveSnapshots, which is a no-op off ZFS.
  • A remote-qualified fallback image (images:debian/13) must go through lx.EnsureImage before lx.Launch — the API cannot auto-fetch it inside the create call (unlike the old lxc launch CLI).

Privilege model

  • vps install runs as root (setup: user creation, units, kernel modules).
  • vps serve runs as the unprivileged vps user. Root commands are pinned in /etc/sudoers.d/vps (installed by ensureSudoers, validated with visudo -c); the panel escalates only those exact commands via internal/su (sudo -n). Never add a new exec.Command("...") that needs root without also adding it to the whitelist.

Documentation

  • docs/README.md — index
  • docs/architecture.md — system design (storage/network/security/bandwidth)
  • docs/configuration.md — /etc/vpsmgr/config.yaml reference
  • docs/ipv6.md — IPv6 pass-through design (large dev-branch feature)
  • docs/development.md — build/test/release/conventions

Keep the top-level READMEs concise; technical detail belongs in docs/ (English).

Key invariants — do not break

  • uninstall.sh without --purge must keep /etc/vpsmgr (config/db) and /etc/haproxy so reinstall adopts them; only --purge deletes them. A pre-HAProxy install's /etc/traefik is kept by the same rule (its config file is inert; the installer removes Traefik's binary/unit/account and the per-domain dynamic directory).
  • IPv6 bridge prefix is clamped to ≥ /64 (bridgePrefixLen): Incus's dnsmasq only serves /64 networks. Container addresses always live in the first /64 of the configured prefix.
  • IPv6 prefix length is required in ipv6_subnet — a bare address is rejected, never silently treated as /64.
  • Container hostnames are random (vps-<8hex>) and never equal the username.
  • install.sh --local-build must always rebuild (never reuse an installed binary) and warn about the branch. --update re-downloads the latest prebuilt release over an existing binary and must keep it untouched on download failure (no fallback rebuild).
  • Image builds (50-image.sh, 60-rhel-image.sh, 80-debian-dev-image.sh) must stay slim (apt/dnf/pacman clean) and delete the base image after publishing. 60-rhel-image.sh, 70-opensuse-image.sh, 80-debian-dev-image.sh and 90-arch-image.sh are optional and must never be part of install.sh (small boxes stay lean). 90-arch-image.sh is a rolling rebuild: it deletes the existing image and re-pulls upstream on every run.
  • Never add cgo or force C compilation.
  • The panel service is vps.service (restart via systemctl restart vps). Do not reintroduce vpsmgr-*.service names on the host. (The in-container vpsmgr-ipv6.service helper is a different thing — it lives inside container images.)

Conventions

  • One commit per small bug/feature; short subject + bullet body.
  • Run git log --oneline -10 for the current commit style before writing a message.
  • Never put a real IP address in the repo. Tests, fixtures, examples, docs and commit messages use the documentation ranges only: IPv4 RFC 5737 (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24) and IPv6 RFC 3849 (2001:db8::/32). Test fixtures are where this leaks most often, and a real prefix names an operator's live allocation (their provider block). The block arithmetic is length-driven, so a doc-range prefix substitutes unchanged. Scrubbing a leak afterwards means rewriting published history — force-pushed tags and every fork/clone broken — so it must never get in. Host addresses, credentials and provider/ASN names belong in the same bucket.
  • The test environment has ~2 GiB RAM; keep CI/local test workloads light.