Skip to content

Releases

v1.5.0

Choose a tag to compare

@github-actions github-actions released this 03 Nov 21:59

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

Read more

Nightly nightly-20261104-5194445

Pre-release

Choose a tag to compare

@github-actions github-actions released this 04 Nov 07:23

Automated build of main at 5194445cf365287df180a508fd809c560b60e54f, published as nightly-20261104-5194445.

This release is replaced by the next nightly. Its download links stay valid only
until then; for a build that keeps its files, use a tagged release.

What's Changed

  • 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: de71d5e...5194445

v1.4.1

Choose a tag to compare

@github-actions github-actions released this 26 Sep 15:28

Compatibility

Axis Version
pluginApi 1.0.0 (unchanged)
Capabilities all 1.0.0, unchanged
Manifest schema unchanged
bundleFormat 1.0.0 (unchanged)
Vault schema 4 (unchanged)
Vault envelope 1 (unchanged)
Audit schema 1 (unchanged)

BREAKING

Nothing.

Changed

The Plugins screen no longer shows a Marketplace tab. Its page said only "Coming soon", so the tab was a click that led nowhere. It returns once there is a marketplace to browse; installing from a source or by URL is unaffected.

Fixed

Connecting with a passphrase-protected key asks for the passphrase. Such a connection could never succeed: instead of a field to type into, a system message box said a custom dialog was needed, and the tab then failed with "Authentication failed". A dialog now asks for the key's passphrase while the connection waits, and the key is cached afterwards as its own policy in the Key Manager says. A mistyped passphrase asks again and says it was wrong, up to three tries. Cancelling the dialog, or running out of tries, ends the connection with "Key passphrase was not entered" or "Wrong key passphrase" rather than a generic failure that points at the server. Closing the tab while the dialog is open takes the dialog down with it. The same applies to keys on jump hosts.

Deleting the folder you are in takes you up a level instead of showing an error. Both file panes — the server and the local one — reload the folder on screen after a delete, and when that folder was the one just deleted the reload failed and opened the error dialog with a "not found" message, leaving the pane empty. The pane now moves to the nearest parent folder that still exists, skipping any that went with it, and says nothing: you asked for the folder to go. The same happens when the folder disappears some other way — deleted from a terminal, from the other pane, or by another program — the next time the pane reloads. A folder that was only expanded in the tree, not open, is simply dropped from it.

Other listing failures, such as a folder you have no permission to read, are still reported, now in the pane's own header like a mistyped path rather than in the dialog over the whole window.

Opening a folder you cannot read keeps you where you were. Double-clicking such a folder in either file pane, or going up into one, moved the pane into it anyway: the path bar showed the folder, the listing was empty, and the header said "permission denied". The pane now changes folder only once the new one has been read, so it stays on the folder you were in, with its listing still on screen and the reason in the header. Typing the path of such a folder already behaved this way and still does. Expanding one with its arrow leaves it closed instead of open and empty.

Cancelling a transfer no longer reports an error. Pressing cancel on an upload, a download or a copy in the Transfers panel stopped it and marked the row "Cancelled" — and then opened the error dialog as well, with "context canceled" or whatever the interrupted write happened to fail with. The same happened when cancelling while a dropped folder was still being scanned. A cancelled transfer now ends quietly with its row saying "Cancelled"; a transfer that actually fails is reported exactly as before.

What's Changed

  • build(deps): bump actions/upload-artifact from 4 to 7 in the github-actions group by @dependabot[bot] in #111
  • build(deps): bump the golang-x group with 3 updates by @dependabot[bot] in #107
  • build(deps): bump the codemirror group in /frontend with 4 updates by @dependabot[bot] in #108
  • build(deps-dev): bump the npm-minor-patch group across 1 directory with 6 updates by @dependabot[bot] in #110
  • fix: 1.4.1 - ask for an encrypted key's passphrase on connect by @teoritty in #112
  • fix(files): stay in the current folder when the one opened cannot be read by @teoritty in #113

Full Changelog: v1.4.0...v1.4.1

v1.4.0

Choose a tag to compare

@github-actions github-actions released this 19 Sep 18:10

What's Changed

Full Changelog: v1.3.0...v1.4.0

v1.3.0

Choose a tag to compare

@github-actions github-actions released this 19 Sep 17:41

What's Changed

  • feat(plugins,i18n): standalone Plugins screen and a translated interface by @teoritty in 84b4dbf
  • fix(embed,loghub): tunnel burst sizing and three debug-log-window fixes by @teoritty in 246328f
  • feat(localshell): a local terminal as the third kind of tab by @teoritty in 3ac95af
  • docs: catch the documentation up with 1.3.0 by @teoritty in 32b00c2

Full Changelog: v1.2.1...v1.3.0

v1.2.1

Choose a tag to compare

@github-actions github-actions released this 19 Sep 17:32

What's Changed

  • feat(plugins): install plugins from GitLab as well as GitHub by @teoritty in 55f3123

Full Changelog: v1.2.0...v1.2.1

v1.2.0

Choose a tag to compare

@github-actions github-actions released this 19 Sep 17:01

What's Changed

  • fix(build): stop an unstamped build reporting version 1.0.0 by @teoritty in #83
  • fix(security): require the master password to weaken plugin trust by @teoritty in #84
  • fix(security): verify every plugin release asset and resolve its entry by path by @teoritty in #85
  • fix(security): take the host key decision from the session, not the caller by @teoritty in #86
  • fix(security): gate non-loopback forward binds and verify the real bound address by @teoritty in #88
  • fix(security): pin the SSH algorithm policy and refuse weak host keys by @teoritty in #87
  • fix(security): rate-limit master password attempts by @teoritty in #89
  • fix(security): stop a plugin raising its own resource limits by @teoritty in #90
  • fix(security): name the filesystem paths a plugin asks for at install time by @teoritty in #91
  • fix(security): revoke a plugin's capability grants when it is uninstalled by @teoritty in #92
  • fix(security): refuse a plugin download redirect that drops TLS by @teoritty in #93
  • fix(security): remove a plugin's files when its install is refused by @teoritty in #94
  • fix(security): require checksums when loading a user-installed plugin by @teoritty in #95
  • fix(security): strip terminal control characters from plugin log lines by @teoritty in #96
  • fix(security): bound how many plugin requests the host handles at once by @teoritty in #97
  • fix(security): stop an unrecognised apply target widening a recursive chmod by @teoritty in #100
  • fix(security): refuse remote entry names that lie about what they are by @teoritty in #98
  • fix(security): bound how far a remote directory walk will recurse by @teoritty in #101
  • fix(security): bound the transfer concurrency and timeout settings by @teoritty in #102

Full Changelog: v1.1.0...v1.2.0

v1.1.0

Choose a tag to compare

@github-actions github-actions released this 13 Aug 10:31
9e9e840

What's Changed

  • fix(plugins): verify the installed tree, not the staging tree by @teoritty in #72
  • feat(plugins): deny plugin processes the shared USER surface on Windows by @teoritty in #73
  • docs: state the plugin trust model and stop calling it a sandbox by @teoritty in #74
  • fix(plugins): delete the second, unvalidated install path by @teoritty in #75
  • fix(plugins): give each plugin instance its own temp directory by @teoritty in #76
  • feat(plugins)!: remove localEmbedServer (Mode B) by @teoritty in #77
  • refactor(plugins): put a childProcess seam under the spawner by @teoritty in #78
  • feat(plugins): report the sandbox mode a plugin process is running behind by @teoritty in #79
  • feat(plugins): confine plugin processes with Landlock (Linux) and AppContainer (Windows) by @teoritty in #80
  • feat: SSH key manager with an encrypted in-vault key store by @teoritty in #81
  • fix(build): report the version in the Windows executable, and drop the committed mutate.exe by @teoritty in #82

Full Changelog: v1.0.0...v1.1.0

v1.0.0

Choose a tag to compare

@github-actions github-actions released this 10 Aug 15:27

What's Changed

  • Freeze the released contracts and fix what the freeze exposed by @teoritty in #69
  • Add the update check the support policy depends on by @teoritty in #70
  • refactor: unify the settings dialog visuals by @teoritty in #71

Full Changelog: nightly...v1.0.0

v1.0.0-rc.3

v1.0.0-rc.3 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 07 Aug 12:17
529d99f

What's Changed

  • Chore/cross platform makefile by @teoritty in #26
  • Fix/file pane selection dismiss by @teoritty in #27
  • fix(tree): derive creation target from selection and delete the whole… by @teoritty in #28
  • Fix/surface tabs and discovery delete by @teoritty in #30
  • Fix/GitHub install ships plugin bundle by @teoritty in #31
  • Feat/adr 015 plugin UI surfaces by @teoritty in #29
  • Feat/adr 015 plugin UI surfaces by @teoritty in #32
  • Refactor/redaction depth and file pane split by @teoritty in #33
  • fix: close the four open code scanning alerts and gate CodeQL by @teoritty in #35
  • Refactor/context usage hygiene by @teoritty in #34

Full Changelog: v1.0.0-rc.2...v1.0.0-rc.3