Skip to content

v0.1.101

Choose a tag to compare

@vlinx-io vlinx-io released this 15 Aug 11:04
· 12 commits to main since this release

Remote access

  • Pick which address the share link uses — Tailscale addresses now show up. The address list only accepted the classic private IPv4 ranges, so VPN meshes such as Tailscale, which assign addresses from the carrier-grade NAT range (100.64.0.0/10), were silently dropped from the remote-access panel and the pairing link — even though the server was already reachable through them. These addresses are now listed, with VPN tunnels ranked last so they never become the default. A new IP selector in the panel — shown before starting and while running — lists every candidate with its interface name and marks VPN tunnels; choosing one moves its URL to the front and regenerates the pairing link with exactly that host, so the link you copy works on a device that can only reach this machine over the VPN, without editing the URL by hand. A QR code under the pairing link lets a phone scan it directly. The choice is remembered; if the chosen interface disappears, the panel falls back to automatic without forgetting it. The server itself is unchanged and keeps listening on all interfaces. Choosing an address that only appeared after the server started — a VPN that connected later, say — now also updates the copied URL and the QR code immediately, instead of only the pairing link until the next restart; VPN tunnels are ranked behind LAN addresses on every platform, an address picked while the server is stopped drives the very first pairing link after it starts, and overlapping link regenerations can no longer overwrite a newer link with an older one.

  • Sharing survives a restart. The pairing token used to be regenerated every time the server started, so quitting and reopening VelaTerm silently invalidated every shared link, and every phone had to be paired again. The token, the paired devices and the device blocklist are now saved to an owner-only file in the data directory: a previously paired device reconnects with its saved URL after a restart — the access password stays a required second factor — and a revoked device stays revoked. VelaTerm also remembers that sharing was on: quit while the server is running and the next launch brings it back on the same port, in the desktop app and on a headless --serve server alike; stop it yourself and nothing starts automatically. If the automatic start fails, because the port is taken for example, the app starts normally and the remote-access panel shows the reason. The port field now remembers the port you actually used instead of resetting to the default, and "Regenerate link" remains the explicit kill switch: it issues a new token at once, invalidates every old link, and overwrites the saved state. The access password itself is never written to disk — only a memory-hard hash (Argon2id) is stored.

Security

  • A paired device can no longer manage the sharing itself. Any paired browser could invoke the same management commands as the desktop app — create a new pairing link (which also clears the device blocklist), list and revoke other devices, or stop and reconfigure the server — and the settings store handed every client the complete settings map, including the memory-hard hash of the access password and the autostart settings the next launch reads. Management commands are now reserved for the desktop app and the Electron shell; the settings API filters the remote-access keys and the Gitea token out of every read from a paired device and rejects writes to them. A paired device keeps what pairing is for — its terminal sessions with full shell access — but it can no longer read the password verifier, invite or evict other devices, or redirect the port the next start uses. Commands that read, write or delete stored secrets — the Gitea token and remembered host passwords — are refused for a paired device as well, and the path-taking commands — reading, previewing, writing, creating, renaming and deleting, and likewise showing a file's git diff or picking the folder a repository is cloned into — resolve symbolic links first and reject paths inside VelaTerm's own data directory, where the pairing state and keys live, while every other path keeps working so remote file browsing and editing stay intact. A test enumerates every remote command that accepts a path, so a new command cannot slip past this check unnoticed. When one of these protections rejects a request, the browser now shows a properly translated message instead of a raw English error.

  • Revoking a device or regenerating the link now also survives the dual-instance setup. On a headless --serve server with automatic start enabled, two server instances each held their own copy of the saved pairing state and wrote it back whole: a revocation or a fresh pairing link made through one could be silently undone by the other. All instances in one process now share a single pairing state per data directory: revocation and rotation take effect everywhere immediately, and exactly one writer persists the file, which remains the source of truth across real restarts.

  • Repeated failed logins are throttled. Checking the access password uses Argon2id, deliberately expensive — and anyone who can reach the port can try. After five failed attempts from one address, further attempts are rejected for a minute before any hashing work happens, and the hashing itself now runs outside the server's event loop with a hard cap on concurrent verifications: a flood of wrong passwords can no longer saturate the server with memory-hard hashing or slow it down for devices that are already connected. The throttle lives in memory and resets with the server; the pairing token and the password remain the actual barrier. The limit is now shared by every server instance using the same data directory, so the dual-instance --serve setup no longer doubles the attempt budget, and an attempt is reserved before the password check starts, so parallel requests from one address cannot slip under the limit. A throttled browser now sees a dedicated rate-limit message on the login screen instead of being told the password was wrong, and being throttled is no longer remembered like a wrong password: once the pause is over, the next attempt goes through again without reloading the page. An attempt abandoned midway — the tab closed while the password was still being checked — now frees its reserved slot immediately instead of counting against the address for the rest of the minute, and a successful login releases only its own reservation instead of clearing the address's whole record: behind a shared network address, one person signing in correctly no longer resets an attacker's attempt budget, and recorded failures expire only with their minute.

  • Secrets on disk and in logs are handled more carefully. The file with the pairing state and the end-to-end encryption key are now created owner-only readable from the start instead of being restricted after the first write, and the session database — which holds the password hash — is restricted to the owner as well. A headless --serve no longer prints the pairing link's long-lived secret into logs: when output is not a terminal the link is withheld and a hint is printed instead; --print-pairing explicitly opts back in. The device registry is capped at 32 entries with length-limited names so a paired client cannot grow the saved file without bound, and when saving a revocation or a new pairing link fails, the error now reaches the caller instead of a log line. Automatic start no longer replaces a server already started by hand, and a stale autostart error clears once you stop the server yourself.

Fixed

  • Pairing can be managed from the Electron shell. Creating a pairing link, listing paired devices and revoking a device existed only as desktop (Tauri) commands; the WebSocket dispatcher used by the Electron shell and browser clients answered "Unknown command", leaving the remote-access panel broken there. All three commands now go through the same core functions on both transports, so the two cannot drift apart, and regression tests cover the new dispatch routes — including creating a real pairing link against a running local server.