Skip to main content

WGPL (WireGuard Peer Lite)

CI License: MIT Python 3.12+ Status: Stable

WGPL is a disconnected Python CLI for hub-and-spoke WireGuard (IPv4). You declare routing intent in SQLite (the SSOT); WGPL derives AllowedIPs, allocates addresses, and tracks peer lifecycle and audit.

Hot-reload the hub with zero downtime (wg syncconf) — bring your own OS interface (BYOI). Mutations stay in the database until you apply or remote syncconf.

Compatibility: Follows Semantic Versioning. Patch and minor releases will not break existing CLI commands, flags, or public --json field names. Breaking changes require a new major version (2.0.0). This documentation describes 1.2.0+ (planned hubs, Ready, --platform export, optional RouterOS peer comments from device names). Confirm with wgpl --version.

See it in action

Add a peer. Get a QR. Done.

WGPL quickstart — Ana_Laptop config and QR

More: split tunnel · branch offices · RouterOS labels · all scenarios · demo/README.md

Start here: Quick Start · Why WGPL · Documentation map

What WGPL is / is not

WGPL is WGPL is not
Declarative hub-and-spoke IPv4 topology manager A network daemon or control plane
Routing intent engine + WireGuard config generator Full-mesh overlay (use Tailscale / Netmaker)
Disconnected CLI; SQLite SSOT A kernel routing or iptables manager
Peer lifecycle, IPAM, append-only audit IPv6 support (IPv4 pools and peers only)
BYOI: you create the OS wg0 (or equivalent) Direct site-to-site P2P without a hub

Architecture and module layers (maintainers): DESIGN.md.

Quick Start

1. Install

Recommended: Python / uv (Python 3.12+)

uv tool install wgpl
# or: pip install wgpl

Experimental: standalone binaries (Linux / macOS)

Unsigned release artifacts for air-gapped routers or testing. Prefer uv/pip when possible. Verify the checksum from the GitHub Release (<binary_name>.sha256) before running.

Linux (amd64)

curl -sL https://github.com/aleaz/wgpl/releases/latest/download/wgpl-linux-amd64 \
  -o /usr/local/bin/wgpl
# Verify SHA-256 against wgpl-linux-amd64.sha256 from the same release, then:
chmod +x /usr/local/bin/wgpl

macOS (Apple Silicon / arm64)

Download the .tar.gz, verify the checksum, extract, and clear Gatekeeper quarantine:

curl -sL https://github.com/aleaz/wgpl/releases/latest/download/wgpl-macos-arm64.tar.gz \
  -o wgpl-macos-arm64.tar.gz
# Verify SHA-256 against wgpl-macos-arm64.tar.gz.sha256 from the same release, then:
tar -xzf wgpl-macos-arm64.tar.gz
xattr -r -d com.apple.quarantine wgpl-macos-arm64  # Required to bypass macOS Gatekeeper

# You can now run the executable inside the folder:
./wgpl-macos-arm64/wgpl-macos-arm64 --version

# Optional: Link it to your PATH for global access
sudo ln -s $(pwd)/wgpl-macos-arm64/wgpl-macos-arm64 /usr/local/bin/wgpl

[!TIP] The standalone binaries are provided for environments without Python. However, the recommended and fastest way to install WGPL on macOS is via PyPI using uv tool install wgpl or pipx install wgpl.

2. Bring your own hub interface (BYOI)

WGPL does not create the OS WireGuard netdev. Create wg0 (or equivalent) first, then copy its public key for interface add.

# Minimal hub: [Interface] with PrivateKey, Address (e.g. 10.0.0.1/24), ListenPort
sudo systemctl enable --now wg-quick@wg0
sudo wg show wg0 public-key   # <WG0_PUBKEY> below

If wgpl apply later fails because the OS interface does not exist, finish this step first — see docs/runbook.md — Troubleshooting.

3. Pin the database and register a hub

Pin a database path so sudo apply and non-root mutations share the same SSOT:

export WGPL_DB_PATH="$HOME/.wgpl.db"
# or pass --db "$HOME/.wgpl.db" on every command

A WGPL hub is the database record for one VPN domain (you may name it wg0 to match your OS interface).

# Register the hub: name, server endpoint host, hub public key, address pool
# Add --port N if the hub does not listen on the default 51820
wgpl interface add wg0 vpn.example.com <WG0_PUBKEY> 10.0.0.0/24

# Attach a Node (default policy: vpn_only). Positional name find-or-creates it.
wgpl peer add "Alice_Laptop" -i wg0
wgpl peer list

Planned hub (no hardware yet): intentional incomplete interface — pool required; PublicKey/endpoint deferred. Create with wgpl interface add NAME POOL --planned, attach peers, then commit wire fields with interface update (--public-key / --endpoint). Clear wire fields later with --clear-public-key / --clear-endpoint (endpoint+port pair). Concepts: runbook — Hub wire fields. Operator workflows (partial commit either order, clear flags, pool change, inspection, fail-closed): runbook — Planned hub workflow.

Defer Step 4 emit (validate/apply/config/qr) until Ready is yes.

wgpl interface add office-c 10.99.0.0/24 --planned
wgpl peer add "Planner_Laptop" -i office-c
# Later: wgpl interface update office-c --public-key <PK> --endpoint vpn.office-c.example.com
Term Meaning
Address pool Tunnel CIDR for peer IPs (IPAM) — not the public server IP
Endpoint Host/IP where clients connect to the hub — not the pool
PublicKey Hub WireGuard public key (BYOI)
Pool ≠ Endpoint ≠ PublicKey Three different fields
Commit interface update of wire-facing fields (not a separate verb)
Planned interface Intentional incomplete hub; peers OK; export, apply, peer config, and peer qr blocked until Ready yes
Ready Derived completeness: yes / planned / invalid (never stored; Ready yes = emit allowed)
peer.role = endpoint An end-user device (laptop/phone), not the server endpoint hostname

Client AllowedIPs default to vpn_only. Policies and overrides: docs/routing.md.

4. Validate, apply, inspect, and distribute

Canonical flow (hub already Ready yes): validate → (explain) → apply/export → distribute clients. Planned hubs: commit wire fields first; validate may pass with warnings while emit still fails closed.

wgpl validate wg0
sudo --preserve-env=WGPL_DB_PATH wgpl apply wg0
# equivalent: sudo wgpl --db "$HOME/.wgpl.db" apply wg0

wgpl peer list                  # copy the peer ID (or unique hex prefix) from the ID column
wgpl peer explain <PEER_ID>
wgpl peer qr <PEER_ID>
wgpl peer config <PEER_ID> > alice.conf
chmod 600 alice.conf

<PEER_ID> is a peer UUID or hex prefix from peer list (not the node name). Multi-hub DBs need -i on secret-bearing commands — docs/cli.md.

Install on the end-user device: docs/runbook.md — Client provisioning.

5. Export for your platform

Same database; --platform selects the target export format; --output / -o writes platform filenames (CLI does not invent names; no ZIP).

Platforms: wireguard, routeros, opnsense, networkd, networkmanager.

wgpl interface export wg0 --platform routeros -o ./routeros/
wgpl peer config <PEER_ID> --platform networkd -o ./networkd/
wgpl peer config <PEER_ID> -o alice.conf   # default wireguard; -o = file path

Full rules and examples: docs/cli.md. RouterOS import: runbook — MikroTik.

Demos

Offline VHS recordings (admin laptop — no local apply). Regenerate with cd demo && make all. Details: demo/README.md.

Demo What it shows
01 Quickstart Peer + client config + QR
02 Split tunnel Cloud VPC via split_tunnel + peer explain
03 Branch offices NY ↔ London subnet_router AllowedIPs matrix
04 Mixed topology Laptops + three branches + validate --strict
05 Remote sync interface export | ssh … wg syncconf
06 RouterOS labels --platform routeros peer comment= from device names

Why WGPL

Replaces hand-edited AllowedIPs and hub restarts with declared routing intent and zero-downtime wg syncconf.

vs wg-quick (manual config files)

Feature wg-quick (manual) wgpl
Peer storage / IPAM Text files; manual IPs SQLite + automatic CIDR IPAM
Routing / AllowedIPs Manual per peer in .conf Declared routing intent; derived at emit
Applying changes Restarts interface (drops connections) Zero-downtime hot-reload (wg syncconf)
Audit / TTL / verify None / manual Append-only audit, --expires, validate + routing matrix

Fit / not a fit

You keep keys, backups, and hub relay; you run apply and OS forwarding yourself — not a mesh control plane.

  • Full-mesh or managed overlay — Tailscale, Netmaker, or similar (WGPL targets one hub per VPN domain, not P2P mesh).
  • Direct site-to-site without a hub — Out of scope. Configure WireGuard manually, or use peer config --allowed-ips for a one-off export override.
  • Site-to-site via a central hub — In scope. Two subnet_router peers; LAN↔LAN through the concentrator. See docs/routing.md — Site-to-site.

Concepts

WGPL models hub-and-spoke routing intent in SQLite — not WireGuard text files. WireGuard ([Interface], [Peer], AllowedIPs) is an emit format produced at apply / export time.

  Node          Peer              Interface
 (who)   →   (attachment)   →   (hub / VPN domain)
Concept Meaning
Node Who — global device identity (wgpl node); rename with node update (peer update has no --name)
Peer How — attachment to one hub (keys, IP, routing intent, lifecycle)
Interface Where — WGPL hub for one VPN domain (pool, server endpoint, optional hub routes)

Full domain table and identity rules: DESIGN.md — Domain model.

Mutations never touch the kernel until you validate and emit:

SQLite intent  →  validate  →  emit
                              ├─ apply / interface export   (hub)
                              └─ peer config / QR           (client)
  • Mutate writes the database only; it does not touch WireGuard.
  • Emit (apply, export, config, QR) derives routes, validates, freezes a Server/Client snapshot, then renders for the selected --platform (default wireguard → WireGuard text). Maintainer detail: DESIGN.md — Projection Engine.
  • wgpl apply fails closed if the database fails consistency checks.

Maintainer architecture: DESIGN.md.

Routing

Declare role, routed_networks, and allowed_ips_policy on hubs and peers. WGPL derives AllowedIPs at emit time; hub packet relay (ip_forward, firewall) stays with the operator — docs/runbook.md — Hub routing relay.

Policy Client AllowedIPs (summary)
vpn_only (default) VPN address pool only
split_tunnel Pool + interface.routed_networks
all_remote_networks Split set + other sites' LANs
full_tunnel 0.0.0.0/0
custom peer.custom_allowed_ips

Eight hub-and-spoke patterns, glossary, LAN↔LAN, and invariants: docs/routing.md. Executable topology spec: docs/routing_matrix.md. Inspect with wgpl peer explain <PEER_ID> (UUID or hex prefix from peer list).

Day-2 ops (validate/apply, TTL, prune, backup, deploy, client OS): docs/runbook.md.

Highlights

  • Unique hub names: Interface name is globally unique per database (RFC-0003). Use the short interface ID from wgpl interface list when referring to a hub by id rather than name.
  • Global IPAM within each hub CIDR; idempotent wgpl apply (deltas only).
  • Node identity via wgpl node; the same Node can attach to several hubs.
  • TTL and cleanup: --expires, soft delete, and prune — runbook.
  • Fail-closed emit/apply/restore; chmod 600 on the DB, QR PNG (peer qr -o), and db dump outputs; --output platform artifacts use default OS permissions — treat them like private keys; append-only audit (no secrets in metadata) — SECURITY.md.
  • --json for automation — stdout contract: docs/cli.md.
  • Wire-safe MTU (minimum 1280 or unset); server endpoints RFC 1123 (IPv4/hostname; IPv6 endpoints rejected).
  • BYOI deploy: systemd, remote syncconf, Docker, MikroTik — runbook — Deployment.

Integrations

Copy-paste starting points in examples/:

Configuration

Pin the DB when using sudo apply so root and your user share the same SSOT.

Variable Description Default
WGPL_DB_PATH Path to the SQLite database (also settable with global --db) ~/.wgpl.db
WGPL_WG_BIN Path to wg for apply / syncconf (ignored when UID 0; defaults to /usr/bin/wg) wg (PATH)
WGPL_EXEC_CMD Optional audit metadata: the CLI command that triggered the mutation (sanitized, bounded) (unset)

wireguard-tools (wg) is required only for wgpl apply on the same machine. Full CLI: wgpl --help or docs/cli.md. Docker image ghcr.io/aleaz/wgpl and DB permissions: runbook — Docker, Environment and permissions.

Documentation map

Operators

Document Contents
docs/cli.md Full CLI reference (--platform / --output artifact export)
docs/runbook.md Production procedures (validate, apply, hub relay, deploy including RouterOS --platform, client OS, backup, compliance)
docs/routing.md Routing model, patterns, scope, invariants
docs/routing_matrix.md Executable topology spec (valid / invalid)
SECURITY.md Threat model and security policies
CHANGELOG.md Release history
docs/platform-projection-acceptance.md Real-target acceptance validation checklist per platform

Contributors / maintainers

Document Contents
DESIGN.md Domain model, layered architecture, emit path, security boundaries
docs/README.md Documentation hub (ops + architecture index)
docs/rfcs/ Normative RFCs and IDDs
docs/architecture/ Closed process history (non-normative)
CONTRIBUTING.md Development workflow and commit conventions
MAINTAINERS.md Maintainer ownership
CODE_OF_CONDUCT.md Community conduct

Contributing

git clone https://github.com/aleaz/wgpl.git
cd wgpl
uv sync --dev
uv tool run pre-commit install
uv run pytest

Please read CONTRIBUTING.md before opening a pull request.

Author

License

MIT

Metadata

Release files for wgpl 1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for wgpl 1.2.0
File Size Uploaded
wgpl-1.2.0.tar.gz 3.7 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for wgpl 1.2.0
File Interpreter ABI Platform
wgpl-1.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 3.8 MB

Release files / wgpl-1.2.0.tar.gz

Download URL wgpl-1.2.0.tar.gz
Size 3.7 MB
Tags Source
SHA-256 checksum
How to use checksums
14a16d36d2a13519d8458e989c3ab582e0e608824f4a7d6bac867e7065ec1698
BLAKE2b-256 checksum
How to use checksums
cf5db74b8c8e140c6cf981b764c225ab97a74e0f3e575ccc0eb77268f4a01f53
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 5, 2026.

Transparency log

Release files / wgpl-1.2.0-py3-none-any.whl

Download URL wgpl-1.2.0-py3-none-any.whl
Size 88.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bd483844e421b454cf072404767952ee1ddd630473b278a5501c6e9a21dc515b
BLAKE2b-256 checksum
How to use checksums
899d00e65f75744935cf1da904daf339383e4ea89ad58dfc1631d354280ce98c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 5, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.2.0 This release

2 release files

1.1.0

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page