# AGENTS.md ## Scope - These rules apply to the entire repository. - Build a local WireGuard management tool for Linux servers. - Keep `wg-manager.sh` as 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.md` as the product scope and acceptance reference. - Treat `PLANS.md` as the required milestone sequence for MVP implementation. - Complete and verify one milestone before starting the next one. - Update the `PLANS.md` progress and execution log as milestones are completed. - Run milestones under the Sol coordinator and Luna worker/reviewer loop defined in `PLANS.md` and `.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.sh` only 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 pipefail` in production entry points. - Understand strict-mode exceptions before suppressing them. - Use `if command; then` when a nonzero status is expected. - Do not append `|| true` unless the ignored failure is intentional and documented. - Set `umask 077` before 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 `printf` rather than `echo -e`. - Use `[[ ... ]]` for Bash conditionals. - Use `(( ... ))` for arithmetic conditions. - Use `case` for command dispatch and finite state handling. - Use `local` for function-local variables. - Declare variables close to first use. - Use `readonly` for 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_config` or `add_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 -t` for line-oriented command output when appropriate. - Use `IFS= read -r` to 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=C` locally for command output that must be parsed predictably. - Do not parse human-formatted output when a stable dump format exists. - Prefer `wg show dump` for 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 `source` a 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=VALUE` parser 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 `trap` for `EXIT`, `INT`, and `TERM`. - 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 NAME` as 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 `gum` is unavailable. - Provide `help` and 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_COLOR` for 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 `gum` as the only planned TUI framework. - Do not add `dialog`, `whiptail`, or a second TUI framework. - Do not download or install `gum` from the application. - Resolve `gum` in this order: `WG_MANAGER_GUM`, local `bin/gum`, then `PATH`. - Validate that the resolved path is executable. - Check required `gum` subcommands or minimum version at startup. - Fail with a clear message when TUI is requested without usable `gum`. - Keep all `gum` invocation details in small UI wrapper functions. - Backend functions must never call `gum` directly. - Use `gum choose` for short finite menus. - Use `gum filter` for searchable client selection. - Use `gum input` for short validated text fields. - Use `gum confirm` before deletion, restoration, or key rotation. - Use `gum table` only for display or selection, not as a data store. - Use `gum pager` for logs, diagnostics, and long text. - Use `gum spin` only 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`, or `SKIP`. - 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.md` and README. - Treat `qrencode`, systemd, `nft`, and `iptables` as 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 `gum` binary 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`, and `iptables` as 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 `PATH` resolution for `gum`. - 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 -n` on every changed shell script. - Run ShellCheck on every changed shell script when available. - Run the relevant automated tests. - Format consistently; use `shfmt` when 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.md` only 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 `gum` resolution order. - Errors are actionable and do not reveal secrets. - Relevant tests pass. - `bash -n` passes. - ShellCheck has no unexplained findings. - Documentation matches observable behavior. - No unrelated files or user changes are overwritten.