wiregui/TODO.md
Stefano Bertelli 0546b44507
Some checks failed
CI / test (push) Failing after 26s
CI / release (push) Has been skipped
CI / docker (push) Has been skipped
feat: initial WireGUI implementation — full VPN management platform
Complete Python/NiceGUI rewrite of the Wirezone (Elixir/Phoenix) VPN
management platform. All 10 implementation phases delivered.

Core stack:
- NiceGUI reactive UI with SQLModel ORM on PostgreSQL (asyncpg)
- Alembic migrations, Valkey/Redis cache, pydantic-settings config
- WireGuard management via subprocess (wg/ip/nft CLIs)
- 164 tests passing, 35% code coverage

Features:
- User/device/rule CRUD with admin and unprivileged roles
- Full device config form with per-device WG overrides
- WireGuard client config generation with QR codes
- REST API (v0) with Bearer token auth for all resources
- TOTP MFA with QR registration and challenge flow
- OIDC SSO with authlib (provider registry, auto-create users)
- Magic link passwordless sign-in via email
- SAML SP-initiated SSO with IdP metadata parsing
- WebAuthn/FIDO2 security key registration
- nftables firewall with per-user chains and masquerade
- Background tasks: WG stats polling, VPN session expiry,
  OIDC token refresh, WAN connectivity checks
- Startup reconciliation (DB ↔ WireGuard state sync)
- In-memory notification system with header badge
- Admin UI: users, devices, rules, settings (3 tabs), diagnostics
- Loguru logging with optional timestamped file output

Deployment:
- Multi-stage Dockerfile (python:3.13-slim)
- Docker Compose prod stack (bridge networking, NET_ADMIN, nftables)
- Forgejo CI: tests → semantic versioning → Docker registry push
- Health endpoint at /api/health
2026-03-30 16:53:46 -05:00

12 KiB

WireGUI Implementation TODO

Migration of Wirezone (Elixir/Phoenix) to Python/NiceGUI. Source: /home/stefanob/PycharmProjects/personal/wirezone

Test count: 164 passing | Coverage: 35%


Phase 1: Foundation — Models, DB, Config

  • pyproject.toml with dependencies, uv sync
  • Package directory structure
  • wiregui/config.py — pydantic-settings (DB, Redis, WG, auth, SMTP, logging)
  • wiregui/db.py — async engine, sessionmaker, init_db()
  • wiregui/redis.py — Valkey connection pool
  • All 8 SQLModel models (User, Device, Rule, MFAMethod, OIDCConnection, ApiToken, ConnectivityCheck, Configuration)
  • Alembic init + initial migration + alembic upgrade head
  • wiregui/main.py — app entrypoint
  • compose.yml — PostgreSQL 17 + Valkey 8
  • wiregui/utils/time.pyutcnow() helper for naive UTC timestamps

Phase 2: Auth System — Login + Sessions

  • wiregui/auth/passwords.py — bcrypt hash/verify (direct bcrypt, not passlib)
  • wiregui/auth/jwt.py — create/decode JWT via python-jose
  • wiregui/auth/session.pyauthenticate_user() email/password verification
  • wiregui/auth/middleware.py — HTTP-level auth middleware (available for REST API)
  • wiregui/auth/seed.py — auto-create admin on first startup
  • wiregui/pages/login.py — login page with email/password form
  • wiregui/pages/home.py — authenticated home (redirects to /devices)
  • Auth guards via app.storage.user on each page
  • Logout clears storage and redirects

Phase 3: Device UI — User-Facing CRUD

  • wiregui/pages/layout.py — shared sidebar + header
  • wiregui/utils/network.py — IPv4/IPv6 allocation (random offset + scan)
  • wiregui/utils/crypto.py — WG keypair + PSK generation via wg CLI
  • wiregui/utils/wg_conf.py — WG client .conf builder
  • wiregui/pages/devices.py/devices list + create dialog + delete
  • /devices/{device_id} — detail page with stats and config flags
  • QR code generation + .conf download
  • Full device create/edit form with all wirezone options (description, per-device config overrides, use_default_* toggles with bound inputs, better layout)

Phase 4: WireGuard Integration

  • wiregui/services/wireguard.py — async subprocess: ensure_interface, add/remove_peer, get_peers, set_private_key, set_listen_port
  • wiregui/services/events.py — event bridge: device CRUD → WG + firewall
  • Device create/delete in UI fires WG events
  • wiregui/tasks/__init__.py — background task registry + cancel_all
  • wiregui/tasks/stats.py — poll WG stats every 60s, update DB
  • wiregui/tasks/reconcile.py — startup reconciliation (diff DB vs WG, add/remove)
  • config.pywg_enabled flag (default False for dev)
  • Startup: ensure_interface → reconcile → stats_loop (when wg_enabled)

Phase 5: Firewall (nftables)

  • wiregui/services/firewall.py — nft CLI: setup_base_tables, masquerade, per-user chains, jump rules, apply_rule, rebuild_all_rules
  • IPv4/IPv6 aware, TCP/UDP port range support
  • wiregui/pages/admin/rules.py/admin/rules CRUD (action, CIDR, protocol, port, user)
  • Events: on_rule_created/deleted, on_device_created adds jump rules
  • Startup: setup_base_tables + setup_masquerade (when wg_enabled)
  • Edit rule — edit dialog in admin rules page with all fields
  • Full user chain rebuild on rule update/delete via _rebuild_user_chain() in events.py

Phase 6: REST API (v0)

  • wiregui/auth/api_token.py — token generation (random → sha256), Bearer resolution with expiry + disabled user checks
  • wiregui/api/deps.py — get_db, get_current_api_user, require_admin
  • wiregui/schemas/ — Pydantic schemas: UserRead/Create/Update, DeviceRead/Create/Update, RuleRead/Create/Update, ConfigurationRead/Update
  • wiregui/api/v0/users.py — full CRUD (admin only)
  • wiregui/api/v0/devices.py — full CRUD (owner or admin, triggers WG/firewall events)
  • wiregui/api/v0/rules.py — full CRUD (admin only, triggers firewall events)
  • wiregui/api/v0/configuration.py — GET/PUT (admin only, auto-creates singleton)
  • Mounted on NiceGUI app at /api/v0

Phase 7: Admin UI

  • /admin/users — table (email, role, devices, status, last sign-in, method, created), create (email/password/role), edit (email/role/password/disabled), delete with cascading cleanup (devices → WG events, rules)
  • /admin/devices — all devices with user filter, full create form (owner, name, description, all use_default_* toggles with bound override inputs), full edit form, delete with WG events, config + QR on creation
  • /admin/settings — 3 tabs:
    • Client Defaults (endpoint, DNS, allowed IPs, MTU, keepalive)
    • Security (VPN session duration, local auth, unpriv device mgmt/config, OIDC auto-disable)
    • Authentication (OIDC provider CRUD with table + dialog; SAML placeholder for Phase 8)
  • /admin/diagnostics — WG interface status, active peers, connectivity checks, system notifications with clear/clear-all
  • wiregui/services/notifications.py — in-memory deque (capped at 100), add/clear/count/current
  • Header notification bell badge (admin only, links to diagnostics)
  • TODO: SAML provider management in Authentication tab

  • TOTP MFA (wiregui/auth/mfa.py) — secret generation, URI/QR, verification with clock drift tolerance
  • MFA challenge page (/mfa) — 6-digit code entry, multi-method support, last-used tracking
  • Login page updated: checks for MFA methods after password auth, redirects to /mfa if present
  • OIDC (wiregui/auth/oidc.py) — provider registry from Configuration, authlib Starlette integration
  • OIDC routes (/auth/oidc/{provider} + /auth/oidc/{provider}/callback) — auth code flow, user lookup/auto-create, refresh token storage in OIDCConnection
  • Login page shows OIDC provider buttons dynamically from config
  • OIDC refresh task (wiregui/tasks/oidc_refresh.py) — every 10min, refreshes all stored tokens, creates notifications on failure, respects disable_vpn_on_oidc_error
  • Magic links (/auth/magic-link + /auth/magic/{user_id}/{token}) — request page, signed JWT with 15min expiry, email via aiosmtplib
  • Email service (wiregui/services/email.py) — aiosmtplib send, magic link template
  • /account page — 3 tabs: Profile (details + password change), Two-Factor Auth (TOTP registration with QR + verification, list/delete methods), API Tokens (create with configurable expiry, list, delete)
  • OIDC providers registered on startup from Configuration
  • WebAuthn MFA (wiregui/auth/webauthn.py) — registration/authentication options generation, response verification, credential storage
  • SAML (wiregui/auth/saml.py + wiregui/pages/auth_saml.py) — SP-initiated SSO, metadata endpoint, ACS callback, IdP metadata parsing, attribute mapping
  • WebAuthn browser-side JS integration in account page — ui.run_javascript() calls navigator.credentials.create(), serializes response, server verifies and stores credential
  • SAML provider management UI in admin settings Authentication tab — table + add/delete dialog (config ID, label, XML metadata, sign requests/metadata/assertions/envelopes toggles, auto-create users)

Phase 9: Background Tasks & VPN Session Management

  • Task scheduler (wiregui/tasks/__init__.py) — register/cancel
  • Stats polling task (Phase 4)
  • OIDC refresh task (Phase 8)
  • VPN session expiry task (wiregui/tasks/vpn_session.py) — every 60s, finds expired sessions based on vpn_session_duration + last_signed_in_at, removes WG peers, creates notifications
  • Connectivity check poller (wiregui/tasks/connectivity.py) — fetches URL, stores result in DB, notification on failure
  • Live stats push — ui.timer(30, ...) on /devices (table refresh), /devices/{id} (RX/TX/handshake/remote IP labels), /admin/devices (table refresh)

Phase 10: Polish, Testing & Deployment

Testing (partially done)

  • pytest + pytest-asyncio setup, conftest with test DB
  • test_models.py (10 tests), test_auth.py (8 tests), test_utils.py (6 tests), test_services.py (6 tests), test_firewall.py (7 tests)
  • test_api.py (6 tests) — token generation, resolution, expiry, disabled user
  • test_notifications.py (9 tests) — add, ordering, count, clear, max cap, to_dict
  • test_admin.py (13 tests) — user CRUD, cascading deletes, config CRUD, OIDC providers, device overrides
  • test_mfa.py (11 tests) — TOTP secret gen, URI, code verification (valid/invalid/wrong secret/empty), QR SVG, DB integration, multi-method
  • test_magic_link.py (4 tests) — token creation/expiry/user mismatch, disabled user rejection
  • test_account.py (8 tests) — password change flow, API token CRUD, OIDC connection CRUD, refresh token update
  • test_integration_mfa.py (7 tests) — full TOTP registration flow, MFA blocks login, wrong code, multi-method, last-used tracking, delete allows bypass, disabled user
  • test_integration_oidc.py (10 tests) — provider config loading, connection create/update, auto-create user, disabled user, refresh token, multi-provider
  • test_tasks.py (6 tests) — VPN session expiry (expired/unlimited/no-config/disabled user), connectivity check (success/failure with notification)
  • HTTP-level integration tests (OIDC redirect/callback flow with respx mocking)

Coverage gaps (35% overall — run uv run pytest --cov=wiregui --cov-report=term-missing --cov-branch)

100% covered: models, schemas, config, auth/passwords, auth/jwt, auth/mfa, auth/api_token, utils/crypto, utils/time, services/notifications

API routes (32-84% — partially covered via httpx TestClient):

  • wiregui/api/v0/users.py (84%) — list/get/create/update/delete
  • wiregui/api/v0/rules.py (71%) — CRUD
  • wiregui/api/v0/devices.py (67%) — CRUD, permissions
  • wiregui/api/v0/configuration.py (61%) — get/update, auto-create
  • wiregui/api/deps.py (32%) — test get_current_api_user with real Bearer header parsing, require_admin rejection

Services (62-89% covered):

  • wiregui/services/wireguard.py (62%) — add/remove/get peers mocked
  • wiregui/services/firewall.py (73%) — base tables, chains, rules, rebuild mocked
  • wiregui/services/events.py (80%) — device + rule events, rebuild chain
  • wiregui/services/email.py (89%) — send_email, magic link, no-smtp fallback
  • wiregui/services/wireguard.py — test ensure_interface, set_private_key, set_listen_port
  • wiregui/services/firewall.py — test _nft/_nft_batch error handling, add_device_jump_rule with only ipv4/ipv6

Tasks (40-84% covered):

  • wiregui/tasks/stats.py (77%) — update from peers, no-op, unmatched peer
  • wiregui/tasks/reconcile.py (84%) — add missing, remove orphaned, in-sync
  • wiregui/tasks/oidc_refresh.py (40%) — no connections, skip unknown provider
  • wiregui/tasks/oidc_refresh.py — test successful refresh, failure with notification, disable_vpn_on_oidc_error

Auth modules (85-92% covered):

  • wiregui/auth/oidc.py (87%) — register providers, get_client, load from config
  • wiregui/auth/webauthn.py (85%) — registration/authentication options
  • wiregui/auth/session.py (90%) — no-password, disabled, nonexistent user
  • wiregui/auth/saml.py (0%) — needs mock SAML IdP metadata + response parsing
  • wiregui/auth/webauthn.py — test verify_registration, verify_authentication with mock credential data

Pages (0% — requires E2E testing):

  • Consider Playwright or NiceGUI's testing utilities for E2E page tests

Logging (done)

  • Loguru configured (wiregui/logging.py), no print statements
  • File logging to logs/ when WG_LOG_TO_FILE=true

Deployment

  • Dockerfile (multi-stage)
  • compose.prod.yml (app + postgres + valkey + caddy)
  • Health endpoint GET /api/health
  • First-run CLI setup command
  • README.md