mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-25 08:08:39 +00:00
v26.10.0
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0149260926 |
v26.10.0 (#1573)
* feat: optional auto reconnect for dropped ssh sessions (#1321) * feat: tighter compact host rows with inline tags (#1325) * feat: bring back the alert feed homepage widget * feat: pick target terminals for snippets again * feat: share button on connection tabs again * feat: recent servers list when linking the desktop app * feat: list the fixed app shortcuts in keyboard shortcuts * fix: translate hardcoded toasts and drop em dashes from ui text * fix: translate leftover hardcoded ui text * fix: toolbar files button opens at the shell directory again * fix: show the remote desktop note under connection origin again * chore: openapi docs for host metrics manager routes * chore: openapi docs for undocumented routes and fix two broken doc blocks * fix: declare terminal.listSessions use for snippet targets * revert: drop the alert feed widget, the notification center replaces it * feat: restore the alert feed homepage widget * fix: quick connect fills the host address from plugin auth editors like tailscale * fix: docker container logs use the base background color * chore: fix dependabot alerts and apply safe npm upgrades * fix: proxmox stats tab reads its switch from plugin host settings * fix: count plugin ssh logins toward host status and stop showing reachable as a warning * fix: hide the built-in source pill in the host editor * fix: host status shows online when the port answers and only warns on a failed login * fix: drop the top border on the split layout menu * fix: host defaults tabs sit where the host editor puts them and skip plugins with nothing to default * fix: remote desktop auth method labels and notes for vnc and telnet * fix: add description to the web endpoint enable switch * fix: crowdin sync and pretranslate for plugin locales, run plugin tests on release * feat: move telemetry out of core into a usage statistics plugin * fix: recent activity card no longer cuts off host names early * fix: remove duplicate top border on split screen menu * fix: redesign session log player controls, fix tooltip overlap and drop dot separators * fix: file manager and sftp text showing raw i18n keys * fix: onboarding rerunning for existing users after upgrading * fix: fleet detail buttons overflowing the sidebar * fix: session log player scrolling off screen horizontally * fix: network graph lag when adding hosts and on every render * fix: maintenance index name, shared host read only view and badge polling * fix: meeting tabs, workspace tab merging, rail panels as tabs and general ui lag * chore: update release notes * fix: remote desktop tabs reconnecting on every tab switch * fix: build plugin sdk before linting in release workflow * chore: lint, format, and bump version to 2.9.0 * chore: skip crowdin pre-translate in release for now * chore: switch to calver, bump version to 26.10.0 * fix(sso): send the PKCE verifier for GitHub login (#1534) * fix(remote-desktop): clip cursor overflow without disabling zoom (#1535) * test(remote-desktop): preserve sessions beyond one hour (#1536) * fix(hosts): expose controls for overflowing editor tabs (#1537) * fix(hosts): expose controls for overflowing editor tabs * test(hosts): mock resize observation in admin panel tests * fix(status): skip TCP probes while host sessions are active (#1538) * fix(database): batch session activity persistence (#1539) * docs(api): describe cookie and API key authentication (#1540) * fix(snippets): repair missing note column on SQLite upgrades (#1541) * fix(docker): retain stored SSH credential association (#1543) * fix(file-manager): render transfer toasts without plugin hooks (#1546) * feat(hosts): keep compact row actions inline (#1547) * fix(auth): allow retrying unavailable second-factor interfaces (#1549) * fix(auth): reject unresolved legacy identity provisioning (#1550) * fix(desktop): ship the plugin SDK inside Mac App Store builds (#1551) * fix(tmux): pass full session info to the terminal's session picker (#1552) The tmux.sessions service reduced detected sessions to bare names, but the picker reads session.name, so every entry rendered blank and selecting one sent an empty name, which created a new session instead of attaching. * fix(ssh): read force keyboard-interactive as a boolean when resolving hosts (#1553) The column holds the text "true"/"false", and the resolver passed it through as-is. The string "false" is truthy, so the password provider treated every saved host as forced keyboard-interactive and left the password out. Jump hops are built straight from the resolved host, so a password hop whose server doesn't offer keyboard-interactive failed with "All configured authentication methods failed". * fix(database): batch database saves for bulk host writes (#1554) * fix(database): yield to the event loop between database saves Each SQLite save serializes and encrypts the whole database into new buffers, which V8 only releases once the event loop turns. Boot migrations and bulk host import write plugin settings per host and per key in an awaited loop, so hundreds of full copies piled up and the container ran out of memory. * fix(database): save once per bulk host write instead of once per row On SQLite every repository write force-saves the whole database. Boot plugin data migrations, host defaults materialization and the bulk host routes write one row per host per setting, so they serialized and encrypted the full database hundreds of times in a row. DatabaseSaveTrigger.batched wraps a function so force saves made while it runs collapse into a single save when it finishes. Nested scopes fold into the outer one, and work that outlives its scope saves normally. * feat(hosts): add instance-wide predefined tag suggestions (#1548) * feat(hosts): add shared predefined tag suggestions * style(hosts): format tag catalog routes * test(database): advance fake timers past the save yield (#1555) * fix(i18n): complete Simplified Chinese core and plugin translations (#1542) * fix(i18n): complete Chinese onboarding and navigation labels * fix(i18n): translate remaining Chinese core and plugin interfaces * fix(i18n): translate host editor tab overflow controls * fix(i18n): use consistent Chinese fleet terminology * fix(i18n): localize hardcoded controls and plugin views * fix(i18n): translate built-in homepage widget catalog * test(homepage): follow translated timezone placeholder * fix(i18n): translate plugin-provided homepage widgets * fix(i18n): localize feature settings section titles * fix: 2.8 oidc accounts unable to sign in after upgrading (#1381) * fix: plugins losing the saved ssh login when copying a host (#1391) * fix: fleets, proxmox and automations not getting the host sudo password * fix: host imports running a defaults pass and metrics restart per host (#1384) * fix: high cpu from status probes and full database saves on every sample (#1300) * fix: user data export freezing the server (#1393) * fix: op:// secret references rejected as ssh keys on credentials (#1394) * fix: slow file deletes from the trash lookups on every delete (#1390) * fix: add openapi docs and error handling to host tag routes * test: compare download stream buffers directly so it stops timing out * chore: drop em dash from host row comment * chore: update release notes for 2.9.1 * feat(ai): add remote coding agent sessions for SSH hosts (#1545) * feat(ai): add host coding agent sessions with configured providers * Wait for agent initialization and shutdown before resuming sessions * Keep Pi transcripts outside its configuration migration directory * Cover Pi session directory separation * Update core permission catalog expectations for remote agents * Install remote agent runtimes from verified official sources * Wait for the identity panel credentials request in its test * Add scoped SSH forwarding setup for remote agents * Verify forwarding setup through a fresh SSH connection * Bound the SSH forwarding verification timeout * Return the forwarding verification callback result * fix(ai): support agent send and interrupt keyboard shortcuts * feat(ai): add agent queues attachments worktrees and session recovery * fix(ai): handle deleted workspace paths and duplicate status events * fix(ai): accept file references in root workspaces * fix(ai): refresh paused queues after agent errors * fix(ai): store agent sessions in plugin tables and lock down OpenCode Agent sessions lived in a single ctx.kv value holding up to 2000 events, which passed the 256 KB kv limit within minutes of a normal run. Every save after that failed, so history, nativeId, queue and archive changes were lost and every event wrote a kv audit row. Sessions, events and queued prompts now have their own tables. A running agent appends events in batches at most once a second with a lazy save; session and queue changes save immediately. Prompts, drafts and stored event text are capped in UTF-8 bytes to fit MySQL TEXT. kv:own is no longer requested. The OpenCode server now requires a per-session password, so other local accounts on the host cannot drive the agent through its loopback port. * test(upgrade): include agent session tables in the reviewed upgrade SQL * test: run the posix-only ai agent tests on linux only * fix: restore space to add host tags and redesign predefined tags editor * fix: host key silently accepted when the host is missing from the database (#1397) * fix: clearer host login failed status label and fix its translations (#1396) * chore: add host key and status label fixes to release notes * fix: sso and ldap provider dialogs overflowing the screen and using mismatched toggles * chore: increment ver * fix(audit): store plugin entries with no acting user as a null user_id (#1556) Plugin audit entries written outside a request used the literal "system" as user_id. That column references users.id, so the insert was refused (Postgres logs it as an FK violation) and the entry was silently dropped. Write null instead and keep "system" / plugin:<id> in username. * chore: add sso/ldap dialog and audit log fixes to release notes * fix: tunnels and host settings missing from shared hosts on desktop * fix: remote desktop logins missing from shared hosts on desktop * fix: simplify host status to online/offline and keep it live without a refresh * chore: sync Crowdin translations for 2.9.1 * fix: build docker frontend/backend natively to stop arm64 hang, build sdk before openapi * chore: rework plugin readmes, json changelogs and standalone repo setup * chore: move plugins to their own repos and drop the plugins folder * chore(deps-dev): bump globals (#1560) Bumps the dev-minor-updates group with 1 update: [globals](https://github.com/sindresorhus/globals). Updates `globals` from 17.12.0 to 17.13.0 - [Release notes](https://github.com/sindresorhus/globals/releases) - [Commits](https://github.com/sindresorhus/globals/compare/v17.12.0...v17.13.0) --- updated-dependencies: - dependency-name: globals dependency-version: 17.13.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-minor-updates ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: drop the 2.8 columns, moves and aliases kept for a downgrade * chore: remove the 2.9 finish list * feat: port the demo redesign with Manage and Settings tabs * chore: pin the plugin signing key and use TERMIX_PAT in workflows * chore: rename the plugin sdk to @termix-ssh/plugin-sdk * feat: add a publish workflow for the plugin sdk * chore: cleanup * fix: harden plugin sockets, public routes, prebuilt binaries and login fallback * chore: add a 2.9.1 upgrade check for postgres and mysql * fix: stop full database rewrites on every plugin db call * fix: stop full database rewrites on every plugin db call * fix: restore release video link in 2.9.2 notes * fix: use TERMIX_PAT in workflows * feat: pin the official plugins in the docker image * feat: implement plugin management UI * feat: shared list kit, settings search, rail order and ui consistency fixes * chore: run all workflows on blacksmith runners * chore: split dashboard quick actions into quarters and drop host status row lines * fix: open plain settings from the profile button, navigation only from the rail menu * feat: consent check, developer mode file installs, install counts and issue links for plugins * feat: row detail options for hosts and credentials, open manage from sidebar header * fix: unify connection failure screens and rebuild stale local plugins * feat: dev:all runner for web, electron and docker with plugin hot reload, termix-plugin dev * chore: clean up npm scripts, make npm run dev the full runner and drop concurrently * fix: settle quick connect panel loads in tests so they stop flaking * fix: stop the dev runner rebuilding plugins when windows reports file reads as changes * chore: update the plugin sdk ui export snapshot * feat: full screen onboarding with a plugin picker and per step versions * feat: changelog.md release notes, v tags, plugin release notes and videos * chore: add temp changelog * chore: release the plugin sdk as 1.0.1 * feat: show only the latest plugin release notes and cap long ones * chore: publish docker images to termix-ssh on ghcr and termixssh on docker hub * chore: rename docker hub token secret to DOCKERHUB_BUGATTIGUY527_TOKEN * chore: renew mac app store provisioning profile * chore: drop docs job from release workflow * chore: run sdk publish on github hosted runner * chore: remove dependabot * chore: general file cleanup * chore: rewrite readme and taglines for plugin-based termix * chore: drop optional from ai plugin row in readme * feat: per-repo issue forms and prefilled bug reports instead of the support repo * feat: add report a bug link to the your account settings section * fix: open settings goes to the plugin's page and hides when it has none * feat: show plugin features from the manifest on the plugin page * fix: retry failed or blocked plugins on restart instead of leaving them disabled * fix: refresh plugin settings after enabling a plugin and match select and disabled bar styling * feat: request a feature link for plugins and use the bug form for unverified plugins * feat: beta channels for core and plugins with one stable/beta release workflow * fix(sync): resolve shared hosts for connect-only recipients (#1568) * fix(ssh): accept full passwords in keyboard-interactive prompts (#1569) * fix(ssh): normalize PKCS8 private keys before connecting (#1570) * fix(sidebar): reclaim space from hidden folder actions (#1571) * fix(hosts): display protocol credential usernames for non-SSH hosts (#1572) * fix: ask for a push/TOTP menu choice as text instead of a push confirm * fix: keep client tunnels open when one of their connections fails (Support#1417) * fix: send plugin form uploads to the plugin's route (Support#1406, Support#1420) * fix: add missing sync ids to old snippet tables so snippets sync again (Support#1419) * fix: start the macOS local terminal when the shell or spawn helper is broken (Support#1418) * fix: refresh cached plugin translations when only a translation changes (Support#1411) * fix: import sshpk as a default so the backend starts under ESM * feat: add a plugin sdk changelog and tag sdk releases, release 1.0.2 * fix: drop the ./ from the plugin sdk bin path so npm stops warning * chore: name core and sdk releases Termix vX and Plugin SDK vX * fix: lock the desktop backend to the app's own origin (GHSA-frx8-qqgg-7j7g) * fix: trust devices by a server-issued token, not a client header (GHSA-6gr3-r7jx-wfmh) * fix: keep global settings out of non-admin data exports (GHSA-v3vp-ffww-q7pw) * fix: refuse sessionless tokens and keep the secrets file owner only * feat: add TRUSTED_PROXIES to choose who nginx takes client IPs from (GHSA-7j7m-85p4-2cpm) * chore: standardize repo setup files * chore: remove unused knip config * chore: add LICENSE and use Node 24 from .nvmrc * chore: update translation guidance, only en.json needs editing * feat: plugin docs and env manifest fields, in-app docs links, SDK 1.0.3 * fix: header separators render as borders and center the status divider * fix: lighter confirm prompt overlay to stop lag when it opens * fix: put plugin-only tailwind classes in core's utilities layer, SDK 1.0.4 * fix: dev stages local plugin builds on paths with spaces and builds them with the local SDK * fix: keep plugin tailwind variants in core's layer so overrides keep their order * fix: manage tab refetches before giving up on a host or credential it cannot find * fix: tab buttons like share show as soon as the terminal is ready * fix: folder rows no longer nest action buttons inside a button * fix: repin remote-desktop after its 1.0.0 re-release * chore: repin bundled plugins after the 1.0.0 re-release * fix: recent activity rows use the same separator as host status * feat: upload banner * fix: reject expired sessions on sockets, api key desktop links and 192.0.0.0/24 * fix: plugin sdk 1.0.5 with optional ui peers, safer timeouts and validate checks * fix: bundled plugins path for source runs and backend cleanup * fix: load plugin strings on guest pages, plugin wording and drop unused keys * fix: beta version stamping, homebrew cask bump, dockerignore and readme platforms * docs: 26.10.0 changelog * fix: current version in the version route example * chore: stop pushing the release- docker tag * feat: remove the donation reminder and its column * docs: note the removed donation reminder * fix: link server wizard always opens inline, onboarding card grows for it * feat: plugin text columns are longtext on mysql so they are not capped at 64KB * fix: linked desktops keep a plugin when its update from the server fails * chore: default mobile download links to v tags * fix: sso only accounts cannot sign in with a passkey or other local method * feat: note in the plugins tab that community plugins are coming * fix: plugin validate accepts beta versions * chore: pin bundled plugins rebuilt on sdk 1.0.5 * fix: skip locale fetches for disabled plugins * fix: show translated names for the built-in admin and user roles * fix: show host keys as SHA256 fingerprints like ssh-keygen * fix: host editor scrolls to a newly enabled protocol section * fix: pass the host to dashboard host row plugins * fix: plugin cards show dividers between author, version and category * fix: refuse plugin archives with paths outside the plugin folder * fix: theme picker shows one dropdown arrow * fix: read activity and audit times as UTC so they show in local time * fix: show the real error for a wrong password or code on plugin sign in * fix: explain that external sign in needs Auto-create external accounts * fix: clear leftover plugin unpack folders at startup * fix: shared session links open for guests again * fix: share panels no longer list yourself as a share target * fix: a host update without an auth type keeps the saved password * fix: guest pages get the tab ids a guest plugin registers * fix: refuse a 2.8 database, which already had a plugins table * fix: dashboard cards fit on a phone * fix: desktop setup no longer asks to secure the local account * fix: the desktop app keeps the desktop layout in a narrow window * fix: the desktop app shows the rail and no phone bar in a narrow window * fix: name every sync type in the Sync panel and pluralize the link prompt * chore: update the sdk ui snapshot for the host key dialog * fix: turning a plugin back on restarts the plugins it turned off * fix: quick connect asks to trust the host key * fix: revoking a manager also removes the shares they handed out * fix: database export works for users with TOTP on (#1426) * fix: rdp and vnc logins use the folder credential when none is picked (#1429) * fix: imported hosts get status checks without a restart * fix: re-pin bundled plugins after 1.0.0 re-release * fix: re-pin bundled plugins after search bar fix * chore: update app icon to smaller version * chore: round the readme logo, keep square source for app icons * feat: grid columns and table view in the plugins tab * fix: match the host key copy button to the fingerprint height * fix: use brand color for toggles in sync settings * fix: re-pin bundled plugins after 1.0.0 re-release * fix: web terminals failing auth behind a proxy that rewrites Host (Support#651) * fix: re-pin snippets and termix-identity after 1.0.0 overwrite * fix: re-pin file-manager after 1.0.0 overwrite * fix: re-pin file-manager after 1.0.0 overwrite * chore: remove Crowdin from the release workflow * chore: remove crowdin config --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: ZacharyZcR <zacharyzcr1984@gmail.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
3643e7af97 |
release-2.9.0 (#1532)
* fix(macos): skip single-instance lock in Mac App Store builds (#1454)
fix(macos): skip single-instance lock in Mac App Store builds
* Fix/backend optional sharp (#1455)
Fix/backend optional sharp
* feat: issue #1218 (#1218)
https://github.com/Termix-SSH/Support/issues/1218
* feat: Add option to use saved global custom theme under Connection Defaults (#1209)
https://github.com/Termix-SSH/Support/issues/1209
* feat: Add Fleet Sharing functionality to the UI (#1228)
https://github.com/Termix-SSH/Support/issues/1228
* feat: Add a list view to the Docker management page (#1237)
https://github.com/Termix-SSH/Support/issues/1237
* feat: issue #1264 (#1264)
https://github.com/Termix-SSH/Support/issues/1264
* feat: 2FA With FortiToken (#1288)
https://github.com/Termix-SSH/Support/issues/1288
* feat: Allow to disable showing paths to folders (#1274)
https://github.com/Termix-SSH/Support/issues/1274
* feat: Sync Network Graph between Desktop and Remote Server (#1245)
https://github.com/Termix-SSH/Support/issues/1245
* feat: Version Number on offline servers (#1291)
https://github.com/Termix-SSH/Support/issues/1291
* feat: Support OrbStack Docker socket path on macOS / Fix "Docker is not installed" on mac... (#1302)
https://github.com/Termix-SSH/Support/issues/1302
* feat: Sidebar host click should focus existing tab instead of opening a new connection (#1289)
https://github.com/Termix-SSH/Support/issues/1289
* fix: Search in side bar shows overlapping hosts when grouped by tags (#1303)
https://github.com/Termix-SSH/Support/issues/1303
* fix: I can't send a file on its own. (#1304)
https://github.com/Termix-SSH/Support/issues/1304
* fix: sudo password not autofilling (#1248)
https://github.com/Termix-SSH/Support/issues/1248
* fix: node-MainThread (#1300)
https://github.com/Termix-SSH/Support/issues/1300
* fix: Tunnel authentication with shared credentials (#1295)
https://github.com/Termix-SSH/Support/issues/1295
* fix: host key updates not syncing, metrics 404 race on first connect
Host key writes never bumped updatedAt so sync never picked them up.
Also retry the first metrics fetch briefly instead of failing right away.
* chore: increment ver
* feat: add category and icon fields to plugin manifest schema
* ci(deps): bump the github-actions group with 2 updates (#1458)
Bumps the github-actions group with 2 updates: [crowdin/github-action](https://github.com/crowdin/github-action) and [actions/github-script](https://github.com/actions/github-script).
Updates `crowdin/github-action` from 2 to 3
- [Release notes](https://github.com/crowdin/github-action/releases)
- [Commits](https://github.com/crowdin/github-action/compare/v2...v3)
Updates `actions/github-script` from 7 to 9
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v7...v9)
---
updated-dependencies:
- dependency-name: crowdin/github-action
dependency-version: '3'
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions
- dependency-name: actions/github-script
dependency-version: '9'
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* chore(deps-dev): bump the dev-patch-updates group with 13 updates (#1459)
Bumps the dev-patch-updates group with 13 updates:
| Package | From | To |
| --- | --- | --- |
| [@codemirror/commands](https://github.com/codemirror/commands) | `6.11.0` | `6.11.1` |
| [@codemirror/search](https://github.com/codemirror/search) | `6.7.1` | `6.7.2` |
| [@codemirror/view](https://github.com/codemirror/view) | `6.43.9` | `6.43.12` |
| [@testing-library/dom](https://github.com/testing-library/dom-testing-library) | `10.4.1` | `10.4.2` |
| [@testing-library/react](https://github.com/testing-library/react-testing-library) | `16.3.2` | `16.3.3` |
| [@testing-library/user-event](https://github.com/testing-library/user-event) | `14.6.5` | `14.6.7` |
| [@types/ssh2](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/ssh2) | `1.15.5` | `1.15.6` |
| [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react) | `6.1.0` | `6.1.1` |
| [cytoscape](https://github.com/cytoscape/cytoscape.js) | `3.34.1` | `3.34.3` |
| [eslint-plugin-react-refresh](https://github.com/ArnaudBarre/eslint-plugin-react-refresh) | `0.5.4` | `0.5.7` |
| [i18next](https://github.com/i18next/i18next) | `26.4.0` | `26.4.2` |
| [prettier](https://github.com/prettier/prettier) | `3.9.6` | `3.9.8` |
| [react-i18next](https://github.com/i18next/react-i18next) | `17.0.12` | `17.0.14` |
Updates `@codemirror/commands` from 6.11.0 to 6.11.1
- [Changelog](https://github.com/codemirror/commands/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codemirror/commands/commits)
Updates `@codemirror/search` from 6.7.1 to 6.7.2
- [Changelog](https://github.com/codemirror/search/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codemirror/search/commits)
Updates `@codemirror/view` from 6.43.9 to 6.43.12
- [Changelog](https://github.com/codemirror/view/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codemirror/view/commits)
Updates `@testing-library/dom` from 10.4.1 to 10.4.2
- [Release notes](https://github.com/testing-library/dom-testing-library/releases)
- [Changelog](https://github.com/testing-library/dom-testing-library/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/dom-testing-library/compare/v10.4.1...v10.4.2)
Updates `@testing-library/react` from 16.3.2 to 16.3.3
- [Release notes](https://github.com/testing-library/react-testing-library/releases)
- [Changelog](https://github.com/testing-library/react-testing-library/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/react-testing-library/compare/v16.3.2...v16.3.3)
Updates `@testing-library/user-event` from 14.6.5 to 14.6.7
- [Release notes](https://github.com/testing-library/user-event/releases)
- [Changelog](https://github.com/testing-library/user-event/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/user-event/compare/v14.6.5...v14.6.7)
Updates `@types/ssh2` from 1.15.5 to 1.15.6
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/ssh2)
Updates `@vitejs/plugin-react` from 6.1.0 to 6.1.1
- [Release notes](https://github.com/vitejs/vite-plugin-react/releases)
- [Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite-plugin-react/commits/plugin-react@6.1.1/packages/plugin-react)
Updates `cytoscape` from 3.34.1 to 3.34.3
- [Release notes](https://github.com/cytoscape/cytoscape.js/releases)
- [Commits](https://github.com/cytoscape/cytoscape.js/compare/v3.34.1...v3.34.3)
Updates `eslint-plugin-react-refresh` from 0.5.4 to 0.5.7
- [Release notes](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/releases)
- [Changelog](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/compare/v0.5.4...v0.5.7)
Updates `i18next` from 26.4.0 to 26.4.2
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v26.4.0...v26.4.2)
Updates `prettier` from 3.9.6 to 3.9.8
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.9.6...3.9.8)
Updates `react-i18next` from 17.0.12 to 17.0.14
- [Changelog](https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/react-i18next/compare/v17.0.12...v17.0.14)
---
updated-dependencies:
- dependency-name: "@codemirror/commands"
dependency-version: 6.11.1
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: "@codemirror/search"
dependency-version: 6.7.2
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: "@codemirror/view"
dependency-version: 6.43.12
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: "@testing-library/dom"
dependency-version: 10.4.2
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: "@testing-library/react"
dependency-version: 16.3.3
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: "@testing-library/user-event"
dependency-version: 14.6.7
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: "@types/ssh2"
dependency-version: 1.15.6
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: "@vitejs/plugin-react"
dependency-version: 6.1.1
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: cytoscape
dependency-version: 3.34.3
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: eslint-plugin-react-refresh
dependency-version: 0.5.7
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: i18next
dependency-version: 26.4.2
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: prettier
dependency-version: 3.9.8
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
- dependency-name: react-i18next
dependency-version: 17.0.14
dependency-type: direct:development
update-type: version-update:semver-patch
dependency-group: dev-patch-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* chore(deps): bump the prod-patch-updates group with 6 updates (#1461)
Bumps the prod-patch-updates group with 6 updates:
| Package | From | To |
| --- | --- | --- |
| [@tanstack/react-virtual](https://github.com/TanStack/virtual/tree/HEAD/packages/react-virtual) | `3.14.10` | `3.14.13` |
| [compression](https://github.com/expressjs/compression) | `1.8.1` | `1.8.2` |
| [jose](https://github.com/panva/jose) | `6.2.9` | `6.2.12` |
| [jszip](https://github.com/Stuk/jszip) | `3.10.1` | `3.10.2` |
| [socks](https://github.com/JoshGlazebrook/socks) | `2.8.9` | `2.8.10` |
| [undici](https://github.com/nodejs/undici) | `8.10.0` | `8.10.2` |
Updates `@tanstack/react-virtual` from 3.14.10 to 3.14.13
- [Release notes](https://github.com/TanStack/virtual/releases)
- [Changelog](https://github.com/TanStack/virtual/blob/main/packages/react-virtual/CHANGELOG.md)
- [Commits](https://github.com/TanStack/virtual/commits/@tanstack/react-virtual@3.14.13/packages/react-virtual)
Updates `compression` from 1.8.1 to 1.8.2
- [Release notes](https://github.com/expressjs/compression/releases)
- [Changelog](https://github.com/expressjs/compression/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/compression/compare/v1.8.1...v1.8.2)
Updates `jose` from 6.2.9 to 6.2.12
- [Release notes](https://github.com/panva/jose/releases)
- [Changelog](https://github.com/panva/jose/blob/main/CHANGELOG.md)
- [Commits](https://github.com/panva/jose/compare/v6.2.9...v6.2.12)
Updates `jszip` from 3.10.1 to 3.10.2
- [Changelog](https://github.com/Stuk/jszip/blob/main/CHANGES.md)
- [Commits](https://github.com/Stuk/jszip/compare/v3.10.1...v3.10.2)
Updates `socks` from 2.8.9 to 2.8.10
- [Release notes](https://github.com/JoshGlazebrook/socks/releases)
- [Commits](https://github.com/JoshGlazebrook/socks/compare/2.8.9...2.8.10)
Updates `undici` from 8.10.0 to 8.10.2
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v8.10.0...v8.10.2)
---
updated-dependencies:
- dependency-name: "@tanstack/react-virtual"
dependency-version: 3.14.13
dependency-type: direct:production
update-type: version-update:semver-patch
dependency-group: prod-patch-updates
- dependency-name: compression
dependency-version: 1.8.2
dependency-type: direct:production
update-type: version-update:semver-patch
dependency-group: prod-patch-updates
- dependency-name: jose
dependency-version: 6.2.12
dependency-type: direct:production
update-type: version-update:semver-patch
dependency-group: prod-patch-updates
- dependency-name: jszip
dependency-version: 3.10.2
dependency-type: direct:production
update-type: version-update:semver-patch
dependency-group: prod-patch-updates
- dependency-name: socks
dependency-version: 2.8.10
dependency-type: direct:production
update-type: version-update:semver-patch
dependency-group: prod-patch-updates
- dependency-name: undici
dependency-version: 8.10.2
dependency-type: direct:production
update-type: version-update:semver-patch
dependency-group: prod-patch-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* feat: add plugin worker protocol, bootstrap and plugin_storage table
Worker-side ctx proxy plus the message envelope it talks over, and the
per-plugin key/value table behind ctx.storage with migrations for all
three dialects. Rows cascade with the plugin so uninstalling leaves
nothing behind.
* feat: add allowlisted host view for plugins
An allowlist rather than a list of fields to strip. A blocklist rots:
the moment a new secret-bearing column lands on SSHHost it starts
leaking silently. With an allowlist a new field stays invisible to
plugins until someone adds it here on purpose.
* feat: add plugin loader with manifest validation and crash restart
Validates an unpacked plugin, spawns a worker_threads worker and manages
activate/deactivate/crash-restart with backoff, disabling after three
tries.
The crash counter clears only after a plugin stays up past a stability
window, not on activation. Clearing it on activation let a plugin that
activates cleanly and then dies restart forever, because every attempt
looked like the first.
loader.ts documents what the worker boundary does and does not buy you.
Node's permission model is process-wide, not per-worker, so it cannot be
enabled for plugin workers alone and is not claimed here.
* feat: route internal events through a shared plugin event bus
Formalises three ad-hoc publish paths that already existed:
automation-events.ts, metrics/automation-bridge.ts and the
hostSessionStatus singleton, now the host.session.status topic.
Both invariants the old code relied on are preserved. Publishing stays
fire-and-forget, so a failing subscriber cannot disturb a metrics poll or
a host delete. The automations engine subscribes to the bus at scheduler
start instead of being imported ad hoc, which keeps the edge
one-directional: automations may import repositories, hosts modules must
not import automations.
* feat: add plugin ctx broker with permission gate and audit
Every ctx call a plugin makes is checked against its granted
capabilities, performed by the main thread, then audited. A capability
must be both declared in the manifest and granted in the database, so
widening a plugin's reach always needs a new manifest the user can see,
never just a database row.
Audit attribution is set by the broker, not the plugin. The plugin
supplies only the details, so it cannot forge the actor.
fix: stop plugin workers seeing plaintext credentials in error messages
A failing ctx.ssh.connect handed the worker the raw error from deep in
the SSH stack, which embeds the connect config. The worker received, in
full:
Authentication failed for root using password <SECRET>
where <SECRET> was the host's real plaintext password. Any plugin with
ssh.exec could read it by catching the error, defeating the whole point
of the handle-based ssh API.
Errors crossing the boundary are now sanitised. Only PluginFacingError
and PluginPermissionError, both authored here, reach a plugin verbatim;
anything else becomes a generic message and the real one goes to the
log. This uses a marker class rather than matching on message text,
because matching would start leaking again the moment an upstream error
happened to contain a familiar phrase.
Covered by credential-isolation.test.ts, which plants a sentinel in
every secret-bearing field and asserts it appears in no message the
worker ever receives.
* feat: dispatch plugin HTTP routes and wire the runtime into startup
ctx.http.route now registers a real router on the /plugin-api/:pluginId
dispatcher, replacing its stub 404. A plugin never touches the Express
req/res: it gets a plain summary and returns a plain object, so sockets
and session cookies stay on this side of the boundary. Authorization and
cookie headers are not forwarded.
Plugins load last in the start-up sequence, after the server is fully
wired, and are terminated before the database on shutdown so none can
outlive it. A plugin that fails to load cannot stop the backend.
fix: correct the nginx plugin-api location regex
The pattern was ^/plugin-api/(/.*)?$, which needs a double slash and so
never matched /plugin-api/<id>/<route>. Every other block in the file
uses ^/prefix(/.*)?$. Left alone, plugin routes would work in dev and
404 only behind Docker.
* fix: update stale SFTP transfer test for single file destination paths
The test still asserted transferToHost was called with the destination
directory "/srv". Commit
|
||
|
+3 |
a64c956c5b |
release-2.6.1 (#1161)
* fix: preserve remote sync references (#1092) * fix: centralize outbound address validation (#1093) * fix: preserve architecture in unpacked ASAR path (#1094) * fix: allow sharing empty folders (#1096) * fix: preserve WoL broadcast address (#1097) * fix: deduplicate shared hosts (#1098) * fix snippet execution result handling (#1099) * fix SSH login alert delivery (#1100) * fix outbound DNS lookup callback shape (#1101) * fix OIDC verification for JWKs without alg (#1102) * fix file manager navigation after permission errors (#1103) * fix database persistence during container shutdown (#1104) * fix: persist host command history setting (#1107) * fix: recognize Windows terminal Tab events (#1109) * fix: recognize Windows terminal Tab events * style: format terminal key event test * fix: export repository user record (#1111) * fix: keep localhost database export same-origin (#1112) * fix: support Tailscale auth in tmux monitor (#1113) * fix: forward Android hardware keyboard keys (#1114) * fix: expose jump tunnels to guacd (#1115) * fix OIDC login with unverifiable ID tokens (#1117) verifyOIDCToken passed the raw id_token straight to jose's jwtVerify, which throws JWSInvalid when the token is not a three-segment compact JWS. Authentik issues an encrypted JWE id_token when the provider has an encryption key set, so the callback threw and every OIDC login failed with 'Invalid Compact JWS'. 2.5.0 hid this behind a catch-all that decoded the unverified payload; removing that fallback fixed the trust bug but turned the pre-existing verification failure into a hard login failure. Check the segment count before verifying and raise a distinct OIDCTokenFormatError, which the callback treats as 'no usable claims here' and falls through to the userinfo endpoint. Signature and claim failures still reject the login. Fixes Termix-SSH/Support#1016 Fixes Termix-SSH/Support#1018 * refuse to start with an empty database when data exists elsewhere (#1118) When the data directory holds no database, startup treats it as a first run and silently creates an empty one. A deployment that loses DATA_DIR — an .env file the service no longer loads, a volume that did not mount — lands in exactly that state, so the user is asked to register an admin account again while the real database sits untouched one directory over. It is indistinguishable from the upgrade having deleted everything. Check the known data locations before creating a new database and refuse to start when one of them already holds a database, naming both directories. ALLOW_EMPTY_DATA_DIR=true starts anyway for anyone deliberately starting over. This matches how a failed decryption already behaves: it throws rather than falling back to an empty database. Closes Termix-SSH/Support#1006 * stop read-only shared hosts from being dragged into folders (#1119) Shared hosts hide their edit, share and delete actions based on the recipient's permission level, but the sidebar row stays draggable regardless. Dropping one on a folder issues a bulk folder update the server rejects, so a recipient without edit rights gets a failure toast for an action the UI offered them. Gate draggable on canEditHost, and skip hosts the recipient cannot edit in the move handler so a mixed selection moves what it can instead of failing whole. Closes Termix-SSH/Support#1011 * apply the configured RDP resolution to the session (#1120) The host editor stores width and height in guacamoleConfig, and the backend passes them to guacd in the connection token. The renderer then appends its own width and height query parameters measured from the container, which take precedence, so a configured resolution never reached the session — only dpi did, because that was the one display field GuacamoleApp read back. Pass the configured width and height alongside dpi, and skip the container-driven sendSize on connect and on resize when a resolution is pinned. rescaleDisplay still fits the fixed display into the available space. Closes Termix-SSH/Support#1039 * honour per-host recording flags and explain a missing recording (#1121) The session recording section offers a recording path, a filename template and four content toggles, but the backend overwrote five of the six on every connection. A host could set none of them and get no indication why. Location and filename genuinely are not the host's to choose — recordings are indexed by them for playback and the backend refuses to read outside its recordings directory — so drop those two inputs rather than keep pretending they apply. The content flags are a host-level decision, so default them instead of forcing them. That still leaves the reported case, where guacd writes the file somewhere the backend cannot see it. The warning now reports both paths and names the two env vars that align them, which is otherwise guesswork for a split-container setup. Closes Termix-SSH/Support#1041 * route desktop guacd calls to the connected remote server (#1122) resolveConnectionOrigin() pins RDP/VNC/Telnet to "remote" because the embedded desktop backend does not bundle guacd, and the Guacamole websocket already follows that. The status check and both token calls did not: they use the shared authApi, which in Electron is hard-coded to the embedded backend. So the desktop app asked the backend without guacd whether guacd was available, got "disconnected", and refused to connect — while the connected server it would actually have used reports it as connected and serves the same host fine from the web client. Send those three calls through a remote-origin instance in Electron, alongside the existing file-manager, tunnel and stats ones. Closes Termix-SSH/Support#1043 * move the Homebrew cask to where a tap looks for it (#1123) A tap discovers casks in a top-level Casks/ directory. The cask sat in packaging/Casks/, so tapping the repository succeeded and every subsequent brew install --cask termix reported that no cask with that name exists. Move it and repoint the five workflow references. The release job still rewrites the version and checksum in place, and the electron job still copies it into the generated and submission trees. Closes Termix-SSH/Support#1044 * stop highlighting inside a split control string (#1124) A control string (OSC/DCS/APC/PM) carries text that must never be displayed — an OSC 0 title holds the user, host and path, and PROMPT_COMMAND emits one on every prompt. Its opener and its terminator routinely land in different websocket frames, and the continuation frame contains no escape byte at all, so every guard in the highlighter misses it: TUI_SEQUENCE, CONTROL_STRING_SEQUENCE and hasIncompleteAnsiSequence all only look at one chunk. Highlighting that continuation injects an SGR sequence into the middle of the open string, which aborts it early in xterm.js and prints the remainder as ordinary text — the stray ~/path glued to the prompt, and the cursor arithmetic drift behind the duplicate prompts and Ctrl+R corruption. Track the state across chunks the way alternate-screen mode already is, and skip any chunk that starts or ends inside a control string. A trailing lone ESC counts as inside, since its meaning only arrives with the next chunk. Closes Termix-SSH/Support#1025 * stop session-log route test importing the real repository layer (#1125) The test mocks db, logger and AuthManager, but the route module also calls PermissionManager.getInstance() at import time and pulls in the repository factory, which loads the drizzle schema and the better-sqlite3 native binding. Importing that costs seconds when the full suite runs its projects concurrently, and the test times out at 5s. On its own it passes, so it read as flaky rather than as a missing mock. Mock both. None of it is under test here, and the file now imports in milliseconds regardless of load. * fail the guacamole-lite patch when an anchor is gone (#1126) Each patch bails out with a console.log and process.exit(0) when its anchor string is missing. The write-back happens at the end of the file, so an upstream release that moves any one anchor drops every patch, exits successfully, and leaves postinstall reporting nothing wrong. Termix then builds and starts normally and drops VNC/RDP sessions at runtime — with no signal pointing at the patch. Every patch here is required for correctness: protocol negotiation, the guacd 1.6.0 name handshake, dynamic argument answering, UTF-8 tokens, read-only joins. A missing anchor means the patch no longer applies, so exit non-zero and say which one and what to do. Unchanged: a missing guacamole-lite still skips quietly, and an already-patched tree still exits 0. * fix: clarify desktop local profile (#1095) * fix: clarify desktop local profile * cover the AccordionSection hidden branch The desktop build hides the Security section because the embedded profile signs in automatically and has no login password, so the controls there would imply a protection that does not exist. Nothing asserted that hidden actually keeps the children out of the DOM rather than merely collapsing them. Export the component and cover both states, including that an expanded hidden section still renders nothing. * fix: show remote sync account identity (#1110) * fix: show remote sync account identity * cover getRemoteSyncUserInfo and make its null contract hold Nothing asserted the renderer-side gate: browser builds must not reach for the IPC bridge, and a missing bridge, an unconfigured server, an expired JWT or a failed channel all have to degrade to no identity rather than throw. Writing that turned up a mismatch — with no preload bridge the optional chain resolved to undefined while the signature promises null. The only caller uses ??, so nothing is broken today, but the type was not telling the truth. The main-process half (token expiry, /users/me, the roles fallback) stays uncovered: remote-sync.cjs requires electron at load, so exercising it means stubbing safeStorage and the filesystem, which is a bigger change than this PR warrants. * improve settings navigation and legal disclosure (#1105) * fix desktop preference synchronization (#1106) * fix: use jump host SOCKS proxy settings (#1116) * ci(deps): bump the github-actions group with 2 updates (#1086) Bumps the github-actions group with 2 updates: [actions/setup-node](https://github.com/actions/setup-node) and [useblacksmith/setup-docker-builder](https://github.com/useblacksmith/setup-docker-builder). Updates `actions/setup-node` from 6 to 7 - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v6...v7) Updates `useblacksmith/setup-docker-builder` from 1 to 2 - [Release notes](https://github.com/useblacksmith/setup-docker-builder/releases) - [Commits](https://github.com/useblacksmith/setup-docker-builder/compare/v1...v2) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: useblacksmith/setup-docker-builder dependency-version: '2' dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump the dev-patch-updates group with 23 updates (#1087) Bumps the dev-patch-updates group with 23 updates: | Package | From | To | | --- | --- | --- | | [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) | `2.5.4` | `2.5.5` | | [@radix-ui/react-accordion](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/accordion) | `1.2.17` | `1.2.20` | | [@radix-ui/react-alert-dialog](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/alert-dialog) | `1.1.20` | `1.1.23` | | [@radix-ui/react-checkbox](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/checkbox) | `1.3.8` | `1.3.11` | | [@radix-ui/react-dialog](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/dialog) | `1.1.20` | `1.1.23` | | [@radix-ui/react-dropdown-menu](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/dropdown-menu) | `2.1.21` | `2.1.24` | | [@radix-ui/react-label](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/label) | `2.1.12` | `2.1.15` | | [@radix-ui/react-popover](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/popover) | `1.1.20` | `1.1.23` | | [@radix-ui/react-progress](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/progress) | `1.1.13` | `1.1.16` | | [@radix-ui/react-scroll-area](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/scroll-area) | `1.2.15` | `1.2.18` | | [@radix-ui/react-select](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/select) | `2.3.4` | `2.3.7` | | [@radix-ui/react-separator](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/separator) | `1.1.12` | `1.1.15` | | [@radix-ui/react-slider](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/slider) | `1.4.4` | `1.4.7` | | [@radix-ui/react-slot](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/slot) | `1.3.0` | `1.3.3` | | [@radix-ui/react-switch](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/switch) | `1.3.4` | `1.3.7` | | [@radix-ui/react-tabs](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/tabs) | `1.1.18` | `1.1.21` | | [@radix-ui/react-tooltip](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/tooltip) | `1.2.13` | `1.2.16` | | [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react) | `6.0.3` | `6.0.4` | | [concurrently](https://github.com/open-cli-tools/concurrently) | `10.0.3` | `10.0.4` | | [radix-ui](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/radix-ui) | `1.6.3` | `1.6.7` | | [react](https://github.com/react/react/tree/HEAD/packages/react) | `19.2.7` | `19.2.8` | | [react-dom](https://github.com/react/react/tree/HEAD/packages/react-dom) | `19.2.7` | `19.2.8` | | [react-i18next](https://github.com/i18next/react-i18next) | `17.0.10` | `17.0.11` | Updates `@biomejs/biome` from 2.5.4 to 2.5.5 - [Release notes](https://github.com/biomejs/biome/releases) - [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md) - [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.5/packages/@biomejs/biome) Updates `@radix-ui/react-accordion` from 1.2.17 to 1.2.20 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/accordion/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/accordion) Updates `@radix-ui/react-alert-dialog` from 1.1.20 to 1.1.23 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/alert-dialog/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/alert-dialog) Updates `@radix-ui/react-checkbox` from 1.3.8 to 1.3.11 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/checkbox/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/checkbox) Updates `@radix-ui/react-dialog` from 1.1.20 to 1.1.23 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/dialog/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/dialog) Updates `@radix-ui/react-dropdown-menu` from 2.1.21 to 2.1.24 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/dropdown-menu/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/dropdown-menu) Updates `@radix-ui/react-label` from 2.1.12 to 2.1.15 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/label/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/label) Updates `@radix-ui/react-popover` from 1.1.20 to 1.1.23 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/popover/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/popover) Updates `@radix-ui/react-progress` from 1.1.13 to 1.1.16 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/progress/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/progress) Updates `@radix-ui/react-scroll-area` from 1.2.15 to 1.2.18 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/scroll-area/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/scroll-area) Updates `@radix-ui/react-select` from 2.3.4 to 2.3.7 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/select/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/select) Updates `@radix-ui/react-separator` from 1.1.12 to 1.1.15 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/separator/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/separator) Updates `@radix-ui/react-slider` from 1.4.4 to 1.4.7 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/slider/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/slider) Updates `@radix-ui/react-slot` from 1.3.0 to 1.3.3 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/slot/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/slot) Updates `@radix-ui/react-switch` from 1.3.4 to 1.3.7 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/switch/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/switch) Updates `@radix-ui/react-tabs` from 1.1.18 to 1.1.21 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/tabs/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/tabs) Updates `@radix-ui/react-tooltip` from 1.2.13 to 1.2.16 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/tooltip/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/tooltip) Updates `@vitejs/plugin-react` from 6.0.3 to 6.0.4 - [Release notes](https://github.com/vitejs/vite-plugin-react/releases) - [Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite-plugin-react/commits/plugin-react@6.0.4/packages/plugin-react) Updates `concurrently` from 10.0.3 to 10.0.4 - [Release notes](https://github.com/open-cli-tools/concurrently/releases) - [Commits](https://github.com/open-cli-tools/concurrently/compare/v10.0.3...v10.0.4) Updates `radix-ui` from 1.6.3 to 1.6.7 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/radix-ui/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/1.6.7/packages/react/radix-ui) Updates `react` from 19.2.7 to 19.2.8 - [Release notes](https://github.com/react/react/releases) - [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/react/react/commits/v19.2.8/packages/react) Updates `react-dom` from 19.2.7 to 19.2.8 - [Release notes](https://github.com/react/react/releases) - [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/react/react/commits/v19.2.8/packages/react-dom) Updates `react-i18next` from 17.0.10 to 17.0.11 - [Changelog](https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md) - [Commits](https://github.com/i18next/react-i18next/compare/v17.0.10...v17.0.11) --- updated-dependencies: - dependency-name: "@biomejs/biome" dependency-version: 2.5.5 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-accordion" dependency-version: 1.2.20 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-alert-dialog" dependency-version: 1.1.23 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-checkbox" dependency-version: 1.3.11 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-dialog" dependency-version: 1.1.23 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-dropdown-menu" dependency-version: 2.1.24 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-label" dependency-version: 2.1.15 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-popover" dependency-version: 1.1.23 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-progress" dependency-version: 1.1.16 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-scroll-area" dependency-version: 1.2.18 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-select" dependency-version: 2.3.7 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-separator" dependency-version: 1.1.15 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-slider" dependency-version: 1.4.7 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-slot" dependency-version: 1.3.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-switch" dependency-version: 1.3.7 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-tabs" dependency-version: 1.1.21 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@radix-ui/react-tooltip" dependency-version: 1.2.16 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: "@vitejs/plugin-react" dependency-version: 6.0.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: concurrently dependency-version: 10.0.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: radix-ui dependency-version: 1.6.7 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: react dependency-version: 19.2.8 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: react-dom dependency-version: 19.2.8 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates - dependency-name: react-i18next dependency-version: 17.0.11 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-patch-updates ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump the prod-patch-updates group with 3 updates (#1088) Bumps the prod-patch-updates group with 3 updates: [@tanstack/react-virtual](https://github.com/TanStack/virtual/tree/HEAD/packages/react-virtual), [jose](https://github.com/panva/jose) and [js-yaml](https://github.com/nodeca/js-yaml). Updates `@tanstack/react-virtual` from 3.14.6 to 3.14.8 - [Release notes](https://github.com/TanStack/virtual/releases) - [Changelog](https://github.com/TanStack/virtual/blob/main/packages/react-virtual/CHANGELOG.md) - [Commits](https://github.com/TanStack/virtual/commits/@tanstack/react-virtual@3.14.8/packages/react-virtual) Updates `jose` from 6.2.3 to 6.2.4 - [Release notes](https://github.com/panva/jose/releases) - [Changelog](https://github.com/panva/jose/blob/main/CHANGELOG.md) - [Commits](https://github.com/panva/jose/compare/v6.2.3...v6.2.4) Updates `js-yaml` from 5.2.1 to 5.2.2 - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodeca/js-yaml/compare/5.2.1...5.2.2) --- updated-dependencies: - dependency-name: "@tanstack/react-virtual" dependency-version: 3.14.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: jose dependency-version: 6.2.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates - dependency-name: js-yaml dependency-version: 5.2.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-patch-updates ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump undici in the prod-minor-updates group (#1089) Bumps the prod-minor-updates group with 1 update: [undici](https://github.com/nodejs/undici). Updates `undici` from 8.7.0 to 8.9.0 - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v8.7.0...v8.9.0) --- updated-dependencies: - dependency-name: undici dependency-version: 8.9.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-minor-updates ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump the major-updates group with 4 updates (#1090) Bumps the major-updates group with 4 updates: [better-sqlite3](https://github.com/WiseLibs/better-sqlite3), [chalk](https://github.com/chalk/chalk), [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) and [typescript](https://github.com/microsoft/TypeScript). Updates `better-sqlite3` from 12.11.1 to 13.0.1 - [Release notes](https://github.com/WiseLibs/better-sqlite3/releases) - [Commits](https://github.com/WiseLibs/better-sqlite3/compare/v12.11.1...v13.0.1) Updates `chalk` from 5.6.2 to 6.0.0 - [Release notes](https://github.com/chalk/chalk/releases) - [Commits](https://github.com/chalk/chalk/compare/v5.6.2...v6.0.0) Updates `@testing-library/jest-dom` from 6.9.1 to 7.0.0 - [Release notes](https://github.com/testing-library/jest-dom/releases) - [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/jest-dom/compare/v6.9.1...v7.0.0) Updates `typescript` from 6.0.3 to 7.0.2 - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Commits](https://github.com/microsoft/TypeScript/commits) --- updated-dependencies: - dependency-name: better-sqlite3 dependency-version: 13.0.1 dependency-type: direct:production update-type: version-update:semver-major dependency-group: major-updates - dependency-name: chalk dependency-version: 6.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: major-updates - dependency-name: "@testing-library/jest-dom" dependency-version: 7.0.0 dependency-type: direct:development update-type: version-update:semver-major dependency-group: major-updates - dependency-name: typescript dependency-version: 7.0.2 dependency-type: direct:development update-type: version-update:semver-major dependency-group: major-updates ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * restore lint by pinning typescript below 7 (#1131) #1090 bumped typescript to 7.0.2. typescript-eslint declares `typescript: >=4.8.4 <6.1.0`, and TypeScript 7 removed `ts.Extension`, which @typescript-eslint/typescript-estree dereferences at import time: node_modules/@typescript-eslint/typescript-estree/dist/create-program/shared.js:59 ts.Extension.Cjs, TypeError: Cannot read properties of undefined (reading 'Cjs') ESLint hits that while loading eslint.config.mjs, so `npm run lint` fails before linting anything. Node reports it as ERR_INTERNAL_ASSERTION, which hides the cause. Every open PR fails this check, not just new ones. Even the latest typescript-eslint prerelease still caps at <6.1.0, so there is nothing to upgrade to yet. Pin back to ~6.0.3 and tell dependabot to hold major typescript bumps until the ecosystem catches up. Also fixes biome.json pointing vcs.defaultBranch at dev-2.5.0, a branch that no longer exists. * make the repository layer engine-agnostic (#1127) DatabaseContext handed every repository a raw better-sqlite3 handle alongside drizzle, and three of them used it for retention queries built on datetime('now', ?) — a SQLite-only function. That handle is the one thing standing between the repository layer and a second engine. Drop it. The two time-based prunes compute their cutoff in JS against the CURRENT_TIMESTAMP text format, which every engine writes the same way and which compares correctly as a string; the health-history prune becomes a select of the rows to keep followed by a NOT IN delete. All three turn async, so their two callers await them. Name the dialect rather than repeating a string literal, so adding an engine is one edit instead of a search. Tests built their schema through context.sqlite?.exec(). Optional chaining meant removing the field type-checked cleanly and then silently created no tables, so the fixture now owns exec() and a raw handle for direct assertions — schema setup belongs to the test harness, not to the interface repositories consume. No behaviour change, and no Postgres yet: this only removes the coupling that would have to be undone first. * keep audit trails and recordings when a user is deleted (#1128) audit_logs and session_recordings both referenced users with ON DELETE CASCADE, so removing an account erased everything it had ever done. An audit trail that disappears with the account it recorded cannot answer the question it exists for, and a recording is evidence about a host as much as about a person. Both foreign keys become ON DELETE SET NULL. audit_logs already denormalises username, so an entry still names who acted once the reference is gone. session_recordings did not, so the column is added and backfilled first — otherwise relaxing the constraint would only trade deleted evidence for anonymous evidence. SQLite cannot alter a foreign key in place, so existing databases are migrated by copy-and-swap, guarded by a PRAGMA check that makes it idempotent. Fresh databases are created in the target shape and skip it. Recordings still cascade from their host. * audit the remaining remote access paths (#1129) Only SSH terminal sessions were audited. Opening a file manager session, an RDP, VNC or Telnet desktop, a Docker session or an SSH tunnel left no audit entry at all — which covers most of the ways data leaves a host or a foothold is established. Each of those four now writes an entry when the session is established, matching the existing ssh_connect: who, which host, from what address, and for tunnels the endpoint and local port being forwarded. Audit writes are fire-and-forget so they cannot delay or fail the connection, consistent with logAudit already swallowing its own errors. getAuditUsername was defined identically in two route files and is needed in four more, so it moves next to logAudit. * fix: honor lookupOptions.all in custom DNS lookup hook (#1084) Node's happy-eyeballs autoSelectFamily calls custom dns lookup functions with all:true and expects the full address array back. Always replying with a single (address, family) pair corrupted net's internal state, surfacing as "Invalid IP address: undefined" instead of a real connect error, breaking outbound notification delivery (webhook/ntfy). Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: ZacharyZcR <zacharyzcr1984@gmail.com> * fix: SSH-login alerts silently dropped (channel load + auth middleware ordering) (#1083) * fix: load notification channels on mount in AlertsPanel Channels only loaded when the Channels tab was visited, so opening Edit Alert Rule before ever switching to that tab showed the channel picker as empty even when channels existed. (cherry picked from commit caed913ee91990a853f5a048849c67ed3f7c329e) * fix: register login-alert route before auth middleware Global JWT auth middleware ran before this internal service-to-service route, rejecting it with 401 before its own IP+token check ever ran — silently dropped every SSH-login alert. Also surface non-OK responses instead of swallowing them. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * test: add coverage for alert-notification fixes Channel-load-on-mount, login-alert non-OK handling, and a source-order guard for the route/auth-middleware regression. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * format AlertsPanel test with prettier --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: ZacharyZcR <zacharyzcr1984@gmail.com> * stop deleting audit trails, and say when they are dropped (#1132) Two ways audit evidence still disappeared silently. Deleting an account removed its audit entries and session recordings outright. #1128 relaxed those foreign keys to ON DELETE SET NULL, but deleteUserAndRelatedData deletes the rows explicitly, so the schema change had no effect on the path that actually matters. Both repositories gain anonymizeByUserId, which nulls the reference and leaves the row; username is already denormalised on both tables, so entries stay attributable to whoever acted. Separately, the log pruned itself at a hard-coded 10000 rows with no signal. Entries well inside any retention window were discarded and nothing recorded it. Retention is now configurable by age via AUDIT_LOG_RETENTION_DAYS, the row cap via AUDIT_LOG_MAX_ENTRIES, and the two are reported differently: expiring an old entry is routine and logged at info, while hitting the cap means the ceiling is too low for how much this install audits and is logged at warn, naming the range discarded and how to stop it. * let the audit log leave the box (#1133) Retention became configurable in #1132, which only helps if entries can be moved somewhere before they expire. Until now the only way out was two GET endpoints built for the UI. Adds GET /audit-logs/export, taking the same filters as the list endpoint and streaming the whole matching set as CSV or NDJSON in batches, so an export is not bounded by the 200-row page cap and does not buffer the result set. Reading the entire trail is itself recorded as export_audit_logs. CSV fields starting with =, +, - or @ are prefixed with a quote. Audit rows carry attacker-influenced values like resource names, and spreadsheet software treats those as formulas on open. Adds optional live forwarding to a collector via AUDIT_LOG_FORWARD_URL, with an optional bearer token. Delivery goes through safeOutboundFetch so a misconfigured URL cannot be turned into an internal network probe, and it is fire-and-forget: the local write stays the source of truth and a dead SIEM must never delay or fail the operation being audited. Repeated failures are reported five times and then suppressed until delivery recovers, so an outage does not bury the logs it is supposed to appear in. * encrypt SSO secrets instead of base64-encoding them (#1135) The OIDC client secret and LDAP bind password were stored behind an encoded: prefix that is base64, not encryption. Anyone reading the database read the secrets. A second path wrote the same thing behind an encrypted: prefix, which was also base64 — and the reader even documented that it could not decrypt it. These belong to the installation rather than to a user: sso_providers has no userId, and the values must be readable during login, before anyone has authenticated, so the per-user DEK used elsewhere does not apply. They are now sealed with AES-256-GCM under the system encryption key, which already protects other installation-level material. Reading handles both legacy prefixes so an existing install is not locked out of SSO login, and a legacy value is upgraded the next time the provider is saved. The three scattered encode/decode sites are replaced by one module. * remove the unwired field encryption boundary (#1136) FieldEncryptionBoundary declared a full sensitive/plaintext policy for six tables and was referenced only by its own test. Nothing in production used it. Its policy is byte-for-byte the same as FieldCrypto.ENCRYPTED_FIELDS, which is the copy that actually runs, so nothing is lost by deleting it. Keeping a second list is the real risk: someone adds a field to this one, sees it classified as sensitive, and ships something that was never encrypted. The one apparent improvement it had — requiring an explicit recordId instead of DataCrypto's temp-${Date.now()} fallback — turns out to guard against nothing. decryptField derives its context from the recordId stored inside the ciphertext, not from the argument, so a temporary id at encryption time still decrypts. * load the database file when encryption is off (#1137) * Groundwork for Postgres and MySQL backends (#1134) * groundwork for postgres and mysql backends #1127 made the repository layer dialect-agnostic. This adds the pieces needed to actually target a second engine, as a foundation only — nothing is wired up and sqlite remains the sole runtime path. - DatabaseDialect covers sqlite, postgres and mysql, resolved from DATABASE_DIALECT and defaulting to sqlite so nothing changes for existing deployments or the desktop build - a column kit holding the per-dialect type choices in one file: booleans are integers on sqlite and native elsewhere, autoincrement differs three ways, and MySQL cannot index unbounded TEXT so key columns need varchar - settings and users declared for all three dialects as a proof slice, chosen because between them they use every construct the real schema does - pg and mysql2 added as dependencies The tests build real queries for all three engines without a server, asserting identifier quoting, placeholder style and boolean storage, so the property the repositories depend on is verified rather than assumed. * verify foreign keys and unique constraints port across dialects The first slice only covered plain columns. The real schema also has 92 foreign keys (80 cascade, 12 set null) and 14 unique columns, so the approach is only viable if those survive the port. Adds audit_logs and ssh_folders to the proof slice: one nullable reference with ON DELETE SET NULL, one required reference with ON DELETE CASCADE, a unique column, and an autoincrement surrogate key — which is spelled three different ways underneath (integer primary key autoincrement, serial, int auto_increment). All of it holds. Worth noting for whoever picks this up: getTableConfig is dialect-specific and silently fails on a table from another dialect, so the test uses each engine's own. * generate the postgres and mysql schemas instead of hand-writing them The proof slice showed the constructs port, but left the maintenance question open. Three hand-written copies of 52 tables is the wrong answer: with foreign keys the copies cross-reference each other, so a renamed table has to land in three places consistently or a key silently points at the wrong one. The mapping is mechanical, so a script does it. schema.ts stays the single source of truth and schema.pg.ts / schema.mysql.ts are derived, covering all 52 tables — the column kit and the two-table portable slice are gone, since the generator now holds those decisions. The transforms are the ones the kit enumerated: integer-backed booleans become native, autoincrement keys become serial or int auto_increment, real becomes double precision or double, and any column that is a primary key, is unique, or sits on either end of a foreign key becomes varchar because MySQL cannot index unbounded TEXT. > termix@2.6.0 lint > node scripts/generate-dialect-schema.cjs --check && eslint . /mnt/c/Users/29037/WebstormProjects/Termix/src/backend/database/routes/homepage-favicon-routes.ts 99:12 warning 'err' is defined but never used unused-imports/no-unused-vars /mnt/c/Users/29037/WebstormProjects/Termix/src/backend/database/routes/homepage-ping-routes.ts 123:12 warning 'err' is defined but never used unused-imports/no-unused-vars /mnt/c/Users/29037/WebstormProjects/Termix/src/backend/database/routes/homepage-rss-routes.ts 144:12 warning 'err' is defined but never used unused-imports/no-unused-vars /mnt/c/Users/29037/WebstormProjects/Termix/src/backend/database/routes/session-log-routes.ts 46:16 warning 'canAccessRecording' is defined but never used. Allowed unused vars must match /^_/u unused-imports/no-unused-vars /mnt/c/Users/29037/WebstormProjects/Termix/src/backend/hosts/vault-signer-core.ts 55:12 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any 75:13 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any /mnt/c/Users/29037/WebstormProjects/Termix/src/backend/tests/hosts/auth-manager.test.ts 18:73 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any /mnt/c/Users/29037/WebstormProjects/Termix/src/backend/tests/utils/shared-host-secrets-manager.test.ts 7:6 warning 'SecretRow' is defined but never used. Allowed unused vars must match /^_/u unused-imports/no-unused-vars /mnt/c/Users/29037/WebstormProjects/Termix/src/backend/utils/auth-manager.ts 510:13 warning 'affectedUsers' is assigned a value but never used. Allowed unused vars must match /^_/u unused-imports/no-unused-vars /mnt/c/Users/29037/WebstormProjects/Termix/src/backend/utils/notification-sender.ts 48:12 warning 'firstErr' is defined but never used unused-imports/no-unused-vars /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/api/ssh-file-operations-api.ts 35:10 warning 'buildFileManagerUrl' is defined but never used. Allowed unused vars must match /^_/u unused-imports/no-unused-vars /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/components/folder-style.tsx 61:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components 116:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components 121:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components 149:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/components/proxmox/ProxmoxDiscoverDialog.tsx 109:19 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any 190:19 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/features/homepage/HomepageCanvas.tsx 345:15 warning Empty block statement no-empty 388:15 warning Empty block statement no-empty 415:15 warning Empty block statement no-empty /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/features/homepage/dialogs/SingleHostEditForm.tsx 24:6 warning React Hook useEffect has a missing dependency: 'filter'. Either include it or remove the dependency array. If 'setHosts' needs the current value of 'filter', you can also switch to useReducer instead of useState and read 'filter' in the reducer react-hooks/exhaustive-deps /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/features/homepage/widgets/AlertFeedWidget.tsx 93:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/features/homepage/widgets/CustomApiWidget.tsx 77:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/features/homepage/widgets/DockerActivityWidget.tsx 50:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/features/homepage/widgets/DockerWidget.tsx 16:10 warning Fast refresh only works when a file has exports. Move your component(s) to a separate file react-refresh/only-export-components /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/features/homepage/widgets/FileManagerWidget.tsx 16:10 warning Fast refresh only works when a file has exports. Move your component(s) to a separate file react-refresh/only-export-components /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/features/homepage/widgets/HostGridWidget.tsx 61:6 warning React Hook useCallback has a missing dependency: 'hostIds'. Either include it or remove the dependency array react-hooks/exhaustive-deps 61:7 warning React Hook useCallback has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked react-hooks/exhaustive-deps /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/features/homepage/widgets/MetricsChartWidget.tsx 168:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/features/homepage/widgets/PingStatusWidget.tsx 79:6 warning React Hook useEffect has a missing dependency: 'fetchAll'. Either include it or remove the dependency array react-hooks/exhaustive-deps 79:7 warning React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked react-hooks/exhaustive-deps /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/features/homepage/widgets/QuickConnectWidget.tsx 64:10 warning Fast refresh only works when a file has exports. Move your component(s) to a separate file react-refresh/only-export-components /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/features/homepage/widgets/RecentActivityWidget.tsx 82:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps 82:17 warning React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked react-hooks/exhaustive-deps /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/features/homepage/widgets/SshQuickConnectWidget.tsx 67:6 warning React Hook useCallback has a missing dependency: 'hostIds'. Either include it or remove the dependency array react-hooks/exhaustive-deps 67:7 warning React Hook useCallback has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked react-hooks/exhaustive-deps 99:17 warning 'online' is assigned a value but never used. Allowed unused vars must match /^_/u unused-imports/no-unused-vars /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/features/homepage/widgets/SshTerminalWidget.tsx 17:10 warning Fast refresh only works when a file has exports. Move your component(s) to a separate file react-refresh/only-export-components /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/features/homepage/widgets/SystemOverviewWidget.tsx 72:6 warning React Hook useEffect has a missing dependency: 'fetchData'. Either include it or remove the dependency array react-hooks/exhaustive-deps /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/features/homepage/widgets/TunnelWidget.tsx 15:10 warning Fast refresh only works when a file has exports. Move your component(s) to a separate file react-refresh/only-export-components /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/features/host-metrics/cards/CpuCard.tsx 14:10 warning 'computeChartData' is defined but never used. Allowed unused vars must match /^_/u unused-imports/no-unused-vars /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/sidebar/FolderPathPicker.tsx 15:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components 22:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components /mnt/c/Users/29037/WebstormProjects/Termix/src/ui/sidebar/HostsPanel.tsx 601:52 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any ✖ 44 problems (0 errors, 44 warnings) now fails if the generated files are out of date, so editing the schema without regenerating cannot reach main. * select durability behaviour per dialect, and document the backends The onWrite hook every repository receives exists to serialise the in-memory SQLite database back to its encrypted file. On a client-server engine a committed write is already durable and there is nothing to flush, so the factory now installs no hook at all rather than one that does nothing. Repositories call it as this.onWrite?.(), so none of the 43 of them change. Also adds docs/database-backends.md, mostly to be explicit about encryption, which is the part most likely to be misread. Field-level encryption is identical on all three engines and covers every credential. Whole-file encryption has no equivalent on Postgres or MySQL, so host names, snippet contents, audit entries and backups are only as protected as the storage underneath them — that is the operator's responsibility and the docs should not imply otherwise. * generate DDL with drizzle-kit, and give settings a synchronous path Two of the three remaining blockers. DDL: db/index.ts hand-writes 67 CREATE TABLE statements and 122 ADD COLUMN migrations, all in SQLite dialect. Rather than port them, drizzle-kit now generates migrations from the schema modules — 817 lines for Postgres, 869 for MySQL, with the type mapping already correct because the schemas it reads are themselves generated. > termix@2.6.0 schema:migrations > drizzle-kit generate --config=drizzle.config.pg.ts && drizzle-kit generate --config=drizzle.config.mysql.ts Reading config file '/mnt/c/Users/29037/WebstormProjects/Termix/drizzle.config.pg.ts' 52 tables alert_firings 11 columns 0 indexes 2 fks alert_rule_channels 3 columns 0 indexes 2 fks alert_rules 11 columns 0 indexes 2 fks api_keys 9 columns 0 indexes 1 fks audit_logs 13 columns 0 indexes 1 fks c2s_tunnel_presets 8 columns 0 indexes 1 fks command_history 5 columns 0 indexes 2 fks dashboard_service_links 8 columns 0 indexes 1 fks dismissed_alerts 4 columns 0 indexes 1 fks file_manager_pinned 6 columns 0 indexes 2 fks file_manager_recent 6 columns 0 indexes 2 fks file_manager_shortcuts 6 columns 0 indexes 2 fks homepage_items 9 columns 0 indexes 1 fks homepage_layouts 4 columns 0 indexes 1 fks host_access 11 columns 0 indexes 5 fks host_health_checks 7 columns 0 indexes 2 fks host_health_history 8 columns 0 indexes 2 fks host_metrics_history 8 columns 0 indexes 1 fks host_metrics_preferences 6 columns 0 indexes 2 fks ssh_data 94 columns 0 indexes 6 fks network_topology 5 columns 0 indexes 1 fks notification_channels 7 columns 0 indexes 1 fks opkssh_tokens 12 columns 0 indexes 2 fks recent_activity 6 columns 0 indexes 2 fks roles 8 columns 0 indexes 0 fks session_recordings 15 columns 0 indexes 3 fks session_share_participants 6 columns 0 indexes 2 fks session_shares 15 columns 0 indexes 3 fks sessions 11 columns 0 indexes 1 fks settings 2 columns 0 indexes 0 fks shared_host_secrets 15 columns 0 indexes 3 fks snippet_access 8 columns 0 indexes 4 fks snippet_folders 8 columns 0 indexes 1 fks snippets 11 columns 0 indexes 1 fks ssh_credential_usage 5 columns 0 indexes 3 fks ssh_credentials 21 columns 0 indexes 1 fks ssh_folders 9 columns 0 indexes 2 fks sso_providers 8 columns 0 indexes 0 fks sync_tombstones 5 columns 0 indexes 1 fks termix_identities 6 columns 0 indexes 1 fks termix_identity_ca 8 columns 0 indexes 2 fks termix_identity_keys 12 columns 0 indexes 3 fks tmux_session_tags 6 columns 0 indexes 2 fks transfer_recent 7 columns 0 indexes 3 fks trusted_devices 8 columns 0 indexes 1 fks user_open_tabs 9 columns 0 indexes 2 fks user_preferences 23 columns 0 indexes 1 fks user_roles 5 columns 0 indexes 3 fks users 20 columns 0 indexes 0 fks vault_profiles 18 columns 0 indexes 1 fks vault_tokens 8 columns 0 indexes 2 fks webauthn_credentials 12 columns 0 indexes 1 fks No schema changes, nothing to migrate 😴 Reading config file '/mnt/c/Users/29037/WebstormProjects/Termix/drizzle.config.mysql.ts' Reading schema files: /mnt/c/Users/29037/WebstormProjects/Termix/src/backend/database/db/schema.mysql.ts 52 tables alert_firings 11 columns 0 indexes 2 fks alert_rule_channels 3 columns 0 indexes 2 fks alert_rules 11 columns 0 indexes 2 fks api_keys 9 columns 0 indexes 1 fks audit_logs 13 columns 0 indexes 1 fks c2s_tunnel_presets 8 columns 0 indexes 1 fks command_history 5 columns 0 indexes 2 fks dashboard_service_links 8 columns 0 indexes 1 fks dismissed_alerts 4 columns 0 indexes 1 fks file_manager_pinned 6 columns 0 indexes 2 fks file_manager_recent 6 columns 0 indexes 2 fks file_manager_shortcuts 6 columns 0 indexes 2 fks homepage_items 9 columns 0 indexes 1 fks homepage_layouts 4 columns 0 indexes 1 fks host_access 11 columns 0 indexes 5 fks host_health_checks 7 columns 0 indexes 2 fks host_health_history 8 columns 0 indexes 2 fks host_metrics_history 8 columns 0 indexes 1 fks host_metrics_preferences 6 columns 0 indexes 2 fks ssh_data 94 columns 0 indexes 6 fks network_topology 5 columns 0 indexes 1 fks notification_channels 7 columns 0 indexes 1 fks opkssh_tokens 12 columns 0 indexes 2 fks recent_activity 6 columns 0 indexes 2 fks roles 8 columns 0 indexes 0 fks session_recordings 15 columns 0 indexes 3 fks session_share_participants 6 columns 0 indexes 2 fks session_shares 15 columns 0 indexes 3 fks sessions 11 columns 0 indexes 1 fks settings 2 columns 0 indexes 0 fks shared_host_secrets 15 columns 0 indexes 3 fks snippet_access 8 columns 0 indexes 4 fks snippet_folders 8 columns 0 indexes 1 fks snippets 11 columns 0 indexes 1 fks ssh_credential_usage 5 columns 0 indexes 3 fks ssh_credentials 21 columns 0 indexes 1 fks ssh_folders 9 columns 0 indexes 2 fks sso_providers 8 columns 0 indexes 0 fks sync_tombstones 5 columns 0 indexes 1 fks termix_identities 6 columns 0 indexes 1 fks termix_identity_ca 8 columns 0 indexes 2 fks termix_identity_keys 12 columns 0 indexes 3 fks tmux_session_tags 6 columns 0 indexes 2 fks transfer_recent 7 columns 0 indexes 3 fks trusted_devices 8 columns 0 indexes 1 fks user_open_tabs 9 columns 0 indexes 2 fks user_preferences 23 columns 0 indexes 1 fks user_roles 5 columns 0 indexes 3 fks users 20 columns 0 indexes 0 fks vault_profiles 18 columns 0 indexes 1 fks vault_tokens 8 columns 0 indexes 2 fks webauthn_credentials 12 columns 0 indexes 1 fks No schema changes, nothing to migrate 😴 regenerates both. Settings: 27 call sites read settings synchronously, during startup and inside request handlers. better-sqlite3 can do that; Postgres and MySQL cannot, and making all 27 async would push await through code that has no reason to be asynchronous. Settings are a handful of rarely-changing rows read constantly, so they are cached in full — primed at startup, kept in step by SettingsRepository on every set/delete/deleteLike. SQLite keeps reading the database directly and stays authoritative; only the other engines use the cache. Opening a connection is still not done. DatabaseContext.drizzle is typed as BetterSQLite3Database and 43 repositories depend on that inference; the three drizzle instance types are not interchangeable, so widening it is a design decision rather than a mechanical change. * exclude drizzle-kit output from prettier The generated migrations and snapshots are tool output; their formatting is drizzle-kit's to decide, and prettier cannot parse the .sql files at all. * absorb the RETURNING gap so mysql stays reachable MySQL has no RETURNING clause and drizzle's mysql-core does not expose the method, while 156 call sites here read the result of a write. That is the real blocker for MySQL, not the connection layer. Classifying those call sites showed the split is favourable: 92 of them only read .length, which every engine reports — as a returned array on sqlite and postgres, as affectedRows on MySQL. rowsAffected() reads both shapes, so those sites need no change in query shape. insertedId() does the same for the autoincrement key, which MySQL reports as insertId. What is left is the ~34 sites that genuinely consume the returned rows. Those cannot be emulated without reading first, which needs a transaction to stay correct under concurrency, so they will be handled individually rather than behind a helper that quietly adds a round trip. supportsReturning() is the seam for that. Identifying the mysql2 result by its own fields rather than by array shape matters: it hands back [ResultSetHeader, fields], which is an array, so shape alone cannot tell it apart from a returning() result. * name the portable database type, and open remote connections Two pieces of the connection layer. drizzle's three Database classes share no base class and their signatures are incompatible, so there is no honest type that covers all three: a union is not callable and a generic would have to be threaded through 43 repositories and every method on them. DatabaseContext.drizzle is now PortableDatabase, still the SQLite type underneath, but named and documented as the deliberate approximation it is. What makes it safe is that the equivalence is asserted in multi-dialect.test.ts rather than assumed, and the one place the surfaces truly differ — RETURNING — is handled explicitly in mutation-result.ts. connect.ts opens Postgres and MySQL from DATABASE_URL, with the schema module and driver imported lazily so neither is loaded on a SQLite deployment. The URL scheme is checked against the configured dialect first: a postgres:// URL with DATABASE_DIALECT=mysql otherwise surfaces as a driver error deep in a stack that never mentions the actual misconfiguration. * open postgres and mysql at startup * count writes without RETURNING * read affected rows without RETURNING on mysql * insert without RETURNING, and split the sync transactions * stop pretending the generated schemas are used at runtime * run the dialect checks in CI * mysql rejects a bare CURRENT_TIMESTAMP default on text * make the read-back mismatch loud, and stop the next bare returning() * run the repository tests on the real schema * skip the byte-level assertions off sqlite * move generated ids past the seeded ones * keep the export order the same on every engine * stop reading better-sqlite3 fields off every write * read counts as numbers, not whatever the driver returns * make the fixture usable against a live server * upsert on the engine that has no ON CONFLICT * run the repository suite on all three engines in CI * mysql cannot index a text column without a length * document how to actually run on postgres or mysql * keep the sqlite-era migrations off the other engines * concat strings in a way mysql agrees with * run every repository test on every engine * bound how long replicas can disagree about settings * generate the sqlite migrations alongside the others * Bump version from 2.6.0 to 2.6.1 * resolve the dialect in the repository factory instead of assuming sqlite (#1143) createCurrentRepositoryContext() hardcoded `dialect: "sqlite"` while the runtime already carried all three engines. That field is not decoration: returning.ts reads it to decide whether it can ask for RETURNING, and whether an upsert spells itself onConflictDoUpdate or onDuplicateKeyUpdate. Reporting sqlite while connected to MySQL means the first upsert calls onConflictDoUpdate on a mysql2 insert builder, which does not have it -- a TypeError, not a rejected query, as the note in returning.ts warned. So MySQL never worked outside the tests, and Postgres worked only because it also supports RETURNING and shares the conflict syntax. Three things were supposed to catch this and none could. The repository suite builds its own DatabaseContext in test-support.ts, verify-dialects.mjs builds its own, and the CI matrix runs both against real Postgres and MySQL containers -- all of them bypassing the one function the application calls. Green on three engines, broken on two. Resolve it from the environment, and test the factory itself rather than a hand-built context: the default, each configured dialect, the write hook it installs only for sqlite, and that an unsupported value throws rather than falling back. Reverting the fix fails two of them. Fixes Termix-SSH/Support#282 * fix remote sync stalling after the first pass and never propagating deletions (#1140) The incremental cursor never matched. updated_at/deleted_at are TEXT columns written by CURRENT_TIMESTAMP ("2026-07-29 10:11:21"), while the client sends an ISO 8601 since ("2026-07-29T10:06:55.172Z"). Both comparisons are lexical and ' ' sorts below 'T', so a newer row lost at position 10 and every ?since= query came back empty. Pass 1 syncs everything (since is null) and persists a cursor; every pass after it returns nothing with lastError: null and reports success. Normalize since into the stored shape on the way in, leaving an already-normalized value alone -- parsing that would treat it as local time and, west of UTC, push the cursor past unsynced rows. POST /sync/tombstones was unreachable. It was registered after POST /:entityType, and "tombstones" is a valid :entityType, so the wildcard answered it with 400 "Unknown entity type" and the handler never ran. The pass has no per-entity error handling, so that 400 also discarded the state of every entity type already synced in the same pass. Move it ahead of the wildcards. The tombstone guard consulted the incremental window. A row deleted on one side and untouched on the other -- the shape every ordinary deletion takes once the two sides converge -- is not in that window, so the tombstone was skipped, and skipped again on each later pass as it slid out of its own window. The guard cannot just be dropped: recording a tombstone for a row that was already gone hands the sender a fresh one to push back, and the two trade the same deletion forever. So only a delete that removed something records a tombstone, which makes the endpoint idempotent and lets the client push every tombstone unconditionally. Deletions missed while the cursor was broken stay missed -- their tombstones predate the persisted cursor. Ordinary edits do come through, since the row's updatedAt is still newer than it. Fixes Termix-SSH/Support#1050 Fixes Termix-SSH/Support#1051 * report why every JWKS fetch failed instead of swallowing the reason (#1142) An OIDC login that cannot reach the provider's keys ends in "Failed to fetch JWKS from any URL" and nothing else. Getting there discards everything worth knowing: a non-2xx response hit an empty else branch, a thrown request hit a bare `continue`, and discovery only logged when it threw -- a 404 or a document without jwks_uri passed in silence. An administrator cannot tell an issuer URL typo from a proxy, a private CA, or an outage at the provider, and neither can anyone reading the report. Collect each attempt with its reason and put them in the thrown error. It reaches the log through the existing "OIDC callback failed" handler; the browser still gets the same generic message it did before. Unwrapping the cause is the part that matters: undici reports every transport failure as "fetch failed" and hangs the real reason -- ENOTFOUND, ECONNREFUSED, a certificate that will not verify -- off error.cause. An attempt list built from the outer messages would be as useless as the single line it replaces. Also require jwks_uri to be a string before using it, so a malformed discovery document is reported as such rather than as a failed fetch of "[object Object]". Refs Termix-SSH/Support#1047 * restore the closing quote on the version string (#1147) "Bump version from 2.6.0 to 2.6.1" ( |