Files
wireguard-scripts/PLANS.md
T
sasa e7e728cd23 Add OpenAI specific configuration
- add AGENTS.md and PLANS.md based on MPV
- add subagents configuration
2026-10-11 23:06:05 +03:00

51 KiB

WireGuard Manager — implementation milestones

1. Purpose of this document

This file is the sequential implementation plan for the MVP described in MVP.md. It is written so that a coding agent with limited project context can complete one milestone at a time without redesigning the project.

The source-of-truth order is:

  1. AGENTS.md — engineering and safety rules.
  2. MVP.md — product scope and expected behavior.
  3. PLANS.md — implementation sequence and verification checkpoints.
  4. Existing code and tests — current implementation state.

If these documents conflict, stop and resolve the conflict before changing production behavior.

2. Required Codex workflow

For every milestone, follow this loop:

  1. Read AGENTS.md, MVP.md, and the complete current milestone.
  2. Inspect the existing files and git status before editing.
  3. Confirm that all prerequisite milestones are complete.
  4. Mark the current milestone as IN PROGRESS in the progress table.
  5. Follow the multi-agent protocol in section 2.2 and delegate only the scope listed under Changes to Luna.
  6. Ensure the Luna worker adds or updates tests in the same milestone as the behavior.
  7. Run every command under Verification.
  8. If any check fails, repair it and rerun the complete milestone verification.
  9. Review the diff for scope creep, unsafe paths, and leaked secrets.
  10. Perform the listed manual checks when the environment supports them.
  11. Mark the milestone DONE only after all acceptance criteria pass.
  12. Add a short entry to Execution log with decisions, checks, and known limitations.
  13. Report the suggested commit message; do not create a commit unless the user asks.

Stop-and-fix rule:

  • Never move to the next milestone while a required verification is failing.
  • Never weaken or delete a test merely to make a milestone pass.
  • If a command cannot run because an optional tool is absent, record SKIP and run the documented fallback.
  • If a required dependency or safe test environment is missing, stop and report the exact blocker.
  • Never test mutations against the host's real /etc/wireguard configuration.
  • Run Git-specific checks only when the directory is already a Git worktree.
  • If it is not a Git worktree, record those checks as SKIP; do not initialize Git unless the user asks.

2.1. Development environment constraint

The development machine does not have a WireGuard server installed or configured. This is an expected project constraint, not a blocker.

Apply these rules to every milestone:

  • Required verification must run without wg, wg-quick, a WireGuard kernel interface, root access, systemd, firewall access, or /etc/wireguard.
  • Tests must use fixtures plus executable mocks placed first in a test-only PATH.
  • Tests must use an isolated temporary root for configs, clients, metadata, backups, logs, locks, and archives.
  • No required test may inspect or depend on the development machine's actual networking, routes, firewall, services, sysctl values, or journal.
  • The absence of real WireGuard commands on the development machine is not grounds for marking a milestone BLOCKED.
  • Mock behavior must cover successful output, malformed output, missing command, nonzero exit, timeout where relevant, apply failure, and rollback failure.
  • Command-contract tests must assert the exact argument arrays passed to external tools.
  • A test must fail if production code bypasses the mock directory and invokes a host networking command.
  • Real WireGuard integration checks are deferred to the deployment server and are not required to mark development milestones complete.
  • Deployment checks remain documented so they can be run manually after the project is copied to a disposable or non-production WireGuard environment.
  • Real-server observations must never be fabricated from mock results.
  • Optional smoke tests with a real gum, qrencode, or ShellCheck may run when those tools are present, but their absence is not a product failure.

2.2. Required multi-agent milestone orchestration

Every milestone implementation uses a senior coordinator and lower-cost execution subagents.

Agent roles

  • The primary coordinator uses gpt-5.6-sol with high reasoning.
  • Initial implementation is delegated to the project agent milestone_worker.
  • Every implementation subagent must explicitly use gpt-5.6-luna.
  • Corrections after review are delegated to milestone_repairer on gpt-5.6-luna.
  • The project defaults are stored in .codex/config.toml.
  • The custom subagent definitions are stored in .codex/agents/.
  • An explicit host or user model override can take precedence over project defaults; do not silently claim the configured model was used when the host reports otherwise.
  • If a milestone starts in a primary session explicitly running a model other than gpt-5.6-sol, stop before implementation and request a Sol session or explicit user direction.

Coordinator responsibilities

The Sol coordinator must:

  1. Read AGENTS.md, MVP.md, the complete current milestone, current progress, and the current worktree state.
  2. Confirm prerequisites and select exactly one milestone.
  3. Mark it IN PROGRESS before delegation.
  4. Break the milestone into bounded tasks with explicit files, behavior, constraints, acceptance criteria, and verification commands.
  5. Delegate implementation to milestone_worker with model = gpt-5.6-luna explicitly selected.
  6. Use multiple Luna workers only when their work is independent and they will not edit the same files.
  7. Prefer sequential delegation for write-heavy work in wg-manager.sh to avoid conflicting edits.
  8. Wait for every delegated task to finish before reviewing the combined result.
  9. Inspect the actual files and diff; never accept only the subagent's summary.
  10. Run or independently confirm every milestone verification command.
  11. Review behavior, tests, quoting, path safety, secret handling, rollback, mock isolation, and acceptance criteria.
  12. Decide whether the milestone passes, needs Luna repair, or is genuinely blocked.
  13. Keep the milestone IN PROGRESS throughout all repair cycles.
  14. Mark it DONE only after the complete verification gate passes and no material review findings remain.
  15. Update the execution log and report the suggested commit message.

The coordinator owns architecture, scope, review, and completion decisions. It must not outsource the final acceptance decision to Luna.

Initial Luna assignment contract

Each milestone_worker prompt must include:

  • the exact milestone identifier and title;
  • the milestone goal;
  • prerequisites already satisfied;
  • files the worker may edit;
  • files or systems it must not touch;
  • the required implementation steps;
  • the development-machine constraint;
  • required fixtures and mocks;
  • exact verification commands;
  • acceptance criteria relevant to the delegated task;
  • the required return format: changed files, tests, command results, uncertainties, and blockers.

Do not delegate a vague request such as “implement the next milestone.” The Luna task must be concrete enough to execute without making architecture decisions.

Mandatory Sol review gate

After Luna finishes, the Sol coordinator must perform all of these checks:

  1. Read the returned summary, but treat it only as a navigation aid.
  2. Inspect every changed file and the complete diff.
  3. Confirm no unrelated file or user change was overwritten.
  4. Confirm the implementation matches the assigned milestone rather than only passing tests.
  5. Confirm tests cover success, expected failure, rollback, and unsafe input where applicable.
  6. Confirm all host interactions remain behind mocks in development verification.
  7. Confirm no private key, preshared key, client config, real endpoint, or secret appears in output or fixtures.
  8. Run the milestone's full required verification, not only the worker's focused tests.
  9. Inspect test output and exit codes rather than relying on a verbal “passed” claim.
  10. Check git diff --check when the directory is a Git worktree.
  11. Compare the result against every acceptance criterion.
  12. Record concrete findings with file paths, observed behavior, expected behavior, and reproduction commands.

Luna repair loop

If the Sol review finds any material problem:

  1. Do not fix the implementation silently in the coordinator thread.
  2. Keep the milestone status IN PROGRESS.
  3. Delegate a repair task to milestone_repairer with model = gpt-5.6-luna explicitly selected.
  4. Provide each finding in this format:
    • location or affected behavior;
    • what is wrong;
    • expected result;
    • reproduction or failing verification command;
    • constraints that the repair must preserve.
  5. Require a regression test for every reproducible defect.
  6. Wait for the repairer to finish.
  7. Reinspect the actual diff and rerun the complete milestone verification as Sol.
  8. Repeat the Luna repair cycle for remaining or newly introduced findings.
  9. Stop only when the milestone passes or a genuine external blocker prevents further safe progress.

Do not impose an arbitrary retry count. Repeated failures are handled by making the next repair prompt more specific with evidence from the latest review. If the same blocker persists and cannot be solved within repository scope, record it as BLOCKED with exact evidence.

Parallelism rules

  • Parallel Luna agents are encouraged for independent read-only exploration, fixture design, or review of disjoint concerns.
  • Only one Luna agent may edit wg-manager.sh at a time.
  • Do not run implementation and repair agents concurrently on the same milestone.
  • Do not let two agents edit the same test or fixture files concurrently.
  • The Sol coordinator merges conclusions and resolves disagreements.
  • All subagents share the repository state, so assignments must name file ownership explicitly.

Completion evidence

The execution-log entry for each completed milestone must include:

  • coordinator model: gpt-5.6-sol;
  • implementation subagent model: gpt-5.6-luna;
  • custom agent names used;
  • number of repair cycles;
  • Sol review findings and their resolution;
  • full verification commands and results;
  • any optional checks recorded as SKIP;
  • the suggested commit message.

3. Intended MVP architecture

The implementation remains a portable project directory with one production entry point:

wireguard-scripts/
├── AGENTS.md
├── MVP.md
├── PLANS.md
├── README.md
├── add-user.sh                 # legacy reference; not the final entry point
├── wg-manager.sh               # production CLI and TUI entry point
├── wg-manager.conf.example     # documented safe example
├── bin/
│   └── gum                     # optional operator-provided binary
├── backups/                    # runtime data; ignored by version control
├── logs/                       # runtime data; ignored by version control
├── state/
│   ├── clients/                # runtime client metadata
│   └── archive/                # recoverable removed-client files
└── tests/
    ├── run.sh
    ├── helpers.sh
    ├── fixtures/
    └── mocks/bin/

For the MVP, production functions may remain in wg-manager.sh, grouped in this order:

  1. constants and exit codes;
  2. generic output and error helpers;
  3. path and dependency resolution;
  4. restricted configuration parsing and validation;
  5. WireGuard config parsing;
  6. client metadata;
  7. locking, backup, staging, apply, and rollback;
  8. client operations;
  9. diagnostics;
  10. CLI presentation;
  11. gum TUI wrappers and screens;
  12. argument parsing and main.

The final guard must allow the script to be sourced by tests without running main:

if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
  main "$@"
fi

Do not introduce an installer, uninstaller, daemon, package manager, database, JSON parser, or second TUI framework.

4. Stable design decisions

These decisions are fixed for the MVP unless the user explicitly changes them:

  • Bash is the implementation language.
  • wg-manager.sh is the only production entry point.
  • The project is run locally and is not installed into PATH.
  • gum is the TUI framework.
  • The operator supplies or installs the gum binary.
  • CLI functions must work without gum.
  • gum lookup order is WG_MANAGER_GUM, local bin/gum, then PATH.
  • qrencode, systemd, nft, and iptables are optional capabilities.
  • Runtime tests use fixtures and mocks, never the live host configuration.
  • Configuration uses a restricted KEY=VALUE parser, not unrestricted source.
  • Client identity is the WireGuard public key; the name is display metadata.
  • Runtime metadata is stored as one root-readable file per managed client.
  • Mutations use flock, backup, staged config, atomic replacement, apply verification, and rollback.
  • Removed client files are archived by default rather than permanently deleted.
  • User-facing messages are in Russian.
  • Code identifiers and CLI command names are in English.

5. Progress tracker

Allowed status values: NOT STARTED, IN PROGRESS, BLOCKED, DONE.

Milestone Status Outcome
M0 NOT STARTED Test harness and safe project skeleton
M1 NOT STARTED Runtime foundation, CLI shell, and config loading
M2 NOT STARTED WireGuard parser, client model, status, and list
M3 NOT STARTED Locking, backups, atomic apply, and rollback engine
M4 NOT STARTED Safe client creation
M5 NOT STARTED Client detail, config export, and QR display
M6 NOT STARTED Safe client removal and archival
M7 NOT STARTED Diagnostics engine and report
M8 NOT STARTED Backup listing/restoration and audit log UX
M9 NOT STARTED TUI foundation, dashboard, and read-only screens
M10 NOT STARTED Complete TUI mutation workflows
M11 NOT STARTED Hardening, documentation, and MVP acceptance

Milestone M0 — Test harness and safe project skeleton

Goal

Create the minimal repository structure and a dependency-free Bash test harness so every later milestone can be implemented with fast, isolated verification.

Prerequisites

  • AGENTS.md, MVP.md, and PLANS.md exist.
  • No implementation milestone has started.

Changes

  1. Create wg-manager.sh with:
    • Bash shebang;
    • set -Eeuo pipefail;
    • umask 077;
    • a no-op main that supports only help initially;
    • the source-safe main guard;
    • no root requirement for help.
  2. Create wg-manager.conf.example with placeholder values only.
  3. Create .gitignore entries for:
    • wg-manager.conf;
    • backups/;
    • logs/;
    • state/;
    • generated client configs and key files;
    • temporary test output.
  4. Create tests/run.sh as a pure Bash test runner.
  5. Create tests/helpers.sh with helpers for:
    • creating one isolated temporary test root;
    • copying fixtures;
    • prepending tests/mocks/bin to PATH;
    • asserting equality, exit status, substring, file existence, and file mode;
    • printing a final pass/fail summary;
    • cleanup through trap.
  6. Create fixture directories and one minimal valid wg0.conf fixture.
  7. Create mock placeholders for wg and wg-quick that fail loudly on unexpected arguments.
  8. Create mock placeholders for ip, systemctl, journalctl, ss, sysctl, nft, iptables, qrencode, and gum as they become needed.
  9. Add initial tests for:
    • sourcing wg-manager.sh does not execute main;
    • help exits with status 0;
    • an unknown command exits nonzero;
    • the test harness never references /etc/wireguard for writes.
  10. Add a guard that records every external mock invocation and fails on unexpected commands or arguments.
  11. Do not implement WireGuard operations yet.

Verification

Run:

bash -n wg-manager.sh
bash -n tests/run.sh
bash -n tests/helpers.sh
bash tests/run.sh
./wg-manager.sh help

If ShellCheck is installed, also run:

shellcheck wg-manager.sh tests/run.sh tests/helpers.sh tests/mocks/bin/*

Review:

git diff --check
git status --short

Acceptance criteria

  • The test suite runs without root.
  • The test suite creates all writable data below a temporary test directory.
  • No test can alter /etc/wireguard.
  • The test suite does not require real wg, wg-quick, root, a network interface, or systemd.
  • Unexpected external command invocations fail loudly.
  • wg-manager.sh help works from outside the project directory.
  • No real key, endpoint, or public IP is committed.
  • All verification commands pass.

Suggested commit message

test: add isolated Bash test harness and project skeleton

Milestone M1 — Runtime foundation, CLI shell, and configuration

Goal

Build the reusable runtime foundation: path discovery, errors, exit codes, dependency detection, restricted configuration loading, and predictable CLI dispatch.

Prerequisites

  • M0 is DONE.

Changes

  1. Define stable exit codes for:
    • success;
    • usage error;
    • validation error;
    • missing dependency;
    • permission error;
    • operation failure;
    • rollback failure.
  2. Implement die, warn, info, and debug helpers.
  3. Keep stdout for command results and stderr for progress/errors.
  4. Resolve SCRIPT_DIR without depending on the current directory.
  5. Add overridable runtime paths derived from SCRIPT_DIR:
    • config path;
    • backup directory;
    • log directory;
    • metadata directory;
    • archive directory;
    • lock file.
  6. Implement a restricted parser for simple KEY=VALUE lines:
    • ignore blank lines and full-line comments;
    • allow only documented keys;
    • reject duplicate keys;
    • reject command substitutions, shell syntax, and embedded newlines;
    • trim only documented surrounding whitespace;
    • do not call source or eval.
  7. Validate required configuration:
    • interface name;
    • absolute server config path;
    • absolute client directory path;
    • endpoint and port;
    • IPv4 prefix and host range;
    • IPv6 settings when enabled;
    • supported client mode;
    • backup retention count;
    • recent-handshake threshold.
  8. Implement command discovery helpers for required and optional commands.
  9. Implement gum resolution in the fixed order:
    • WG_MANAGER_GUM;
    • ${SCRIPT_DIR}/bin/gum;
    • command -v gum.
  10. Add CLI dispatch for:
    • help;
    • version;
    • placeholder known commands that report “not implemented” with a stable status;
    • unknown command and extra argument errors.
  11. Require root only inside future mutating command paths, not globally.
  12. Add unit tests for all parsing, validation, path, and gum resolution branches.

Verification

Run:

bash -n wg-manager.sh
bash tests/run.sh
./wg-manager.sh help
./wg-manager.sh version

Verify execution from a different directory:

(cd /tmp && /home/sasa/share/bash/wireguard-scripts/wg-manager.sh help)

Verify invalid configuration fixtures fail without executing embedded text:

bash tests/run.sh config

If available:

shellcheck wg-manager.sh tests/*.sh tests/mocks/bin/*

Then run:

git diff --check

Acceptance criteria

  • No configuration content is executed as shell code.
  • Running from another working directory resolves project-local paths correctly.
  • CLI help and version do not require root, WireGuard, or gum.
  • CLI operations can be tested with an alternate config and runtime root.
  • Missing real WireGuard tools are handled through dependency checks and controlled mocks.
  • All invalid configuration cases fail before any mutation.
  • gum resolution order is covered by tests.

Suggested commit message

feat(core): add validated configuration and CLI foundation

Milestone M2 — WireGuard parser, client model, status, and list

Goal

Read persistent and active WireGuard state without mutation, normalize peers into a client model, and expose reliable status and list commands.

Prerequisites

  • M1 is DONE.

Changes

  1. Add fixtures for:
    • interface with no peers;
    • one managed peer;
    • multiple managed peers;
    • a legacy ### Client NAME peer;
    • an unmanaged peer;
    • duplicate public keys;
    • duplicate and overlapping AllowedIPs;
    • IPv4 and IPv6 peers.
  2. Implement parsing of [Interface] and [Peer] sections without executing config text.
  3. Preserve exact public keys and normalized address lists.
  4. Parse managed block markers and legacy client comments.
  5. Implement one metadata file per client under state/clients.
  6. Define and validate metadata fields:
    • NAME;
    • PUBLIC_KEY;
    • IPV4;
    • optional IPV6;
    • CREATED_AT;
    • CONFIG_PATH;
    • CLIENT_MODE;
    • MANAGED.
  7. Never trust metadata over the public key and current config.
  8. Implement normalized client records combining:
    • disk config;
    • metadata;
    • wg show <interface> dump output.
  9. Implement client states:
    • active when latest handshake is within the configured threshold;
    • inactive when a nonzero handshake is older;
    • never when handshake is zero;
    • unmanaged when no trusted managed identity exists.
  10. Implement status with:
    • interface name;
    • runtime availability;
    • listening port;
    • peer count;
    • managed/unmanaged counts;
    • active/recent count.
  11. Implement list with deterministic, tabular plain-text output.
  12. Do not use ANSI decoration when stdout is not a terminal.
  13. Ensure read-only commands return useful results when systemd is absent.
  14. Add tests for parser errors and all state classifications.

Verification

Run:

bash -n wg-manager.sh
bash tests/run.sh parser
bash tests/run.sh status
bash tests/run.sh list
bash tests/run.sh

Verify redirected output entirely through fixtures and mocks:

bash tests/run.sh list-redirected

Using only test configuration and mocks, confirm deterministic output:

LC_ALL=C bash tests/run.sh list-deterministic

If available:

shellcheck wg-manager.sh tests/*.sh tests/mocks/bin/*

Finally:

git diff --check

Acceptance criteria

  • Persistent config and runtime dump parsing are independently tested.
  • A client name is never used as peer identity without a matching public key.
  • Managed, legacy, and unmanaged peers are distinguishable.
  • Empty and stopped interfaces produce useful output rather than crashes.
  • No read-only command writes runtime state.
  • status and list work without gum.
  • Required verification does not query the development host's actual interfaces or services.

Suggested commit message

feat(clients): add WireGuard state parsing and client listing

Milestone M3 — Transaction, backup, apply, and rollback engine

Goal

Provide one safe mutation primitive that all later add, remove, and restore operations must use.

Prerequisites

  • M2 is DONE.

Changes

  1. Add mocks for successful and failing variants of:
    • flock behavior where practical;
    • wg-quick strip;
    • wg syncconf;
    • active-state verification.
  2. Implement exclusive locking with one documented lock path.
  3. Re-read source config and relevant metadata after locking.
  4. Implement timestamped backup creation with:
    • UTC timestamp;
    • interface name;
    • original file mode and ownership information where available;
    • copied metadata relevant to the operation;
    • a small manifest.
  5. Implement backup retention without removing the only known-good backup.
  6. Create staging files on the same filesystem as the target config.
  7. Use umask 077 and explicitly enforce restrictive modes.
  8. Validate a staged config with wg-quick strip before replacement.
  9. Atomically replace the server config.
  10. Apply runtime configuration using wg syncconf with stripped configuration.
  11. Verify that expected public keys and AllowedIPs are active.
  12. On apply or verification failure:
    • restore the prior disk config;
    • restore prior runtime state;
    • verify rollback;
    • return a distinct rollback-failure status if recovery fails.
  13. Preserve the original config if staging validation fails.
  14. Ensure cleanup preserves the operation's final exit status.
  15. Expose the engine only through internal functions; do not add add/remove yet.
  16. Add interruption tests where feasible using a controlled mock.
  17. Assert exact wg-quick strip and wg syncconf argument arrays in command-contract tests.

Verification

Run:

bash -n wg-manager.sh
bash tests/run.sh transaction
bash tests/run.sh rollback
bash tests/run.sh locking
bash tests/run.sh backups
bash tests/run.sh

Verify the original fixture remains byte-identical after each failure test:

bash tests/run.sh mutation-preserves-source

Inspect permissions in the test root:

bash tests/run.sh secret-modes

If available:

shellcheck wg-manager.sh tests/*.sh tests/mocks/bin/*

Then:

git diff --check

Acceptance criteria

  • Concurrent mutations cannot both enter the critical section.
  • Invalid staging content never replaces the live file.
  • Apply failure restores disk and runtime state in tests.
  • Rollback failure is visible and has a distinct status.
  • Backups are root-readable only by default.
  • No later mutation needs to reimplement locking or rollback.
  • Transaction verification is complete using mocks; it does not claim a real WireGuard apply occurred.

Suggested commit message

feat(safety): add atomic WireGuard mutation and rollback engine

Milestone M4 — Safe client creation

Goal

Replace the unsafe mutation behavior of add-user.sh with a tested add command built on the transaction engine.

Prerequisites

  • M3 is DONE.

Changes

  1. Implement strict client-name validation.
  2. Reject names with:
    • spaces;
    • slashes;
    • leading option markers when unsafe;
    • control characters;
    • newlines;
    • glob characters;
    • characters outside [A-Za-z0-9._-].
  3. Check for conflicts in:
    • managed metadata;
    • server peer comments;
    • public keys;
    • client config and key files;
    • IPv4 and IPv6 AllowedIPs.
  4. Implement deterministic next-free IPv4 selection within the configured range.
  5. Implement optional IPv6 allocation derived from the validated host identity.
  6. Fail cleanly when the address pool is exhausted.
  7. Generate private, public, and preshared keys using wg without logging them.
  8. Avoid placing key values in command-line arguments.
  9. Build the client config with:
    • address;
    • DNS;
    • server public key;
    • preshared key;
    • endpoint and port;
    • persistent keepalive;
    • full-tunnel or split-tunnel AllowedIPs.
  10. Build an explicit managed peer block in the server config.
  11. Keep the block format stable and covered by golden fixtures.
  12. Write client key and config files with mode 0600.
  13. Persist metadata only when the server mutation succeeds.
  14. Remove newly generated files if the operation fails before commit.
  15. Use the M3 transaction engine for the server config.
  16. Add add NAME CLI parsing and optional documented flags for mode or endpoint.
  17. Require root for the actual add operation.
  18. Keep add-user.sh unchanged as a legacy reference during this milestone.
  19. Add tests for successful add, collision cases, pool exhaustion, and rollback.
  20. Use deterministic fake key material from the mock wg; do not require cryptographically valid WireGuard keys on the development machine.

Verification

Run:

bash -n wg-manager.sh
bash tests/run.sh add-client
bash tests/run.sh address-allocation
bash tests/run.sh add-collisions
bash tests/run.sh add-rollback
bash tests/run.sh secret-modes
bash tests/run.sh no-secret-logs
bash tests/run.sh

Inspect generated fixture output for exact sections:

bash tests/run.sh add-golden-config

Verify invalid names never reach the mocked wg genkey command:

bash tests/run.sh add-invalid-names

If available:

shellcheck wg-manager.sh tests/*.sh tests/mocks/bin/*

Then:

git diff --check

Acceptance criteria

  • A client can be added through CLI using only mocked test state.
  • All conflicts are detected before persistent mutation.
  • Generated secret files have mode 0600.
  • Failed apply leaves no active peer, persistent peer, or orphaned metadata.
  • Full-tunnel and split-tunnel configs are independently tested.
  • No secret appears in captured stdout, stderr, or audit output.
  • Tests verify the wg genkey, wg pubkey, and wg genpsk command contract without requiring the real binaries.

Suggested commit message

feat(clients): add transactional client creation

Milestone M5 — Client detail, config export, and QR display

Goal

Allow an administrator to inspect a client, locate its configuration, and display a QR code without exposing secrets accidentally.

Prerequisites

  • M4 is DONE.

Changes

  1. Implement show NAME with non-secret fields:
    • name;
    • public key;
    • assigned addresses;
    • client mode;
    • creation date;
    • endpoint;
    • last handshake;
    • transfer counters;
    • managed/unmanaged state;
    • client config path when available.
  2. Implement config NAME to print the validated config path by default.
  3. Require an explicit flag or interactive confirmation before printing full config content.
  4. Never print separate private key or preshared key files.
  5. Implement qr NAME using qrencode -t ansiutf8 when available.
  6. If qrencode is absent:
    • return the documented optional-capability status;
    • show the config path and a clear message;
    • do not fail unrelated commands.
  7. Reject unmanaged peers for config and QR operations unless a safe managed file association exists.
  8. Validate that the config path stays inside the configured client directory.
  9. Reject symlink or ownership surprises that could expose an unrelated file.
  10. Add tests for missing clients, missing config, unmanaged peers, and missing qrencode.

Verification

Run:

bash -n wg-manager.sh
bash tests/run.sh show-client
bash tests/run.sh config-export
bash tests/run.sh qr
bash tests/run.sh path-safety
bash tests/run.sh no-secret-default-output
bash tests/run.sh

Confirm normal detail output contains no private material:

bash tests/run.sh no-secret-logs

If available:

shellcheck wg-manager.sh tests/*.sh tests/mocks/bin/*

Then:

git diff --check

Acceptance criteria

  • show is safe to use in routine administration.
  • Full config content requires an explicit action.
  • qr works with a mock qrencode and degrades clearly without it.
  • Unmanaged peers cannot cause arbitrary file reads.
  • Config paths are validated before access.

Suggested commit message

feat(clients): add client details and secure config export

Milestone M6 — Safe client removal and archival

Goal

Remove a managed peer from persistent and active WireGuard state while preserving recoverable client artifacts.

Prerequisites

  • M5 is DONE.

Changes

  1. Implement remove NAME for managed clients only.
  2. Resolve the exact target through metadata and public key.
  3. Refuse ambiguous names, duplicate identities, and unmanaged peers.
  4. Display the exact name, public key suffix, and addresses before confirmation.
  5. For interactive CLI, require explicit confirmation.
  6. For noninteractive CLI, require a documented --yes flag.
  7. Remove only the exact managed peer block.
  8. Reject removal if block boundaries or public key do not match expectations.
  9. Use the transaction engine to apply the new server config.
  10. After successful server mutation, move client files and metadata into a timestamped archive directory.
  11. Do not follow unexpected symlinks while archiving.
  12. If artifact archival fails after peer removal:
    • report the partial failure clearly;
    • keep metadata sufficient for recovery;
    • do not silently claim full success.
  13. Record the removal in the audit log without secrets.
  14. Do not implement permanent purge in the MVP.
  15. Add tests for cancellation, exact-block removal, rollback, and archival failure.

Verification

Run:

bash -n wg-manager.sh
bash tests/run.sh remove-client
bash tests/run.sh remove-cancel
bash tests/run.sh remove-exact-block
bash tests/run.sh remove-rollback
bash tests/run.sh archive-client
bash tests/run.sh archive-path-safety
bash tests/run.sh

Confirm a neighboring peer is byte-identical after removal:

bash tests/run.sh remove-preserves-neighbors

If available:

shellcheck wg-manager.sh tests/*.sh tests/mocks/bin/*

Then:

git diff --check

Acceptance criteria

  • Only the selected public key is removed.
  • Cancellation makes no changes.
  • Apply failure restores the peer and leaves artifacts in place.
  • Successful removal archives client files with restrictive permissions.
  • Unmanaged peers cannot be removed through the managed-client command.
  • The audit event contains no key material beyond a permitted shortened identifier.

Suggested commit message

feat(clients): add safe client removal and archival

Milestone M7 — Diagnostics engine

Goal

Provide an evidence-based diagnose command that identifies common WireGuard, routing, permission, and firewall problems without modifying the server.

Prerequisites

  • M6 is DONE.

Changes

  1. Define a diagnostic result record with:
    • check identifier;
    • status: OK, WARN, FAIL, or SKIP;
    • concise Russian summary;
    • optional remediation guidance.
  2. Keep checks independent so one failure does not stop the report.
  3. Implement checks for:
    • required commands;
    • readable server config;
    • config and key permissions;
    • interface existence;
    • active WireGuard dump;
    • systemd service state when systemd is present;
    • config parsing and duplicate keys;
    • duplicate or overlapping AllowedIPs;
    • IPv4 forwarding;
    • IPv6 forwarding when enabled;
    • expected interface addresses and routes;
    • configured UDP listening port using available tools;
    • firewall/NAT visibility through nft or iptables when available;
    • disk versus runtime peer differences;
    • recent relevant journal entries when journalctl is usable.
  4. Never claim a firewall is correct merely because one command exists.
  5. Use SKIP for unsupported init systems or missing optional tools.
  6. Use WARN for uncertain or policy-dependent conditions.
  7. Use FAIL only for conditions that clearly prevent expected operation or violate a required invariant.
  8. Return nonzero when any FAIL exists.
  9. Add diagnose --quiet or equivalent machine-friendly summary if useful, but JSON is not required.
  10. Add fixtures and mocks for every status branch.
  11. Ensure diagnostics are read-only.
  12. Force diagnostic tests to consume mock host state rather than the development machine's actual /proc, service manager, routes, firewall, or journal.

Verification

Run:

bash -n wg-manager.sh
bash tests/run.sh diagnostics
bash tests/run.sh diagnostics-systemd
bash tests/run.sh diagnostics-firewall
bash tests/run.sh diagnostics-forwarding
bash tests/run.sh diagnostics-runtime-diff
bash tests/run.sh diagnostics-read-only
bash tests/run.sh

Verify exit behavior:

bash tests/run.sh diagnostics-exit-codes

If available:

shellcheck wg-manager.sh tests/*.sh tests/mocks/bin/*

Then:

git diff --check

Acceptance criteria

  • Every check reports exactly one recognized status.
  • Independent checks continue after failures.
  • Missing optional tools produce SKIP, not a crash.
  • At least one tested scenario produces each of OK, WARN, FAIL, and SKIP.
  • The command exits nonzero when critical failures exist.
  • No diagnostic check mutates config, runtime state, firewall, routes, or sysctl values.
  • Results describe fixture state only and do not depend on the development host.

Suggested commit message

feat(diagnostics): add read-only WireGuard health checks

Milestone M8 — Backup restoration and audit log UX

Goal

Expose safe backup management and administrator-visible audit history using the primitives already created by mutations.

Prerequisites

  • M7 is DONE.

Changes

  1. Finalize structured, line-oriented audit events with:
    • UTC timestamp;
    • effective UID or operator identity;
    • operation;
    • safe target identifier;
    • outcome;
    • concise error code or summary.
  2. Ensure all existing add, remove, apply, rollback, and backup actions log their final outcome.
  3. Implement log with:
    • newest or oldest ordering documented;
    • optional bounded number of entries;
    • readable behavior when no log exists.
  4. Implement backup to create an explicit manual backup.
  5. Implement backups to list valid backup manifests only.
  6. Treat malformed or incomplete backup directories as invalid and report them safely.
  7. Implement restore BACKUP with:
    • strict backup identifier validation;
    • containment inside the configured backup directory;
    • validation of the backup config;
    • preview of timestamp and interface;
    • explicit confirmation;
    • a backup of current state before restoration;
    • transaction-engine apply and rollback.
  8. Require --yes for noninteractive restoration.
  9. Do not restore a backup for a different interface without explicit future support.
  10. Add tests for tampered manifests, traversal attempts, apply failure, and restoration rollback.
  11. Add tests that search all audit output for fixture secrets.

Verification

Run:

bash -n wg-manager.sh
bash tests/run.sh audit
bash tests/run.sh audit-no-secrets
bash tests/run.sh backup-list
bash tests/run.sh restore
bash tests/run.sh restore-invalid
bash tests/run.sh restore-rollback
bash tests/run.sh backup-path-safety
bash tests/run.sh

Inspect permissions:

bash tests/run.sh runtime-file-modes

If available:

shellcheck wg-manager.sh tests/*.sh tests/mocks/bin/*

Then:

git diff --check

Acceptance criteria

  • Audit logs cover every mutating path and rollback result.
  • Fixture private and preshared keys are absent from logs.
  • Backup listing ignores invalid entries without executing content.
  • Restore cannot escape the configured backup directory.
  • Restore validates before mutation and backs up current state first.
  • Failed restore returns the host to its prior test state.

Suggested commit message

feat(backup): add audited backup listing and restoration

Milestone M9 — TUI foundation and read-only screens

Goal

Create a consistent gum TUI shell and expose the dashboard, client list, client detail, diagnostics, logs, and backups without duplicating backend logic.

Prerequisites

  • M8 is DONE.

Changes

  1. Implement small TUI wrapper functions for:
    • style/header;
    • choose;
    • filter;
    • input;
    • confirm;
    • table;
    • pager;
    • spinner.
  2. Keep all raw gum flags inside wrappers.
  3. Resolve and validate gum before entering the TUI.
  4. Detect noninteractive stdin/stdout and refuse TUI cleanly.
  5. Treat Escape and cancellation as normal navigation.
  6. Implement the main dashboard with:
    • configured interface;
    • runtime state;
    • managed peer count;
    • active/recent count;
    • warning count where inexpensive to obtain.
  7. Implement a shallow main menu for:
    • status;
    • clients;
    • add placeholder or action entry;
    • remove placeholder or action entry;
    • diagnostics;
    • audit log;
    • backups;
    • exit.
  8. Implement searchable client selection using stable hidden identity, not only display text.
  9. Implement read-only screens by calling existing backend functions:
    • status;
    • list;
    • show;
    • diagnostics;
    • log;
    • backups.
  10. Use a pager for long output.
  11. Do not parse decorated TUI output back into backend data.
  12. Keep the layout usable at 80 columns.
  13. Do not rely on color alone for status.
  14. Add a mock gum that records arguments and returns scripted selections.
  15. Add tests for menu routing, cancellation, missing gum, resolution order, and non-TTY refusal.

Verification

Run:

bash -n wg-manager.sh
bash tests/run.sh gum-resolution
bash tests/run.sh tui-routing
bash tests/run.sh tui-cancel
bash tests/run.sh tui-read-only
bash tests/run.sh tui-no-gum
bash tests/run.sh tui-non-tty
bash tests/run.sh

If a usable gum binary and interactive terminal are available, an optional smoke test may run with the test configuration and mocked system commands:

WG_MANAGER_GUM="$(command -v gum)" \
WG_MANAGER_CONFIG="$(pwd)/tests/fixtures/configs/valid.conf" \
PATH="$(pwd)/tests/mocks/bin:$PATH" \
./wg-manager.sh

Manual checks:

  • open and exit every read-only screen;
  • press Escape from each selection screen;
  • verify long output is scrollable;
  • verify no screen writes production state;
  • resize the terminal to approximately 80 columns.

If real gum is unavailable, record the manual smoke test as SKIP; the scripted mock-TUI tests remain the required gate.

If available:

shellcheck wg-manager.sh tests/*.sh tests/mocks/bin/*

Then:

git diff --check

Acceptance criteria

  • TUI startup fails clearly when gum is absent or unusable.
  • CLI remains fully operational without gum.
  • Every read-only screen uses existing backend data/functions.
  • Cancellation returns to the expected previous screen.
  • TUI tests do not require a real terminal or real WireGuard instance.
  • No second TUI framework is introduced.
  • Required TUI verification does not require real WireGuard or real gum.

Suggested commit message

feat(tui): add gum dashboard and read-only workflows

Milestone M10 — Complete TUI mutation workflows

Goal

Expose add, remove, QR, manual backup, and restore workflows through gum while preserving all CLI safety guarantees.

Prerequisites

  • M9 is DONE.

Changes

  1. Implement the add-client TUI flow:
    • prompt for name;
    • validate immediately;
    • choose full-tunnel or split-tunnel;
    • collect split networks when required;
    • optionally confirm configured endpoint;
    • preview non-secret result;
    • confirm creation;
    • call the existing add backend;
    • show final config path and optional QR action.
  2. Implement the remove-client TUI flow:
    • select only managed clients;
    • display exact target and addresses;
    • require explicit confirmation;
    • call the existing remove backend;
    • display archive location.
  3. Implement client actions:
    • details;
    • show config after confirmation;
    • show QR when available;
    • remove.
  4. Implement manual backup from TUI.
  5. Implement restore flow:
    • select a validated backup;
    • show manifest details;
    • use a stronger confirmation prompt;
    • call the restore backend;
    • show rollback outcome on failure.
  6. Use spinners only around backend operations whose errors are captured and shown.
  7. Never place secrets inside gum command arguments.
  8. Never use a spinner that discards actionable stderr.
  9. Return to a stable menu after success or cancellation.
  10. Stop returning to the menu after a critical rollback failure; show the error and exit nonzero.
  11. Add scripted mock-TUI tests for each success, cancel, validation failure, backend failure, and rollback-failure flow.

Verification

Run:

bash -n wg-manager.sh
bash tests/run.sh tui-add
bash tests/run.sh tui-remove
bash tests/run.sh tui-client-actions
bash tests/run.sh tui-backup
bash tests/run.sh tui-restore
bash tests/run.sh tui-backend-failure
bash tests/run.sh tui-no-secret-args
bash tests/run.sh

With a usable gum and a test-only mocked backend, optionally verify:

  • add flow success and cancellation;
  • invalid name correction;
  • split-tunnel input;
  • removal confirmation defaults to safe cancellation;
  • QR fallback when qrencode is missing;
  • restore requires deliberate confirmation;
  • backend errors remain readable after a spinner.

If real gum is unavailable, record these manual checks as SKIP; all scripted mutation-flow tests remain mandatory.

If available:

shellcheck wg-manager.sh tests/*.sh tests/mocks/bin/*

Then:

git diff --check

Acceptance criteria

  • TUI mutation paths call the same backend functions as CLI paths.
  • Cancellation makes no changes.
  • User input is validated before backend execution.
  • Destructive actions show the exact target and require confirmation.
  • Secrets are absent from recorded gum arguments and logs.
  • Critical rollback failure exits instead of pretending the TUI can continue safely.

Suggested commit message

feat(tui): add safe client and backup workflows

Milestone M11 — Hardening, documentation, and MVP acceptance

Goal

Verify the complete MVP as one coherent tool, close safety gaps, document operation, and produce a release-ready baseline.

Prerequisites

  • M0 through M10 are DONE.

Changes

  1. Review every requirement in MVP.md and map it to code and tests.
  2. Review every applicable rule in AGENTS.md.
  3. Add end-to-end fixture scenarios for:
    • fresh server with no clients;
    • existing managed clients;
    • mixed managed and unmanaged peers;
    • add, inspect, QR fallback, remove, and restore;
    • failed apply and successful rollback;
    • failed rollback;
    • missing optional dependencies;
    • stopped WireGuard interface;
    • malformed configuration and metadata.
  4. Add a test that scans repository fixtures and test output for known secret markers.
  5. Add a test that prevents writes outside the isolated test root.
  6. Add a test that fails if production code invokes host networking or service commands instead of test mocks.
  7. Review all filesystem operations for traversal and symlink risks.
  8. Review every command invocation for quoting and option injection.
  9. Review every error path for secret leakage.
  10. Review exit codes for consistency.
  11. Decide the legacy add-user.sh outcome:
    • keep it explicitly documented as deprecated reference; or
    • replace it with a small compatibility wrapper;
    • do not leave two independent mutation implementations.
  12. Create README.md with:
    • purpose and limitations;
    • supported environment;
    • directory layout;
    • required and optional dependencies;
    • operator-managed gum placement;
    • configuration procedure;
    • CLI examples;
    • TUI launch;
    • backup and rollback behavior;
    • file permissions;
    • safe test procedure;
    • troubleshooting;
    • warning that handshake data is not a full connection history.
  13. Document that development verification is mock-based and does not prove kernel, distribution, firewall, or live tunnel compatibility.
  14. Add a separate deployment-server verification checklist to README.
  15. Ensure examples contain placeholder endpoints and keys only.
  16. Update MVP.md only if implemented behavior intentionally changes the approved scope.
  17. Complete the execution log and decision log below.

Verification

Run the complete automated gate:

bash -n wg-manager.sh
bash -n tests/run.sh
bash -n tests/helpers.sh
bash tests/run.sh

If available, run:

shellcheck wg-manager.sh tests/*.sh tests/mocks/bin/*

Run repository checks:

git diff --check
git status --short

Run focused security checks:

bash tests/run.sh no-secret-logs
bash tests/run.sh path-safety
bash tests/run.sh symlink-safety
bash tests/run.sh no-host-writes
bash tests/run.sh no-host-command-bypass
bash tests/run.sh rollback

The following deployment-server checklist is not run on the development machine and does not block M11. Run it later only on a disposable or explicitly authorized non-production WireGuard interface:

  1. Create or select a non-production test interface and config.
  2. Point wg-manager.conf only to that interface and isolated client directory.
  3. Run status, list, and diagnose.
  4. Add a test client through CLI.
  5. Confirm disk config, runtime peer, metadata, file modes, and audit event.
  6. Inspect the client through TUI and display a QR code when available.
  7. Remove the test client through TUI.
  8. Confirm the peer is gone and artifacts are archived.
  9. Restore the pre-add backup.
  10. Confirm disk and runtime state match the initial state.

Additional deployment checks:

  1. Run diagnose and compare its claims with independently observed server state.
  2. Confirm the actual wg-quick and WireGuard versions are supported.
  3. Confirm the supplied gum binary matches the server architecture and starts over SSH.
  4. Confirm the server firewall backend is detected conservatively.
  5. Record distribution-specific differences as follow-up issues rather than weakening offline tests.

Do not run this checklist against a production interface as part of unattended verification. Do not mark mock results as evidence that this checklist passed.

Acceptance criteria

  • Every MVP criterion has a corresponding passing check or documented manual verification.
  • Full automated tests pass without root and without touching host WireGuard state.
  • Shell syntax checks pass.
  • ShellCheck has no unexplained findings when available.
  • CLI works without gum.
  • TUI behavior passes scripted gum mock tests; a real-gum smoke test is optional on the development machine.
  • Add, remove, and restore are transactional and recoverable.
  • No known secret is present in logs, diffs, or test artifacts.
  • README instructions match observed behavior.
  • No independent unsafe mutation path remains in add-user.sh.
  • git diff --check passes when the project is a Git worktree.
  • Required verification passes without real WireGuard commands, root access, systemd, or host networking access.
  • The deferred deployment-server checklist is documented and clearly marked as not yet executed.

Suggested commit message

docs: finalize WireGuard Manager MVP and operations guide

Suggested MVP release commit message

Use this only if the completed milestones are squashed into one commit:

feat: deliver portable WireGuard management MVP

Add a Bash CLI and gum-based TUI for listing, creating, inspecting,
diagnosing, removing, backing up, and restoring WireGuard clients with
transactional configuration updates, rollback, and secret-safe auditing.

6. Decision log

Add entries only for decisions that affect later milestones. Do not record routine implementation details.

Date Milestone Decision Reason Consequence
YYYY-MM-DD Mx Example decision Why it was necessary What later work must preserve

7. Execution log

Append one concise entry after completing or blocking a milestone.

Use this template:

## YYYY-MM-DD — Mx — DONE|BLOCKED

- Implemented: ...
- Coordinator: `gpt-5.6-sol` (`high`)
- Subagents: `milestone_worker` / `milestone_repairer`, `gpt-5.6-luna` (`medium`)
- Repair cycles: 0 | number
- Sol review: findings and resolution | no material findings
- Verification: `command`, `command`
- Result: ...
- Decisions: none | link to decision-log entry
- Known limitations: none | ...
- Next milestone: My

8. Final completion rule

The MVP is complete only when:

  • M0 through M11 are marked DONE;
  • the complete M11 verification passes;
  • all known critical failures are fixed;
  • optional skipped checks are documented;
  • the execution log describes the delivered state;
  • the user receives the final verification summary and suggested release commit message.

Do not mark the project complete merely because the implementation exists. Completion requires evidence from tests, static checks, diff review, and the documented acceptance flow.