e7e728cd23
- add AGENTS.md and PLANS.md based on MPV - add subagents configuration
15 KiB
15 KiB
AGENTS.md
Scope
- These rules apply to the entire repository.
- Build a local WireGuard management tool for Linux servers.
- Keep
wg-manager.shas the primary entry point. - The project is copied as a directory and is not installed into
PATH. - Do not add installer or uninstaller scripts unless explicitly requested.
- Treat
MVP.mdas the product scope and acceptance reference. - Treat
PLANS.mdas the required milestone sequence for MVP implementation. - Complete and verify one milestone before starting the next one.
- Update the
PLANS.mdprogress and execution log as milestones are completed. - Run milestones under the Sol coordinator and Luna worker/reviewer loop defined in
PLANS.mdand.codex/. - The coordinator must independently review Luna's changes and delegate explicit Luna repairs until verification passes or a genuine blocker remains.
- Use the existing
add-user.shonly as a behavioral reference. - New code must improve its safety rather than copy its weaknesses.
General priorities
- Preserve a working WireGuard configuration above all else.
- Prefer correctness and recoverability over cleverness.
- Keep CLI behavior independent from the TUI.
- Make every mutating operation explicit and auditable.
- Keep secrets out of output, logs, tests, and error messages.
- Minimize required dependencies.
- Support operation over SSH and ordinary terminal emulators.
- Write user-facing text in Russian unless the surrounding interface uses another language.
- Keep identifiers, functions, CLI command names, and code comments in English.
- Avoid unrelated refactors while implementing a focused change.
Bash baseline
- Use Bash, not POSIX
sh. - Start executable scripts with
#!/usr/bin/env bash. - Enable
set -Eeuo pipefailin production entry points. - Understand strict-mode exceptions before suppressing them.
- Use
if command; thenwhen a nonzero status is expected. - Do not append
|| trueunless the ignored failure is intentional and documented. - Set
umask 077before creating configs, keys, metadata, logs, or backups. - Require a supported Bash version at startup when using version-specific features.
- Prefer Bash builtins over unnecessary subprocesses.
- Use
printfrather thanecho -e. - Use
[[ ... ]]for Bash conditionals. - Use
(( ... ))for arithmetic conditions. - Use
casefor command dispatch and finite state handling. - Use
localfor function-local variables. - Declare variables close to first use.
- Use
readonlyfor constants after configuration is loaded. - Use uppercase names only for exported variables and true constants.
- Use lowercase snake_case for local variables and functions.
- Give functions verb-based names such as
load_configoradd_client. - Keep functions focused and normally below about 60 lines.
- Return statuses from functions; print data only when output is the contract.
- Do not mix machine-readable results with progress messages.
- Send errors and diagnostics to stderr.
- Use explicit exit codes for expected CLI failure categories.
Quoting and data handling
- Double-quote parameter expansions unless splitting is deliberately required.
- Use
"${array[@]}"when forwarding array elements. - Use arrays to build commands; never construct commands as strings.
- Never use
eval. - Never pass user-controlled text to
bash -c. - Avoid command substitution for multiline or NUL-sensitive data.
- Use
mapfile -tfor line-oriented command output when appropriate. - Use
IFS= read -rto read literal lines. - Use
read -r -d ''or NUL delimiters for arbitrary file names. - Use
--before user-derived positional arguments where supported. - Do not rely on locale-sensitive parsing.
- Set
LC_ALL=Clocally for command output that must be parsed predictably. - Do not parse human-formatted output when a stable dump format exists.
- Prefer
wg show <interface> dumpfor WireGuard state parsing. - Treat names, endpoints, addresses, paths, and config values as untrusted input.
- Validate input with allowlists and semantic checks.
- Reject control characters and embedded newlines in user input.
- Never derive a filesystem path from an unchecked client name.
- Canonicalize or constrain paths before reading or writing them.
Configuration
- Keep server-specific values in
wg-manager.conf. - Resolve the project directory from
${BASH_SOURCE[0]}. - Do not depend on the caller's current working directory.
- Do not silently overwrite administrator configuration.
- Validate every required setting at startup.
- Reject unknown enum values such as unsupported tunnel modes.
- Validate numeric ranges before arithmetic use.
- Validate IPv4, IPv6, CIDR, port, and hostname values before use.
- Do not
sourcea configuration file that may be writable by untrusted users. - If sourcing is retained, require root ownership and non-group/world-writable permissions.
- Prefer a restricted
KEY=VALUEparser for long-term safety. - Environment overrides must be documented and narrowly scoped.
- Never accept key material through command-line arguments.
- Avoid exporting secrets into the process environment.
Temporary files and cleanup
- Create temporary files with
mktemp. - Create mutation staging files on the same filesystem as the target.
- Never use predictable temporary filenames.
- Register cleanup immediately after creating temporary resources.
- Use a cleanup function with
trapforEXIT,INT, andTERM. - Quote trap bodies safely; avoid premature expansion.
- Preserve the original exit status during cleanup.
- Do not remove a path unless this process created it or validated it exactly.
- Apply restrictive permissions before writing secret content.
- Do not leave key material in world-readable temporary directories.
WireGuard safety
- Require root only for commands that need privileged access or mutation.
- Allow safe read-only commands without root when system permissions permit it.
- Check that the configured interface and config file agree.
- Identify peers by public key, not only by display name.
- Treat comments such as
### Client NAMEas metadata, not identity. - Detect duplicate public keys and overlapping
AllowedIPs. - Detect address collisions before generating any persistent file.
- Distinguish managed peers from unmanaged peers.
- Never modify an unmanaged peer implicitly.
- Do not regenerate server keys automatically.
- Do not rotate or replace client keys without explicit confirmation.
- Never print private or preshared keys in routine output.
- Never include key values in traces or debug logs.
- Disable shell tracing around all secret-handling code.
- Store client files with mode
0600. - Keep directories containing client material at mode
0700.
Mutating operations
- Serialize mutations with
flock. - Acquire the lock before re-reading mutable state.
- Revalidate assumptions after the lock is acquired.
- Create a backup before every persistent change.
- Write the complete proposed config to a staging file.
- Validate the staged config before replacing the live config.
- Preserve the target owner, group, and restrictive mode.
- Replace files atomically with a same-filesystem rename.
- Apply the new configuration only after durable storage succeeds.
- Verify active state after applying the change.
- Roll back both disk and runtime state on failure.
- Report rollback failure as a critical error.
- Never append directly to the live WireGuard config.
- Never use broad text deletion to remove a peer block.
- Match an exact managed block and its public key.
- Default to archiving client files instead of permanent deletion.
- Require explicit confirmation before destructive or restorative actions.
- Make repeated operations fail safely or behave idempotently.
Backups and audit log
- Timestamp backups in an unambiguous UTC format.
- Include the interface name in backup metadata or filenames.
- Keep a bounded number of automatic backups.
- Never prune the only known-good backup.
- Validate a backup before offering restoration.
- Back up the current state before restoring an older state.
- Record who initiated a mutation when that information is available.
- Log the operation, target, timestamp, outcome, and concise error.
- Do not log private keys, preshared keys, or full client configs.
- Avoid logging full environment dumps or command lines with secrets.
- Make logs readable only by root by default.
- Log failed mutations and rollback outcomes.
CLI design
- TUI actions must call the same backend functions as CLI commands.
- Keep CLI commands usable when
gumis unavailable. - Provide
helpand useful usage errors. - Reject unknown commands and extra positional arguments.
- Keep command names stable once documented.
- Use stdout for requested data and stderr for warnings or progress.
- Do not add decorative ANSI output when stdout is not a terminal.
- Honor
NO_COLORfor CLI colors where colors are used. - Return zero only when the requested operation succeeded.
- Use distinct nonzero statuses for usage, validation, dependency, and runtime errors.
- Avoid interactive prompts when a CLI command is explicitly noninteractive.
- Require a confirmation flag for destructive noninteractive commands.
- Never let localization change machine-parsed values or exit codes.
TUI with gum
- Use
gumas the only planned TUI framework. - Do not add
dialog,whiptail, or a second TUI framework. - Do not download or install
gumfrom the application. - Resolve
gumin this order:WG_MANAGER_GUM, localbin/gum, thenPATH. - Validate that the resolved path is executable.
- Check required
gumsubcommands or minimum version at startup. - Fail with a clear message when TUI is requested without usable
gum. - Keep all
guminvocation details in small UI wrapper functions. - Backend functions must never call
gumdirectly. - Use
gum choosefor short finite menus. - Use
gum filterfor searchable client selection. - Use
gum inputfor short validated text fields. - Use
gum confirmbefore deletion, restoration, or key rotation. - Use
gum tableonly for display or selection, not as a data store. - Use
gum pagerfor logs, diagnostics, and long text. - Use
gum spinonly when the wrapped operation can safely hide routine output. - Preserve stderr from failed commands behind a spinner.
- Never show secret content inside a spinner command line.
- Keep stable internal values separate from translated display labels.
- Treat cancellation and Escape as normal control flow, not an error.
- Return to the previous screen after cancellation.
- Do not trap users in an endless menu loop after repeated failures.
- Keep the main menu shallow and task-oriented.
- Show interface name and current status on the main screen.
- Use consistent verbs: view, add, remove, diagnose, restore, exit.
- Show the exact target before asking for destructive confirmation.
- Require stronger confirmation for restoring backups or deleting client material.
- Keep colors readable on light and dark terminals.
- Do not communicate status by color alone.
- Keep layouts usable at 80 columns.
- Avoid assumptions about mouse support.
- Do not clear valuable terminal output after an error.
- Detect noninteractive stdin or stdout before starting TUI.
- Never emit TUI control output into pipelines or redirected files.
Error handling
- Provide one concise user-facing error plus actionable context.
- Preserve the failing command's status where meaningful.
- Do not expose secret variable values in error messages.
- Distinguish validation errors from system failures.
- Distinguish skipped diagnostic checks from successful checks.
- Diagnostic results use
OK,WARN,FAIL, orSKIP. - A critical diagnostic failure must produce a nonzero status.
- Include remediation guidance when it is reliable and specific.
- Avoid guessing firewall fixes across distributions.
- Continue independent diagnostic checks after a nonfatal failure.
Dependencies and portability
- Assume a Linux server with Bash and WireGuard tools.
- Keep required dependencies listed in
MVP.mdand README. - Treat
qrencode, systemd,nft, andiptablesas optional capabilities. - Detect optional commands before using them.
- Do not fail the entire diagnostic run because one optional tool is absent.
- Prefer feature detection over distribution detection.
- Do not assume systemd is PID 1 on every supported server.
- Support at least Linux
x86_64; document additional tested architectures. - Do not assume the bundled
gumbinary matches the current architecture. - Report architecture mismatch clearly.
Tests
- Add regression tests for every fixed bug.
- Tests must not modify the host's real
/etc/wireguard. - Use fixtures for server configs, peer blocks, and metadata.
- Mock external commands through a controlled test
PATH. - Cover
wg,wg-quick,ip,systemctl,journalctl,nft, andiptablesas needed. - Test success, expected failure, rollback, and interruption paths.
- Test client names containing spaces, newlines, glob characters, and option prefixes.
- Test duplicate keys, duplicate IPs, and overlapping networks.
- Test empty peer lists and unmanaged peers.
- Test CLI behavior without
gum. - Test local binary, environment override, and
PATHresolution forgum. - Test that logs do not contain generated secrets.
- Test concurrent mutation attempts.
- Test restoration failure and failed rollback reporting.
- Keep tests deterministic; do not depend on external network access.
- Use temporary test directories and remove them safely.
Quality gates
- Run
bash -non every changed shell script. - Run ShellCheck on every changed shell script when available.
- Run the relevant automated tests.
- Format consistently; use
shfmtwhen the project adopts a fixed style. - Do not silence ShellCheck globally to hide local problems.
- Add a narrow inline suppression only with an explanatory comment.
- Review diffs for accidental key material before finishing.
- Review file permissions in tests that create secrets.
- Verify that failure paths preserve the original config.
- Verify CLI output with stdout redirected.
- Verify TUI entry and cancellation in an interactive terminal.
Documentation
- Update
MVP.mdonly when product scope or architecture changes. - Keep operational usage in README once implementation begins.
- Document every CLI command and its exit behavior.
- Document required and optional dependencies separately.
- Include examples that use placeholder keys and addresses only.
- Never commit real endpoints, private keys, preshared keys, or client configs.
- Explain backup and restore behavior before users rely on it.
- Document limitations of handshake history and traffic visibility.
Definition of done
- The requested behavior is implemented through shared backend functions.
- Mutations are locked, validated, backed up, atomic, and reversible.
- CLI operation remains available independently of the TUI.
- TUI uses the configured
gumresolution order. - Errors are actionable and do not reveal secrets.
- Relevant tests pass.
bash -npasses.- ShellCheck has no unexplained findings.
- Documentation matches observable behavior.
- No unrelated files or user changes are overwritten.