Skip to content

tailnet: Tailscale status & control - #373

Merged
ItsLemmy merged 5 commits into
noctalia-dev:mainfrom
rylos:add-tailnet
Aug 16, 2026
Merged

tailnet: Tailscale status & control#373
ItsLemmy merged 5 commits into
noctalia-dev:mainfrom
rylos:add-tailnet

Conversation

@rylos

@rylos rylos commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Plugin

  • Id: rylos/tailnet
  • New plugin
  • Update to an existing plugin (version bumped in plugin.toml)

This replaces #72, which I opened as rylos/tailscale before
davemhammer/tailscale landed. Renamed to rylos/tailnet to leave that id
alone, and reworked after @ItsLemmy's review; the launcher prefix is /tn.

What it does

Tailscale status and control from the bar: whether you are connected, who else
is on the tailnet, and the handful of actions you would otherwise reach for the
CLI to do.

  • Bar widget — connection state, with the peer count.
  • Panel — this node's name and IP with copy buttons, connect/disconnect,
    the admin console, a row of five tailscale set toggles (shields-up, accept
    routes, Tailscale SSH, advertise as exit node, LAN access while on an exit
    node), any tailscale status health warnings, then the peer list: online
    first, filterable, with per-row copy IP, copy name, ping, SSH, and an
    exit-node toggle on peers that offer one.
  • Launcher (/tn) — every host, Enter copies its IP.
  • Taildrop — a header button to receive pending files.
  • Desktop widget and a shortcut entry.

External dependencies

  • tailscale — every action shells out to it; the binary can be pointed
    elsewhere in settings (tailscale_bin) for non-standard installs.
  • ssh — only when the SSH button on a peer row is used; it opens in the
    configured terminal.
  • xdg-user-dir — to find the Downloads directory for Taildrop.
  • gio / xdg-open — to open the admin console.

All declared in dependencies.

Testing

In daily use on niri against my own tailnet (18 hosts, mixed
Linux/Android/OpenWrt), which is where the panel, launcher and bar widget in the
screenshots come from.

  • Panel: connect and disconnect, the five preference toggles, exit node set and
    clear, ping, SSH, copy IP and name, Taildrop receive, the admin-console
    button, and the filter — exercised over the course of using it.
  • Launcher: /tn, filtering and Enter to copy, re-checked for this PR.
  • Reload with the service restarted, and with tailscale stopped, to see the
    stopped and error states.
  • noctalia plugins lint tailnet and
    python3 .github/workflows/scripts/validate-plugins.py are clean.

Since #72: the numbered callback slots @ItsLemmy asked about are gone —
every peer row passes a closure that captures its own peer (plugin_api 9), so
the list is no longer capped at 24 rows and there are no ~96 numbered globals.
curl is gone, the openers are declared, the poll is generation-counted so a
late subprocess cannot overwrite a newer snapshot, and the health box, the
preference toggles, tailscale_bin and the admin-console button are new.

One note on tailscale ping: it exits 1 with "direct connection not
established" when a peer only answers over a DERP relay, even though the pongs
arrive. The plugin reports that as reachable-via-relay rather than as an error.

  • Tested on Niri
  • Tested on Hyprland
  • Tested on Sway
  • Tested on another compositor:
  • Noctalia version tested against: 5.0.0 (97917d9ca07e)
  • Plugin API level: 9

Screenshots / Videos

Panel — the toggle row is under the node card; Tailscale IPs are masked:

Panel

Launcher, /tn (IPs masked, one hostname replaced):

Launcher

Bar widget — the Tailscale dots, next to the Syncthing arrows:

Bar widget

Checklist

  • 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.
  • I created thumbnail.webp with the thumbnail generator.
  • 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:

  • 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.

rylos and others added 5 commits August 15, 2026 17:50
Tailscale status and control for Noctalia v5, ported from the v4 tailscale
plugin: bar widget, panel with an online-first peer list, headless poller,
up/down control-center shortcut, /tn launcher provider and a desktop
widget. The core flow is host access — fuzzy-search the tailnet and copy a
peer's IP or short name in one gesture.

The plugin is called tailnet rather than tailscale because
davemhammer/tailscale already owns the tailscale/ directory and the /ts
launcher prefix; the two share no ids, state keys or prefix, and the README
says what each one focuses on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three things the freshly merged davemhammer/tailscale does that this plugin
did not, plus the fix its poller design suggested:

- Every poll carries a generation number. Callbacks from a superseded poll
  are dropped, and a poll still pending after 20s is treated as lost so the
  next one can take over — a poll spans several subprocesses, and one
  callback that never fires used to wedge the poller for good.
- The panel surfaces the daemon's own Health warnings, which explain most
  'connected but nothing works' situations.
- A row of toggles drives tailscale set: shields-up, accept-routes, the SSH
  server, advertise-exit-node and exit-node-allow-lan-access. Reading them
  back needs tailscale debug prefs, whose output also carries the node's
  private key; the plugin keeps the five booleans and discards the rest,
  which the README states.
- tailscale_bin for installs that keep the binary off PATH, and an admin
  console button (admin_url overrides the default).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The repo README asks contributors for translations/en.json alone — other
locales come back through i18n.noctalia.dev as maintainer commits, and no
it.json exists anywhere in the catalog. The Italian translation stays in
the local install.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The repo validator rejects en.json keys that are not single lowercase
segments, so pref.shieldsUp and its four siblings never would have passed
CI. Each toggle now carries its own translation key alongside the
tailscale set flag, since neither can be the snapshot field name.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The old one still read "Tailscale" and showed a panel header from before
the rename. Regenerated with the official generator, with the current
panel (preference toggle row included) and the addresses masked.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ItsLemmy
ItsLemmy merged commit a1961ce into noctalia-dev:main Aug 16, 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.

2 participants