# AGENTS.md
## Hard rules
- Never commit during development. Commit only when the user says ship, then in this exact order: `cargo clippy` warning-clean and `cargo test` all green, bump `version` in Cargo.toml, one commit, `git push origin main`, `cargo publish` (dry-run first), and tag `vX.Y.Z` ONLY after publish succeeds (a tag must never point at a version that failed to publish).
- Commit messages: short conventional one-liners (`feat:`, `fix:`, `release vX.Y.Z:`). Never co-authored, no trailers.
- No em-dashes in any user-facing text (help, errors, README, commit messages).
- Test by driving the built binary against temp dirs only; never touch real WireGuard interfaces, `/etc/wireguard`, or a user's real keys.
## Invariants and gotchas
- Keys are pure Rust (x25519-dalek); no `wg` binary is required. The private key is clamped on generation so output round-trips identically to `wg genkey`/`wg pubkey`; when touching key code, keep the clamp or generated keys diverge from what WireGuard accepts. A regression test pins a known private/public pair; keep it.
- Public key is `x25519(private, basepoint)`; that function clamps its scalar, matching `wg pubkey`.
- Mesh generation is "all peers minus self": a node must never appear in its own `[Peer]` list, since its address is already the `[Interface]` and a self-peer collides on AllowedIPs.
- Peers are routed by `/32` (the node's single mesh IP), never the interface prefix, or traffic for the whole subnet would be sent to one peer.
- `private_key` is optional in the manifest so a public-only manifest can be shared; a missing key writes a `<PASTE PRIVATE KEY>` placeholder, never a crash or a silently-wrong config. `mesh list --json` never includes private keys.
- The manifest rejects duplicate node names and duplicate mesh addresses; overlapping addresses break routing.
- Command taxonomy is deliberate: ops (`list`/`status`/`up`/`down`/`dir`/TUI) act on live `.conf` files across registered dirs; mesh (`mesh add`/`list`/`rm`/`gen`) edits a manifest and generates configs. `status` shows only interfaces that are up; `list` (alias `ls`) shows all with up/down state. Keep these separate.
- Reading the config dir and toggling interfaces needs root; the tool auto-re-execs under `sudo` (via its own absolute path) when a dir is unreadable. `EWG_NO_SUDO=1` disables that; `EWG_REGISTRY` and `EWG_DIR` override paths (tests rely on both).
- `wg-quick` output is captured, not inherited, or its wall of `[#] ...` lines corrupts the TUI's alternate screen.
- Color is TTY-gated in `main` (disabled when stdout is not a terminal) so piped output stays byte-for-byte clean.
- User-facing errors are lowercase, name things in backticks, and say what to do; never let a raw OS error reach the user.
## Build / test
- `cargo build` / `cargo build --release`
- `cargo test` (unit tests at the bottom of each source file, e2e in `tests/cli.rs`)
- `cargo clippy`
## Overview
`ewg` (crate `easywireguard`) generates WireGuard key material and manages live interfaces, and from a single mesh manifest generates each node's `.conf` as "all peers minus itself". Built for full-mesh WireGuard (every node an equal peer, no central hub) plus day-to-day interface management, with no external `wg` binary needed for its core.
If this file contradicts the code, the code wins; fix this file the same session.