tbenandClaude Opus 5.5 77ced6cc52 Add editing of client access rules
Saved rules and client form drafts can now be edited through the rule
modal. On the Rules tab, saving re-runs client setup so the firewall
matches, and both add and edit ask for confirmation.

Comma-separated destinations were rejected because drafts were validated
before being split; validate each one after splitting instead. When
editing, the first destination updates the rule and the rest are added
as new rules.

Pin the id in the client_rules PUT handler so a request body can't
change it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-01 14:27:10 +01:00
2026-10-16 06:29:21 +01:00
2026-11-01 14:27:10 +01:00
2026-09-12 13:56:36 +01:00
2026-10-21 09:13:59 +01:00
2026-09-12 13:56:36 +01:00
2026-10-21 09:13:59 +01:00
2026-09-12 13:56:36 +01:00

Wireguard UI

An admin dashboard for Wireguard — manage hosts, clients, tunnels, and IP rules through a web interface.

Features

  • Host Management — Create and configure WireGuard hosts with public keys, IP addresses, and listen ports
  • Client Management — Add clients per host with WireGuard configs, allowed IPs, and notes
  • Client Rules — Define IP table rules per client (rule type, destination, ports, protocol)
  • Media Management — Upload and manage media assets (images, documents, etc.) with tagging, thumbnails, and RAG ingestion
  • User Management — Multi-user support with role-based access (user types)
  • Persistent Storage — SQLite database with automatic schema generation
  • WebSocket Support — Real-time updates via WebSocket connections
  • Responsive UI — Tailwind CSS v4 with twui design system components

Prerequisites

  • Bun 1.3.1 or later (install)
  • SQLite (via @moduletrace/bun-sqlite)
  • git
  • zip and unzip
  • Linux (Debian, Ubuntu, Fedora, Arch, Alpine, or similar)
  • Root access (for installation script and system service setup)

Installation

Run the install script on a fresh machine:

curl -fsSL https://url.tben.me/wg-ui | sudo bash

Or clone the repo and run the script manually:

git clone https://git.tben.me/Moduletrace/wireguard-ui.git
cd wireguard-ui
bash src/scripts/install-wg-ui.sh

The install script will:

  1. Install system dependencies (git, curl, ca-certificates, openssl)
  2. Install Bun 1.3.14 to /opt/bun
  3. Set up library directories under /var/lib/wgui (keys, clients, hosts, iptables, scripts)
  4. Clone or update the repo to /var/lib/wgui/webapp
  5. Install app dependencies (bun install)
  6. Generate .env with encryption secrets
  7. Run WireGuard setup (if available)
  8. Install Docker (if missing) and run Caddy as the wgui-caddy container
  9. Install the wg-quick helper script
  10. Set up systemd/OpenRC services for wgui and certificate renewal

The installer never modifies your firewall — it will print the commands to temporarily allow the dashboard port so you can reach the first login, and then point you to Setup → Firewall for reviewing and removing rules.

Firewall

Firewall rules are managed per host, automatically:

  1. Bootstrap (manual, temporary) — allow the dashboard port so you can reach the web UI once:
    ufw allow 10752/tcp          # ufw (Debian/Ubuntu)
    firewall-cmd --permanent --add-port=10752/tcp && firewall-cmd --reload   # firewalld (RHEL/Fedora)
    
  2. Host creation — every host setup automatically allows its entire wireguard subnet (e.g. 10.5.0.1 → 10.5.0.0/24 from anywhere) and the host's wireguard listen port (e.g. 51821/udp) in ufw or firewalld. Detailed per-client traffic rules are enforced by the existing iptables chain scripts, not the host firewall.
  3. Host deletion — the host's subnet and listen-port rules are removed automatically.
  4. Cleanup — once everything is reachable via the tunnel, remove the temporary bootstrap rule and review all rules under Setup → Firewall:
    ufw delete allow 10752/tcp
    firewall-cmd --permanent --remove-port=10752/tcp && firewall-cmd --reload
    
    Rules that no longer match any host (temporary/orphaned) are flagged with copyable removal commands on the setup page.

TLS for Tunnel Services

Client routes expose internal services to WireGuard clients over HTTPS. The data plane is a dedicated Caddy container (wgui-caddy, host networking) on 8443; wg-ui owns the route and certificate records and regenerates the Caddyfile. Caddy never binds ports 80 or 443, and the host's existing proxy is left alone. Docker is required — the installer starts the daemon if it is not running.

  1. If Caddy isn't installed yet, open Setup → TLS ingress and click Install Caddy (or use the Caddy action on Setup → Maintenance). The installer pulls caddy:2.11.4-alpine, (re)creates the wgui-caddy container, and sets up certificate support.

  2. Open a route form from Routes (or a host's Routes panel) and set the ACME email and staging toggle. No DNS provider token is needed: certificates use manual DNS-01.

    Let's Encrypt requires a public domain you control; internal-only names such as home.arpa or .local need a private CA instead.

  3. Add an A/AAAA record for each route hostname pointing at the host's WireGuard IP. Caddy also restricts each route to the subnet of the host that owns it.

  4. Open the route page and click Issue (or Renew). The dashboard shows the exact _acme-challenge.<hostname> TXT record. Add it at Cloudflare, wait for propagation, then click Validate.

  5. Clients use https://<hostname>:8443/. Portless URLs require the host's existing 443 proxy to forward to 127.0.0.1:8443; wg-ui does not take over 443. Caddy allows localhost so that proxy can reach it, which means the proxy itself must enforce who may use those portless URLs.

Manual DNS-01 certificates are renewed from the route page because validation needs your registrar. The route list shows expiry warnings. A successful issuance reloads Caddy without restarting wg-ui. Check docker logs wgui-caddy for reload failures.

Upgrading from the old native Caddy install

Earlier versions ran Caddy as a host binary in /opt/caddy with a wgui-caddy systemd/OpenRC service. Re-running the installer migrates the host in place: it pulls the image, starts the container, and only then removes the old service and binary. The Caddyfile is rewritten to use the new admin socket path (/var/lib/wgui/caddy/admin.sock); certificates in /var/lib/wgui/tls are untouched.

To remove a leftover native install without installing the container:

sudo bash src/scripts/install-caddy.sh --purge-legacy

Add --purge to also remove the container, the image and the certificate renewal job (the Caddyfile and certificates are always kept):

sudo bash src/scripts/install-caddy.sh --purge-legacy --purge

Manual (Development)

git clone https://git.tben.me/Moduletrace/wireguard-ui.git
cd wireguard-ui
bun install
NODE_ENV=development bun run dev

The dev server runs on port 10752 by default.

Configuration

Environment Variables

The .env file (gitignored) controls runtime behavior. It is auto-generated during installation if missing:

Variable Description Default
NODE_ENV Environment (development/production) production
ENCRYPTION_KEY Encryption key for sensitive data Auto-generated
ENCRYPTION_SALT Encryption salt for sensitive data Auto-generated
DATA_DIR Application data directory $INSTALL_DIR/.data
CADDY_VERSION Caddy image tag (caddy:<version>-alpine) 2.11.4
CADDY_HTTPS_PORT Caddy ingress HTTPS port (never 80/443) 8443
CADDY_HTTP_PORT Caddy plain HTTP port (never 80) 8081
CADDY_INGRESS_ENABLED Set to 0 to skip Caddy config sync 1
ACME_STAGING Set to 1 to use the Let's Encrypt staging CA 0

Database Configuration

Edit bun-sqlite.config.ts to change database settings:

const config: BunSQLiteConfig = {
    db_name: "wgui", // Database file name
    db_dir: "./db", // Database directory
    typedef_file_path: "./db/types/db.ts", // Generated types
    db_schema_file_name: "schema.ts", // Schema definition
};

Server Port

The server port is defined in src/data/site-data.ts:

export const SiteData = {
    ServerPort: 10752,
    // ...
} as const;

Available Scripts

Command Description
bun run dev Start development server (requires root)
bun run start Start production server
bun run db:schema Generate/update database schema and types
bun run db:admin Open database admin interface

Project Structure

wireguard-ui/
├── src/
│   ├── pages/              # File-system based routes (__root.tsx, index.tsx, etc.)
│   │   ├── (sections)/     # Page sections
│   │   ├── (partials)/     # Page-local components
│   │   ├── (data)/         # Page-local data
│   │   ├── (functions)/    # Page-local functions
│   │   ├── (utils)/        # Page-local utilities
│   │   └── (hooks)/        # Page-local hooks
│   ├── components/
│   │   ├── twui/           # Local design system (publishable package)
│   │   ├── ui/             # shadcn/ui components
│   │   ├── shadcnblocks/   # shadcnblocks registry components
│   │   └── general/        # Shared page components (chat, date-picker, logo, etc.)
│   ├── hooks/              # Shared React hooks
│   ├── functions/
│   │   ├── backend/        # Server-only: DB queries, email, media upload, RAG
│   │   ├── frontend/       # Client-safe: CRUD handlers, fetch handlers
│   │   └── general/        # Platform-agnostic: join builders
│   ├── utils/              # Utility modules (auth, crypto, CSRF, hashing, etc.)
│   ├── dict/               # Constants and dictionaries (user types, gates, etc.)
│   ├── data/               # Static data (app constants, location data, etc.)
│   ├── types/              # Type definitions
│   ├── scripts/            # Install and setup scripts
│   └── server.ts           # Entry point — starts Bun.serve with WebSocket support
├── db/
│   ├── schema.ts           # Database schema definition
│   └── types/db.ts         # Generated database types
├── public/                 # Static assets served by the server
├── assets/                 # Project assets
├── bun-sqlite.config.ts    # Database configuration
├── bunext.config.ts        # Bunext configuration
├── package.json            # Dependencies and scripts
├── tsconfig.json           # TypeScript configuration
└── .env                    # Environment variables (gitignored)

Database Schema

The database consists of the following tables:

Table Description
users User accounts (name, email, username, password)
user_types User type assignments per user
media Media assets (images, documents, etc.)
media_paradigms Media paradigm classifications per media item
clients WireGuard clients per host
hosts WireGuard host configurations
client_rules IP table rules for each client
client_routes HTTPS routes exposed to WireGuard clients
tls_certificates Certificates used by the Caddy ingress
variables Application-wide key-value variables

Generate types from the schema:

bun run db:schema

Admin Route Hierarchy

Route Pattern Access Features
/admin Admin Dashboard overview
/admin/hosts Admin Host management (add, edit, per-host detail)
/admin/routes Admin Global HTTPS route list with host and certificate status
/admin/hosts/[host_id]/routes Admin HTTPS routes and upstreams exposed to tunnel clients
/admin/hosts/[host_id]/routes/[route_id] Admin Route details, TLS settings and certificate issue/renew
/admin/clients Admin Client management (add, edit, client rules per host)
/admin/settings Admin Account settings
/admin/setup Super admin System setup (hosts, WireGuard tools, update, restart)
/admin/setup/tls Super admin Caddy ingress installation and status

Development

Development Mode

Set NODE_ENV=development and run:

NODE_ENV=development bun run dev

In development mode, the install script skips cloning and system service setup — the server is assumed to already be running.

Path Alias

@/* resolves from the repository root. Examples:

import { Button } from "@/src/components/ui/button";
import type { BUN_SQLITE_WGUI_USERS } from "@/db/types/db";

Type-Checking

Run type checks with:

bunx tsc --noEmit

Testing

Run individual test files:

bun test path/to/test.file.test.ts

Repository

S
Description
A web-based admin dashboard for wireguard
Readme
2 MiB
Languages
TypeScript 83.9%
JavaScript 13.2%
Shell 1.9%
CSS 1%