Teleport client for omarchy using v2rayA and wireguard-tools under the hood

README.md

Teleport for Omarchy #

A Quickshell bar plugin with v2rayA and WireGuard connection controls.

Connected to Teleport for macOS, sharing its animated portal icon and connection-control concept.

Features #

  • Compact, theme-aware portal icon, animated when either connection is active.
  • Popup with separate v2rayA and WireGuard sections.
  • Muted labels and status subtitles, right-aligned values, and compact controls.
  • v2rayA token entry and private local storage.
  • WireGuard config discovery and per-config switches.
  • Five-second polling, explicit desired-state changes, and duplicate-action protection.

The plugin id remains io.github.mchernigin.v2raya to preserve existing bar placement and credentials.

Requirements #

  • Omarchy's plugin-capable Quickshell shell and shared qs.Ui components.
  • Python 3 (standard library only) and xdg-open.
  • v2rayA listening at http://127.0.0.1:2017, with a valid session token.
  • For WireGuard: wireguard-tools, systemd, pkexec, and a graphical polkit agent.

Built against this machine's Omarchy shell and v2rayA 2.5.9 API. Other versions are not yet verified.

Installation #

cd ~/Projects/Omarchy/v2rayA-plugin
plugin_dir="$HOME/.config/omarchy/plugins/io.github.mchernigin.v2raya"
mkdir -p "$plugin_dir"
cp manifest.json Widget.qml TeleportIcon.qml WireGuardSection.qml StatusRow.qml backend.py wireguard.py "$plugin_dir/"
omarchy-shell shell rescanPlugins

Allow discovery to finish, then enable and position the widget:

omarchy plugin enable io.github.mchernigin.v2raya --section right
omarchy bar move io.github.mchernigin.v2raya --before omarchy.network

To deploy later edits, repeat the copy. If hot-reload retains an old QML component, run omarchy restart shell. Restarting the shell does not stop the proxy or WireGuard services.

Bar controls #

  • Left click: open/close Teleport.
  • Right click: toggle v2rayA only, when its status is known.
  • Hover: see connection details.

The portal animates if v2rayA or a managed WireGuard tunnel is active. A warning dot indicates unavailable, paused, or externally managed states. The icon does not guarantee remote reachability.

v2rayA #

Paste a session token into the masked field and click Validate & save token. This performs a read-only status check; it does not activate the proxy. Raw tokens and Bearer … values are accepted.

To obtain your token, open the authenticated v2rayA web interface, open browser developer tools, select Network, reload, and inspect the Authorization request header of /api/touch. Treat the value like a password; do not share it or commit it.

The section has:

  • An on/off switch controlling the proxy core.
  • Server, transparent-proxy mode, and rule-mode values from the API.
  • Web UI, Refresh, and Log out buttons, each with an icon and text.

Log out requires a second confirmation click. It deletes the plugin's saved token without disconnecting the proxy or logging out the browser session. Enter a new token afterward to sign back in.

The plugin uses POST /api/v2ray and DELETE /api/v2ray. It does not stop the v2rayA systemd service, alter server selection, or edit routing settings. Unknown status is not displayed as “off.” Failed actions remain visible after polling recovers.

Token storage #

$XDG_DATA_HOME/omarchy/plugins/io.github.mchernigin.v2raya/credentials.json

Default when XDG_DATA_HOME is unset:

~/.local/share/omarchy/plugins/io.github.mchernigin.v2raya/credentials.json

The directory is 0700, the file is 0600, and replacement is atomic. Tokens enter the helper through stdin, never command-line arguments, and are not returned in responses, logged, or stored in shell.json.

Storage is plaintext protected by filesystem permissions, not encryption/keyring storage. Other code running as your user can read it. Expired tokens require replacement. The API address is fixed to loopback, proxy environment variables are bypassed, and redirects are not followed.

WireGuard #

Click Load configs to list /etc/wireguard/*.conf. Because the directory is normally root-only, a graphical administrator prompt authorizes filename discovery. The plugin never reads config contents or private keys. Names are cached in wireguard.json alongside plugin data with 0600 permissions.

Each config appears as a compact name-and-switch row. Use Reload configs after adding/removing files, and Refresh to update status without administrator permission. Hover a switch for additional status information.

Switches use systemctl start/stop wg-quick@<name>.service through a graphical administrator prompt. They do not enable services at boot. Interfaces outside the matching wg-quick systemd service are marked externally managed and cannot be toggled here. Service activation does not establish that a handshake succeeded.

Untested full-tunnel configs can change default routes and interrupt connections. Starting WireGuard while v2rayA is running requires extra confirmation. The plugin never automatically disconnects another connection, inspects AllowedIPs, or rewrites routing policy. Review and test your configs yourself.

For activation failures, inspect journalctl -u wg-quick@<name>.service. Review output for sensitive data before sharing it.

Validation #

python3 -m unittest discover -s tests -v

Tests use temporary directories and a mock v2rayA API or mocked system commands. They never toggle real connections. They cover credential permissions, validation, authentication failures, state confirmation, discovery, safe config names, duplicate operations, and unmanaged interfaces.

Optional QML syntax checks:

/usr/lib/qt6/bin/qmlformat -n Widget.qml >/dev/null
/usr/lib/qt6/bin/qmlformat -n WireGuardSection.qml >/dev/null
/usr/lib/qt6/bin/qmlformat -n TeleportIcon.qml >/dev/null
/usr/lib/qt6/bin/qmlformat -n StatusRow.qml >/dev/null

Live activation/deactivation is left to the user, since disconnecting can interrupt the development/API connection. No actual proxy or WireGuard toggles were performed during development.

Disable #

omarchy plugin disable io.github.mchernigin.v2raya

Disabling/removing the plugin does not change connection states. Use Log out before disabling to remove the token, or delete its credential file manually. Plugin data is not automatically removed with the source.

Source layout #

File Purpose
manifest.json Plugin metadata
Widget.qml Main popup, bar icon, and v2rayA controls
TeleportIcon.qml Animated portal icon
WireGuardSection.qml WireGuard discovery and switches
StatusRow.qml Shared label/value layout
backend.py v2rayA API and credential storage
wireguard.py WireGuard filename discovery and service controls
tests/ Mock-based backend tests