diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..18c81695 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,155 @@ + + +# Purpose + +Concise, action-first guidance for AI coding agents to be productive in +`wireguard-tools`. Focus: where to change code, how to build/test, and +project conventions that differ from typical C projects. + +# Big picture + +- Repo provides userspace tooling for WireGuard: the `wg(8)` CLI and + `wg-quick(8)` helper scripts. Primary code: `src/`. Platform specifics live + under `uapi/` (kernel headers) and `wincompat/` (Windows portability). +- Major areas: `src/` (C CLI tools), `wg-quick/` (shell helpers), `uapi/`, + `completion/`, `systemd/`, `man/`, and `contrib/` + `external-tests/`. + +# Quick start (commands you'll use most) + +- Build: `cd src && make` +- Verbose build/debug: `cd src && make V=1 DEBUG=yes` +- Install for packaging tests: `make install PREFIX=/usr DESTDIR= WITH_WGQUICK=yes WITH_BASHCOMPLETION=yes` +- Static analysis: `cd src && make check` (runs `scan-build` if available) +- Cross-build for Windows: set `PLATFORM=windows` (Makefile will use `wincompat/`). + +# Code change conventions (important) + +- Adding a C source: place under `src/` — the `src/Makefile` builds `*.c` + by default. Keep flags consistent with `CFLAGS` in `src/Makefile`. +- Platform-specific headers go in `uapi//` and are added with + `-isystem uapi/$(PLATFORM)`; do not diverge from kernel WireGuard headers + without coordinating upstream kernel changes. +- When adding runtime files (scripts, manpages, completion, systemd units), + update the `install` target in `src/Makefile` so packaging includes them. +- Prefer existing helpers (`ipc-*.h`, `netlink.h`) for IPC and netlink logic. + +# Files to read first (high value) + +- `src/Makefile` — build, platform detection, install flags (`WITH_WGQUICK`, + `WITH_BASHCOMPLETION`, `WITH_SYSTEMDUNITS`). +- `src/wg.c`, `src/setconf.c`, `src/show.c` — core CLI behavior and parsing. +- `wg-quick/` scripts — show how configs are consumed and expected runtime + behavior for `wg-quick`. +- `uapi/` — kernel/user API headers (keeps user tooling in sync with kernel). + +# Testing / validation checklist before PR + +- Run `cd src && make V=1` to reproduce compile issues. +- Run `cd src && make check` if changing parsing, memory-handling, or IPC. +- If adding files that affect packaging, run `make install DESTDIR=` and + verify installed layout. + +# Notes on portability & packaging + +- Minimal dependencies: project targets portability and a plain libc. +- Keep changes small and portable. Avoid introducing new heavy deps. + +# PR tips for reviewers + +- Include the minimal build and install commands you used to verify the + change. Show `make V=1` output if build flags or toolchain changes were + required. +- If you changed `uapi/`, state why it must differ from upstream kernel + headers and include references. + +# If you're stuck + +- Read `README.md` and `src/Makefile` first. If build errors persist, paste + `make V=1` output and relevant `gcc/clang` errors into the PR description. + +--- +If you'd like, I can (a) shorten or expand any section, (b) add a short +walkthrough for adding a CLI flag in `wg.c`, or (c) include a sample PR +checklist. Which would you prefer? + +## Walkthrough — Add a small CLI flag to an existing subcommand + +Example: add a simple `--example-flag` to the `show` subcommand. + +- Files to edit: `src/show.c` (subcommand implementation) and, if needed, + tests or `man/` pages. +- Typical steps: + 1. Locate the subcommand entry in `src/wg.c` (the `subcommands[]` table lists + available subcommands and their `*_main` functions; `show` maps to `show_main`). + 2. Open `src/show.c` and find `show_main` — it handles argc/argv for `show`. + 3. Add minimal flag parsing near the top of `show_main`. This project uses + simple `argv` checks rather than a heavy option parser; follow existing + patterns (see checks for `argc` and `argv[1]` already in `show_main`). + 4. Implement the behavior (set a local `bool` or configuration struct and + branch later in the printing functions such as `pretty_print` or + `ugly_print`). + 5. Build and run the subcommand locally: `cd src && make V=1 && ./wg show --help`. + 6. Run `make check` if you changed parsing or memory handling. + +- Minimal illustrative code sketch (adapt to project's helpers): + +```c +// in src/show.c, inside show_main before printing +bool example_flag = false; +for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--example-flag")) { + example_flag = true; + // optionally remove the consumed arg or shift argv + } +} +// Later, in pretty_print or dump_print, use `example_flag` to alter output +``` + +Keep changes small and follow existing formatting and error handling patterns. + +## Walkthrough — Add a `uapi//` header + +Use this when you need to add platform-specific kernel/user API headers +that the `src/` build will include via `-isystem uapi/$(PLATFORM)`. + +- Files to edit: create `uapi//linux/wireguard.h` (or matching + platform path) and keep contents aligned with kernel headers. +- Typical steps: + 1. Duplicate the closest existing platform header under `uapi/` and + adapt only the necessary differences. The project expects these + headers to match the kernel WireGuard API. + 2. Update `src/Makefile` only if you need to add a new install step + for the header; usually `-isystem uapi/$(PLATFORM)` is sufficient. + 3. Build locally: `cd src && make V=1` and ensure the compiler uses + the `uapi/` header (compiler include paths appear with `V=1`). + 4. Add tests or mention kernel version differences in the PR description + when the header intentionally diverges from upstream kernel headers. + +## Commit & DCO checklist + +Before pushing, ensure commits are signed off to satisfy the DCO bot: + +- Set git identity (one-time): + +```bash +git config user.name "Your Name" +git config user.email "you@example.com" +``` + +- Make a commit and sign-off: + +```bash +git add +git commit -s -m "docs: update copilot instructions (concise)" +``` + +- If DCO fails on the PR, amend the offending commit with a sign-off and force-push: + +```bash +git commit --amend --no-edit --signoff +git push --force-with-lease +``` + +- Use `gh` to open the PR or post follow-up comments if you fix DCO or CI. + +This checklist is intentionally minimal — use it to avoid common DCO issues. diff --git a/.github/copilot-instructions.original.md b/.github/copilot-instructions.original.md new file mode 100644 index 00000000..c1822aed --- /dev/null +++ b/.github/copilot-instructions.original.md @@ -0,0 +1,104 @@ + + +# Purpose + +Short, actionable guidance for AI coding agents to be immediately productive in +this repository. Focus on how the project is structured, build/test/debug +workflows, and concrete file examples you should read before changing code. + +# Big picture + +- **What this repo is**: userspace tooling for WireGuard: the `wg(8)` CLI and + `wg-quick(8)` helper scripts. Primary code lives in `src/` and platform + specifics under `uapi/` and `wincompat/`. +- **Major components**: + - `src/` — main C sources (`wg.c`, `setconf.c`, `show.c`, etc.) and `src/Makefile`. + - `wg-quick/` — platform-specific shell scripts (bash) used by install. + - `uapi/` — platform-specific kernel/user API headers included at build time. + - `contrib/` and `external-tests/` — sample integrations and language bindings. + - `wincompat/` — compatibility layer and resources for Windows builds. + +# What to read first (examples) + +- `README.md` — project overview and canonical build/install commands. +- `src/Makefile` — most important: how compilation, platform detection, and + installation variables work (`PREFIX`, `WITH_WGQUICK`, `WITH_BASHCOMPLETION`). +- `src/wg.c`, `src/setconf.c`, `src/show.c` — core CLI behavior and parsing. +- `wg-quick/` — how the quick helper expects configuration files. +- `uapi/linux/` — shows kernel-compatible headers used when compiling. + +# Build, test and debug (concrete commands) + +- Build the tools (recommended): + + `cd src && make` + +- Install (honors packaging env vars): + + `make install PREFIX=/usr DESTDIR=... WITH_WGQUICK=yes WITH_BASHCOMPLETION=yes` + +- Useful Makefile knobs: + - `V=1` — disable pretty short messages and print full compiler/linker commands. + - `DEBUG=yes` — compile with `-g` for debugging symbols. + - `PLATFORM=$(uname -s | tr '[:upper:]' '[:lower:]')` is auto-detected. + +- Static analysis: + + `cd src && make check` # runs `scan-build` if available + +- Windows cross-build: `wincompat/` files are used when `PLATFORM=windows`. + The Makefile sets `CC` for mingw (`x86_64-w64-mingw32-clang`) when building + for Windows; inspect `wincompat/` for resource and manifest handling. + +# Project-specific conventions & patterns + +- Minimal dependencies: the top-level `README.md` states "no dependencies other + than a good C compiler and a sane libc." Expect simple, portable C patterns. +- Platform headers: `src/Makefile` adds `-isystem uapi/$(PLATFORM)` when that + directory exists. Prefer adding platform-specific headers under `uapi/`. +- Packaging variables and conditional installs are controlled in `src/Makefile`. + When adding files that should be packaged, update `install` target there. +- Shell helpers: `wg-quick` is intentionally a small, opinionated bash script; + changes to interface/flags must remain compatible with it. + +# Integration points & external interfaces + +- Kernel/user API: code relies on headers in `uapi/` to match kernel wireguard + definitions — don't diverge without coordinating kernel changes. +- System integration: systemd unit templates are in `systemd/`; `install` + target will optionally install them if `WITH_SYSTEMDUNITS` is enabled. +- Completion scripts are under `completion/` and are installed when + `WITH_BASHCOMPLETION=yes`. +- `contrib/embeddable-wg-library/` contains an example of embedding wireguard + logic into other languages; use it as a reference for integrations. + +# Testing & external test harnesses + +- Look at `external-tests/` for example consumers in Go, Rust, Python and + Haskell. These show how others interact with the CLI or wireguard APIs. +- Fuzzing harness is in `fuzz/` with its own `Makefile` — follow that + directory's README before changing the fuzz targets. + +# Helpful patterns and examples for edits + +- When adding a new source file to `src/`, add it to the wildcard `*.c` pattern + in `src/Makefile` (it already builds all `*.c`). Keep compilation flags + consistent with the existing `CFLAGS` defined there. +- To preserve portable behavior, prefer using existing helpers in `ipc-*.h` + and `netlink.h` rather than adding custom platform IPC code. + +# What an AI agent should do when making a change + +1. Read `src/Makefile` and the relevant `uapi//` headers. +2. Run `cd src && make V=1` locally to verify build commands and locate + compile-time failures. +3. Update `install` target only if adding runtime files (scripts, manpages, + completions, systemd units) and test installation locally using `DESTDIR`. +4. Run `make check` if the change touches memory/undefined behavior sensitive + code and static analysis is available. + +# Questions / feedback + +If any of these sections are unclear or you'd like more examples (e.g. a +short walkthrough of adding a new CLI flag in `wg.c`), tell me which area to +expand and I'll update this file. diff --git a/contrib/external-tests/show_flag_test.sh b/contrib/external-tests/show_flag_test.sh new file mode 100644 index 00000000..3aa649a1 --- /dev/null +++ b/contrib/external-tests/show_flag_test.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MIT +# Small integration test: build `wg` and check that `wg show --help` mentions --show-keys +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +cd "$REPO_ROOT/src" + +echo "Building src/ (this may take a moment)..." +make V=1 || { echo "Build failed"; exit 2; } + +WG_BIN="$REPO_ROOT/src/wg" +if [ ! -x "$WG_BIN" ]; then + echo "wg binary not found at $WG_BIN" >&2 + exit 3 +fi + +echo "Checking 'wg show --help' for --show-keys flag" +HELP_OUT="$($WG_BIN show --help 2>&1 || true)" + +if echo "$HELP_OUT" | grep -q -- "--show-keys"; then + echo "OK: --show-keys documented in show help" + exit 0 +else + echo "FAIL: --show-keys not found in show help output:" >&2 + echo "$HELP_OUT" >&2 + exit 4 +fi diff --git a/external-tests/show_flag_test.sh b/external-tests/show_flag_test.sh new file mode 100755 index 00000000..f185b6d1 --- /dev/null +++ b/external-tests/show_flag_test.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Simple smoke test: build the `wg` binary and verify `--show-keys` appears +# in the `wg show --help` output. This runs from the repository root so it +# works when invoked from CI or the workspace root. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +SRC_DIR="${REPO_ROOT}/src" + +echo "Repository root: ${REPO_ROOT}" +echo "Building src/... (this may take a moment)" +cd "${SRC_DIR}" +make V=1 + +echo "Checking help for --show-keys" +if ./wg show --help 2>&1 | grep -q -- '--show-keys'; then + echo "OK: --show-keys present in help" + exit 0 +else + echo "FAIL: --show-keys not found in help output" + ./wg show --help 2>&1 | sed -n '1,200p' + exit 2 +fi diff --git a/preview/README.md b/preview/README.md new file mode 100644 index 00000000..feca9970 --- /dev/null +++ b/preview/README.md @@ -0,0 +1,8 @@ +# Preview placeholder + +This directory contains a simple placeholder page (`index.html`) used to +prevent 404 responses when using preview/forwarding services (codespaces, +app.github.dev, etc.) that expect a file at a particular path. + +If you want a custom preview page or a different path, tell me what to add +and I'll update it. diff --git a/preview/index.html b/preview/index.html new file mode 100644 index 00000000..72a497fc --- /dev/null +++ b/preview/index.html @@ -0,0 +1,24 @@ + + + + + + Preview — wireguard-tools + + + +
+

Preview placeholder — wireguard-tools

+

This is a lightweight placeholder page created so repository previews do not return 404.

+

Repo: SupananWC/wireguard-tools

+

Branch: update/copilot-instructions

+

If you want a different preview path or content, tell me the desired filepath or HTML and I'll update it.

+
+ + diff --git a/src/completion/wg.bash-completion b/src/completion/wg.bash-completion index 3c062b4a..1787185a 100644 --- a/src/completion/wg.bash-completion +++ b/src/completion/wg.bash-completion @@ -22,7 +22,7 @@ _wg_completion() { fi if [[ $COMP_CWORD -eq 3 && ${COMP_WORDS[1]} == show && ${COMP_WORDS[2]} != interfaces ]]; then - COMPREPLY+=( $(compgen -W "public-key private-key listen-port peers preshared-keys endpoints allowed-ips fwmark latest-handshakes persistent-keepalive transfer dump" -- "${COMP_WORDS[3]}") ) + COMPREPLY+=( $(compgen -W "--show-keys public-key private-key listen-port peers preshared-keys endpoints allowed-ips fwmark latest-handshakes persistent-keepalive transfer dump" -- "${COMP_WORDS[3]}") ) return fi diff --git a/src/man/wg.8 b/src/man/wg.8 index a0fc04c0..81db8ed0 100644 --- a/src/man/wg.8 +++ b/src/man/wg.8 @@ -51,6 +51,13 @@ fwmark. Subsequent lines are printed for each peer and contain in order separate by tab: public-key, preshared-key, endpoint, allowed-ips, latest-handshake, transfer-rx, transfer-tx, persistent-keepalive. .TP +\fBshow\fP [\fI--show-keys\fP] { \fI\fP | \fIall\fP | \fIinterfaces\fP } [\fIpublic-key\fP | \fIprivate-key\fP | \fIlisten-port\fP | \fIfwmark\fP | \fIpeers\fP | \fIpreshared-keys\fP | \fIendpoints\fP | \fIallowed-ips\fP | \fIlatest-handshakes\fP | \fIpersistent-keepalive\fP | \fItransfer\fP | \fIdump\fP] +.PP +If the special option \fI--show-keys\fP is provided before the interface +argument, the pretty-printing output will reveal private and preshared keys +for debugging (this respects the `WG_HIDE_KEYS=never` override). Use with care +as keys are sensitive. +.TP \fBshowconf\fP \fI\fP Shows the current configuration of \fI\fP in the format described by \fICONFIGURATION FILE FORMAT\fP below. diff --git a/src/show.c b/src/show.c index 13777cf0..461324a8 100644 --- a/src/show.c +++ b/src/show.c @@ -24,6 +24,8 @@ #include "encoding.h" #include "subcommands.h" +static bool show_keys = false; + static int peer_cmp(const void *first, const void *second) { time_t diff; @@ -86,6 +88,8 @@ static const char *masked_key(const uint8_t masked_key[static WG_KEY_LEN]) { const char *var = getenv("WG_HIDE_KEYS"); + if (show_keys) + return key(masked_key); if (var && !strcmp(var, "never")) return key(masked_key); return "(hidden)"; @@ -202,9 +206,9 @@ static char *bytes(uint64_t b) static const char *COMMAND_NAME; static void show_usage(void) { - fprintf(stderr, "Usage: %s %s { | all | interfaces } [public-key | private-key | listen-port | fwmark | peers | preshared-keys | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive | dump]\n", PROG_NAME, COMMAND_NAME); + fprintf(stderr, "Usage: %s %s [--show-keys] { | all | interfaces } [public-key | private-key | listen-port | fwmark | peers | preshared-keys | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive | dump]\n", PROG_NAME, COMMAND_NAME); + fprintf(stderr, "Options:\n --show-keys Reveal keys temporarily for debugging (respects WG_HIDE_KEYS=never)\n"); } - static void pretty_print(struct wgdevice *device) { struct wgpeer *peer; @@ -382,12 +386,28 @@ int show_main(int argc, const char *argv[]) COMMAND_NAME = argv[0]; - if (argc > 3) { + /* Build a local argv that filters out recognized flags (currently + * only `--show-keys`) so the remainder of the function can assume + * positional parameters as before. */ + const char *local_argv[4]; + int local_argc = 0; + + local_argv[0] = argv[0]; + local_argc = 1; + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--show-keys")) { + show_keys = true; + continue; + } + local_argv[local_argc++] = argv[i]; + } + + if (local_argc > 3) { show_usage(); return 1; } - if (argc == 1 || !strcmp(argv[1], "all")) { + if (local_argc == 1 || !strcmp(local_argv[1], "all")) { char *interfaces = ipc_list_devices(), *interface; if (!interfaces) { @@ -403,8 +423,8 @@ int show_main(int argc, const char *argv[]) fprintf(stderr, "Unable to access interface %s: %s\n", interface, strerror(errno)); continue; } - if (argc == 3) { - if (!ugly_print(device, argv[2], true)) { + if (local_argc == 3) { + if (!ugly_print(device, local_argv[2], true)) { ret = 1; free_wgdevice(device); break; @@ -418,10 +438,10 @@ int show_main(int argc, const char *argv[]) ret = 0; } free(interfaces); - } else if (!strcmp(argv[1], "interfaces")) { + } else if (!strcmp(local_argv[1], "interfaces")) { char *interfaces, *interface; - if (argc > 2) { + if (local_argc > 2) { show_usage(); return 1; } @@ -434,17 +454,17 @@ int show_main(int argc, const char *argv[]) for (size_t len = 0; (len = strlen(interface)); interface += len + 1) printf("%s%c", interface, strlen(interface + len + 1) ? ' ' : '\n'); free(interfaces); - } else if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "help"))) + } else if (local_argc == 2 && (!strcmp(local_argv[1], "-h") || !strcmp(local_argv[1], "--help") || !strcmp(local_argv[1], "help"))) show_usage(); else { struct wgdevice *device = NULL; - if (ipc_get_device(&device, argv[1]) < 0) { + if (ipc_get_device(&device, local_argv[1]) < 0) { perror("Unable to access interface"); return 1; } - if (argc == 3) { - if (!ugly_print(device, argv[2], false)) + if (local_argc == 3) { + if (!ugly_print(device, local_argv[2], false)) ret = 1; } else pretty_print(device);