Skip to content

Add andrewdems/vpn-manager - #632

Merged
Noctalia-CI merged 16 commits into
noctalia-dev:mainfrom
AndrewDemsDS:add-vpn-manager
Sep 7, 2026
Merged

Add andrewdems/vpn-manager#632
Noctalia-CI merged 16 commits into
noctalia-dev:mainfrom
AndrewDemsDS:add-vpn-manager

Conversation

@AndrewDemsDS

@AndrewDemsDS AndrewDemsDS commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Plugin

  • Id: andrewdems/vpn-manager
  • New plugin
  • Update to an existing plugin (version bumped in plugin.toml)

What it does

A NetworkManager VPN front end: connect, disconnect, import, inspect, edit and
delete VPN connections of any protocol NetworkManager supports, plus optional
auto-connect whenever the active network is not on a trusted list.

The plugin implements no VPN protocol itself. It drives nmcli, so it supports
whatever the installed NetworkManager VPN plugins support (OpenVPN, WireGuard,
IPsec/IKEv2, L2TP, PPTP, SSTP, OpenConnect, vpnc), and any connection created
with nmtui or nm-connection-editor shows up automatically.

In the panel:

  • Connections - connect/disconnect, star one as the auto-connect default,
    expand a row for its live details, or delete it behind a confirmation.
  • Details - state, device, IPv4/IPv6 address and gateway of an active
    connection, read from nmcli, with Edit connection opening a full editor.
  • Add VPN - a graphical file picker, a typed path, or pasted config text.
  • Trusted networks - the SSIDs and wired connection names where the VPN
    should stay off.

Auto-connect is off by default. When enabled, it connects the starred VPN once
the active network has been off the trusted list for N consecutive polls
(default 3, to avoid flapping while roaming), and disconnects it again on a
trusted network - but only if this plugin was what started it, so a VPN brought
up by hand is left alone.

Three behaviours worth flagging for review, each written to match how the shell
actually behaves:

  • The panel closes when the file picker opens. A panel left open holds a
    layer-shell grab and the picker renders but cannot be clicked. udiskie and
    screen-toolkit close their panels for the same reason. The chosen file
    imports on its own.
  • A failed import is not lost with the toast. Since the panel is closed by
    then, every failure is recorded; reopening the panel lands on the import form
    with the reason and the offending path, and the protocol selector to retry.
  • A connection whose password is agent-owned cannot be activated headlessly.
    NetworkManager reports No valid secrets when no secret agent is running,
    which is the normal state of a bare Wayland session. A manual connect is then
    rerun as nmcli --ask connection up in a terminal so the password can be
    typed; auto-connect never opens a terminal and reports the cause instead.

External dependencies

Every subprocess is spawned as an argv list rather than a shell string, except
the two noted below.

Command Why
nmcli everything: listing, connect/disconnect, delete, import, details, and the per-connection vpn.service-type / SSID lookups
kdialog the file picker for Add VPN
zenity file picker fallback where kdialog is absent
nm-connection-editor Edit connection
nmtui-edit editor fallback, opened in a terminal

None of the last four is required: without a picker you can type a path or paste
the config, and without an editor the button reports that none was found. Both
are probed with commandExists first.

The two shell strings. The picker is spawned detached (cmd > tmp 2>/dev/null &)
because it outlives the runAsync call, and the nmtui-edit fallback goes
through runInTerminal. Both interpolate only a plugin-generated temp path or a
UUID validated against ^[0-9a-fA-F-]+$; no user-supplied string reaches either.

Filesystem writes. Trusted networks, the chosen default VPN, and which VPN
the plugin auto-started live as JSON under noctalia.pluginDataDir() - runtime
lists edited from the panel, so they do not belong in manifest settings. The
picker writes the chosen path to pick.tmp in the same directory, consumed and
deleted on the next tick. Importing pasted text writes it to a temp file for
the duration of the nmcli connection import call and deletes it immediately on
every exit path, so a config's secrets do not linger outside NetworkManager's
own storage. Importing from a path writes nothing.

Network calls. None. All state comes from NetworkManager through nmcli.

Privileges. None beyond what nmcli itself does; connecting or deleting a
system connection goes through NetworkManager's own polkit policy, the same one
nm-applet and nmtui use. The plugin never escalates.

Secrets. Only UUIDs are passed to nmcli; no password is ever placed on a
command line, and nothing sensitive is written to the log - the byte count of a
pasted config is logged, never its body.

Testing

Run on Arch with NetworkManager 1.58 against a WireGuard and an OpenVPN
connection. Exercised for this submission: the bar widget in both states, the
panel opened by clicking the widget and with
noctalia msg panel-toggle andrewdems/vpn-manager:panel, the connection list
with its per-connection controls, expanding a row for details, the file picker
opening and being cancelled (the browse button re-enables within seconds rather
than waiting out the timeout), a failed import surfacing its reason and path in
the panel, and the No valid secrets path opening a terminal prompt.

Parsers are additionally covered by offline tests against captured real output:
nmcli -t detail lines including indexed keys (IP4.ADDRESS[1]) and IPv6
values containing colons, and the secret-error matcher against the three
messages NetworkManager actually emits, checked not to swallow unrelated
failures.

noctalia plugins lint is clean.

  • Tested on Niri
  • Tested on Hyprland
  • Tested on Sway
  • Tested on another compositor:
  • Noctalia version tested against: 5.0.0_beta.10
  • Plugin API level: 24

Screenshots / Videos

The panel, with the connection list, per-connection controls and the import
entry point (connection names are placeholders here):

VPN Manager panel

Checklist

Ready-for-review requirement: Every box in this section must be checked. If any statement is not true, keep the
pull request as Draft. An explanation does not replace a required check.

  • The directory name matches the part of id after the / in plugin.toml exactly.
  • It ships plugin.toml, README.md, thumbnail.webp, and translations/en.json.
  • README.md follows the
    README template, documents
    every entry id and dependency, and includes exact panel IPC commands and launcher prefixes where applicable.
  • thumbnail.webp is present and relevant; for a new plugin I created it with the thumbnail generator, and for an update I regenerated it with the generator if the visual identity or user-facing appearance changed.
  • version follows semver and is bumped in this PR; plugin_api is the oldest API level this plugin requires.
  • Every non-English translation in this PR uses a locale supported by Noctalia core, and I can read, write, and
    understand that language well enough to review and maintain it (no unreviewed machine/LLM translations).
  • I did not edit catalog.toml; CI generates it.
  • This PR touches exactly one plugin directory.

Code review attestation

Plugins run as trusted, unsandboxed Luau in the user's session. Confirm:
Ready-for-review requirement: Every attestation below must be checked.

  • The code is readable and not obfuscated, minified, or generated.
  • It does not download and execute remote code.
  • Every network call, filesystem write, and spawned process is something the description above accounts for.
  • I have the right to publish this code under the license declared in plugin.toml.

Every user-facing string goes through noctalia.tr(), resolving against translations/en.json; only product names in the import-type list (OpenVPN, WireGuard, Cisco) are left untranslated. This plugin was written with AI assistance, disclosed in an AI assistance section in its README.md; every line is hand-reviewed, plain readable Luau.

A NetworkManager VPN front end: connect, disconnect, import and delete
VPN connections of any protocol, with optional auto-connect whenever the
active network is not on a trusted list.

Assisted-by: AI
@ItsLemmy

ItsLemmy commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

The description now contains the required template structure.

@ItsLemmy
ItsLemmy marked this pull request as draft September 5, 2026 07:09
@AndrewDemsDS
AndrewDemsDS marked this pull request as ready for review September 5, 2026 07:14
@AndrewDemsDS
AndrewDemsDS marked this pull request as draft September 5, 2026 09:21
@AndrewDemsDS
AndrewDemsDS marked this pull request as ready for review September 5, 2026 10:11
@AndrewDemsDS
AndrewDemsDS marked this pull request as draft September 5, 2026 10:15
…, false auto-disconnect failure, unguarded select index

Assisted-by: AI
@AndrewDemsDS
AndrewDemsDS marked this pull request as ready for review September 7, 2026 12:51
@AndrewDemsDS

Copy link
Copy Markdown
Contributor Author

Added a launcher provider (`/vpn`): "Open VPN Manager" plus a connect or disconnect action for every configured connection, fuzzy-matched by name. It's read-only against `vpn.connections` and writes to the existing `vpn.cmd` channel `service.luau` already handles - no new dependencies, filesystem writes, or network calls. Documented in the README's Usage section and the Plugin table's Entries/Launcher Prefix rows.

Tested end-to-end with real keystroke input: root query shows "Open VPN Manager", typing a connection's name surfaces the right connect/disconnect action, and activation dispatches through the existing command channel correctly.

@Noctalia-CI
Noctalia-CI merged commit e1310a7 into noctalia-dev:main Sep 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants