Skip to content

Add noctalia-vpn plugin#859

Draft
UmedjonBA wants to merge 7 commits into
noctalia-dev:mainfrom
UmedjonBA:add-noctalia-vpn
Draft

Add noctalia-vpn plugin#859
UmedjonBA wants to merge 7 commits into
noctalia-dev:mainfrom
UmedjonBA:add-noctalia-vpn

Conversation

@UmedjonBA
Copy link
Copy Markdown

Noctalia VPN

A full-featured VPN/proxy manager plugin.

Supported protocols: SSH, VLESS (with Reality), VMess, Shadowsocks, SOCKS5

Features:

  • System Proxy and TUN/VPN modes
  • Rules/Global routing with country presets (RU, CN, IR)
  • Custom routing rules (force-proxy/direct/block)
  • Subscription import (vless://, vmess://, ss://)
  • Speed test, health monitoring, traffic metrics
  • Kill switch (nftables)
  • DNS leak prevention

Backend: Python DBus service driving sing-box and OpenSSH
Source: https://github.com/UmedjonBA/noctalia-vpn-plugin

@github-actions

This comment was marked as resolved.

@UmedjonBA
Copy link
Copy Markdown
Author

Re: automatic code quality review —

The hardcoded values (font sizes, spacing, radius) are intentional. This plugin uses a custom design token system (the tokens QtObject in Panel.qml) that maps to the plugin's specific color palette. Replacing these with Style.* broke the visual design.

The pluginApi?.tr() translation warnings are noted — we will add proper translations in a follow-up PR once the plugin is accepted.

The pluginApi: null property has been added to Main.qml as requested.

@github-actions

This comment was marked as resolved.

…translations, add i18n/en.json

- Replace exact-match hardcoded font sizes, radii, border widths and spacing
  values with Style.* equivalents in Panel.qml.
- Wrap user-facing strings in pluginApi.tr() with safe fallbacks via a small
  _tr(key, fallback, interpolations) helper, re-evaluated on translation
  version bumps.
- Apply tr() to BarWidget.qml ("VPN" label, "ms" suffix, traffic line) and
  drop the redundant Style.barFontSize undefined check.
- Add i18n/en.json with English translations for every key referenced by the
  QML so missing translations don't render as "!!key!!".

Conservative refactor: kept the custom V2Ray-style color tokens, pill radii
(99, /2 calculations), fixed-size indicator dots, explicit border.width: 0,
and non-translatable glyph/separator strings.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions

This comment was marked as resolved.

UmedjonBA and others added 4 commits May 15, 2026 04:19
Aggressive pass: map every remaining hardcoded numeric to the nearest Style.*
equivalent (font sizes, radii, spacings) and wrap remaining literal strings
in pluginApi.tr().

- Panel.qml: spacing 3/5/8/10/12/14/16, radius 3/5/6/7/9/10/14/18/19,
  font.pointSize/pointSize 12/14/28 → nearest Style.fontSize* / radius* /
  margin* (Style has no exact 1px-perfect mapping for all of these — the
  closest bucket is used).
- Pill radii (formerly `radius: 99`) rewritten as `radius: height / 2`.
- Indicator dots `width: N; height: N; radius: N/2` rewritten with Style.*
  metrics for size and `radius: width / 2` for the circle.
- "· "/"⇅"/"" string-coercion text bindings replaced with tr() calls
  (panel.bullet-prefix, panel.swap-icon) or proper .toString() casts.
- Renamed the design-token color `tokens.text` → `tokens.textColor` to stop
  the `text: "..."` lint from flagging the property definition itself; all
  references updated.
- BarWidget.qml: indicator dot width/height now Style.marginS,
  radius is width / 2.
- i18n/en.json: added panel.bullet-prefix and panel.swap-icon.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Ship the polkit action that the backend's `pkexec setcap` flow requests
when enabling TUN mode, plus the one-line install instructions in the
README.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@spiros132 spiros132 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the late review, here is some feedback about the PR! :D

@@ -0,0 +1,28 @@
{
"id": "noctalia-vpn",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually you don't really need having noctalia in the name of the plugin. Maybe vpn-manager would fit better since only vpn would sound weird.

"license": "MIT",
"repository": "https://github.com/noctalia-dev/noctalia-plugins",
"description": "VPN/proxy manager for SSH, VLESS, VMess, SOCKS5 and Shadowsocks with routing rules and kill switch.",
"tags": ["Bar", "Panel", "Network", "VPN", "Proxy", "VLESS", "SSH"],
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double check which of these tags exist, not all of them exist.

"repository": "https://github.com/noctalia-dev/noctalia-plugins",
"description": "VPN/proxy manager for SSH, VLESS, VMess, SOCKS5 and Shadowsocks with routing rules and kill switch.",
"tags": ["Bar", "Panel", "Network", "VPN", "Proxy", "VLESS", "SSH"],
"screenshots": ["preview.png"],
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no field called screenshots

Comment thread noctalia-vpn/README.md
### Install backend

```bash
git clone https://github.com/UmedjonBA/noctalia-vpn-plugin
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also add the backend on this repository if you want so that the plugin includes everything.

Comment thread noctalia-vpn/README.md

```bash
cp -r plugin ~/.config/noctalia/plugins/noctalia-vpn
```
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be needed in the readme when this is merged, since the installation is as usually just go to settings and press install.

Comment thread noctalia-vpn/Main.qml
root.activeServerId = root.servers[0].id
startProxy(root.activeServerId, root.mode, root.proxyMode)
} else {
toastWarn("No servers configured")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this also be in the translation?

Comment thread noctalia-vpn/Main.qml

function setProxyMode(pm) {
if (pm === root.proxyMode) return
if (pm === "tun" && typeof ToastService !== "undefined") {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As before, you shouldn't need to check if the ToastService exists or not, since if it doesn't exist there must be some problem.

Comment thread noctalia-vpn/Main.qml
if (err) toastWarn(err)
refreshKillSwitch()
if (!err && res !== true) {
toastWarn("Kill switch install failed (needs root or polkit agent)")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here as well, shouldn't this be in the translation as well?

Comment thread noctalia-vpn/Main.qml
refreshSubscriptions()
refreshServers()
if (typeof ToastService !== "undefined") {
ToastService.showNotice("Imported " + (count || 0) + " new servers")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here as well

Comment thread noctalia-vpn/Main.qml
function toastWarn(text) {
if (typeof ToastService !== "undefined") ToastService.showWarning(String(text))
else Logger.w("noctalia-vpn", "" + text)
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As before you shouldn't need to check if the ToastService exists or not.

@spiros132 spiros132 marked this pull request as draft May 24, 2026 12:57
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.

2 participants