Files
wireguard-admin/docs/DEVELOPMENT.md
T
2026-10-18 21:39:45 +03:00

10 KiB

Engineering Guidelines

Read the relevant sections before implementing filesystem, secret-handling, terminal, or system operations. These detailed rules supplement AGENTS.md; they remain applicable after being moved out of the automatically loaded instructions.

Product boundaries

  • Build a native Rust TUI for root on a Linux VPN server, managing one existing WireGuard interface and a configurable systemd service.
  • Use public keys as client identities. Aliases may repeat; selection and destructive actions must resolve to a public key, not an alias or row position alone.
  • Keep the server configuration as the source of truth. Modify only client [Peer] sections and associated managed metadata comments. Preserve all other settings and administrator comments byte-for-byte. Do not fully reserialize the file through a generic INI writer.
  • Model draft, saved, and applied state explicitly. Commit writes files and the vault only. It must never reload, sync, restart, or otherwise change the running VPN. Restore also changes saved state only.
  • Restart is a separate explicit action. Check systemd completion, interface presence, and saved client correspondence before marking changes applied or releasing deleted clients' addresses.
  • Allocate IPv4 and IPv6 from configured ranges and generate full-tunnel client configurations. Preserve existing IPv4-only clients and existing connection parameters.
  • Preserve original client files during import. Report anomalies; do not silently repair discrepancies or invent unknown creation dates.
  • Do not add multi-interface support, remote management, initial VPN server setup, concurrent edit reconciliation, automatic restart rollback, traffic history, or additional connection settings to version 1.

Architecture and implementation order

Follow IMPLEMENTATION.md: model/settings, preserving configuration edits, keys/vault, backups/Commit/restore, runtime adapter, delivery/TUI, then acceptance documentation and target build.

  • Separate pure domain logic from filesystem, cryptography, WireGuard, systemd, and terminal adapters. The TUI invokes application operations rather than implementing their rules in key handlers.
  • Keep changes and state transitions testable without root, a terminal, a running VPN, or a real service manager. Prefer narrow interfaces and simple types over speculative frameworks.
  • Resolve settings relative to the executable, not the working directory. Default data paths are Backup/data and Backup/archives alongside the executable. Honor configured paths; do not substitute system-wide installation directories.
  • Distinguish current data from archives. Never include previous archives recursively in a backup.
  • Identify and document versioned formats for managed comments, the vault, and backup manifests before persisting them. Reject unsupported versions with actionable errors rather than guessing.
  • Keep runtime statistics separate from desired configuration. Missing runtime data is unknown/unavailable, not zero traffic, a successful apply, or proof of a disconnected client.

Rust and dependency practices

  • Prefer maintained Rust libraries where appropriate. A TUI library, QR library, and established cryptographic format are legitimate dependencies. A single native executable does not mean a dependency-free build.
  • New dependencies outside the approved list in DEPENDENCIES.md are allowed, but require prior agreement with the user. Before adding one, establish the need, compare suitable alternatives (including existing dependencies or the standard library where applicable), and recommend the best fit with its trade-offs. Add the dependency only after the user agrees to the choice; do not modify dependency manifests or lockfiles to introduce it before that agreement. This applies to runtime, build, and development dependencies.
  • Check official crate documentation and compatibility before choosing versions. Add only needed features and dependencies; record any unavoidable runtime system utility and why it is necessary.
  • Commit Cargo.lock for the application once the project is initialized. Keep builds reproducible; do not force dependency updates unrelated to the task.
  • Use typed errors and Result for recoverable failures. Add operation/path context without exposing secrets. Avoid unwrap, expect, and panics in production paths that handle files, user input, archives, cryptography, or services.
  • Validate input at boundaries: keys, aliases, endpoint, address families/ranges, paths, and service name. Reject aliases containing line breaks or control sequences that could inject configuration or terminal content. Do not enumerate an entire IPv6 subnet to find an address.
  • Avoid unnecessary unsafe. If required by a low-level interface, isolate it and document its safety invariants. Keep privileged operations out of build scripts.
  • Use a direct supported API where practical. If an external program is necessary, pass arguments explicitly without a shell, check its exit status, bound its execution time, and redact sensitive output. Never execute arbitrary configuration text as a shell command.

Files, secrets, and backups

  • Require root for actual management operations; ordinary builds and tests must not require root or change host services.
  • Use established asymmetric encryption and password protection with operating-system randomness. Do not design cryptographic primitives or silently weaken password protection for convenience.
  • Encrypt new client secrets with the vault public key without prompting. Require a password separately for each client reissue; do not retain an unlocked vault across unrelated operations.
  • Keep plaintext secrets short-lived and use secret-handling/zeroization facilities where supported. Exclude passwords, private keys, complete client configurations, and secret-bearing command output from logs, errors, Debug output, snapshots, and fixtures. Explicit file/QR delivery is the intended disclosure path.
  • Do not bypass reissue authentication by falling back to retained plaintext source files. Original files remain untouched, but their presence is not an alternative password mechanism.
  • First delivery is allowed only after a successful Commit in the client's creation session. Preserve retry eligibility after failed file output, including a change of directory. After successful delivery or application exit, require the password for reissue.
  • Always save a normal client configuration file when delivering; QR is additional. Confirm before overwriting an existing file. Create secret-bearing files with mode 0600 and private directories with mode 0700 from the start, including temporary files.
  • Use same-directory temporary files and atomic replacement where applicable. Handle write, flush, and rename errors. For operations involving multiple files, define failure/recovery behavior explicitly; a partially written state must not be reported as a successful Commit. This is write integrity, not concurrent editing support.
  • Guard privileged file operations against unintended symlink traversal and unsafe archive paths. Validate archive contents and permitted destinations before replacement; reject traversal, link escapes, and unexpected entries. Do not blindly extract an archive over the filesystem.
  • Create a completed backup before every Commit, restore, and restart. Backup failure must stop that operation before its protected mutations. Backups include settings, server configuration, internal metadata, and the complete encrypted vault; exclude drafts and original client files.
  • Restoring is a previewed, confirmed full replacement with a precautionary backup, not a merge. It requires no vault password and never implicitly restarts WireGuard. Server paths are invariant; do not add migration machinery.
  • Do not label the newest archive as known-working merely because it exists. Track verified application status separately. Do not delete old archives automatically.
  • The vault password does not encrypt the entire backup or server private key. Keep this distinction accurate in documentation and UI.

Terminal and console behavior

  • Keep the TUI responsive during filesystem, cryptographic, and service operations. Show progress and actionable failures; do not block the input/render loop indefinitely or allow duplicate submission of an operation in progress.
  • Support keyboard navigation, visible focus, discoverable shortcuts, cancellation, terminal resizing, and readable empty/error states. Do not convey draft, unapplied, or anomalous state by color alone.
  • Restore raw mode, alternate screen, and cursor visibility on normal exit, errors, and handled interruptions, including panic cleanup where feasible. Avoid mixed diagnostic output corrupting the active screen.
  • Require a suitable terminal before entering interactive mode. Provide plain diagnostics and a nonzero exit status if interactive startup is impossible. Support conventional --help and --version without requiring root or reading the vault.
  • Keep diagnostics on stderr outside the TUI, reserve stdout for intentional output, and avoid terminal escape sequences when output is redirected. Do not invent a separate scripting interface as part of version 1.
  • Treat aliases, imported comments, filenames, and external error strings as untrusted display text; escape terminal control characters. Mask password input.
  • Make destructive confirmations identify the selected client or archive unambiguously. Before restart/exit, offer the specified draft-discard confirmation; cancellation keeps the current draft. Never silently commit on exit.
  • Display last-handshake time and current byte counters, not an inferred online flag or historical totals. Bound periodic refresh work and report unavailable statistics without terminating the management session.

Completion report and maintenance

For each delivered change, state what works, which specification behavior it covers, what was checked, and any remaining limitations. Separate unit/fixture results from dedicated real-system integration results. Do not call a milestone complete while its acceptance criteria remain unverified.

Keep examples synthetic and documentation synchronized with actual commands, formats, paths, and recovery behavior. Keep AGENTS.md concise; maintain detailed guidance in these supporting documents, requirements in the specification, and implementation rationale in ADRs.