# wireguard-admin This repository contains the Rust package, startup boundary, validated settings loader, client domain model, a byte-preserving WireGuard configuration editor, encrypted client vault and read-only import APIs, recoverable saved-file persistence, private backups, Commit, and previewed full restore. The library also provides read-only WireGuard runtime observation, technical peer comparison, bounded systemd restart handling, and explicit restart orchestration. The terminal management interface is not implemented, and management startup is not connected to these library operations. The package requires Rust 1.97.1 or newer and uses the Rust 2024 edition. From the repository root, run: ```sh cargo run -- --help cargo run -- --version ``` `--help` and `--version` work without root. Starting with no arguments requires root; until the management interface is implemented, startup exits with an error and does not read server configuration or vault files. ## Settings Settings use TOML in the fixed file `wireguard-admin.toml` beside the executable. Start from [`config/wireguard-admin.example.toml`](config/wireguard-admin.example.toml). The required fields are: - `interface_name`: existing WireGuard interface name (1–15 ASCII characters). - `server_config_path`: path to the existing server configuration. - `client_source_directory`: directory containing existing client files. - `server_endpoint`: hostname or IPv4 address followed by `:port`, or IPv6 in brackets followed by `:port` (for example, `[2001:db8::2]:51820`). - `ipv4_allocation_range` and `ipv6_allocation_range`: CIDR ranges of the matching address family. - `dns_addresses`: non-empty list of IPv4 and/or IPv6 literal addresses. - `systemd_unit`: service unit name ending in `.service`. - `restart_timeout_seconds`: positive integer. `data_root` is optional. It defaults to `Backup` beside the executable; current internal data and the encrypted vault are under `data`, and archives are under `archives`. Relative configured paths, including an explicit `data_root`, resolve from the executable directory. Absolute paths remain absolute. Settings validation checks syntax and values only; it does not require the referenced files, directories, or service to exist and does not perform DNS lookups. ## Library configuration API The library exposes `wireguard_admin::wireguard::ConfigDocument` for UTF-8 configuration bytes supplied by the caller. It retains the source and renders unmodified input byte-for-byte, including line endings and a missing final newline. Peer inspection exposes public identities, parsed `AllowedIPs`, spans, and redacted issues; arbitrary directive values are not included in the view. The edit methods `add_client`, `delete_client`, `rename_client`, and `insert_legacy_metadata` return new in-memory documents. They do not read or write files, save changes, or alter the running VPN. See [`docs/CONFIG_FORMAT.md`](docs/CONFIG_FORMAT.md) for the managed metadata block format. Example: ```rust,ignore use wireguard_admin::wireguard::ConfigDocument; let document = ConfigDocument::parse(config_bytes)?; let updated = document.rename_client(&public_key, "office laptop")?; let bytes_to_review = updated.render(); // no persistence or VPN operation ``` Management startup still exits with an explicit “not implemented” error after its root check. Runtime and restart library behavior is covered with synthetic fixtures and fake adapters; it has not been validated against a real WireGuard interface or systemd service. Real WireGuard integration is deferred to the isolated Alpine milestone, and real systemd/D-Bus acceptance requires the disposable VM milestone. ## Saved state, Commit, and backups wireguard_admin::app::Application opens saved files through wireguard_admin::persistence::SavedStore, stages client and vault changes, creates manual and mandatory operation backups, and previews/confirms a full restore. Commit writes saved files only. Restore also changes saved files only; neither operation reloads or restarts WireGuard. The server file is kept at its trusted configured path, while internal state and the encrypted vault are kept under Backup/data by default. Archives are stored under Backup/archives and contain fixed logical members, not source client files or prior archives. The persistence journal recovers interrupted file replacement on reopen and returns a recovery error while earlier work remains unresolved. A confirmed restart marks the saved generation applied only after a successful service job, a matching complete runtime observation, and journaled verification-state write. Restoring a backup does not restore runtime state: the running interface is unknown until a later explicit restart and verification. Pending deleted-client addresses remain reserved through Commit, reopen, and restore. See [docs/BACKUP_FORMAT.md](docs/BACKUP_FORMAT.md) and [docs/PERSISTENCE.md](docs/PERSISTENCE.md) for archive and recovery details, and [docs/RUNTIME.md](docs/RUNTIME.md) for observation and restart boundaries. ## Vault and existing-client import library `wireguard_admin::vault` provides WireGuard key handling, an encrypted versioned vault bundle, caller-buffer client configuration parsing, and reconciliation against a supplied server document. The source adapter can prepare an initial encrypted bundle or explicitly import candidates into an existing prepared bundle. These operations return in-memory data only: they do not install or save a vault, edit source files or server configuration, Commit, or touch the VPN. Directory scans use the resolved `client_source_directory` only. A candidate is an immediate regular file whose name ends in `.conf` (case-insensitive); the adapter does not recurse, follow symlinks, or read files larger than 1 MiB. A scan is limited to 4,096 directory entries, 1,024 candidates, and 32 MiB total input. Per-file failures are redacted and do not prevent other files from being examined. Enumeration, read, or limit failures mark the scan incomplete. The original files remain untouched. Adapter examples and behavior are covered with synthetic temporary directories in `tests/vault_import.rs`. ## Client delivery and reissue `Application::prepare_new_client` stages a dual-stack peer and encrypted key. After a successful `commit`, `deliver_first_issue_file` can publish its first configuration without a vault password during that same session; a failed file publication can be retried to a newly selected destination. The first file is mandatory. `deliver_first_issue_file_and_qr` optionally returns a QR matrix from the same authorized bytes after file publication. Both success results carry an activation warning unless this session has verified the exact installed saved generation and its matching applied peer snapshot; historical verification and unknown runtime state do not suppress it. Later delivery uses `Application::reissue_client_file`, which takes the client public key, a password for that action, a prepared destination, and an optional QR choice. It freshly unlocks the installed saved vault each time, reads saved server/settings bytes rather than draft state, preserves validated imported parameters, and rejects blocked or stale records. It never reads original plaintext client files as a fallback. The result reports a secret-free failure stage and warns when the current saved peer set has not been verified as applied in this live session. Historical verification timestamps do not suppress that warning after reopen. File output uses mode `0600`, explicit overwrite confirmation, and separate QR failure reporting. These APIs have synthetic fixture coverage; the TUI password prompt and terminal QR rendering are not implemented.