Repository navigation
Compatibility
| Axis | Version |
|---|---|
pluginApi |
1.0.0 (unchanged) |
| Capabilities | all 1.0.0, unchanged |
| Manifest schema | + optional sync.paths (additive) |
bundleFormat |
1.0.0 (unchanged) |
| Vault schema | 5 (was 4) |
| Vault envelope | 1 (unchanged) |
| Audit schema | 1 (unchanged) |
syncProtocol |
1.0.0 (new) |
| Sync crypto suite | 1 (new) |
| Sync record schemas | 1 for every record type (new) |
Sync state schema (data/sync/state.db) |
1 (new) |
Server store schema (xqsyncd.db) |
2 (new; a database from a pre-release build at 1 is copied aside as xqsyncd.db.v1.bak and migrated on first start) |
BREAKING
Vault schema 4 → 5. The first unlock after the upgrade copies the vault aside as vault.age.v4.bak and rewrites it as schema 5, which records the team vault each synced item belongs to. xQuakShell 1.4 and older refuse a schema 5 vault rather than open it and drop that field; to go back, restore the backup. Nothing about keys or passwords changes in the upgrade, and no passphrase is asked for.
Added
Sync between your devices, through a server you run. Settings → Sync connects a vault to xqsyncd, the self-hosted sync server that ships with this release for Windows and Linux (admin guide). Everything is encrypted and signed on the device; the server cannot read a host name, a password or a folder name, cannot alter a record, and cannot roll a vault back or add a device without every device noticing. Signing in is one screen: paste the connect link the server prints (https://host:8443/#fp=…) and the application checks the server's fingerprint itself, or enter the address and compare the fingerprint it shows with the one its operator gave you. Fingerprints, the device list, the sync password and Shred now sit under Advanced. A second device signs in with the sync password; every other device announces it and can remove it, which replaces the account keys. Removing a device you do not recognise can replace the sync password in the same step, so whoever typed it to add that device cannot sign it in again; left empty, the device is still removed at once and the password can be changed afterwards. Edits made offline on two devices merge field by field; the same credential edited on both becomes a conflict you resolve, and the sync indicator turns red and says so until you do. A change from another device that would weaken this one — turning off plugin signature checks, loosening the lockout, replacing or deleting a known host key, a peer identity or a key — waits for this device's master password, in a team vault as in the personal one. Sync is off until you turn it on, and needs a master password of at least 12 characters; a new sync password needs at least 12 too.
Teams. A team vault shares connections with other accounts on the same server. You invite a person after comparing their fingerprint over another channel, and give them the admin, editor or viewer role. Nobody has to read the fingerprint aloud: each person copies their contact code (xqs-member:name#fp=…) from the Teams page and sends it in a chat or a mail, and pasting it into the invite field — or into a member's row, to verify an existing member — makes the application compare it with the key the server gives for that account. A code that does not match leaves nothing to invite. Being invited is not joining: the invited person sees the invitation on their Teams page with the inviter's fingerprint, and nothing from the team reaches their connections until they accept it; declining leaves the team. A team accepted on one device is joined on all of them. Credentials are not shared: each member binds their own key or password to a team connection, and a member who changes a shared connection's address suspends everyone else's binding to it until they confirm. Removing a member moves the team to new keys, so nothing written afterwards opens for them. Each team appears in the connection tree under its own row, below your personal connections; create a connection or folder there to keep it in the team, or move one in and out from the tree's menu, the editor's "Stored in" field, or by dragging it onto another vault. A move into a team links your own key or password for you, and a move back takes that link with it as the connection's credential. A viewer sees the team's connections and can use them, but cannot add, change or move them.
The sync server as a container image. Every release also publishes xqsyncd to ghcr.io/teoritty/xqsyncd for linux/amd64 and linux/arm64, tagged with the version, its minor and major lines and latest; a pre-release gets its own version only. The image is the static server binary on a distroless base, running unprivileged with its data on a volume. xqsyncd serve --init initialises an empty data directory before serving and leaves an initialised one untouched, so a container needs one command; a Compose file and the backup routine for a containerised server are in the admin guide.
Trash and Shred now. Deleted synced items stay in a trash for the vault's retention window (30 days by default) and can be restored. After that they are purged from the server, and within one and a half more windows the keys that covered them are destroyed on every device. Shred now does all of it at once.
Plugins from your other devices. A plugin installed on one device is offered on the others — never installed by itself. Installing the offer runs the usual download, checksum, signature and consent steps and then checks that the build is exactly the one your other device installed; a release replaced behind the same tag is refused unless you accept it as a new version. A plugin may declare files of its own (sync.paths in plugin.json) that follow you between devices; they are written only while the plugin is stopped, and the Sync page shows what waits for a restart.
Search across terminals. Ctrl+Shift+F turns the sidebar into a search view, the way an editor's search replaces its file tree: a query box with match case, whole word and regular expression switches, the terminals to search, and every match grouped by terminal underneath. Enter and Shift+Enter walk the matches, bringing each one's terminal to the front with the match scrolled into view and selected; Escape hands the keyboard back to the terminal and brings the connection tree back exactly as it was. Every match is tinted inside the terminals too, the current one brighter. The search covers the whole scrollback, finds a word even where the terminal wrapped it onto the next line, and follows new output while the panel is open. It works in SSH sessions, local terminals and plugin terminals alike. It starts on the terminal you were in, and opening it with text selected searches for that text.
The binding is not Ctrl+F, because inside a terminal Ctrl+F already belongs to the program running there — moving the cursor in bash and zsh, paging down in vim, less and man. It can be rebound in Settings → Hotkeys, to Ctrl+F as well for anyone who prefers it.
Typing into several terminals at once. Alt+M outlines every open terminal in red and puts a number on it; click a terminal or press its number to choose it — it turns green — and press Enter. From then on whatever you type into one of the green terminals reaches all of them, pastes included, and the green outline stays on them so it is always clear what a keystroke will touch. Typing into a terminal outside the group reaches that terminal alone. A terminal hidden behind another tab is chosen by its tab, which carries the same number. In the picker, A chooses all and N none; Escape leaves everything as it was. Alt+M again changes the group. Alt+Shift+M ends it — Escape cannot, because it belongs to whatever runs in the terminal — as do Stop in the top bar and Enter in the picker with fewer than two chosen; the rest of the time Alt+Shift+M reaches the terminal as usual. A terminal that closes leaves the group, and a group down to one ends on its own. On SSH sessions each command is recorded in the audit log of every server it reached.
All three shortcuts are listed on the start screen with the others, and can be changed in Settings → Hotkeys, where a clash with another shortcut is reported before saving.
Duplicating a saved connection. Right-click a connection and choose Duplicate: an exact copy appears directly beneath it, named " - copy" in the interface language ("- копия" in Russian), then "- copy 2", "- copy 3" as those are taken — duplicating a copy numbers it rather than adding another suffix. With several connections selected, each is duplicated. The copy uses the same saved passwords and keys, as two connections set up by hand to share them would; everything it owns is its own, so editing or deleting either one leaves the other as it was. Port forwards are copied with ids of their own, and a plugin connection's secret fields are copied into the vault under the new connection rather than shared with the original.
Reconnecting on its own after a dropped connection. When an established session loses its connection, the Reconnect button starts counting down with a spinner in it and reconnects when it reaches zero: after 5 seconds, then 15, then 30, then every 60 seconds until the connection is back. Clicking the button reconnects straight away, Stop retrying ends the countdown and leaves the tab as it is, and closing the tab ends it too. The reconnected session keeps its tab's place, its tile and the focus, and by default the terminal output stays: the scrollback and the screen are kept, a dim "Reconnected" line marks where the new session begins, and the new shell carries on below. Only the output is kept - programs that were running on the server do not survive the dropped connection. It works the same for SSH and for protocol plugins. Only a lost connection is retried: a shell you left with exit stays closed, and a connection that failed at the first attempt — a wrong password, an unreachable host — is not retried on a timer. Both the automatic reconnect and keeping the terminal can be turned off in Settings → Network → Reconnect.
Agent forwarding with keys from the vault. A shell can now lend your keys to the server you opened it on, so from there git pull from a private repository, ssh to the next host, and scp or rsync to another server work without copying a key anywhere. It takes two yeses: a key allows forwarding in the Key Manager (off for every key, including existing ones), and a connection picks which of those keys it forwards. Every signature then asks first, naming the server that asks, the key, the user it logs in as and where the signature will be used - the destination host by its known_hosts name when the server's OpenSSH proves it (8.9 and later), and a plain warning when it does not. A connection can also offer "allow for N minutes", which covers only the same key to the same proven host in the same tab. Only logins are signed: the server cannot get a git commit or anything else signed, cannot add, remove or lock keys, and cannot reach the agent after the tab closes or the vault locks. Jump hosts and plugin connections never get the agent. Every request, allowed or refused, is in the audit log by key fingerprint. Tabs with a forwarded agent carry a key mark. The vault schema does not change; a vault saved by an older build loses these settings, which leaves forwarding off.
Fixed
Built with Go 1.26.10, which fixes eleven vulnerabilities in the standard library - in net/http, net/textproto and crypto/tls (GO-2026-6603, 6605, 6607 to 6613, 6617) - that the update check, the plugin catalogue and the embed tunnels reach, and in crypto/x509, which still trusted a set of compromised intermediate certificates (GO-2026-6809) and is reached by every TLS connection the application makes, sync included. go.mod now requires Go 1.26.10 itself, so a build picks up a patched standard library because the repository says so, not because the build machine happened to have one, and an older compiler fetches it or refuses to build.
Connecting to a server by its IPv6 address works. The address and the port were joined without brackets, so 2a0f::2 with port 22 became the unparseable 2a0f::2:22: a direct connection, a jump host, the ping check and the host key prompt all failed or showed a wrong address for an IPv6 server. Every address is now written in its standard bracketed form, [2a0f::2]:22, however it was typed in the editor, so the same server also no longer asks to be trusted again for each way its address was spelled. Host key verification is as strict as before.
A connection whose key or password is missing says so. A connection naming a key or password that is not in the vault - deleted, or never imported - failed in the tab with "Authentication failed", which points at the server for something that was never sent to it. The tab now says the key or password is not in your vault and to set one in the connection's editor.
Starting xQuakShell a second time no longer risks losing what you changed in the first. Two copies started from the same folder each opened the vault and saved their own version of it, so whichever saved last quietly undid the other: a key generated in one window was gone the next time the other one saved a connection, with no error in either. Starting it again now brings the window that is already open to the front and does nothing else. If that window cannot be found — the other copy is still starting, or still saving on its way out — the new one waits a few seconds for it, and then says xQuakShell is already running and closes.
Copies in different folders are separate installations with separate vaults and still run side by side. The folder is claimed through the operating system, so a crash, a forced close or a USB stick pulled out mid-session never leaves it marked as in use. On read-only media, where nothing can be saved anyway, xQuakShell starts as before.
What's Changed
- feat(startup): allow one process per data folder by @teoritty in #114
- feat(connections): duplicate a saved connection from the tree by @teoritty in #116
- feat(terminal): search across terminals and type into several at once by @teoritty in #115
- feat(sessions): automatic reconnect after a lost connection by @teoritty in #117
- feat: agent forwarding signed by vault keys by @teoritty in #120
- feat: state sync between devices and teams through a self-hosted server by @teoritty in #123
- Feat/sync by @teoritty in #125
- Feat/sync by @teoritty in #128
- fix(sync): team credentials, conflict indicator, retry and device removal; IPv6 addresses by @teoritty in #132
- build(deps): bump github.com/labstack/echo/v4 from 4.15.3 to 4.16.3 in the go-security group across 1 directory by @dependabot[bot] in #126
- build(deps): bump the github-actions group with 4 updates by @dependabot[bot] in #131
- build(deps): bump the codemirror group across 1 directory with 4 updates by @dependabot[bot] in #129
- build(deps-dev): bump the npm-minor-patch group across 1 directory with 5 updates by @dependabot[bot] in #127
- build(deps): bump the go-minor-patch group across 1 directory with 4 updates by @dependabot[bot] in #130
- build(deps-dev): bump qs from 6.16.0 to 6.17.1 in /frontend in the npm-security group across 1 directory by @dependabot[bot] in #133
- Docs/changelog 1.5.0 catch up by @teoritty in #134
Full Changelog: v1.4.1...v1.5.0