Files
Termix/src/ui/locales/en.json
T
+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" (2a66775) wrote

  "version": "2.6.1,

dropping the closing quote, so package.json has not been valid JSON since.
Anything that parses it fails: npm install, npm run build, and every CI run
on this branch -- vitest cannot even load its config, because vite reads
package.json before it gets to the test files.

2.6.1 cannot be built or released until this is fixed, which is why it goes
in on its own rather than riding along with anything else.

* Revert "fix remote sync stalling after the first pass and never propagating deletions (#1140)" (#1146)

This reverts commit ca7abf8426.

Reverted for process, not for content. Both defects were reported by
@kacperpietrzyk in Support#1050 and Support#1051, and he opened #1138 and
#1139 fixing them 4.5 hours before #1140 was filed. Merging #1140 made two
PRs from the person who found and diagnosed the bugs redundant.

#1138 and #1139 stand on their own: the same root-cause analysis, complete
regression tests, and a tombstone guard that only pays for its extra lookup
on a pass that actually carries a deletion. There is no technical reason to
prefer the reverted commit over them.

The sync fixes land through those two PRs instead.

* fix: make sync deletions reach the other side (#1139)

* fix: apply sync tombstones to rows outside the incremental window

Deletions never reached the other side. `syncEntity` decides whether to apply a
tombstone by looking the row up in `localBySyncId` / `remoteBySyncId`, which are
built from `pullSide(..., since)` -- the incremental window. A row deleted on one
side and untouched on the other is by definition absent from that window, which
is the shape every ordinary deletion takes once the two sides have converged, so
the tombstone was silently skipped and never retried.

The guard cannot simply be dropped. `POST /sync/tombstones` records a tombstone
on the receiving side, so an unconditional push would give the other side a fresh
tombstone to push back on the following pass, and the two would trade deletions
forever.

Instead ask the receiving side what it still holds, ignoring the window, and only
when there is a deletion to apply -- so an ordinary pass costs nothing extra, and
a pass carrying a deletion costs one additional list per affected entity type.
Once the row is gone the push stops, so nothing ping-pongs.

Note this only becomes observable together with the cursor fix in
Termix-SSH/Support#1050: while that defect is present the tombstone endpoint
returns nothing at all, so there is no tombstone to apply in the first place.

Refs Termix-SSH/Support#1034

* fix: make the sync tombstone endpoint reachable

`POST /sync/tombstones` was registered after `POST /:entityType`, and Express
matches in registration order, so every deletion push was swallowed by the
wildcard: "tombstones" is a perfectly good value for :entityType, fails
isValidEntityType, and comes back as 400 "Unknown entity type". The handler
below it has never run.

Registering the literal path before the parameterised one restores it. The
regression test reads the router stack rather than the source, so a future
re-order fails the test rather than silently disabling deletions again.

The GET pair is unaffected -- "/:entityType/tombstones" and "/:entityType" have
different segment counts, so they cannot shadow each other.

* feat: add host export dialog with host and field selection (#1108)

* fix: compare sync cursors independently of timestamp layout (#1138)

* fix: compare sync cursors independently of timestamp layout

Incremental sync returned nothing after its first pass. `GET /sync/:entityType`
filters with `gt(table.updatedAt, since)` on a TEXT column, and the tombstone
endpoint does the same through `listSince`, but the two sides of that comparison
are written in different layouts: the columns default to `CURRENT_TIMESTAMP`
("2026-07-29 10:11:21") while the desktop engine sends `new Date().toISOString()`
("2026-07-29T10:06:55.172Z"). Text comparison is decided at position 10, where
' ' (0x20) sorts below 'T' (0x54), so the predicate answers on layout rather than
on time and is false for every CURRENT_TIMESTAMP row however new it is.

The engine only sends a cursor from the second pass onward, so pass 1 synced
everything and passes 2..n pulled zero rows and zero tombstones while reporting
success -- edits and deletions silently stopped propagating in both directions.

This was masked until now: before the reference fix in #1092 the loop threw
before persisting state, so the cursor never advanced past null and every cycle
was a full sync.

Comparing "YYYY-MM-DD HH:MM:SS" on both sides is layout-independent. `replace`
and `substr` are used rather than `datetime()` to keep the expression portable
across engines, since the repository layer is deliberately drizzle-only. The
comparison is `>=` because normalising truncates sub-second precision, and a
strict `>` would permanently skip rows written in the cursor's own second; the
re-sent boundary rows are a no-op, as the engine pushes only when one side is
strictly newer.

`updatedAt` is written in both layouts across the codebase (14 sites use
toISOString, 11 use CURRENT_TIMESTAMP), so the tests cover rows of each kind.

Closes Termix-SSH/Support#1050

* test: seed the cursor tests against the migrated schema

#1134 moved schema creation into the repository test harness, so the
hand-written CREATE TABLE blocks here collided with tables that already
existed. Seeding into the real tables instead surfaced two constraints the
local definitions had papered over: the harness enables foreign keys and
both `sync_tombstones.user_id` and `ssh_credentials.user_id` reference
`users`, so the owning row has to be seeded first; and `auth_type` is NOT
NULL with no default, unlike the local copy.

`exec` is awaited, since it only returns synchronously on SQLite.

The assertions are unchanged.

* Make Proxmox guest discovery and import reliable over a jump host (#1144)

* fix: repair unterminated version string in package.json

The version field on dev-2.6.1 reads "2.6.1, (no closing quote), which
makes package.json invalid JSON and breaks every npm invocation on the
branch. Close the string so the branch builds.

* fix(proxmox): reliable guest discovery and import over jump hosts

Importing Proxmox guests from a node reachable only through a jump host
(with the guests behind the same jump) failed in a chain of small ways.

- Discovery timed out intermittently: execCommand capped every pvesh call
  at 8s, but a single call over a jump measured ~8.3s. Raised to 25s for
  core calls and 12s for best-effort agent/interface lookups.
- No IPs were resolved (so nothing imported): resolveIp fanned out 6
  concurrent pvesh calls; on a small node they contend (3 concurrent
  already exceeded the timeout), so every IP came back empty. Lowered
  CONCURRENCY to 2.
- RDP guests aborted the whole sync via NOT NULL on ssh_data.username;
  use "" instead of null (matches the normal create path).
- Guests without a resolvable IP (e.g. QEMU with no guest agent) were
  skipped entirely; they now import with a 0.0.0.0 placeholder, and
  re-sync preserves any manually entered IP (guest.ip || existing.ip).
- Manual import did not inherit the source host's jump chain or
  credential (guests ended up unreachable with authType "none"). The
  discovery result now carries the source jumpHosts, and
  resolveProxmoxImportAuth uses an available credential even under the
  default "password" authType (explicit secretless choices still win).
- Long discoveries had no feedback and fought client/proxy timeouts;
  added an SSE endpoint GET /proxmox/discover/stream (heartbeat + n/N
  progress), keeping POST /discover as a fallback. Also always render the
  IP cell in the discovery table so IP-less rows stay aligned.

Adds a unit test for resolveProxmoxImportAuth covering the credential
inheritance behaviour.

* test(proxmox): lock resolveProxmoxImportAuth matrix on both copies; fix agent secretless drift

- extract the backend decision into src/backend/database/routes/proxmox-import-auth.ts
  (leaf module mirroring the UI copy) so it is unit-testable without pulling the
  whole backend module graph into the test env
- add src/backend/tests/database/routes/proxmox-import-auth.test.ts asserting the
  shared matrix (lifted from #1141, thanks @ZacharyZcR)
- consolidate the UI test into src/ui/tests/components/proxmox/proxmox-import-auth.test.ts
  and drop the duplicate src/ui/tests/proxmox/ copy
- add 'agent' to the UI SECRETLESS_AUTH_TYPES: the one real auth type where the two
  copies still diverged (UI -> credential, backend -> passthrough)

* fix(hosts): parse portKnockSequence JSON in host-resolver (#1149)

host-resolver JSON-parses jumpHosts/tunnelConnections/statsConfig/
terminalConfig/socks5ProxyChain/quickActions but NOT portKnockSequence.
Empty knock is stored as the string "[]" (UI save of empty array); the
terminal code then checks portKnockSequence.length > 0 on the STRING, so
"[]".length === 2 is truthy -> logs 'Loaded 2 port knock(s)' and attempts
a bogus knock. Real knock sequences (JSON string) are likewise never
parsed to the Array<{port,...}> that performPortKnocking expects, so a
genuine knock would never fire. Parse portKnockSequence like the other
JSON columns: '[]' -> [] (length 0, no knock), real seq -> array.

Adds unit tests for both cases.

Co-authored-by: XtraLarge <xtralarge@users.noreply.github.com>

* Feature request map OIDC provider groups to RBAC roles (#1148)

* Bump version from 2.6.0 to 2.6.1 in package-lock.json

* Fix formatting issue in package-lock.json

* Feature request map OIDC provider groups to RBAC roles

Group membership from an OIDC provider currently drives only a single
boolean: OIDC_ADMIN_GROUP toggles isAdmin and switches the user between
the built-in `admin` and `user` roles. There is no way to map a provider
group onto a custom role, so deployments that use host_access grants for
environment-scoped access (e.g. a role that can reach staging hosts and
another that can reach production) have to assign those roles by hand
for every user.

Add OIDC_ROLE_MAP, a comma- or newline-separated list of `group:role`
pairs, reconciled against the user's roles on each OIDC login:

  OIDC_ROLE_MAP=devops-interns:devops-intern,devops-seniors:devops-senior

Only roles named in the map are ever added or removed. Roles assigned by
hand, and the admin/user pair maintained by the existing admin-group
sync, are deliberately left untouched so the two mechanisms don't fight
each other. Group names are matched case-insensitively with leading
slashes stripped, so providers that emit full group paths (Keycloak's
"Full group path" option) work without extra configuration.

Reuses the existing extractOidcGroups claim handling, so custom claim
paths via OIDC_GROUP_CLAIM are supported too, and invalidates the
permission cache when roles change so new grants apply to the session
that triggered the sync. Malformed map entries are skipped and a failed
sync is logged but non-fatal — neither can block a valid login.

Adds unit tests for the parser and resolver covering full group paths,
multi-group membership, colons in group names and malformed input.

---------

Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>

* feat: support for overriding shared host ssh credentials (#1145)

* Bump version from 2.6.0 to 2.6.1 in package-lock.json

* Fix formatting issue in package-lock.json

* feat: support for overriding ssh credentials

---------

Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>

* ci(deps): bump the github-actions group with 2 updates (#1150)

* Bump version from 2.6.0 to 2.6.1 in package-lock.json

* Fix formatting issue in package-lock.json

* Update README to remove Tailscale and add Ginernet

Removed Tailscale logo and link from the README. Added Ginernet logo and link.

* Update README.md

* ci(deps): bump the github-actions group with 2 updates

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>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump the prod-minor-updates group with 3 updates (#1154)

* Bump version from 2.6.0 to 2.6.1 in package-lock.json

* Fix formatting issue in package-lock.json

* Update README to remove Tailscale and add Ginernet

Removed Tailscale logo and link from the README. Added Ginernet logo and link.

* Update README.md

* chore(deps): bump the prod-minor-updates group with 3 updates

Bumps the prod-minor-updates group with 3 updates: [axios](https://github.com/axios/axios), [motion](https://github.com/motiondivision/motion) and [undici](https://github.com/nodejs/undici).


Updates `axios` from 1.18.1 to 1.19.0
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.18.1...v1.19.0)

Updates `motion` from 12.42.2 to 12.43.0
- [Changelog](https://github.com/motiondivision/motion/blob/main/CHANGELOG.md)
- [Commits](https://github.com/motiondivision/motion/compare/v12.42.2...v12.43.0)

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: axios
  dependency-version: 1.19.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: motion
  dependency-version: 12.43.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- 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>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix: data guard test failure

* chore(deps): bump 23 dependencies and fix dialect-unsafe queries

Applies the non-major updates from the open dependabot PRs directly,
since dependabot rebases against main and could not resolve its
lockfiles against this branch. Holds back typescript 7 and jsdom 30;
those majors need their own pass.

Reformats with prettier 3.9.6, which collapses short union types onto
one line. Formatting only: the compiled backend output is byte for byte
identical.

Also fixes two lint errors in the shared host auth override repository,
where onConflictDoUpdate and .returning() are SQLite-only and broke the
Postgres and MySQL builds, and drops unused imports left over from the
shared host auth override merge.

* chore: reversal of legal work

* feat: improve pin side rail button position and added env var for telemetrics

* Add Ctrl+F terminal search (#1156)

* Bump version from 2.6.0 to 2.6.1 in package-lock.json

* Fix formatting issue in package-lock.json

* Update README to remove Tailscale and add Ginernet

Removed Tailscale logo and link from the README. Added Ginernet logo and link.

* Update README.md

* Add Ctrl+F terminal search

---------

Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>
Co-authored-by: LukeGus <bugattiguy527@gmail.com>

* fix: host export dialog using incorrect widths

* fix: made logger display expanded errors

* feat: added support for multi disk usage in file manager and host metrics

* chore: harden nginx headers and improve static asset caching

* chore: format

* chore: update release notes

* fix: default font size to md instead of lg

* feat: support Tailscale SSH check mode

* chore: sync Crowdin translations for 2.6.1

---------

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>
Co-authored-by: Brennan Neoh <497569+brennanneoh@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: kacperpietrzyk <105545577+kacperpietrzyk@users.noreply.github.com>
Co-authored-by: Max <50905012+maxiwolleb@users.noreply.github.com>
Co-authored-by: XtraLarge <eMail@WilliWerres.de>
Co-authored-by: XtraLarge <xtralarge@users.noreply.github.com>
Co-authored-by: Devin Dissanayaka <dsdissanayaka2002@gmail.com>
Co-authored-by: Peter Cinibulk <petercinibulk@gmail.com>
Co-authored-by: Med Ali Ezzeddine <47082236+xDaly@users.noreply.github.com>
2026-08-06 14:41:39 -05:00

3750 lines
176 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"termixId": {
"title": "Termix ID",
"loadFailed": "Failed to load Termix ID",
"claimTitle": "Claim your Termix ID",
"claimIntro": "Pick a unique handle. Your SSH public keys will be published at a public URL you can add to any server's authorized_keys file.",
"handleLabel": "Handle",
"handlePlaceholder": "alice",
"checking": "Checking…",
"available": "Available",
"taken": "Already taken",
"invalidHandle": "Lowercase letters, numbers, - and _ only",
"descriptionLabel": "Description (optional)",
"descriptionPlaceholder": "Work laptop & phone keys",
"create": "Create Termix ID",
"created": "Termix ID created",
"createFailed": "Failed to create Termix ID",
"deleteConfirm": "Delete your Termix ID and all published keys? Servers you provisioned will keep the keys until you remove them manually.",
"deleted": "Termix ID deleted",
"deleteFailed": "Failed to delete Termix ID",
"copyFailed": "Copy failed",
"resolverUrlLabel": "Public resolver URL",
"provisionLabel": "Provision a server",
"publishTitle": "Publish a public key",
"generate": "Generate",
"generateTooltip": "Generate an Ed25519 key pair — publishes the public key and downloads the private key to your device",
"saveToVault": "Save To Credentials",
"keyPlaceholder": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... user@host",
"labelPlaceholder": "Label (optional)",
"add": "Add",
"importFromCredential": "Or import from a stored credential",
"keyPublished": "Key published",
"addKeyFailed": "Failed to add key",
"generatedSaved": "Key pair generated and saved to your credentials vault. Private key also downloaded.",
"generatedOnly": "Key pair generated — private key downloaded (shown only once).",
"generateFailed": "Failed to generate key",
"imported": "Key imported from credential",
"importFailed": "Failed to import key",
"noKeys": "No public keys published yet.",
"keysTitle": "Published keys",
"published": "Published",
"hidden": "Hidden",
"keyRemoved": "Key removed",
"removeKeyFailed": "Failed to remove key",
"updateKeyFailed": "Failed to update key",
"fromVault": "From credentials vault",
"linkedToTermixId": "Published via Termix ID",
"selectCredential": "Select a credential...",
"import": "Import",
"caTitle": "Certificate authority",
"caIntro": "Trust this CA on a server and it accepts any certificate you sign. Rotate to revoke everything at once; certificates also expire on their own.",
"caEnable": "Enable CA",
"caEnabled": "CA enabled",
"caCreateFailed": "Failed to enable CA",
"caPublicKeyLabel": "CA public key",
"caTrustLabel": "Trust on a server (run as root)",
"caRotate": "Rotate",
"caRotateConfirm": "Rotate the CA? Every certificate it has signed will stop being accepted and must be re-issued.",
"caRotated": "CA rotated — previous certificates revoked",
"caRotateFailed": "Failed to rotate CA",
"caDelete": "Remove CA",
"caDeleteConfirm": "Remove the certificate authority?",
"caDeleted": "CA removed",
"caDeleteFailed": "Failed to remove CA",
"caValidityLabel": "Cert validity (days)",
"issueCert": "Certificate",
"issueCertTooltip": "Issue an SSH certificate for this key, signed by your CA",
"certIssued": "Certificate issued and downloaded",
"certIssueFailed": "Failed to issue certificate"
},
"credentials": {
"folders": "Folders",
"folder": "Folder",
"password": "Password",
"key": "Key",
"sshPrivateKey": "SSH Private Key",
"upload": "Upload",
"keyPassword": "Key Password",
"sshKey": "SSH Key",
"uploadPrivateKeyFile": "Upload Private Key File",
"searchCredentials": "Search credentials...",
"addCredential": "Add Credential",
"caCertificate": "CA Certificate (-cert.pub)",
"caCertificateDescription": "Optional: Upload or paste the CA-signed certificate file (e.g. id_ed25519-cert.pub). Required when your SSH server uses certificate-based authorization.",
"uploadCertFile": "Upload -cert.pub File",
"clearCert": "Clear",
"certLoaded": "Certificate loaded",
"certPublicKeyLabel": "CA Certificate",
"certTypeLabel": "Certificate type",
"pasteOrUploadCert": "Paste or upload a -cert.pub certificate...",
"hasCaCert": "Has CA Certificate",
"noCaCert": "No CA Certificate",
"noPublicKeyAvailable": "No public key available. Open the credential editor first.",
"deployCommandCopied": "Deploy command copied",
"sortCredentials": "Sort Credentials",
"sortDefault": "Default Order",
"sortNameAsc": "Name (A → Z)",
"sortNameDesc": "Name (Z → A)",
"sortUsernameAsc": "Username (A → Z)",
"sortUsernameDesc": "Username (Z → A)",
"filterCredentials": "Filter Credentials",
"filterClearAll": "Clear Filters",
"filterTypeGroup": "Type",
"filterTypePassword": "Password",
"filterTypeKey": "SSH Key",
"filterTagsGroup": "Tags"
},
"homepage": {
"title": "Homepage",
"addWidget": "Add Widget",
"editWidget": "Edit Widget",
"deleteWidget": "Delete",
"widgetTypes": "Widget Types",
"serviceLink": "Service Link",
"folder": "Folder",
"clock": "Clock",
"notes": "Notes",
"hostStatus": "Host Status",
"bookmarkList": "Bookmarks",
"zoomIn": "Zoom In",
"zoomOut": "Zoom Out",
"resetView": "Reset View",
"lockLayout": "Lock Layout",
"unlockLayout": "Unlock Layout",
"noWidgets": "Right-click or click + to add your first widget",
"openFullView": "Open Full View",
"previewTitle": "Homepage Preview",
"cancel": "Cancel",
"save": "Save",
"title_label": "Title",
"widgetTitlePlaceholder": "Widget title (optional)",
"url": "URL",
"imageUrl": "Custom Image URL",
"imageUrlHint": "Leave blank to use the site favicon automatically",
"showImage": "Show Image",
"description": "Description",
"color": "Color",
"icon": "Icon",
"expanded": "Expanded by default",
"timezone": "Timezone",
"showSeconds": "Show seconds",
"format12h": "12-hour",
"format24h": "24-hour",
"content": "Content",
"backgroundColor": "Background Color",
"host": "Host",
"showMetrics": "Show Metrics",
"links": "Links",
"addLink": "Add Link",
"linkLabel": "Label",
"linkUrl": "URL",
"removeLink": "Remove",
"categoryLinks": "Links",
"categoryInfo": "Info",
"categorySystem": "System",
"widgetServiceLinkDesc": "A clickable tile linking to a service URL",
"widgetFolderDesc": "A container to group related widgets",
"widgetClockDesc": "A live clock with configurable timezone",
"widgetNotesDesc": "A markdown notes widget",
"widgetHostStatusDesc": "Shows live CPU, memory and disk for an SSH host",
"widgetBookmarkListDesc": "A list of quick links",
"copyLink": "Copy Link",
"linkCopied": "Link copied!",
"location": "Location",
"temperatureUnit": "Temperature Unit",
"showForecast": "Show 3-day forecast",
"scrolling": "Allow Scrolling",
"feedUrl": "Feed URL",
"maxItems": "Max Items",
"showDescription": "Show Description",
"widgetWeatherName": "Weather",
"widgetWeatherDesc": "Live weather for any location",
"widgetIframeName": "iFrame Embed",
"widgetIframeDesc": "Embed any URL in an iframe",
"widgetRssName": "RSS Feed",
"widgetRssDesc": "Display items from an RSS or Atom feed",
"dragToFolder": "Drag widgets here or use + to add",
"showDisk": "Show Disk Usage",
"addToFolder": "Add widget to folder",
"noHostSelected": "No host selected",
"metricsNotAvailable": "Metrics not available",
"selectHost": "Select a host...",
"displayedMetrics": "Displayed Metrics",
"metricCpu": "CPU",
"metricMemory": "Memory",
"metricDisk": "Disk",
"metricUptime": "Uptime",
"metricSystem": "System",
"metricOs": "OS",
"metricKernel": "Kernel",
"metricHostname": "Hostname",
"metricNetwork": "Network",
"metricProcesses": "Processes",
"metricProcessesTotal": "total",
"metricProcessesRunning": "running",
"categoryMonitoring": "Monitoring",
"loading": "Loading...",
"noData": "No data available",
"allClear": "All clear",
"acknowledgeAlert": "Acknowledge",
"noPingUrls": "No URLs configured",
"pingLabel": "Label",
"addPingUrl": "Add URL",
"showLatency": "Show Latency",
"refreshInterval": "Refresh Interval",
"seconds": "seconds",
"filterActivityTypes": "Filter Types",
"filterTypesHint": "Leave empty to show all",
"showTimestamp": "Show Timestamp",
"uptimeUnavailable": "Uptime unavailable",
"uptimeLabel": "Uptime",
"overviewVersion": "Version",
"overviewUpdate": "Up to date",
"overviewUpdateAvailable": "Update available",
"overviewDatabase": "Database",
"overviewUptime": "Uptime",
"noHosts": "No hosts configured",
"hostGridHosts": "Hosts",
"hostGridAllHint": "Leave empty to show all hosts",
"columns": "Columns",
"showIp": "Show IP Address",
"connectionType": "Connection Type",
"layout": "Layout",
"showStatus": "Show Status",
"showHostName": "Show Host Name",
"noDockerActivity": "No Docker activity",
"noActivity": "No activity",
"showAcknowledged": "Show Acknowledged",
"showCurrentValue": "Show Current Value",
"chartMetric": "Metric",
"metricRange": "Range",
"widgetMetricsChartName": "Metrics Chart",
"widgetMetricsChartDesc": "Historical CPU, memory, disk or network chart for a host",
"widgetHostGridName": "Host Grid",
"widgetHostGridDesc": "Grid view of SSH host statuses",
"widgetAlertFeedName": "Alert Feed",
"widgetAlertFeedDesc": "Live alert firings with acknowledge support",
"widgetPingStatusName": "Ping Status",
"widgetPingStatusDesc": "HTTP ping status for one or more URLs",
"widgetRecentActivityName": "Recent Activity",
"widgetRecentActivityDesc": "Scrollable feed of recent Termix activity",
"widgetTermixUptimeName": "Termix Uptime",
"widgetTermixUptimeDesc": "Live uptime counter for the Termix server",
"widgetSystemOverviewName": "System Overview",
"widgetSystemOverviewDesc": "Termix version, database health and uptime at a glance",
"widgetSshQuickConnectName": "SSH Quick Connect",
"widgetSshQuickConnectDesc": "One-click buttons to open SSH sessions",
"widgetDockerActivityName": "Docker Activity",
"widgetDockerActivityDesc": "Recent Docker container events across all hosts",
"widgetCalendarName": "Calendar",
"widgetCalendarDesc": "A monthly calendar with today highlighted",
"widgetCountdownName": "Countdown",
"widgetCountdownDesc": "Countdown timer to a target date",
"widgetSearchBarName": "Search Bar",
"widgetSearchBarDesc": "Quick web search widget",
"widgetTextBannerName": "Text Banner",
"widgetTextBannerDesc": "A bold label or section header for the canvas",
"widgetImageWidgetName": "Image",
"widgetImageWidgetDesc": "Display an image from a URL",
"widgetMarkdownNotesName": "Markdown Notes",
"widgetMarkdownNotesDesc": "Rich notes with inline markdown rendering",
"widgetCustomApiName": "Custom API",
"widgetCustomApiDesc": "Fetch and display data from any JSON API",
"widgetServiceGridName": "Service Grid",
"widgetServiceGridDesc": "A configurable grid of service tile links",
"widgetDashboardLinksName": "Dashboard Links",
"widgetDashboardLinksDesc": "Display your configured service links from the dashboard",
"widgetSearchLinksName": "Search Shortcuts",
"widgetSearchLinksDesc": "Quick search shortcut buttons with inline input",
"widgetLinkTreeName": "Link Tree",
"widgetLinkTreeDesc": "Grouped sections of links with headings",
"calMon": "Mo",
"calTue": "Tu",
"calWed": "We",
"calThu": "Th",
"calFri": "Fr",
"calSat": "Sa",
"calSun": "Su",
"startOnMonday": "Start week on Monday",
"countdownNoDate": "No target date set",
"countdownPast": "Event has passed",
"countdownDays": "days",
"countdownHours": "hrs",
"countdownMinutes": "min",
"countdownSeconds": "sec",
"countdownLabel": "Label",
"countdownLabelPlaceholder": "e.g. Launch Day",
"countdownShowDays": "Show Days",
"countdownShowHours": "Show Hours",
"targetDate": "Target Date",
"searchEngine": "Search Engine",
"customSearchUrl": "Custom Search URL",
"searchPlaceholder": "Search...",
"searchGo": "Go",
"searchPlaceholderLabel": "Placeholder Text",
"searchPlaceholderHint": "Text shown inside the search input",
"openInNewTab": "Open in New Tab",
"searchQueryPlaceholder": "Enter query...",
"noSearchShortcuts": "No shortcuts configured",
"addSearchShortcut": "Add Shortcut",
"fontSize": "Font Size",
"textAlign": "Text Align",
"fontWeight": "Font Weight",
"clearColor": "Clear Color",
"imageFit": "Image Fit",
"imageLinkUrl": "Link URL",
"noImage": "No image URL set",
"altText": "Alt Text",
"altTextPlaceholder": "Describe the image",
"renderMarkdown": "Render Markdown",
"displayMode": "Display Mode",
"displayField": "Display Field",
"jsonPath": "JSON Path",
"customApiLabel": "Label",
"customApiLabelPlaceholder": "e.g. Temperature",
"customApiUnit": "Unit",
"customApiNoUrl": "No API URL configured",
"customApiError": "Failed to fetch",
"customApiNotArray": "Response is not an array",
"addService": "Add Service",
"showLabels": "Show Labels",
"iconSize": "Icon Size",
"noDashboardLinks": "No dashboard links configured",
"noLimit": "No limit",
"sectionHeading": "Section Heading",
"addSection": "Add Section",
"compactMode": "Compact Mode",
"showDetailed": "Show Detailed",
"selectHosts": "Select Hosts",
"allHosts": "All hosts",
"listLayout": "List",
"gridLayout": "Grid",
"terminal": "Terminal",
"files": "Files",
"docker": "Docker",
"range15m": "15 minutes",
"range1h": "1 hour",
"range6h": "6 hours",
"range24h": "24 hours",
"metricNetRx": "Net Download",
"metricNetTx": "Net Upload",
"severityFilter": "Severity Filter",
"filterAll": "All",
"accentColor": "Accent Color",
"widgetSshTerminalName": "SSH Terminal",
"widgetSshTerminalDesc": "An inline SSH terminal connected to a configured host",
"sshTerminalNoHost": "No host configured",
"sshTerminalConnect": "Connect",
"sshTerminalAutoConnect": "Auto-connect on load",
"widgetQuickConnectName": "Quick Connect",
"widgetQuickConnectDesc": "One-click launch buttons for any connection type across your hosts",
"connectionTypes": "Connection Types",
"connType_terminal": "Terminal",
"connType_files": "File Manager",
"connType_docker": "Docker",
"connType_tunnel": "Tunnel",
"connType_host-metrics": "Host Metrics",
"connType_rdp": "RDP",
"connType_vnc": "VNC",
"connType_telnet": "Telnet",
"widgetFileManagerName": "File Manager",
"widgetFileManagerDesc": "Embedded SFTP file manager for a configured host",
"widgetDockerName": "Docker Manager",
"widgetDockerDesc": "Embedded Docker container manager for a configured host",
"widgetTunnelName": "Tunnel Manager",
"widgetTunnelDesc": "Embedded SSH tunnel manager for a configured host",
"widgetNoHostSelected": "No host configured"
},
"serverConfig": {
"title": "Server Configuration",
"description": "Configure the Termix server URL to connect to your backend services",
"serverUrl": "Server URL",
"enterServerUrl": "Please enter a server URL",
"saveFailed": "Failed to save configuration",
"saveError": "Error saving configuration",
"saving": "Saving...",
"saveConfig": "Save Configuration",
"helpText": "Enter the URL where your Termix server is running (e.g., http://localhost:30001 or https://your-server.com)",
"changeServer": "Change Server",
"mustIncludeProtocol": "Server URL must start with http:// or https://",
"allowInvalidCertificate": "Allow invalid certificate",
"allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.",
"useEmbedded": "Use Local Server",
"embeddedDesc": "Run Termix with the built-in local server (no remote server needed)",
"embeddedConnecting": "Connecting to local server...",
"embeddedNotReady": "Local server is not ready yet. Please wait a moment and try again.",
"localServer": "Local Server",
"savedServers": "Saved Servers",
"noSavedServers": "No saved servers",
"removeServer": "Remove"
},
"migrationNotice": {
"title": "Termix Desktop now runs standalone",
"body1": "This app used to connect straight to your Termix server. It's been reworked to run fully on its own, storing hosts and credentials locally so it keeps working offline. Two-way sync with a self-hosted Termix server is now optional.",
"body2": "Your existing hosts and credentials are still on {{url}}. Turn on Remote Sync and reconnect to that server to bring them back and keep both in sync going forward.",
"dismiss": "Not now",
"setUpSync": "Set Up Remote Sync"
},
"remoteSync": {
"title": "Remote Sync",
"description": "Optionally connect this desktop app to a self-hosted Termix server to sync your hosts, credentials, and snippets across devices. The app always works fully offline whether or not you connect.",
"notConnected": "Not connected",
"connected": "Connected",
"connectedTo": "Connected to {{url}}",
"lastSynced": "Last synced {{time}}",
"neverSynced": "Never synced",
"syncError": "Sync error: {{message}}",
"needsReauth": "Sign-in expired",
"connectButton": "Connect to Server",
"disconnectButton": "Disconnect",
"syncNowButton": "Sync Now",
"syncing": "Syncing...",
"serverUrl": "Server URL",
"enterServerUrl": "Please enter a server URL",
"mustIncludeProtocol": "Server URL must start with http:// or https://",
"connectionTestFailed": "Could not reach a Termix server at that URL",
"allowInvalidCertificate": "Allow invalid certificate",
"allowInvalidCertificateDesc": "Use only for trusted self-hosted servers with self-signed or IP-address certificates.",
"savedServers": "Saved Servers",
"removeServer": "Remove",
"continueButton": "Continue",
"cancelButton": "Cancel",
"signInTitle": "Sign in to {{url}}",
"originTitle": "Connection Origin",
"originDescription": "Choose where SSH connections originate from by default. This can be overridden per host.",
"originLocal": "This device (local network)",
"originRemote": "Remote server",
"bannerReconnect": "Reconnect",
"bannerMessage": "Remote sync needs re-authentication"
},
"versionCheck": {
"error": "Version Check Error",
"checkFailed": "Failed to check for updates",
"upToDate": "App is Up to Date",
"currentVersion": "You are running version {{version}}",
"updateAvailable": "Update Available",
"newVersionAvailable": "A new version is available! You are running {{current}}, but {{latest}} is available.",
"betaVersion": "Beta Version",
"betaVersionDesc": "You are running {{current}}, which is newer than the latest stable release {{latest}}.",
"releasedOn": "Released on {{date}}",
"downloadUpdate": "Download Update",
"checking": "Checking for updates...",
"checkUpdates": "Check for Updates",
"checkingUpdates": "Checking for updates...",
"updateRequired": "Update Required"
},
"common": {
"close": "Close",
"minimize": "Minimize",
"online": "Online",
"offline": "Offline",
"unknown": "Unknown",
"continue": "Continue",
"maintenance": "Maintenance",
"degraded": "Degraded",
"error": "Error",
"warning": "Warning",
"unsavedChanges": "Unsaved changes",
"dismiss": "Dismiss",
"loading": "Loading...",
"optional": "Optional",
"connect": "Connect",
"copied": "Copied",
"connecting": "Connecting...",
"updateAvailable": "Update Available",
"appName": "Termix",
"openInNewTab": "Open in New Tab",
"noReleases": "No Releases",
"updatesAndReleases": "Updates & Releases",
"newVersionAvailable": "A new version ({{version}}) is available.",
"failedToFetchUpdateInfo": "Failed to fetch update information",
"preRelease": "Pre-release",
"noReleasesFound": "No releases found.",
"cancel": "Cancel",
"username": "Username",
"login": "Login",
"logout": "Logout",
"register": "Register",
"password": "Password",
"confirmPassword": "Confirm Password",
"back": "Back",
"save": "Save",
"saving": "Saving...",
"delete": "Delete",
"rename": "Rename",
"edit": "Edit",
"add": "Add",
"confirm": "Confirm",
"no": "No",
"or": "OR",
"next": "Next",
"previous": "Previous",
"refresh": "Refresh",
"language": "Language",
"checking": "Checking...",
"checkingDatabase": "Checking database connection...",
"checkingAuthentication": "Checking authentication...",
"backendReconnected": "Server connection restored",
"connectionDegraded": "Server connection lost, recovering…",
"reload": "Reload",
"remove": "Remove",
"create": "Create",
"update": "Update",
"copy": "Copy",
"copyFailed": "Failed to copy to clipboard",
"maximize": "Maximize",
"restore": "Restore",
"of": "of",
"saved": "Saved",
"deleted": "Deleted",
"deleteFailed": "Failed to delete",
"saveFailed": "Failed to save",
"required": "Required"
},
"nav": {
"home": "Home",
"terminal": "Terminal",
"docker": "Docker",
"tunnels": "Tunnels",
"fileManager": "File Manager",
"serverStats": "Host Metrics",
"hostMetrics": "Host Metrics",
"admin": "Admin",
"termixId": "ID",
"userProfile": "User Profile",
"splitScreen": "Split Screen",
"confirmClose": "Close this active session?",
"close": "Close",
"cancel": "Cancel",
"sshManager": "SSH Manager",
"cannotSplitTab": "Cannot split this tab",
"hostTabTitle": "{{username}}@{{ip}}:{{port}}",
"copyPassword": "Copy Password",
"copySudoPassword": "Copy Sudo Password",
"passwordCopied": "Password copied to clipboard",
"noPasswordAvailable": "No password available",
"failedToCopyPassword": "Failed to copy password",
"refreshTab": "Refresh connection",
"renameTab": "Rename tab",
"openFileManager": "Open File Manager",
"dashboard": "Dashboard",
"networkGraph": "Network Graph",
"tmuxMonitor": "Tmux Monitor",
"homepage": "Homepage",
"quickConnect": "Quick Connect",
"sshTools": "SSH Tools",
"history": "History",
"sessionLogs": "Session Logs",
"sidebarSettings": "Sidebar Settings...",
"hosts": "Hosts",
"snippets": "Snippets",
"hostManager": "Host Manager",
"credentials": "Credentials",
"connections": "Connections",
"alerts": "Alerts",
"serial": "Serial",
"roleAdministrator": "Administrator",
"roleUser": "User"
},
"hosts": {
"hosts": "Hosts",
"noHosts": "No SSH Hosts",
"retry": "Retry",
"refresh": "Refresh",
"optional": "Optional",
"downloadSample": "Download Sample",
"failedToDeleteHost": "Failed to delete {{name}}",
"importSkipExisting": "Import (skip existing)",
"importSSHConfig": "Import from SSH config",
"connectionDetails": "Connection Details",
"ssh": "SSH",
"telnet": "Telnet",
"remoteDesktop": "Remote Desktop",
"port": "Port",
"username": "Username",
"folder": "Folder",
"tags": "Tags",
"pin": "Pin",
"addHost": "Add Host",
"editHost": "Edit Host",
"cloneHost": "Clone Host",
"enableTerminal": "Enable Terminal",
"enableTunnel": "Enable Tunnel",
"enableFileManager": "Enable File Manager",
"enableDocker": "Enable Docker",
"defaultPath": "Default Path",
"connection": "Connection",
"upload": "Upload",
"authentication": "Authentication",
"password": "Password",
"key": "Key",
"credential": "Credential",
"none": "None",
"sshPrivateKey": "SSH Private Key",
"keyType": "Key Type",
"uploadFile": "Upload File",
"tabGeneral": "General",
"tabSsh": "SSH",
"tabTerminal": "Terminal",
"tabRdp": "RDP",
"tabVnc": "VNC",
"tabTunnels": "Tunnels",
"tabDocker": "Docker",
"tabFiles": "Files",
"tabStats": "Host Metrics",
"tabHostMetrics": "Host Metrics",
"tabTelnet": "Telnet",
"tabSharing": "Sharing",
"tabAuthentication": "Authentication",
"terminal": "Terminal",
"tunnel": "Tunnel",
"fileManager": "File Manager",
"serverStats": "Host Metrics",
"status": "Status",
"folderRenamed": "Folder \"{{oldName}}\" renamed to \"{{newName}}\" successfully",
"failedToRenameFolder": "Failed to rename folder",
"movedToFolder": "Moved {{count}} host(s) to \"{{folder}}\"",
"editHostTooltip": "Edit host",
"statusChecks": "Status Checks",
"metricsCollection": "Metrics Collection",
"metricsInterval": "Metrics Collection Interval",
"metricsIntervalDesc": "How often to collect server statistics (5s - 1h)",
"behavior": "Behavior",
"themePreview": "Theme Preview",
"theme": "Theme",
"fontFamily": "Font Family",
"fontSize": "Font Size",
"letterSpacing": "Letter Spacing",
"lineHeight": "Line Height",
"cursorStyle": "Cursor Style",
"cursorBlink": "Cursor Blink",
"scrollbackBuffer": "Scrollback Buffer",
"bellStyle": "Bell Style",
"rightClickSelectsWord": "Right Click Selects Word",
"fastScrollModifier": "Fast Scroll Modifier",
"fastScrollSensitivity": "Fast Scroll Sensitivity",
"sshAgentForwarding": "SSH Agent Forwarding",
"backspaceMode": "Backspace Mode",
"startupSnippet": "Startup Snippet",
"selectSnippet": "Select snippet",
"forceKeyboardInteractive": "Force Keyboard-Interactive",
"overrideCredentialUsername": "Override Credential Username",
"overrideCredentialUsernameDesc": "Use the username specified above instead of the credential's username",
"oidcUsernameHint": "Use $oidc.preferred_username to substitute your OIDC login name.",
"tailscaleUsernameHint": "This must be a Unix user your Tailscale identity is granted in the tailnet's SSH ACL, not necessarily root.",
"jumpHostChain": "Jump Host Chain",
"portKnocking": "Port Knocking",
"addKnock": "Add Port",
"addProxyNode": "Add Node",
"proxyNode": "Proxy Node",
"proxyType": "Proxy Type",
"quickActions": "Quick Actions",
"sudoPasswordAutoFill": "Sudo Password Auto-Fill",
"sudoPassword": "Sudo Password",
"keepaliveInterval": "Keepalive Interval (ms)",
"moshCommand": "MOSH Command",
"environmentVariables": "Environment Variables",
"addVariable": "Add Variable",
"docker": "Docker",
"copyTerminalUrl": "Copy Terminal URL",
"copyFileManagerUrl": "Copy File Manager URL",
"copyRemoteDesktopUrl": "Copy Remote Desktop URL",
"failedToConnect": "Failed to connect to console",
"connect": "Connect",
"disconnect": "Disconnect",
"start": "Start",
"enableStatusCheck": "Enable Status Check",
"enableMetrics": "Enable Metrics",
"bulkUpdateFailed": "Bulk update failed",
"selectAll": "Select All",
"deselectAll": "Deselect All",
"protocols": "Protocols",
"secureShell": "Secure Shell",
"virtualNetwork": "Virtual Network",
"unencryptedShell": "Unencrypted shell",
"addressIp": "Address / IP",
"friendlyName": "Friendly Name",
"macAddress": "MAC Address",
"wolBroadcastAddress": "WoL Broadcast Address",
"wolBroadcastAddressDesc": "Optional directed broadcast for Docker/routed networks (e.g. 192.168.1.255). Leave empty to use 255.255.255.255.",
"folderAndAdvanced": "Folder & Advanced",
"privateNotes": "Private Notes",
"privateNotesPlaceholder": "Details about this server...",
"pinToTop": "Pin to Top",
"pinToTopDesc": "Always show this host at the top of the list",
"portKnockingSequence": "Port Knocking Sequence",
"addKnockBtn": "Add Knock",
"noPortKnocking": "No port knocking configured.",
"knockPort": "Knock Port",
"protocol": "Protocol",
"delayAfterMs": "Delay After (ms)",
"useSocks5Proxy": "Use SOCKS5 Proxy",
"useSocks5ProxyDesc": "Route connection through a proxy server",
"connectionOrigin": "Connection Origin",
"connectionOriginDesc": "Where this host's SSH connection originates from. Overrides the desktop app's global default.",
"connectionOriginDefault": "Use default",
"connectionOriginLocal": "This device (local network)",
"connectionOriginRemote": "Remote server",
"proxyHost": "Proxy Host",
"proxyPort": "Proxy Port",
"proxyUsername": "Proxy Username",
"proxyPassword": "Proxy Password",
"proxySingleMode": "Single Proxy",
"proxyChainMode": "Proxy Chain",
"you": "You",
"jumpHostChainLabel": "Jump Host Chain",
"addJumpBtn": "Add Jump",
"noJumpHosts": "No jump hosts configured.",
"selectAServer": "Select a server...",
"sshPort": "SSH Port",
"authMethod": "Auth Method",
"storedCredential": "Stored Credential",
"selectACredential": "Select a credential...",
"vaultProfile": "Vault Signer Profile",
"selectAVaultProfile": "Select a Vault profile...",
"vaultProfileHint": "Settings come from the shared profile; you'll sign in to Vault via OIDC when you connect. No secrets are stored.",
"vaultNewProfile": "New profile",
"vaultManageProfiles": "Manage Vault profiles",
"vaultAddrLabel": "Vault Address",
"vaultNamespaceLabel": "Namespace",
"vaultOidcMountLabel": "OIDC Auth Mount",
"vaultOidcRoleLabel": "OIDC Role",
"vaultSshMountLabel": "SSH Secrets Mount",
"vaultSshRoleLabel": "SSH Signer Role",
"vaultValidPrincipalsLabel": "Valid Principals",
"vaultKeyTypeLabel": "Ephemeral Key Type",
"vaultSharedLabel": "Share with all users",
"vaultCreateProfile": "Create profile",
"vaultProfileCreated": "Vault profile created",
"vaultProfileSaved": "Vault profile saved",
"vaultProfileDeleted": "Vault profile deleted",
"vaultProfileSaveFailed": "Failed to save Vault profile",
"vaultProfileDeleteFailed": "Failed to delete Vault profile",
"vaultSaveProfile": "Save profile",
"vaultProfileValidationError": "Name, Vault address and SSH signer role are required",
"vaultNoProfiles": "No Vault profiles yet.",
"vaultSharedBadge": "shared",
"keyTypeLabel": "Key Type",
"keyTypeAuto": "Auto Detect",
"keyPasteTab": "Paste",
"keyUploadTab": "Upload",
"keyFileLoaded": "Key file loaded",
"keyUploadClick": "Click to upload .pem / .key / .ppk",
"clearKey": "Clear key",
"keySaved": "SSH key saved",
"keyReplaceNotice": "paste a new key below to replace it",
"keyPassphraseSaved": "Passphrase saved, type to change",
"replaceKey": "Replace key",
"docsLink": "View docs",
"opksshLabel": "OPKSSH",
"opksshDesc": "Sign in to this host using your identity provider instead of a password or key. Requires OPKSSH set up on the server.",
"warpgateLabel": "Warpgate Gateway",
"warpgateDesc": "This host connects through a Warpgate SSH proxy. Termix will handle the browser-based approval flow automatically after authenticating.",
"agentLabel": "SSH Agent",
"agentDesc": "Authenticate using an SSH agent running on the Termix server (Bitwarden, 1Password, gpg-agent, KeeAgent, ssh-agent). The agent must be running on the machine where Termix is hosted.",
"agentSocketPathLabel": "Agent Socket Path",
"agentSocketPathPlaceholder": "Leave empty to use SSH_AUTH_SOCK",
"agentSocketPathHint": "Leave empty to auto-detect from the SSH_AUTH_SOCK environment variable, or enter a custom socket path (e.g. /run/user/1000/gnupg/S.gpg-agent.ssh).",
"shareSshAuthLabel": "Share SSH Authentication",
"shareSshAuthDesc": "Give recipients encrypted copies of this host's SSH authentication. A recipient's personal credential still takes precedence.",
"tailscaleDeviceSelect": "Select Tailscale device",
"tailscaleDeviceSelectPlaceholder": "Select a device...",
"tailscaleNoApiKey": "No Tailscale API key configured. Add one in Admin Settings to enable device discovery.",
"tailscaleDocsLink": "View docs",
"tailscaleLoadingDevices": "Loading devices...",
"tailscaleNoDevices": "No devices found in your tailnet.",
"tailscaleDeviceAutoFill": "Selecting a device will auto-fill the host IP address.",
"forceKeyboardInteractiveLabel": "Force Keyboard Interactive",
"forceKeyboardInteractiveShortDesc": "Force manual password entry even if keys are present",
"allowLegacyAlgorithmsLabel": "Allow Legacy Algorithms",
"allowLegacyAlgorithmsDesc": "Enable deprecated SSH algorithms (ssh-dss, ssh-rsa, diffie-hellman-group1-sha1, hmac-md5, 3des-cbc) for connections to old devices that cannot be upgraded.",
"insecure": "Insecure",
"terminalAppearance": "Terminal Appearance",
"colorTheme": "Color Theme",
"fontFamilyLabel": "Font Family",
"fontSizeLabel": "Font Size",
"cursorStyleLabel": "Cursor Style",
"letterSpacingPx": "Letter Spacing (px)",
"lineHeightLabel": "Line Height",
"bellStyleLabel": "Bell Style",
"backspaceModeLabel": "Backspace Mode",
"cursorBlinking": "Cursor Blinking",
"cursorBlinkingDesc": "Enable blinking animation for the terminal cursor",
"rightClickSelectsWordLabel": "Right-click Selects Word",
"rightClickSelectsWordShortDesc": "Select the word under cursor on right-click",
"backgroundImageLabel": "Background Image URL",
"backgroundImageDesc": "Optional URL for a terminal background image",
"backgroundImageOpacityLabel": "Background Image Opacity",
"customThemeColors": "Custom Colors",
"customThemeBackground": "Background",
"customThemeForeground": "Foreground",
"customThemeCursor": "Cursor",
"customThemeCursorAccent": "Cursor Accent",
"customThemeSelection": "Selection",
"customThemeAnsiColors": "ANSI Colors",
"customThemeBlack": "Black",
"customThemeRed": "Red",
"customThemeGreen": "Green",
"customThemeYellow": "Yellow",
"customThemeBlue": "Blue",
"customThemeMagenta": "Magenta",
"customThemeCyan": "Cyan",
"customThemeWhite": "White",
"customThemeBrightBlack": "Bright Black",
"customThemeBrightRed": "Bright Red",
"customThemeBrightGreen": "Bright Green",
"customThemeBrightYellow": "Bright Yellow",
"customThemeBrightBlue": "Bright Blue",
"customThemeBrightMagenta": "Bright Magenta",
"customThemeBrightCyan": "Bright Cyan",
"customThemeBrightWhite": "Bright White",
"customThemeResetTooltip": "Reset to defaults",
"savedThemesLabel": "Saved Themes",
"saveAsGlobalTheme": "Save as Global Theme",
"saveGlobalThemeNamePrompt": "Enter a name for this theme",
"saveGlobalThemeSuccess": "Theme saved",
"saveGlobalThemeError": "Failed to save theme",
"applyGlobalThemeTooltip": "Apply this theme",
"deleteGlobalThemeTooltip": "Delete this theme",
"noSavedThemes": "No saved themes yet",
"syntaxHighlightingLabel": "Syntax Highlighting",
"syntaxHighlightingDesc": "Colorize terminal output (errors, paths, IPs, timestamps)",
"syntaxHighlightingCategories": "Highlight Categories",
"syntaxHighlightingCategoriesDesc": "Choose which types of content to colorize",
"syntaxCategoryLogLevels": "Log Levels",
"syntaxCategoryLogLevelsDesc": "error, warn, info, debug, fatal",
"syntaxCategoryPaths": "File Paths",
"syntaxCategoryPathsDesc": "/usr/share/doc, ~/file.txt",
"syntaxCategoryTimestamps": "Timestamps",
"syntaxCategoryTimestampsDesc": "[12:34:56], 2024-01-15",
"syntaxCategoryIpAddresses": "IP Addresses",
"syntaxCategoryIpAddressesDesc": "192.168.1.1, 10.0.0.1:8080",
"syntaxCategoryUrls": "URLs",
"syntaxCategoryUrlsDesc": "https://example.com",
"syntaxCategoryNumbers": "Labeled Numbers",
"syntaxCategoryNumbersDesc": "port 8080, exit 1, status 404",
"behaviorAndAdvanced": "Behavior & Advanced",
"scrollbackBufferLabel": "Scrollback Buffer",
"scrollbackMaxLines": "Maximum number of lines kept in history",
"sshAgentForwardingLabel": "SSH Agent Forwarding",
"sshAgentForwardingShortDesc": "Pass your local SSH keys to this host",
"useSSHTitleLabel": "Use SSH Window Title",
"useSSHTitleDesc": "Update the tab title from the shell's window title instead of the host name",
"enableAutoMosh": "Enable Auto-Mosh",
"enableAutoMoshDesc": "Prefer Mosh over SSH if available",
"enableAutoTmux": "Enable Auto-Tmux",
"enableAutoTmuxDesc": "Automatically launch or attach to tmux session",
"enableSessionLogging": "Session Logging",
"enableSessionLoggingDesc": "Record terminal session output for later review",
"allowSessionSharing": "Allow Session Sharing",
"allowSessionSharingDesc": "Let live sessions on this host be shared via link or with other users",
"enableCommandHistory": "Command History",
"enableCommandHistoryDesc": "Record commands run in this terminal for history and autocomplete",
"linkClickBehaviorLabel": "Link Click Behavior",
"linkClickBehaviorDesc": "Controls what happens when you click a link in the terminal. Use 'Default' to follow the app-level setting.",
"linkClickBehaviorDefault": "Default (follow app setting)",
"linkClickBehaviorConfirm": "Show popup to open or copy",
"linkClickBehaviorDirect": "Open directly",
"sudoPasswordAutoFillLabel": "Sudo Password Auto-fill",
"sudoPasswordAutoFillShortDesc": "Automatically provide sudo password when prompted",
"sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.",
"sudoPasswordLabel": "Sudo Password",
"environmentVariablesLabel": "Environment Variables",
"addVariableBtn": "Add Variable",
"noEnvVars": "No environment variables configured.",
"fastScrollModifierLabel": "Fast Scroll Modifier",
"fastScrollSensitivityLabel": "Fast Scroll Sensitivity",
"moshCommandLabel": "Mosh Command",
"startupSnippetLabel": "Startup Snippet",
"keepaliveIntervalLabel": "Keepalive Interval (seconds)",
"maxKeepaliveMisses": "Max Keepalive Misses",
"tunnelSettings": "Tunnel Settings",
"enableTunneling": "Enable Tunneling",
"enableTunnelingDesc": "Enable SSH tunnel functionality for this host",
"serverTunnelsSection": "Server Tunnels",
"addTunnelBtn": "Add Tunnel",
"noTunnelsConfigured": "No tunnels configured.",
"tunnelLabel": "Tunnel {{number}}",
"tunnelType": "Tunnel Type",
"tunnelModeLocalDesc": "Forward a local port to a port on the remote server (or a host reachable from it).",
"tunnelModeRemoteDesc": "Forward a port on the remote server back to a local port on your machine.",
"tunnelModeDynamicDesc": "Create a SOCKS5 proxy on a local port for dynamic port forwarding.",
"sameHost": "This host (direct tunnel)",
"endpointHost": "Endpoint Host",
"endpointHostPlaceholder": "Same host, SSH host, or reachable host/IP",
"endpointPort": "Endpoint Port",
"bindHost": "Bind Host",
"sourcePort": "Source Port",
"maxRetries": "Max Retries",
"retryIntervalS": "Retry Interval (s)",
"autoStartLabel": "Auto-start",
"autoStartDesc": "Automatically connect this tunnel when the host is loaded",
"tunnelConnecting": "Tunnel connecting...",
"tunnelDisconnected": "Tunnel disconnected",
"failedToConnectTunnel": "Failed to connect",
"failedToDisconnectTunnel": "Failed to disconnect",
"dockerIntegration": "Docker Integration",
"enableDockerMonitor": "Enable Docker",
"enableDockerMonitorDesc": "Monitor and manage containers on this host via Docker",
"containerRuntime": "Container Runtime",
"containerRuntimeDesc": "Choose the CLI used for container management on this host",
"containerRuntimeDocker": "Docker",
"containerRuntimePodman": "Podman",
"enableTmuxMonitor": "Enable Tmux Monitor",
"enableTmuxMonitorDesc": "Show this host in the Tmux Monitor and add its tmux actions to the sidebar",
"tabProxmox": "Proxmox",
"proxmoxIntegration": "Proxmox Integration",
"enableProxmox": "Enable Proxmox",
"enableProxmoxDesc": "Mark this host as a Proxmox node. Enables guest discovery and import directly from this host.",
"proxmoxDefaultAuthType": "Default Auth Type",
"proxmoxDefaultAuthTypeDesc": "Authentication method applied to imported guest hosts. Choose the auth type that matches how you connect to your guests.",
"authTypePassword": "Password",
"authTypeKey": "SSH Key",
"authTypeCredential": "Credential",
"authTypeOpkssh": "OPKSSH",
"authTypeNone": "None",
"proxmoxDefaultCredential": "Default Credential",
"proxmoxDefaultCredentialDesc": "Credential used for imported guest hosts when auth type is set to Credential.",
"proxmoxWindowsDetection": "Windows / RDP detection",
"proxmoxWindowsDetectionDesc": "Comma-separated name patterns that trigger RDP instead of SSH (case-insensitive)",
"proxmoxDockerDetection": "Docker detection",
"proxmoxDockerDetectionDesc": "Comma-separated name patterns that enable Docker for matching guests",
"proxmoxPreferredRanges": "Preferred IP ranges",
"proxmoxPreferredRangesDesc": "Comma-separated prefixes in priority order for IP selection when a guest has multiple interfaces",
"proxmoxAutoSync": "Auto sync guests",
"proxmoxAutoSyncDesc": "Periodically discover this Proxmox node and create or update imported guest hosts while your session is unlocked.",
"proxmoxSyncInterval": "Sync interval (minutes)",
"proxmoxSyncIntervalDesc": "Minimum 5 minutes. The scheduler skips locked user sessions.",
"proxmoxMarkMissing": "Mark missing guests",
"proxmoxMarkMissingDesc": "Add a proxmox-missing tag when a previously imported guest disappears instead of deleting it.",
"proxmoxLastSync": "Last sync",
"proxmoxLastSyncNever": "Not synced yet",
"proxmoxLastSyncNoResult": "No result details",
"proxmoxLastSyncSummary": "{{created}} created, {{updated}} updated, {{markedMissing}} missing, {{skipped}} skipped",
"proxmoxLastSyncStatus": {
"success": "Success",
"error": "Failed",
"pending": "Pending"
},
"proxmoxDiscoverAction": "Discover & import Proxmox guests",
"proxmoxImportTitle": "Import from Proxmox",
"proxmoxSelectHost": "Select a Proxmox host…",
"proxmoxDiscover": "Discover",
"proxmoxDiscovering": "Discovering…",
"proxmoxDiscoverGuests": "Discover guests",
"proxmoxGuestsSelected_one": "{{count}} guest — {{selected}} selected",
"proxmoxGuestsSelected_other": "{{count}} guests — {{selected}} selected",
"proxmoxSelectAll": "Select all",
"proxmoxDeselectAll": "Deselect all",
"proxmoxNoGuests": "No guests found on this Proxmox node.",
"proxmoxImportButton_one": "Import {{count}} host",
"proxmoxImportButton_other": "Import {{count}} hosts",
"proxmoxResultImported": "{{count}} imported",
"proxmoxResultUpdated": "{{count}} updated",
"proxmoxResultFailed": "{{count}} failed",
"proxmoxResultSkippedNoIp": "{{count}} skipped (no IP found)",
"proxmoxImportComplete": "Proxmox import complete: {{summary}}",
"proxmoxDiscoveryFailed": "Discovery failed",
"proxmoxImportFailed": "Import failed",
"enableFileManagerMonitor": "Enable File Manager",
"enableFileManagerMonitorDesc": "Browse and manage files on this host over SFTP",
"scpLegacyLabel": "SCP Legacy Mode",
"scpLegacyDesc": "Use legacy file transfer for servers that do not support the SFTP subsystem (e.g. embedded or minimal SSH servers).",
"defaultPathLabel": "Default Path",
"fileManagerPathHint": "The directory to open when the file manager launches for this host.",
"statusChecksLabel": "Status Checks",
"enableStatusChecks": "Enable Status Checks",
"enableStatusChecksDesc": "Periodically ping this host to verify availability",
"useGlobalInterval": "Use Global Interval",
"useGlobalIntervalDesc": "Override with the server-wide status check interval",
"checkIntervalS": "Check Interval (s)",
"checkIntervalDesc": "Seconds between each connectivity ping",
"metricsCollectionLabel": "Metrics Collection",
"enableMetricsLabel": "Enable Metrics",
"enableMetricsDesc": "Collect CPU, RAM, disk, and other metrics from this host",
"useGlobalMetrics": "Use Global Interval",
"useGlobalMetricsDesc": "Override with the server-wide metrics interval",
"metricsIntervalS": "Metrics Interval (s)",
"metricsIntervalDesc2": "Seconds between metric snapshots",
"visibleWidgets": "Visible Widgets",
"widgetsMovedToHostMetrics": "Cards are now added, arranged, and resized directly in the Host Metrics tab. Open Host Metrics for this host and use Customize to choose which cards are shown.",
"cpuUsageLabel": "CPU Usage",
"cpuUsageDesc": "CPU percent, load averages, sparkline graph",
"memoryLabel": "Memory Usage",
"memoryDesc": "RAM usage, swap, cached",
"storageLabel": "Disk Usage",
"storageDesc": "Disk usage per mount point",
"networkLabel": "Network Interfaces",
"networkDesc": "Interface list and bandwidth",
"uptimeLabel": "Uptime",
"uptimeDesc": "System uptime and boot time",
"systemInfoLabel": "System Info",
"systemInfoDesc": "OS, kernel, hostname, architecture",
"recentLoginsLabel": "Recent Logins",
"recentLoginsDesc": "Successful and failed login events",
"topProcessesLabel": "Top Processes",
"topProcessesDesc": "PID, CPU%, MEM%, command",
"listeningPortsLabel": "Listening Ports",
"listeningPortsDesc": "Open ports with process and state",
"firewallLabel": "Firewall",
"firewallDesc": "Firewall, AppArmor, SELinux status",
"quickActionsLabel": "Quick Actions",
"quickActionsToolbar": "Quick actions appear as buttons in the Host Metrics toolbar for one-click command execution.",
"noQuickActions": "No quick actions yet.",
"buttonLabel": "Button label",
"selectSnippetPlaceholder": "Select snippet...",
"addActionBtn": "Add Action",
"hostSharedSuccessfully": "Host shared successfully",
"failedToShareHost": "Failed to share host",
"accessRevoked": "Access revoked",
"failedToRevokeAccess": "Failed to revoke access",
"cancelBtn": "Cancel",
"savingBtn": "Saving...",
"addHostBtn": "Add Host",
"hostUpdated": "Host updated",
"hostCreated": "Host created",
"failedToSave": "Failed to save host",
"credentialUpdated": "Credential updated",
"credentialCreated": "Credential created",
"failedToSaveCredential": "Failed to save credential",
"credentialNameRequired": "Please enter a name for the credential",
"credentialAuthRequired": "Add a password, an SSH key, or both",
"createCredentialFromHostBtn": "Create Credential",
"createCredentialFromHostTitle": "Create Credential From Host",
"createCredentialFromHostDesc": "Create a reusable, shareable credential entry prefilled with this host's current username, password, and/or SSH key.",
"backToHosts": "Back to Hosts",
"backToCredentials": "Back to Credentials",
"pinned": "Pinned",
"noHostsFound": "No hosts found",
"tryDifferentTerm": "Try a different term",
"addFirstHost": "Add your first host to get started",
"noCredentialsFound": "No credentials found",
"addCredentialBtn": "Add Credential",
"updateCredentialBtn": "Update Credential",
"features": "Features",
"noFolder": "(No folder)",
"deleteSelected": "Delete",
"exitSelection": "Exit selection",
"importSkip": "Import (skip existing)",
"importOverwrite": "Import (overwrite)",
"collapseBtn": "Collapse",
"importExportBtn": "Import / Export",
"hostStatusesRefreshed": "Host statuses refreshed",
"failedToRefreshHosts": "Failed to refresh hosts",
"movedHostTo": "Moved {{host}} to \"{{folder}}\"",
"failedToMoveHost": "Failed to move host",
"folderRenamedTo": "Folder renamed to \"{{name}}\"",
"deletedFolder": "Deleted folder \"{{name}}\"",
"failedToDeleteFolder": "Failed to delete folder",
"deleteAllInFolder": "Delete all hosts in \"{{name}}\"? This cannot be undone.",
"folderPickerPlaceholder": "No folder",
"folderPickerSearch": "Search or create (use / for subfolders)...",
"folderPickerNone": "No folder",
"folderPickerCreate": "Create \"{{path}}\"",
"folderPickerEmpty": "No matching folders",
"newFolder": "New folder",
"createFolderTitle": "Create folder",
"editFolderTitle": "Edit folder",
"folderDialogDescription": "Choose a name, color, and icon. Use / to nest folders.",
"folderNameLabel": "Folder name",
"folderNamePlaceholder": "e.g. Production/Web",
"folderNestingHint": "Use / to separate levels and create nested folders.",
"folderColor": "Color",
"folderIcon": "Icon",
"folderCredential": "Credential",
"folderCredentialNone": "No credential assigned",
"folderCredentialHint": "Hosts in this folder that use \"Stored credential\" auth without their own credential selected will inherit this one.",
"folderPreview": "Preview",
"folderNameFallback": "Untitled folder",
"createFolderButton": "Create folder",
"saveFolderButton": "Save folder",
"cancel": "Cancel",
"iconSearchPlaceholder": "Search icons...",
"openAllSessions": "Open all sessions",
"editFolder": "Edit folder",
"deleteFolder": "Delete folder",
"folderSaved": "Folder saved",
"failedToSaveFolder": "Failed to save folder",
"folderDeleted": "Deleted folder \"{{name}}\"",
"deleteFolderConfirm": "Delete \"{{name}}\" and its {{count}} host(s)? This cannot be undone.",
"failedToMoveHosts": "Failed to move hosts",
"expandAll": "Expand all folders",
"collapseAll": "Collapse all folders",
"moreActions": "More",
"groupBy": "Group by",
"GroupByFolder": "Folder",
"GroupByTag": "Tag",
"GroupByStatus": "Status",
"GroupByProtocol": "Protocol",
"GroupByAuth": "Auth type",
"groupUngrouped": "Ungrouped",
"deletedHost": "Deleted {{name}}",
"copiedToClipboard": "Copied to clipboard",
"terminalUrlCopied": "Terminal URL copied",
"fileManagerUrlCopied": "File Manager URL copied",
"tunnelUrlCopied": "Tunnel URL copied",
"dockerUrlCopied": "Docker URL copied",
"hostMetricsUrlCopied": "Host Metrics URL copied",
"tmuxMonitorUrlCopied": "Tmux Monitor URL copied",
"rdpUrlCopied": "RDP URL copied",
"vncUrlCopied": "VNC URL copied",
"telnetUrlCopied": "Telnet URL copied",
"remoteDesktopUrlCopied": "Remote Desktop URL copied",
"expandActions": "Expand actions",
"collapseActions": "Collapse actions",
"wakeOnLanAction": "Wake on LAN",
"wakeOnLanSuccess": "Magic packet sent to {{name}}",
"wakeOnLanError": "Failed to send magic packet",
"cloneHostAction": "Clone Host",
"copyAddress": "Copy Address",
"copyLink": "Copy Link",
"copyTerminalUrlAction": "Copy Terminal URL",
"copyFileManagerUrlAction": "Copy File Manager URL",
"copyTunnelUrlAction": "Copy Tunnel URL",
"copyDockerUrlAction": "Copy Docker URL",
"copyHostMetricsUrlAction": "Copy Host Metrics URL",
"copyTmuxMonitorUrlAction": "Copy Tmux Monitor URL",
"copyRdpUrlAction": "Copy RDP URL",
"copyVncUrlAction": "Copy VNC URL",
"copyTelnetUrlAction": "Copy Telnet URL",
"copyRemoteDesktopUrlAction": "Copy Remote Desktop URL",
"deleteCredentialConfirm": "Delete credential \"{{name}}\"?",
"deletedCredential": "Deleted {{name}}",
"deploySSHKeyTitle": "Deploy SSH Key",
"deployingBtn": "Deploying...",
"deployBtn": "Deploy",
"failedToDeployKey": "Failed to deploy key",
"deleteHostsConfirm": "Delete {{count}} host{{plural}}? This cannot be undone.",
"movedToRoot": "Moved to root",
"enableTerminalFeature": "Enable Terminal",
"disableTerminalFeature": "Disable Terminal",
"enableFilesFeature": "Enable Files",
"disableFilesFeature": "Disable Files",
"enableTunnelsFeature": "Enable Tunnels",
"disableTunnelsFeature": "Disable Tunnels",
"enableDockerFeature": "Enable Docker",
"disableDockerFeature": "Disable Docker",
"enableProxmoxFeature": "Enable Proxmox",
"disableProxmoxFeature": "Disable Proxmox",
"addTagsPlaceholder": "Add tags...",
"authDetails": "Authentication Details",
"credType": "Type",
"generateKeyPairDesc": "Generate a new key pair, both private and public keys will be filled automatically.",
"generatingKey": "Generating...",
"generateLabel": "Generate {{label}}",
"uploadFileBtn": "Upload file",
"keyPassphraseOptional": "Key Passphrase (Optional)",
"sshPublicKeyOptional": "SSH Public Key (Optional)",
"publicKeyGenerated": "Public key generated",
"failedToGeneratePublicKey": "Failed to derive public key",
"publicKeyCopied": "Public key copied",
"keyPairGenerated": "{{label}} key pair generated",
"failedToGenerateKeyPair": "Failed to generate key pair",
"searchHostsPlaceholder": "Search hosts, addresses, tags…",
"searchCredentialsPlaceholder": "Search credentials…",
"refreshBtn": "Refresh",
"addTag": "Add tags...",
"deleteConfirmBtn": "Delete",
"tunnelRequirementsText": "The SSH server must have GatewayPorts yes, AllowTcpForwarding yes, and PermitRootLogin yes set in /etc/ssh/sshd_config.",
"deleteHostConfirm": "Delete \"{{name}}\"?",
"enableAtLeastOneProtocol": "Enable at least one protocol above to configure authentication and connection settings.",
"keyPassphrase": "Key Passphrase",
"connectBtn": "Connect",
"disconnectBtn": "Disconnect",
"basicInformation": "Basic Information",
"authDetailsSection": "Authentication Details",
"credTypeLabel": "Type",
"hostsTab": "Hosts",
"credentialsTab": "Credentials",
"selectMultiple": "Select multiple",
"selectHosts": "Select hosts",
"connectionLabel": "Connection",
"authenticationLabel": "Authentication",
"generateKeyPairTitle": "Generate Key Pair",
"generateKeyPairDescription": "Generate a new key pair, both private and public keys will be filled automatically.",
"generateFromPrivateKey": "Generate from Private Key",
"refreshBtn2": "Refresh",
"exitSelectionTitle": "Exit selection",
"addHostBtn2": "Add Host",
"addCredentialBtn2": "Add Credential",
"checkingHostStatuses": "Checking host statuses...",
"pinnedSection": "Pinned",
"hostsExported": "Hosts exported successfully",
"export": {
"menuItem": "Export...",
"title": "Export hosts",
"scope": "Scope",
"scopeAll": "All",
"scopeSelected": "Selected",
"searchHosts": "Search hosts...",
"include": "Include",
"groupConnection": "Connection",
"groupCredentials": "Credentials",
"groupNotes": "Notes",
"groupTags": "Tags & pin",
"groupTunnels": "Tunnels",
"groupJumpHosts": "Jump hosts",
"groupQuickActions": "Quick actions",
"groupFeatureFlags": "Feature flags",
"groupAdvanced": "Advanced config",
"preview": "Preview",
"moreHosts": "... {{count}} more hosts",
"summary": "{{selected}} of {{total}} hosts",
"credentialsIncluded": "credentials included",
"credentialsExcluded": "credentials excluded",
"noneSelected": "No hosts selected",
"cancel": "Cancel",
"confirm": "Export",
"fetchFailed": "Failed to load hosts for export",
"bulkButton": "Export"
},
"sampleDownloaded": "Sample file downloaded",
"failedToDeleteCredential2": "Failed to delete credential",
"noFolderOption": "(No folder)",
"nSelected": "{{count}} selected",
"featuresMenu": "Features",
"moveMenu": "Move",
"connectSelected": "Connect",
"cancelSelection": "Cancel",
"deployDialogDesc": "Deploy {{name}} to a host's authorized_keys.",
"targetHostLabel": "Target Host",
"selectHostOption": "Select a host...",
"keyDeployedSuccess": "Key deployed successfully",
"failedToDeployKey2": "Failed to deploy key",
"deletedCount": "Deleted {{count}} hosts",
"failedToDeleteCount": "Failed to delete {{count}} hosts",
"duplicatedHost": "Duplicated \"{{name}}\"",
"failedToDuplicateHost": "Failed to duplicate host",
"updatedCount": "Updated {{count}} hosts",
"friendlyNameLabel": "Friendly Name",
"descriptionLabel": "Description",
"loadingHost": "Loading host...",
"loadingHosts": "Loading hosts...",
"loadingCredentials": "Loading credentials...",
"noHostsYet": "No hosts yet",
"noHostsMatchSearch": "No hosts match your search",
"hostNotFound": "Host not found",
"searchHosts": "Search hosts...",
"sortHosts": "Sort Hosts",
"sortDefault": "Default Order",
"sortNameAsc": "Name (A → Z)",
"sortNameDesc": "Name (Z → A)",
"sortIpAsc": "IP Address (Asc)",
"sortIpDesc": "IP Address (Desc)",
"sortOnlineFirst": "Online First",
"sortOfflineFirst": "Offline First",
"sortPinnedFirst": "Pinned First",
"filterHosts": "Filter Hosts",
"filterClearAll": "Clear Filters",
"filterStatusGroup": "Status",
"filterOnline": "Online",
"filterOffline": "Offline",
"filterPinned": "Pinned",
"filterAuthGroup": "Auth Type",
"filterAuthPassword": "Password",
"filterAuthKey": "SSH Key",
"filterAuthCredential": "Credential",
"filterAuthNone": "None",
"filterAuthOpkssh": "OPKSSH",
"filterProtocolGroup": "Protocol",
"filterProtocolSsh": "SSH",
"filterProtocolRdp": "RDP",
"filterProtocolVnc": "VNC",
"filterProtocolTelnet": "Telnet",
"filterFeaturesGroup": "Features",
"filterFeatureTerminal": "Terminal",
"filterFeatureFileManager": "File Manager",
"filterFeatureTunnel": "Tunnel",
"filterFeatureDocker": "Docker",
"filterTagsGroup": "Tags",
"shareHost": "Share Host",
"shareHostTitle": "Share: {{name}}",
"shareFolder": "Share Folder",
"shareFolderTitle": "Share folder: {{name}}",
"folderSharedSuccessfully": "Shared {{count}} host(s) in folder",
"failedToShareFolder": "Failed to share folder",
"sharing": {
"loadError": "Failed to load sharing data. Please try again.",
"shareWithSection": "Share with",
"usersTab": "Users",
"rolesTab": "Roles",
"searchPlaceholder": "Search users or roles...",
"noMatches": "No matches found",
"permissionLevelLabel": "Permission level",
"levels": {
"connect": {
"label": "Connect",
"description": "Open sessions only: terminal, remote desktop, file manager, tunnels and Docker. No access to the host configuration."
},
"view": {
"label": "View",
"description": "Connect, plus see the host configuration. Secrets are never shown."
},
"edit": {
"label": "Edit",
"description": "View, plus modify non-authentication host settings. The owner's SSH authentication stays private and owner-only."
},
"manage": {
"label": "Manage",
"description": "Edit, plus share the host with others, change permission levels and revoke access."
}
},
"expiryLabel": "Access expiry",
"expiry": {
"never": "Never",
"oneHour": "1 hour",
"oneDay": "24 hours",
"sevenDays": "7 days",
"thirtyDays": "30 days",
"custom": "Custom"
},
"customHoursPlaceholder": "Hours until access expires",
"shareButton": "Share",
"shareWithCount": "Share ({{count}})",
"currentAccess": "Current access",
"noAccessEntries": "This host has not been shared yet",
"folderShareSummary": "Shared {{shared}} of {{total}} host(s) in this folder",
"grantedBy": "Granted by",
"expires": "Expires",
"expired": "Expired",
"never": "Never",
"revoke": "Revoke",
"accessUpdated": "Access updated",
"accessUpdateFailed": "Failed to update access",
"sharedBadge": "Shared",
"sharedBadgeTooltip": "Shared by {{owner}} ({{level}} access)",
"viewOnlyBanner": "This host is shared with you by {{owner}} with view access. The configuration is read-only.",
"sharedEditBanner": "This host is shared with you by {{owner}} with edit access. Changes apply to the real host; authentication references can only be changed by the owner.",
"ownerOnlyControl": "Only the host owner can change this",
"ownerAuthPrivate": "The host owner's SSH authentication is private. Use “Set personal SSH authentication” from the host menu to choose your own credential.",
"ownerAuthShared": "The host owner has shared SSH authentication for this host. You can use it or choose your own credential from “Set personal SSH authentication.”",
"authOverrideAction": "Set personal SSH authentication",
"authOverrideTitle": "Personal SSH authentication",
"authOverrideDescriptionPrivate": "The host owner's SSH credentials stay private. Choose one of your saved credentials for connections to {{host}}.",
"authOverrideDescriptionShared": "Use the authentication shared by the host owner, or replace it with one of your saved credentials for connections to {{host}}.",
"authOverrideCredentialLabel": "Authentication credential",
"useSharedAuthentication": "Use shared host authentication",
"noPersonalCredential": "No personal credential",
"authOverrideNoCredentials": "You do not have any saved SSH credentials yet. Create one in Credentials to connect to hosts that require authentication.",
"authOverrideRequired": "This host requires one of your saved credentials before you can connect.",
"authOverridePrivateHint": "This credential is private to you. The host owner and other recipients cannot see or use it.",
"authOverrideSaved": "Personal SSH authentication saved",
"authOverrideCleared": "Personal SSH authentication removed",
"authOverrideClearedToShared": "Using shared host authentication",
"authOverrideLoadError": "Failed to load your SSH authentication. Please try again.",
"authOverrideSaveError": "Failed to save your SSH authentication"
},
"guac": {
"connection": "Connection",
"authentication": "Authentication",
"storedCredential": "Stored Credential",
"noCredential": "No credential (direct credentials below)",
"authMethod": "Auth Method",
"authTypeDirect": "Direct",
"authTypeCredential": "Credential",
"authTypeNone": "None",
"authTypeNoneDesc": "No credentials are stored. You'll be prompted for a username and password each time you connect; they are not saved.",
"selectCredential": "Select a credential...",
"connectionSettings": "Connection Settings",
"displaySettings": "Display Settings",
"audioSettings": "Audio Settings",
"rdpPerformance": "RDP Performance",
"deviceRedirection": "Device Redirection",
"session": "Session",
"gateway": "Gateway",
"remoteApp": "RemoteApp",
"clipboard": "Clipboard",
"sessionRecording": "Session Recording",
"wakeOnLan": "Wake-on-LAN",
"vncSettings": "VNC Settings",
"terminalSettings": "Terminal Settings",
"rdpPort": "RDP Port",
"username": "Username",
"password": "Password",
"passwordSaved": "Password saved, type to change",
"domain": "Domain",
"securityMode": "Security Mode",
"colorDepth": "Color Depth",
"width": "Width",
"height": "Height",
"dpi": "DPI",
"resizeMethod": "Resize Method",
"clientName": "Client Name",
"initialProgram": "Initial Program",
"serverLayout": "Server Layout",
"timezone": "Timezone",
"loadBalanceInfo": "Load Balance Info / Cookie",
"loadBalanceInfoDesc": "RD Connection Broker cookie for RDS farm load balancing (e.g. tsv://MS Terminal Services Plugin.1.CollectionName)",
"guacdProxy": "guacd Proxy",
"guacdHostname": "guacd Host",
"guacdHostnamePlaceholder": "Global default",
"guacdPort": "guacd Port",
"guacdProxyDesc": "Override the global guacd instance for this connection. Leave blank to use the globally configured guacd.",
"gatewayHostname": "Gateway Hostname",
"gatewayPort": "Gateway Port",
"gatewayUsername": "Gateway Username",
"gatewayPassword": "Gateway Password",
"gatewayDomain": "Gateway Domain",
"remoteAppProgram": "RemoteApp Program",
"workingDirectory": "Working Directory",
"arguments": "Arguments",
"normalizeLineEndings": "Normalize Line Endings",
"recordingPath": "Recording Path",
"recordingName": "Recording Name",
"macAddress": "MAC Address",
"broadcastAddress": "Broadcast Address",
"udpPort": "UDP Port",
"waitTimeS": "Wait Time (s)",
"driveName": "Drive Name",
"drivePath": "Drive Path",
"ignoreCertificate": "Ignore Certificate",
"ignoreCertificateDesc": "Allow connections to hosts with self-signed certificates",
"forceLossless": "Force Lossless",
"forceLosslessDesc": "Force lossless image encoding (higher quality, more bandwidth)",
"disableAudio": "Disable Audio",
"disableAudioDesc": "Mute all audio from the remote session",
"enableAudioInput": "Enable Audio Input (Microphone)",
"enableAudioInputDesc": "Forward local microphone to the remote session",
"wallpaper": "Wallpaper",
"wallpaperDesc": "Show desktop wallpaper (disabling improves performance)",
"theming": "Theming",
"themingDesc": "Enable visual themes and styles",
"fontSmoothing": "Font Smoothing",
"fontSmoothingDesc": "Enable ClearType font rendering",
"fullWindowDrag": "Full Window Drag",
"fullWindowDragDesc": "Show window contents while dragging",
"desktopComposition": "Desktop Composition",
"desktopCompositionDesc": "Enable Aero glass effects",
"menuAnimations": "Menu Animations",
"menuAnimationsDesc": "Enable menu fade and slide animations",
"disableBitmapCaching": "Disable Bitmap Caching",
"disableBitmapCachingDesc": "Turn off bitmap cache (may help with glitches)",
"disableOffscreenCaching": "Disable Offscreen Caching",
"disableOffscreenCachingDesc": "Turn off offscreen cache",
"disableGlyphCaching": "Disable Glyph Caching",
"disableGlyphCachingDesc": "Turn off glyph cache",
"enableGfx": "Enable GFX",
"enableGfxDesc": "Use RemoteFX graphics pipeline",
"enablePrinting": "Enable Printing",
"enablePrintingDesc": "Redirect local printers to the remote session",
"enableDriveRedirection": "Enable Drive Redirection",
"enableDriveRedirectionDesc": "Map a local folder as a drive in the remote session",
"createDrivePath": "Create Drive Path",
"createDrivePathDesc": "Automatically create the folder if it does not exist",
"disableDownload": "Disable Download",
"disableDownloadDesc": "Prevent downloading files from the remote session",
"disableUpload": "Disable Upload",
"disableUploadDesc": "Prevent uploading files to the remote session",
"enableTouch": "Enable Touch",
"enableTouchDesc": "Enable touch input forwarding",
"consoleSession": "Console Session",
"consoleSessionDesc": "Connect to the console (session 0) instead of a new session",
"sendWolPacket": "Send WOL Packet",
"sendWolPacketDesc": "Send a magic packet to wake this host before connecting",
"disableCopy": "Disable Copy",
"disableCopyDesc": "Prevent copying text from the remote session",
"disablePaste": "Disable Paste",
"disablePasteDesc": "Prevent pasting text into the remote session",
"createPathIfMissing": "Create Path if Missing",
"createPathIfMissingDesc": "Automatically create the recording directory",
"excludeOutput": "Exclude Output",
"excludeOutputDesc": "Do not record screen output (metadata only)",
"excludeMouse": "Exclude Mouse",
"excludeMouseDesc": "Do not record mouse movements",
"includeKeystrokes": "Include Keystrokes",
"includeKeystrokesDesc": "Record raw keystrokes in addition to screen output",
"vncPort": "VNC Port",
"vncPassword": "VNC Password",
"vncUsernameOptional": "Username (optional)",
"vncLeaveBlank": "Leave blank if not required",
"cursorMode": "Cursor Mode",
"swapRedBlue": "Swap Red/Blue",
"swapRedBlueDesc": "Swap the red and blue color channels (fixes some colour issues)",
"readOnly": "Read-only",
"readOnlyDesc": "View the remote screen without sending any input",
"telnetPort": "Telnet Port",
"terminalType": "Terminal Type",
"fontName": "Font Name",
"fontSize": "Font Size",
"colorScheme": "Color Scheme",
"backspaceKey": "Backspace Key",
"saveHostFirst": "Save the host first.",
"sharingOptionsAfterSave": "Sharing options are available after the host has been saved.",
"permissionLevel": "Permission Level",
"typeHeader": "Type",
"targetHeader": "Target",
"permissionHeader": "Permission",
"cancelBtn": "Cancel",
"savingBtn": "Saving...",
"updateHostBtn": "Update Host",
"addHostBtn": "Add Host"
}
},
"commandPalette": {
"searchPlaceholder": "Search hosts, commands, or settings...",
"quickActions": "Quick Actions",
"hostManager": "Host Manager",
"hostManagerDesc": "Manage, add, or edit hosts",
"addNewHost": "Add New Host",
"addNewHostDesc": "Register a new host",
"adminSettings": "Admin Settings",
"adminSettingsDesc": "Configure system preferences and users",
"userProfile": "User Profile",
"userProfileDesc": "Manage your account and preferences",
"addCredential": "Add Credential",
"addCredentialDesc": "Store SSH keys or passwords",
"tmuxMonitor": "Tmux Monitor",
"tmuxMonitorDesc": "Monitor tmux sessions across your hosts",
"recentActivity": "Recent Activity",
"serversAndHosts": "Servers & Hosts",
"noHostsFound": "No hosts found matching \"{{search}}\"",
"links": "Links",
"navigate": "Navigate",
"select": "Select",
"toggleWith": "Toggle with"
},
"splitScreen": {
"paneEmpty": "Pane {{index}} - empty",
"noTabAssigned": "No tab assigned",
"focusedPane": "Active pane"
},
"connections": {
"noConnections": "No connections",
"noConnectionsDesc": "Open a terminal, file manager, or remote desktop to see connections here",
"connectedFor": "Connected for {{duration}}",
"connected": "Connected",
"disconnected": "Disconnected",
"closeTab": "Close tab",
"closeConnection": "Close connection",
"forgetTab": "Forget",
"removeBackground": "Remove",
"reconnect": "Reconnect",
"reopenTab": "Reopen",
"sectionOpen": "Open",
"sectionBackground": "Background",
"backgroundDesc": "Sessions persist for 30 minutes after disconnect and can be reconnected.",
"persisted": "Persisted in background",
"expiresIn": "Expires in {{duration}}",
"search": "Search connections...",
"noSearchResults": "No connections match your search",
"rename": "Rename session",
"sectionSharedWithMe": "Shared with me",
"sharedBy": "Shared by {{username}}",
"join": "Join",
"sharedSessionLabel": "{{hostName}} (shared)"
},
"sessionSharing": {
"guestView": {
"loading": "Connecting to shared session...",
"linkInvalid": "This share link is invalid, expired, or has been revoked",
"rateLimited": "Too many attempts, please try again shortly",
"sessionEnded": "This session has ended",
"readOnlyBadge": "View only"
},
"modalTitle": "Share session",
"shareButton": "Share",
"notReadyToShare": "Session is not ready to share yet",
"modeTab": {
"link": "Link",
"user": "User"
},
"linkModeDescription": "Anyone with this link can join, no account required.",
"userModeDescription": "Share with a specific user who already has access to this host. If they do not have access yet, share the host with them first or use a link instead. Once shared, the session appears in their Connections tab.",
"permissionLevel": {
"label": "Permission level",
"readOnly": "Read-only",
"readOnlyDescription": "Can watch the session live but cannot type or interact.",
"readWrite": "Read-write",
"readWriteDescription": "Can type and interact with the session just like the owner."
},
"expiryLabel": "Link expiry",
"createLinkButton": "Create link",
"createShareButton": "Share with user",
"searchUsersPlaceholder": "Search users...",
"noUsersFound": "No users found",
"linkCreated": "Share link created",
"linkCopied": "Link copied to clipboard",
"copyLink": "Copy link",
"shareCreated": "Session shared. It will appear in their Connections tab.",
"shareFailed": "Failed to create share",
"userLacksHostAccess": "That user does not have access to this host yet. Share the host with them first, or use a link instead.",
"activeShares": "Active shares",
"noActiveShares": "No active shares for this session",
"revoke": "Revoke",
"revokeConfirmTitle": "Revoke this share?",
"revokeConfirmDescription": "Anyone using this share will lose access immediately.",
"revoked": "Share revoked",
"revokeFailed": "Failed to revoke share",
"joinCount": "{{count}} join",
"joinCount_other": "{{count}} joins",
"expiresAt": "Expires {{date}}",
"linkShareBadge": "Link",
"userShareBadge": "User: {{username}}",
"loadSharesFailed": "Failed to load active shares"
},
"guacamole": {
"connecting": "Connecting to {{type}} session...",
"connectionError": "Connection error",
"connectionFailed": "Connection failed",
"failedToConnect": "Failed to get connection token",
"hostNotFound": "Host not found",
"noHostSelected": "No host selected",
"reconnect": "Reconnect",
"retry": "Retry",
"guacdUnavailable": "Remote desktop service (guacd) is not available. Please ensure guacd is running and accessible and configured properly in admin settings.",
"credentialPromptTitle": "Enter RDP Credentials",
"credentialPromptDescription": "This host is set to prompt for credentials on connect. They are used for this session only and are not saved.",
"connect": "Connect",
"ctrlAltDel": "Ctrl+Alt+Del",
"toolbar": {
"ctrlAltDel": "Ctrl+Alt+Del",
"winL": "Win+L (Lock Screen)",
"winKey": "Windows Key",
"ctrl": "Ctrl",
"alt": "Alt",
"shift": "Shift",
"win": "Win",
"stickyActive": "{{key}} (latched - click to release)",
"stickyInactive": "{{key}} (click to latch)",
"esc": "Escape",
"tab": "Tab",
"home": "Home",
"end": "End",
"pageUp": "Page Up",
"pageDown": "Page Down",
"arrowUp": "Arrow Up",
"arrowDown": "Arrow Down",
"arrowLeft": "Arrow Left",
"arrowRight": "Arrow Right",
"fnToggle": "Function Keys",
"reconnect": "Reconnect Session",
"collapse": "Collapse toolbar",
"expand": "Expand toolbar",
"dragHandle": "Drag to reposition",
"switchToTrackpad": "Switch to trackpad mode (drag to move cursor, tap to click)",
"switchToTouch": "Switch to touch mode (tap directly where you want to click)"
}
},
"terminal": {
"connect": "Connect to Host",
"clear": "Clear",
"paste": "Paste",
"reconnect": "Reconnect",
"connectionLost": "Connection lost",
"connected": "Connected",
"clipboardWriteFailed": "Failed to copy to clipboard. Make sure the page is served over HTTPS or localhost.",
"clipboardReadFailed": "Failed to read from clipboard. Make sure clipboard permissions are granted.",
"clipboardHttpWarning": "Paste requires HTTPS. Use Ctrl+Shift+V or serve Termix over HTTPS.",
"passwordPromptFillTitle": "Fill the saved password into this prompt?",
"unknownError": "Unknown error occurred",
"websocketError": "WebSocket connection error",
"connecting": "Connecting...",
"noHostSelected": "No host selected",
"reconnecting": "Reconnecting... ({{attempt}}/{{max}})",
"reconnected": "Reconnected successfully",
"tmuxSessionCreated": "tmux session created: {{name}}",
"tmuxSessionAttached": "tmux session attached: {{name}}",
"tmuxUnavailable": "tmux is not installed on the remote host, falling back to standard shell",
"tmuxSessionPickerTitle": "tmux Sessions",
"tmuxSessionPickerDesc": "Existing tmux sessions found on this host. Select one to reattach or create a new session.",
"tmuxWindows": "Windows",
"tmuxWindowCount": "{{count}} window",
"tmuxAttached": "Attached clients",
"tmuxAttachedCount": "{{count}} attached",
"tmuxLastActivity": "Last activity",
"tmuxTimeJustNow": "just now",
"tmuxTimeMinutes": "{{count}}m ago",
"tmuxTimeHours": "{{count}}h ago",
"tmuxTimeDays": "{{count}}d ago",
"tmuxCreateNew": "Start new session",
"tmuxCopyHint": "Adjust selection and press Enter to copy to clipboard",
"tmuxDetach": "Detach from tmux session",
"tmuxDetached": "Detached from tmux session",
"searchPlaceholder": "Find",
"searchCaseSensitive": "Match Case",
"searchWholeWord": "Match Whole Word",
"searchRegex": "Use Regular Expression",
"searchNoResults": "No results",
"searchResultCount": "{{index}} of {{count}}",
"searchNext": "Next Match (Enter)",
"searchPrevious": "Previous Match (Shift+Enter)",
"searchClose": "Close (Escape)",
"maxReconnectAttemptsReached": "Maximum reconnection attempts reached",
"closeTab": "Close",
"connectionTimeout": "Connection timeout",
"terminalTitle": "Terminal - {{host}}",
"terminalWithPath": "Terminal - {{host}}:{{path}}",
"runTitle": "Running {{command}} - {{host}}",
"totpRequired": "Two-Factor Authentication Required",
"totpCodeLabel": "Verification Code",
"totpVerify": "Verify",
"mfaPromptRequired": "Authentication Required",
"mfaPushRequired": "Push Authentication Required",
"mfaMenuPlaceholder": "Enter your response",
"mfaWaitingApproval": "Waiting for approval on your device...",
"mfaSendRequest": "Send Request",
"warpgateAuthRequired": "Warpgate Authentication Required",
"warpgateSecurityKey": "Security Key",
"warpgateAuthUrl": "Authentication URL",
"warpgateOpenBrowser": "Open in Browser",
"warpgateContinue": "I've Completed Authentication",
"opksshAuthRequired": "OPKSSH Authentication Required",
"opksshAuthDescription": "Complete authentication in your browser to continue. This session will remain valid for 24 hours.",
"opksshOpenBrowser": "Open Browser to Authenticate",
"opksshWaitingForAuth": "Waiting for authentication in browser...",
"opksshAuthenticating": "Processing authentication...",
"opksshTimeout": "Authentication timed out. Please try again.",
"opksshAuthFailed": "Authentication failed. Please check your credentials and try again.",
"opksshSignInWith": "Sign in with {{provider}}",
"tailscaleCheckRequired": "Tailscale Authentication Required",
"tailscaleCheckDescription": "Tailscale SSH requires an additional check. Authenticate in your browser to continue.",
"tailscaleCheckOpenBrowser": "Open Browser to Authenticate",
"tailscaleCheckWaiting": "Waiting for Tailscale authentication...",
"tailscaleCheckTimeout": "Tailscale authentication timed out. Please try again.",
"vaultAuthTitle": "Vault sign-in required",
"vaultAuthDescription": "A window has opened to sign in to HashiCorp Vault. Complete the sign-in there; this connection will continue automatically.",
"vaultAuthFailed": "Vault authentication failed. Please try again.",
"vaultReopen": "Reopen sign-in window",
"sudoPasswordPopupTitle": "Insert Password?",
"linkDialogTitle": "Open Link",
"linkDialogOpen": "Open",
"linkDialogCopy": "Copy",
"websocketAbnormalClose": "Connection closed unexpectedly. This may be due to a reverse proxy or SSL configuration issue. Please check server logs.",
"connectionLogTitle": "Connection Log",
"connectionLogCopy": "Copy logs to clipboard",
"connectionLogEmpty": "No connection logs yet",
"connectionLogWaiting": "Waiting for connection logs...",
"connectionLogCopied": "Connection logs copied to clipboard",
"connectionLogCopyFailed": "Failed to copy logs to clipboard",
"connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.",
"hostKeyRejected": "SSH host key verification rejected. Connection cancelled.",
"sessionTakenOver": "Session was opened in another tab. Reconnecting...",
"split": {
"splitTab": "Split Tab",
"addToSplit": "Add to Split",
"removeFromSplit": "Remove from Split"
}
},
"fileManager": {
"noHostSelected": "No host selected",
"initializingEditor": "Initializing editor...",
"file": "File",
"folder": "Folder",
"uploadFile": "Upload File",
"downloadFile": "Download",
"extractArchive": "Extract Archive",
"extractingArchive": "Extracting {{name}}...",
"archiveExtractedSuccessfully": "{{name}} extracted successfully",
"extractFailed": "Extract failed",
"compressFile": "Compress File",
"compressFiles": "Compress Files",
"compressFilesDesc": "Compress {{count}} items into an archive",
"archiveName": "Archive Name",
"enterArchiveName": "Enter archive name...",
"compressionFormat": "Compression Format",
"selectedFiles": "Selected files",
"andMoreFiles": "and {{count}} more...",
"compress": "Compress",
"compressingFiles": "Compressing {{count}} items into {{name}}...",
"filesCompressedSuccessfully": "{{name}} created successfully",
"compressFailed": "Compression failed",
"edit": "Edit",
"preview": "Preview",
"previous": "Previous",
"next": "Next",
"pageXOfY": "Page {{current}} of {{total}}",
"zoomOut": "Zoom Out",
"zoomIn": "Zoom In",
"newFile": "New File",
"newFolder": "New Folder",
"rename": "Rename",
"uploading": "Uploading...",
"uploadingFile": "Uploading {{name}}...",
"fileName": "File Name",
"folderName": "Folder Name",
"fileUploadedSuccessfully": "File \"{{name}}\" uploaded successfully",
"failedToUploadFile": "Failed to upload file",
"fileDownloadedSuccessfully": "File \"{{name}}\" downloaded successfully",
"failedToDownloadFile": "Failed to download file",
"fileCreatedSuccessfully": "File \"{{name}}\" created successfully",
"folderCreatedSuccessfully": "Folder \"{{name}}\" created successfully",
"failedToCreateItem": "Failed to create item",
"operationFailed": "{{operation}} operation failed for {{name}}: {{error}}",
"failedToResolveSymlink": "Failed to resolve symlink",
"itemsDeletedSuccessfully": "{{count}} items deleted successfully",
"failedToDeleteItems": "Failed to delete items",
"sudoPasswordRequired": "Administrator Password Required",
"enterSudoPassword": "Enter sudo password to continue this operation",
"sudoPassword": "Sudo password",
"sudoOperationFailed": "Sudo operation failed",
"sudoAuthFailed": "Sudo authentication failed",
"dragFilesToUpload": "Drop files here to upload",
"emptyFolder": "This folder is empty",
"searchFiles": "Search files...",
"upload": "Upload",
"selectHostToStart": "Select a host to start file management",
"sshRequiredForFileManager": "File manager requires SSH. This host does not have SSH enabled.",
"failedToConnect": "Failed to connect to SSH",
"failedToLoadDirectory": "Failed to load directory",
"noSSHConnection": "No SSH connection available",
"copy": "Copy",
"cut": "Cut",
"paste": "Paste",
"copyPath": "Copy Path",
"copyPaths": "Copy Paths",
"delete": "Delete",
"properties": "Properties",
"refresh": "Refresh",
"downloadFiles": "Download {{count}} files to Browser",
"copyFiles": "Copy {{count}} items",
"cutFiles": "Cut {{count}} items",
"deleteFiles": "Delete {{count}} items",
"filesCopiedToClipboard": "{{count}} items copied to clipboard",
"filesCutToClipboard": "{{count}} items cut to clipboard",
"pathCopiedToClipboard": "Path copied to clipboard",
"pathsCopiedToClipboard": "{{count}} paths copied to clipboard",
"failedToCopyPath": "Failed to copy path to clipboard",
"copyFolderLink": "Copy Link to Folder",
"copyCurrentFolderLink": "Copy Link to Current Folder",
"folderLinkCopied": "Folder link copied to clipboard",
"failedToCopyFolderLink": "Failed to copy folder link",
"movedItems": "Moved {{count}} items",
"failedToDeleteItem": "Failed to delete item",
"itemRenamedSuccessfully": "{{type}} renamed successfully",
"failedToRenameItem": "Failed to rename item",
"download": "Download",
"openExternalEditor": "Open externally",
"chooseExternalEditor": "Choose external editor",
"externalEditorSelected": "External editor selected",
"externalEditorDesktopOnly": "External editor is only available in the desktop app",
"externalEditorOpened": "Opened in external editor. Saving there will upload changes back to the server.",
"failedToOpenExternalEditor": "Failed to open external editor",
"failedToSelectExternalEditor": "Failed to select external editor",
"permissions": "Permissions",
"size": "Size",
"modified": "Modified",
"path": "Path",
"confirmDelete": "Are you sure you want to delete {{name}}?",
"permissionDenied": "Permission denied",
"serverError": "Server Error",
"fileSavedSuccessfully": "File saved successfully",
"failedToSaveFile": "Failed to save file",
"confirmDeleteSingleItem": "Are you sure you want to permanently delete \"{{name}}\"?",
"confirmDeleteMultipleItems": "Are you sure you want to permanently delete {{count}} items?",
"confirmDeleteMultipleItemsWithFolders": "Are you sure you want to permanently delete {{count}} items? This includes folders and their contents.",
"confirmDeleteFolder": "Are you sure you want to permanently delete the folder \"{{name}}\" and all its contents?",
"permanentDeleteWarning": "This action cannot be undone. The item(s) will be permanently deleted from the server.",
"recent": "Recent",
"pinned": "Pinned",
"folderShortcuts": "Folder Shortcuts",
"failedToReconnectSSH": "Failed to reconnect SSH session",
"openTerminalHere": "Open Terminal Here",
"run": "Run",
"openTerminalInFolder": "Open Terminal in This Folder",
"openTerminalInFileLocation": "Open Terminal at File Location",
"runningFile": "Running - {{file}}",
"onlyRunExecutableFiles": "Can only run executable files",
"directories": "Directories",
"removedFromRecentFiles": "Removed \"{{name}}\" from recent files",
"removeFailed": "Remove failed",
"unpinnedSuccessfully": "Unpinned \"{{name}}\" successfully",
"unpinFailed": "Unpin failed",
"removedShortcut": "Removed shortcut \"{{name}}\"",
"removeShortcutFailed": "Remove shortcut failed",
"clearedAllRecentFiles": "Cleared all recent files",
"clearFailed": "Clear failed",
"removeFromRecentFiles": "Remove from recent files",
"clearAllRecentFiles": "Clear all recent files",
"unpinFile": "Unpin file",
"removeShortcut": "Remove shortcut",
"pinFile": "Pin file",
"addToShortcuts": "Add to shortcuts",
"pasteFailed": "Paste failed",
"noUndoableActions": "No undoable actions",
"undoCopySuccess": "Undid copy operation: Deleted {{count}} copied files",
"undoCopyFailedDelete": "Undo failed: Could not delete any copied files",
"undoCopyFailedNoInfo": "Undo failed: Could not find copied file information",
"undoMoveSuccess": "Undid move operation: Moved {{count}} files back to original location",
"undoMoveFailedMove": "Undo failed: Could not move any files back",
"undoMoveFailedNoInfo": "Undo failed: Could not find moved file information",
"undoDeleteNotSupported": "Delete operation cannot be undone: Files have been permanently deleted from server",
"undoTypeNotSupported": "Unsupported undo operation type",
"undoOperationFailed": "Undo operation failed",
"unknownError": "Unknown error",
"confirm": "Confirm",
"find": "Find...",
"replace": "Replace",
"downloadInstead": "Download Instead",
"keyboardShortcuts": "Keyboard Shortcuts",
"searchAndReplace": "Search & Replace",
"editing": "Editing",
"search": "Search",
"findNext": "Find Next",
"findPrevious": "Find Previous",
"save": "Save",
"selectAll": "Select All",
"undo": "Undo",
"redo": "Redo",
"moveLineUp": "Move Line Up",
"moveLineDown": "Move Line Down",
"toggleComment": "Toggle Comment",
"autoComplete": "Auto Complete",
"imageLoadError": "Failed to load image",
"startTyping": "Start typing...",
"unknownSize": "Unknown size",
"fileIsEmpty": "File is empty",
"largeFileWarning": "Large File Warning",
"largeFileWarningDesc": "This file is {{size}} in size, which may cause performance issues when opened as text.",
"fileNotFoundAndRemoved": "File \"{{name}}\" not found and has been removed from recent/pinned files",
"failedToLoadFile": "Failed to load file: {{error}}",
"serverErrorOccurred": "Server error occurred. Please try again later.",
"autoSaveFailed": "Auto-save failed",
"fileAutoSaved": "File auto-saved",
"moveFileFailed": "Failed to move {{name}}",
"moveOperationFailed": "Move operation failed",
"canOnlyCompareFiles": "Can only compare two files",
"comparingFiles": "Comparing files: {{file1}} and {{file2}}",
"dragFailed": "Drag operation failed",
"filePinnedSuccessfully": "File \"{{name}}\" pinned successfully",
"pinFileFailed": "Failed to pin file",
"fileUnpinnedSuccessfully": "File \"{{name}}\" unpinned successfully",
"unpinFileFailed": "Failed to unpin file",
"shortcutAddedSuccessfully": "Folder shortcut \"{{name}}\" added successfully",
"addShortcutFailed": "Failed to add shortcut",
"operationCompletedSuccessfully": "{{operation}} {{count}} items successfully",
"operationCompleted": "{{operation}} {{count}} items",
"downloadFileSuccess": "File {{name}} downloaded successfully",
"downloadFileFailed": "Download failed",
"moveTo": "Move to {{name}}",
"diffCompareWith": "Diff compare with {{name}}",
"dragOutsideToDownload": "Drag outside window to download ({{count}} files)",
"newFolderDefault": "NewFolder",
"newFileDefault": "NewFile.txt",
"successfullyMovedItems": "Successfully moved {{count}} items to {{target}}",
"move": "Move",
"searchInFile": "Search in file (Ctrl+F)",
"showKeyboardShortcuts": "Show keyboard shortcuts",
"decreaseFontSize": "Decrease font size",
"increaseFontSize": "Increase font size",
"startWritingMarkdown": "Start writing your markdown content...",
"loadingFileComparison": "Loading file comparison...",
"reload": "Reload",
"compare": "Compare",
"sideBySide": "Side by Side",
"inline": "Inline",
"fileComparison": "File Comparison: {{file1}} vs {{file2}}",
"fileTooLarge": "File too large: {{error}}",
"sshConnectionFailed": "SSH connection failed. Please check your connection to {{name}} ({{ip}}:{{port}})",
"loadFileFailed": "Failed to load file: {{error}}",
"connecting": "Connecting...",
"connectedSuccessfully": "Connected successfully",
"totpVerificationFailed": "TOTP verification failed",
"warpgateVerificationFailed": "Warpgate authentication failed",
"authenticationFailed": "Authentication failed",
"incorrectPassphrase": "Incorrect passphrase. Please try again.",
"verificationCodePrompt": "Verification code:",
"changePermissions": "Change Permissions",
"currentPermissions": "Current Permissions",
"owner": "Owner",
"group": "Group",
"others": "Others",
"read": "Read",
"write": "Write",
"execute": "Execute",
"permissionsChangedSuccessfully": "Permissions changed successfully",
"failedToChangePermissions": "Failed to change permissions",
"name": "Name",
"sortByName": "Name",
"sortByDate": "Date Modified",
"sortBySize": "Size",
"ascending": "Ascending",
"descending": "Descending",
"root": "Root",
"new": "New",
"sortBy": "Sort By",
"items": "Items",
"selected": "Selected",
"editor": "Editor",
"octal": "Octal",
"storage": "Storage",
"disk": "Disk",
"used": "Used",
"of": "of",
"toggleSidebar": "Toggle Sidebar",
"cannotLoadPdf": "Cannot load PDF",
"pdfLoadError": "There was an error loading this PDF file.",
"loadingPdf": "Loading PDF...",
"loadingPage": "Loading page..."
},
"transfer": {
"copyToHost": "Copy to host…",
"moveToHost": "Move to host…",
"copyItemsToHost": "Copy {{count}} items to host…",
"moveItemsToHost": "Move {{count}} items to host…",
"noHostsConnected": "No other file-manager hosts available.",
"noHostsConnectedHint": "Add another SSH host with File Manager enabled in Host Manager.",
"selectDestinationHost": "Select destination host",
"destinationPath": "Destination path",
"recentDestinations": "Recent destinations",
"collapseRecentDestinations": "Collapse recent destinations",
"expandRecentDestinations": "Expand recent destinations",
"browseFolders": "Browse destination folders",
"browseDestination": "Browse or enter path",
"confirmCopy": "Copy",
"confirmMove": "Move",
"transferring": "Transferring…",
"compressing": "Compressing…",
"extracting": "Extracting…",
"transferringItems": "Transferring {{current}} of {{total}} items…",
"transferSuccess": "Transfer complete",
"transferError": "Transfer failed",
"transferPartial": "Transfer completed with {{count}} errors",
"transferPartialHint": "Could not transfer: {{paths}}",
"itemsSummary": "{{count}} items",
"destMustBeDirectory": "Destination must be a directory for multi-item transfers",
"selectThisFolder": "Select this folder",
"browsePathWillBeCreated": "This folder does not exist yet. It will be created when the transfer starts.",
"browsePathError": "Could not open this path on the destination host.",
"goUp": "Go up",
"copyFolderToHost": "Copy folder to host…",
"moveFolderToHost": "Move folder to host…",
"hostReady": "Ready",
"hostConnecting": "Connecting…",
"hostDisconnected": "Not connected",
"hostAuthRequired": "Authentication required — open File Manager on this host first",
"hostConnectionFailed": "Connection failed",
"metricsTitle": "Transfer timings",
"metricsPrepare": "Prepare destination: {{duration}}",
"metricsCompress": "Compress on source: {{duration}}",
"metricsHopSourceRead": "Source → server: {{throughput}}",
"metricsHopDestSftpWrite": "Server → dest (SFTP): {{throughput}}",
"metricsHopDestLocalWrite": "Server → dest (local): {{throughput}}",
"metricsTransfer": "End-to-end: {{throughput}} ({{duration}})",
"metricsExtract": "Extract on destination: {{duration}}",
"metricsSourceDelete": "Remove from source: {{duration}}",
"metricsTotal": "Total: {{duration}}",
"progressCompressing": "Compressing on source host…",
"progressExtracting": "Extracting on destination…",
"progressTransferring": "Transferring data…",
"progressReconnecting": "Reconnecting…",
"parallelSegmentsLabel": "Parallel transfer lanes",
"parallelSegmentsOption": "{{count}} lanes",
"parallelSegmentsHint": "Large files are split into 256 MB chunks. Multiple lanes use separate connections (like starting several transfers) for higher total throughput.",
"progressTotalSpeed": "{{speed}} total ({{lanes}} lanes)",
"progressTransferringItems": "Transferring files ({{current}} of {{total}})…",
"progressBytes": "{{transferred}} / {{total}}",
"progressItems": "{{current}} / {{total}} files",
"sourceNotDeletedPartial": "Source files kept (partial transfer)",
"jumpHostLimitation": "Both hosts must be reachable from the Termix server. Direct host-to-host routing is not supported.",
"cancel": "Cancel",
"methodLabel": "Transfer method",
"methodAuto": "Auto",
"methodTar": "Tar archive",
"methodItemSftp": "Per-file SFTP",
"methodAutoHint": "Picks tar or per-file SFTP based on file count, size, and compressibility. Single files always use streaming SFTP.",
"methodTarHint": "Compress on source, transfer one archive, extract on destination. Requires tar on both Unix hosts.",
"methodItemSftpHint": "Transfer each file individually over SFTP. Works on all hosts including Windows.",
"methodPreviewLoading": "Calculating transfer method…",
"methodPreviewError": "Could not preview transfer method. The server will still pick a method when you start.",
"methodPreviewWillUseTar": "Will use: Tar archive",
"methodPreviewWillUseItemSftp": "Will use: Per-file SFTP",
"methodPreviewScanSummary": "{{fileCount}} files, {{totalSize}} total (scanned on source host).",
"methodItemSftpLimitation": "Each file uses the same SFTP stream as a single-file copy, one after another. Progress is combined across all files, so the bar moves slowly during large files.",
"methodReason": {
"user_item_sftp": "You chose per-file SFTP.",
"user_tar": "You chose tar archive.",
"tar_unavailable": "Tar is not available on one or both hosts — per-file SFTP will be used instead.",
"windows_host": "A Windows host is involved — tar is not used.",
"auto_multi_large": "Auto: multiple files including a large file ({{largestSize}}) with compressible data — tar bundles into one transfer.",
"auto_single_large_in_archive": "Auto: one large file ({{largestSize}}) in this set — per-file SFTP.",
"auto_many_incompressible": "Auto: mostly incompressible data — per-file SFTP.",
"auto_many_files": "Auto: many files ({{fileCount}}) — tar reduces per-file overhead.",
"auto_default": "Auto: per-file SFTP for this set."
},
"progressCancel": "Cancel",
"progressCancelling": "Cancelling…",
"progressStalled": "Stalled",
"resumedHint": "Reconnected to an active transfer started in another window.",
"transferCancelled": "Transfer cancelled",
"transferCancelledCopyHint": "Partial files may remain on the destination.",
"transferCancelledMoveHint": "Partial files may remain on the destination. Source files were not removed.",
"cleanupDestFiles": "Clean up destination",
"cleanupDestFilesSuccess": "Removed partial files from the destination",
"cleanupDestFilesPartial": "Some partial files could not be removed",
"cleanupDestFilesNothing": "Nothing to clean up on the destination",
"cleanupDestFilesError": "Cleanup failed",
"retryTransfer": "Retry",
"retryTransferError": "Retry failed",
"transferFailedRetryHint": "Partial data was kept on the destination. Retry will resume when the connection is back."
},
"tunnels": {
"noSshTunnels": "No SSH Tunnels",
"createFirstTunnelMessage": "You haven't created any SSH tunnels yet. Configure tunnel connections in the Host Manager to get started.",
"connected": "Connected",
"disconnected": "Disconnected",
"connecting": "Connecting...",
"error": "Error",
"canceling": "Canceling...",
"connect": "Connect",
"disconnect": "Disconnect",
"cancel": "Cancel",
"port": "Port",
"localPort": "Local Port",
"remotePort": "Remote Port",
"currentHostPort": "Current Host Port",
"endpointPort": "Endpoint Port",
"bindIp": "Local IP",
"endpointSshConfig": "Endpoint SSH Configuration",
"endpointSshHost": "Endpoint SSH Host",
"endpointSshHostPlaceholder": "Select a configured host",
"endpointSshHostRequired": "Select an endpoint SSH host for each client tunnel.",
"attempt": "Attempt {{current}} of {{max}}",
"nextRetryIn": "Next retry in {{seconds}} seconds",
"clientTunnels": "Client Tunnels",
"clientTunnel": "Client Tunnel",
"addClientTunnel": "Add Client Tunnel",
"noClientTunnels": "No client tunnels configured on this desktop.",
"tunnelName": "Tunnel Name",
"remoteHost": "Remote Host",
"autoStart": "Auto Start",
"clientAutoStartDesc": "Starts when this desktop client opens and stays connected.",
"clientManualStartDesc": "Use Start and Stop from this row. Termix will not open it automatically.",
"clientRemoteServerNote": "Remote forwarding may require AllowTcpForwarding and GatewayPorts on the endpoint SSH server. The remote port closes when this desktop disconnects.",
"clientTunnelStarted": "Client tunnel started",
"clientTunnelStopped": "Client tunnel stopped",
"tunnelTestSucceeded": "Tunnel test succeeded",
"tunnelTestFailed": "Tunnel test failed",
"localSaved": "Client tunnels saved",
"localSaveError": "Failed to save local client tunnels",
"invalidBindIp": "Local IP must be a valid IPv4 address.",
"invalidLocalTargetIp": "Local target IP must be a valid IPv4 address.",
"invalidLocalPort": "Local port must be between 1 and 65535.",
"invalidRemotePort": "Remote port must be between 1 and 65535.",
"invalidLocalTargetPort": "Local target port must be between 1 and 65535.",
"invalidEndpointPort": "Endpoint port must be between 1 and 65535.",
"duplicateAutoStartBind": "Only one auto-start client tunnel can use {{bind}}.",
"manualControlError": "Failed to update tunnel state.",
"active": "Active",
"start": "Start",
"stop": "Stop",
"test": "Test",
"type": "Tunnel Type",
"typeLocal": "Local (-L)",
"typeRemote": "Remote (-R)",
"typeDynamic": "Dynamic (-D)",
"typeServerLocalDesc": "Current host to endpoint.",
"typeServerRemoteDesc": "Endpoint back to current host.",
"typeClientLocalDesc": "Local computer to endpoint.",
"typeClientRemoteDesc": "Endpoint back to local computer.",
"typeClientDynamicDesc": "SOCKS on local computer.",
"typeDynamicDesc": "Forward SOCKS5 CONNECT traffic through SSH",
"forwardDescriptionServerLocal": "Current host {{sourcePort}} → endpoint {{endpointPort}}.",
"forwardDescriptionServerRemote": "Endpoint {{endpointPort}} → current host {{sourcePort}}.",
"forwardDescriptionServerDynamic": "SOCKS on current host {{sourcePort}}.",
"forwardDescriptionClientLocal": "Local {{sourcePort}} → remote {{endpointPort}}.",
"forwardDescriptionClientRemote": "Remote {{sourcePort}} → local {{endpointPort}}.",
"forwardDescriptionClientDynamic": "SOCKS on local port {{sourcePort}}.",
"summaryClientLocal": "{{localHost}}:{{localPort}} → {{endpoint}}:{{remotePort}}",
"summaryClientRemote": "{{endpoint}}:{{remotePort}} → {{localHost}}:{{localPort}}",
"summaryClientDynamic": "{{localHost}}:{{localPort}} → SOCKS via {{endpoint}}",
"autoNameClientLocal": "Local {{localPort}} → {{endpoint}} {{remotePort}}",
"autoNameClientRemote": "{{endpoint}} {{remotePort}} → local {{localPort}}",
"autoNameClientDynamic": "SOCKS {{localPort}} via {{endpoint}}",
"route": "Route:",
"lastStarted": "Last started",
"lastTested": "Last tested",
"lastError": "Last error",
"maxRetries": "Max Retries",
"maxRetriesDescription": "Maximum amount of retry attempts.",
"retryInterval": "Retry Interval (seconds)",
"retryIntervalDescription": "Time to wait between retry attempts.",
"local": "Local",
"remote": "Remote",
"destination": "Destination",
"host": "Host",
"mode": "Mode",
"noHostSelected": "No host selected",
"working": "Working..."
},
"cardGrid": {
"dragToMove": "Drag to move",
"dragToResize": "Drag to resize",
"changeWidth": "Change width",
"removeCard": "Remove {{label}}",
"addCard": "Add",
"columns": "Columns",
"empty": "No cards. Use Add below to place cards."
},
"hostMetrics": {
"cpu": "CPU",
"memory": "Memory",
"disk": "Disk",
"network": "Network",
"uptime": "Uptime",
"processes": "Processes",
"available": "Available",
"free": "Free",
"connecting": "Connecting...",
"connectionFailed": "Failed to connect to server",
"naCpus": "N/A CPU(s)",
"cpuCores_one": "{{count}} Core",
"cpuCores_other": "{{count}} Cores",
"cpuUsage": "CPU Usage",
"memoryUsage": "Memory Usage",
"diskUsage": "Disk Usage",
"selectFilesystem": "Select filesystem",
"temperature": "Temperature",
"highestTemperature": "Highest temperature",
"failedToFetchHostConfig": "Failed to fetch host configuration",
"serverOffline": "Server Offline",
"cannotFetchMetrics": "Cannot fetch metrics from offline server",
"totpFailed": "TOTP verification failed",
"noneAuthNotSupported": "Host Metrics does not support 'none' authentication type.",
"noHostSelected": "No host selected",
"load": "Load",
"systemInfo": "System Information",
"hostname": "Hostname",
"operatingSystem": "Operating System",
"kernel": "Kernel",
"seconds": "seconds",
"networkInterfaces": "Network Interfaces",
"noInterfacesFound": "No network interfaces found",
"noProcessesFound": "No processes found",
"processesTotal": "total",
"processesRunning": "running",
"loginStats": "SSH Login Statistics",
"noRecentLoginData": "No recent login data",
"executingQuickAction": "Executing {{name}}...",
"quickActionSuccess": "{{name}} completed successfully",
"quickActionFailed": "{{name}} failed",
"quickActionError": "Failed to execute {{name}}",
"ports": {
"title": "Listening Ports",
"protocol": "Protocol",
"port": "Port",
"address": "Address",
"process": "Process",
"search": "Search ports...",
"allProtocols": "All",
"noData": "No listening ports data"
},
"firewall": {
"title": "Firewall",
"inactive": "Inactive",
"policy": "Policy",
"rules": "rules",
"noRules": "No rules in this chain",
"noData": "No firewall data available",
"action": "Action",
"protocol": "Proto",
"port": "Port",
"source": "Source",
"anywhere": "Anywhere",
"chains": "chains"
},
"loadAvg": "Load Avg",
"swap": "Swap",
"architecture": "Architecture",
"refresh": "Refresh",
"retry": "Retry",
"customize": "Customize layout",
"reset": "Reset",
"tabLive": "Live",
"editModeInstructions": "Drag cards to rearrange, drag the bottom edge to resize, and use the width button to change a card's span. Add or remove cards below.",
"managers": {
"services": "Services",
"processInspector": "Process Inspector",
"logViewer": "Log Viewer",
"cron": "Cron Jobs",
"packages": "Packages",
"ssl": "SSL Certificates",
"firewall": "Firewall",
"users": "Users & Permissions",
"healthCheck": "Health Checks",
"diskBreakdown": "Disk Breakdown",
"systemdTimers": "Timers",
"topMemory": "Top by Memory",
"noData": "No data",
"sudoHint": "Set a sudo password for this host in the host editor to enable privileged actions.",
"filter": "Filter...",
"start": "Start",
"stop": "Stop",
"restart": "Restart",
"actionDone": "{{name}} updated",
"actionFailed": "Action failed",
"signalSent": "Signal sent to PID {{pid}}",
"killHint": "Click: terminate (SIGTERM). Right-click: force kill (SIGKILL).",
"working": "Working...",
"update": "Update",
"upgradeAll": "Upgrade all",
"allUpToDate": "Everything is up to date",
"save": "Save",
"command": "Command",
"enabled": "Enabled",
"cronSaved": "Crontab updated",
"clients": "Clients",
"dryRun": "Dry run",
"renew": "Renew",
"noAcmeClient": "No ACME client (certbot or acme.sh) found on this host.",
"addInputRule": "Add INPUT rule",
"firewallWarning": "Changes are runtime-only until persisted. Be careful not to lock yourself out.",
"ruleApplied": "Rule applied",
"invalidPort": "Enter a valid port (1-65535)",
"newUsername": "New username",
"addUser": "Add",
"deleteUser": "Delete user",
"noHealthChecks": "No health checks configured yet.",
"follow": "Follow",
"noLogData": "No log output.",
"tree": "Tree",
"enableDisable": "Enable / disable at boot",
"grantSudo": "Grant sudo",
"revokeSudo": "Revoke sudo",
"sslIssueCert": "Issue certificate",
"sslExpired": "Expired",
"sslInDays": "in {{days}}d",
"sslNeedDomain": "Enter at least one domain",
"sslIssued": "Certificate issued",
"sslDomainsPlaceholder": "example.com, www.example.com",
"sslHttpStandalone": "HTTP (standalone)",
"sslHttpWebroot": "HTTP (webroot)",
"sslDns": "DNS",
"sslDnsProvider": "DNS provider (e.g. cloudflare)",
"sslIssueHint": "DNS provider credentials must already be configured on the host.",
"sslRevoke": "Revoke certificate",
"sslRevoked": "Certificate revoked",
"sslRevokeConfirm": "Revoke and remove the certificate \"{{name}}\"? This cannot be undone.",
"healthRun": "Run",
"healthName": "Name",
"healthTarget": "Host / address",
"healthAddCheck": "Add check",
"healthSaved": "Health checks saved",
"healthMissingFields": "Each check needs a name and target",
"logFile": "File",
"logUnit": "Unit",
"logCustomPath": "Custom path under /var/log (optional)",
"logGrep": "Filter lines...",
"firewallPersist": "Persist rules",
"firewallPersisted": "Firewall rules persisted",
"wireguard": "WireGuard",
"tailscale": "Tailscale",
"wgNotInstalled": "WireGuard is not installed on this host",
"wgNoInterfaces": "No WireGuard interfaces configured",
"wgInterfaceUp": "Bring up",
"wgInterfaceDown": "Bring down",
"wgBringingUp": "Bringing {{name}} up...",
"wgBringingDown": "Bringing {{name}} down...",
"wgInterfaceUpDone": "{{name}} is up",
"wgInterfaceDownDone": "{{name}} is down",
"wgListenPort": "Port",
"wgPublicKey": "Public key",
"wgEndpoint": "Endpoint",
"wgAllowedIPs": "Allowed IPs",
"wgLastHandshake": "Last handshake",
"wgHandshakeNever": "Never",
"wgTransfer": "Transfer",
"tsNotInstalled": "Tailscale is not installed on this host",
"tsRunning": "Running",
"tsStopped": "Stopped",
"tsEnable": "Connect",
"tsDisable": "Disconnect",
"tsEnabling": "Connecting to Tailscale...",
"tsDisabling": "Disconnecting from Tailscale...",
"tsEnabled": "Tailscale connected",
"tsDisabled": "Tailscale disconnected",
"tsIPs": "Tailscale IPs",
"tsPeers": "Peers",
"tsOnline": "Online",
"tsOffline": "Offline",
"tsExitNode": "Exit node",
"tsExitNodeActive": "Exit node active",
"tsHostname": "Hostname"
}
},
"auth": {
"tagline": "Self-hosted SSH and remote desktop management",
"loginTitle": "Welcome back",
"registerTitle": "Create Account",
"forgotPassword": "Forgot Password?",
"rememberMe": "Remember Device for 30 Days (includes TOTP)",
"noAccount": "Don't have an account?",
"hasAccount": "Already have an account?",
"twoFactorAuth": "Two-Factor Authentication",
"enterCode": "Enter verification code",
"backupCode": "Or use backup code",
"verifyCode": "Verify Code",
"redirectingToApp": "Redirecting to app...",
"sshAuthenticationRequired": "SSH Authentication Required",
"sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable",
"sshAuthenticationFailed": "Authentication Failed",
"sshAuthenticationTimeout": "Authentication Timeout",
"sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.",
"sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.",
"sshTimeoutDescription": "The authentication attempt timed out. Please try again.",
"sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.",
"sshPasswordDescription": "Enter the password for this SSH connection.",
"sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.",
"passphraseRequired": "Passphrase Required",
"passphraseRequiredDescription": "The SSH key is encrypted. Please enter the passphrase to unlock it.",
"back": "Back",
"firstUser": "First User",
"firstUserMessage": "You are the first user and will be made an admin. You can view admin settings in the sidebar user dropdown. If you think this is a mistake, check the docker logs, or create a GitHub issue.",
"external": "External",
"loginWithExternal": "Login with External Provider",
"loginWithExternalDesc": "Login using your configured external identity provider",
"externalNotSupportedInElectron": "External authentication is not supported in the Electron app yet. Please use the web version for OIDC login.",
"loginWithProvider": "Login with {{name}}",
"orContinueWith": "or continue with",
"ldapUsername": "LDAP Username",
"ldapPassword": "LDAP Password",
"ldapSignIn": "Sign In",
"ldapLoginFailed": "LDAP login failed",
"resetPasswordButton": "Reset Password",
"sendResetCode": "Send Reset Code",
"resetCodeDesc": "Enter your username to receive a password reset code. The code will be logged in the docker container logs.",
"resetCode": "Reset Code",
"verifyCodeButton": "Verify Code",
"enterResetCode": "Enter the 6-digit code from the docker container logs for user:",
"newPassword": "New Password",
"confirmNewPassword": "Confirm Password",
"enterNewPassword": "Enter your new password for user:",
"signUp": "Sign Up",
"desktopApp": "Desktop App",
"loggingInToDesktopApp": "Logging in to the desktop app",
"loadingServer": "Loading server...",
"dataLossWarning": "Resetting your password this way will delete all your saved SSH hosts, credentials, and other encrypted data. This action cannot be undone. Only use this if you have forgotten your password and are not logged in.",
"authenticationDisabled": "Authentication Disabled",
"authenticationDisabledDesc": "All authentication methods are currently disabled. Please contact your administrator.",
"passwordLoginDisabledDesc": "Password login is disabled. Use a passkey or an external authentication provider.",
"signInWithPasskey": "Sign in with passkey",
"passkeyLoginFailed": "Passkey login failed",
"attemptsRemaining": "{{count}} attempts remaining",
"confirmResetDataWipe": "This account has not logged in since the encryption upgrade, so its stored data cannot be recovered without the old password. Resetting will permanently delete its hosts, credentials and snippets. Continue?"
},
"hostKey": {
"verifyNewHost": "Verify SSH Host Key",
"keyChangedWarning": "SSH Host Key Changed",
"firstConnectionTitle": "First time connecting to this host",
"firstConnectionDescription": "The authenticity of this host cannot be established. Verify the fingerprint matches what you expect.",
"keyChangedDescription": "The host key for this server has changed since your last connection. This could indicate a security issue.",
"previousKey": "Previous Key",
"newFingerprint": "New Fingerprint",
"fingerprint": "Fingerprint",
"verifyInstructions": "If you trust this host, click Accept to continue and save this fingerprint for future connections.",
"securityWarning": "Security Warning",
"acceptAndContinue": "Accept & Continue",
"acceptNewKey": "Accept New Key & Continue"
},
"errors": {
"databaseConnection": "Could not connect to the database",
"unknownError": "Unknown error",
"loginFailed": "Login failed",
"failedPasswordReset": "Failed to initiate password reset",
"failedVerifyCode": "Failed to verify reset code",
"failedCompleteReset": "Failed to complete password reset",
"invalidTotpCode": "Invalid TOTP code",
"failedOidcLogin": "Failed to start OIDC login",
"silentSigninOidcUnavailable": "Silent sign-in was requested, but OIDC login is not available.",
"failedUserInfo": "Failed to get user info after login",
"oidcAuthFailed": "OIDC authentication failed",
"invalidAuthUrl": "Invalid authorization URL received from backend",
"requiredField": "This field is required",
"minLength": "Minimum length is {{min}}",
"passwordMismatch": "Passwords do not match",
"passwordLoginDisabled": "Username/password login is currently disabled",
"sessionExpired": "Session expired - please log in again",
"totpRateLimited": "Rate limited: Too many TOTP verification attempts. Please try again later.",
"totpRateLimitedWithTime": "Rate limited: Too many TOTP verification attempts. Please wait {{time}} seconds before trying again.",
"resetCodeRateLimited": "Rate limited: Too many verification attempts. Please try again later.",
"resetCodeRateLimitedWithTime": "Rate limited: Too many verification attempts. Please wait {{time}} seconds before trying again.",
"authTokenSaveFailed": "Failed to save authentication token",
"failedToLoadServer": "Failed to load server",
"remoteServerRequired": "Remote server required. Connect a remote server in Settings to use this connection type."
},
"messages": {
"registrationDisabled": "New account registration is currently disabled by an admin. Please log in or contact an administrator.",
"userNotAllowed": "Your account is not authorized to register. Please contact an administrator.",
"databaseConnectionFailed": "Failed to connect to the database server",
"resetCodeSent": "Reset code sent to Docker logs",
"codeVerified": "Code verified successfully",
"passwordResetSuccess": "Password reset successfully",
"loginSuccess": "Login successful",
"registrationSuccess": "Registration successful"
},
"profile": {
"c2sTunnelConfigDesc": "Local desktop tunnels targeting configured SSH hosts.",
"c2sTunnelPresets": "Client Tunnel Presets",
"c2sTunnelPresetsDesc": "Save this desktop client's local tunnel list as a named server preset, or load a preset back into this client.",
"c2sTunnelPresetsUnavailable": "Client tunnel presets are only available in the desktop client.",
"c2sPresetName": "Preset Name",
"c2sPresetNamePlaceholder": "Client preset name",
"c2sPresetToLoad": "Preset To Load",
"c2sNoPresetSelected": "No preset selected",
"c2sNoPresets": "No presets saved",
"c2sLoadPreset": "Load",
"c2sCurrentLocalConfig": "{{count}} local client tunnel(s) configured on this desktop.",
"c2sPresetSyncNote": "Presets are explicit snapshots; loading one replaces this desktop client's local client tunnel list.",
"c2sPresetSaved": "Client tunnel preset saved",
"c2sPresetLoaded": "Client tunnel preset loaded locally",
"c2sPresetRenamed": "Client tunnel preset renamed",
"c2sPresetDeleted": "Client tunnel preset deleted",
"c2sPresetLoadError": "Failed to load client tunnel presets"
},
"placeholders": {
"maxRetries": "3",
"retryInterval": "10",
"language": "Language",
"keyPassword": "key password",
"pastePrivateKey": "Paste your private key here...",
"localListenerHost": "127.0.0.1 (listen locally)",
"localTargetHost": "127.0.0.1 (target on this computer)",
"socksListenerHost": "127.0.0.1 (SOCKS listener)",
"enterPassword": "Enter your password",
"defaultPort": "22",
"defaultEndpointPort": "224"
},
"dashboard": {
"title": "Dashboard",
"loading": "Loading dashboard...",
"github": "GitHub",
"support": "Support",
"discord": "Discord",
"docs": "Docs",
"donate": "Donate",
"serverOverview": "Server Overview",
"version": "Version",
"upToDate": "Up to Date",
"updateAvailable": "Update Available",
"beta": "Beta",
"uptime": "Uptime",
"database": "Database",
"healthy": "Healthy",
"error": "Error",
"totalHosts": "Total Hosts",
"totalTunnels": "Total Tunnels",
"totalCredentials": "Total Credentials",
"recentActivity": "Recent Activity",
"reset": "Reset",
"loadingRecentActivity": "Loading recent activity...",
"noRecentActivity": "No recent activity",
"quickActions": "Quick Actions",
"addHost": "Add Host",
"addCredential": "Add Credential",
"adminSettings": "Admin Settings",
"userProfile": "User Profile",
"serverStats": "Server Stats",
"loadingServerStats": "Loading server stats...",
"noServerData": "No server data available",
"cpu": "CPU",
"ram": "RAM",
"customizeLayout": "Customize Dashboard",
"dashboardSettings": "Dashboard Settings",
"enableDisableCards": "Enable/Disable Cards",
"resetLayout": "Reset to Default",
"serverOverviewCard": "Server Overview",
"recentActivityCard": "Recent Activity",
"networkGraphCard": "Network Graph",
"networkGraph": "Network Graph",
"quickActionsCard": "Quick Actions",
"serverStatsCard": "Server Stats",
"panelMain": "Main",
"panelSide": "Side",
"justNow": "just now",
"serviceLinks": "Service Links",
"homepagePreview": "Homepage Preview"
},
"donation": {
"title": "Enjoying Termix?",
"body": "Termix is free and open source, built and maintained by a two-person team in our spare time. If it's replaced a commercial tool you'd otherwise be paying for, a donation helps cover hosting costs and keeps development going. As of now, donations are crypto only.",
"milestones": "Donations also help fund the time to research and learn what's needed to build features like SAML and Kubernetes support. See the progress on the donate page.",
"cta": "Donate",
"dismiss": "Maybe later"
},
"dashboardTab": {
"stable": "STABLE",
"hostsOnline": "Hosts Online",
"activeTunnels": "Active Tunnels",
"registerNewServer": "Register a new server",
"storeSshKeysOrPasswords": "Store SSH keys or passwords",
"manageUsersAndRoles": "Manage users and roles",
"manageYourAccount": "Manage your account",
"hostStatus": "Host Status",
"noHostsConfigured": "No hosts configured",
"online": "ONLINE",
"offline": "OFFLINE",
"checking": "CHECKING",
"onlineLower": "Online",
"nodes": "{{count}} nodes",
"add": "Add:",
"commandPalette": "Command Palette",
"done": "Done",
"editModeInstructions": "Drag cards to reorder · Drag the column divider to resize columns · Drag the bottom edge of a card to resize its height · Trash to remove",
"empty": "Empty",
"clear": "Clear",
"serviceLinksTitle": "Service Links",
"serviceLinksEmpty": "No service links yet",
"serviceLinksAddLabel": "Label",
"serviceLinksAddUrl": "URL",
"serviceLinksAdd": "Add",
"serviceLinksLabelPlaceholder": "My Service",
"serviceLinksUrlPlaceholder": "http://192.168.1.10:8080",
"serviceLinksInvalidUrl": "Enter a valid web address",
"serviceLinksAddFailed": "Failed to add service link",
"disk": "Disk",
"viewServerDetails": "View server details"
},
"sessionLogs": {
"title": "Session Logs",
"noLogs": "No session logs yet",
"noLogsDesc": "Enable session logging on a host to start recording",
"duration": "Duration",
"viewLog": "View log",
"downloadLog": "Download",
"deleteLog": "Delete",
"confirmDelete": "Delete this session log?",
"confirmDeleteDesc": "This action cannot be undone.",
"copyContent": "Copy",
"copied": "Copied!",
"loadError": "Failed to load session logs",
"deleteError": "Failed to delete session log",
"filterByHost": "Filter by host..."
},
"networkGraph": {
"addHost": "Add Host",
"addGroup": "Add Group",
"addLink": "Add Link",
"zoomIn": "Zoom In",
"zoomOut": "Zoom Out",
"resetView": "Reset View",
"selectHost": "Select Host",
"chooseHost": "Choose a host...",
"parentGroup": "Parent Group",
"noGroup": "No Group",
"groupName": "Group Name",
"color": "Color",
"source": "Source",
"target": "Target",
"moveToGroup": "Move to Group",
"selectGroup": "Select group...",
"addConnection": "Add Connection",
"hostDetails": "Host Details",
"removeFromGroup": "Remove from Group",
"addHostHere": "Add Host Here",
"editGroup": "Edit Group",
"delete": "Delete",
"add": "Add",
"create": "Create",
"move": "Move",
"connect": "Connect",
"createGroup": "Create Group",
"selectSourcePlaceholder": "Select Source...",
"selectTargetPlaceholder": "Select Target...",
"invalidFile": "Invalid File",
"hostAlreadyExists": "Host is already in the topology",
"connectionExists": "Connection already exists",
"unknown": "Unknown",
"name": "Name",
"ip": "IP",
"status": "Status",
"failedToAddNode": "Failed to add node",
"sourceDifferentFromTarget": "Source and target must be different",
"exportJSON": "Export JSON",
"importJSON": "Import JSON",
"terminal": "Terminal",
"fileManager": "File Manager",
"tunnel": "Tunnel",
"docker": "Docker",
"serverStats": "Host Metrics",
"hostMetrics": "Host Metrics",
"noNodes": "No nodes yet"
},
"docker": {
"notEnabled": "Docker is not enabled for this host",
"validating": "Validating Docker...",
"connecting": "Connecting...",
"error": "Error",
"version": "Docker {{version}}",
"connectionFailed": "Failed to connect to Docker",
"containerStarted": "Container {{name}} started",
"failedToStartContainer": "Failed to start container {{name}}",
"containerStopped": "Container {{name}} stopped",
"failedToStopContainer": "Failed to stop container {{name}}",
"containerRestarted": "Container {{name}} restarted",
"failedToRestartContainer": "Failed to restart container {{name}}",
"containerPaused": "Container {{name}} paused",
"containerUnpaused": "Container {{name}} unpaused",
"failedToTogglePauseContainer": "Failed to toggle pause state for container {{name}}",
"containerRemoved": "Container {{name}} removed",
"failedToRemoveContainer": "Failed to remove container {{name}}",
"image": "Image",
"ports": "Ports",
"noPorts": "No ports",
"start": "Start",
"confirmRemoveContainer": "Are you sure you want to remove the container '{{name}}'? This action cannot be undone.",
"runningContainerWarning": "Warning: This container is currently running. Removing it will stop the container first.",
"loadingContainers": "Loading containers...",
"manager": "Docker Manager",
"autoRefresh": "Auto Refresh",
"timestamps": "Timestamps",
"lines": "Lines",
"filterLogs": "Filter logs...",
"refresh": "Refresh",
"download": "Download",
"clear": "Clear",
"logsDownloaded": "Logs downloaded successfully",
"last50": "Last 50",
"last100": "Last 100",
"last500": "Last 500",
"last1000": "Last 1000",
"allLogs": "All Logs",
"noLogsMatching": "No logs matching \"{{query}}\"",
"noLogsAvailable": "No logs available",
"noContainersFound": "No containers found",
"noContainersFoundHint": "No Docker containers are available on this host",
"searchPlaceholder": "Search containers...",
"allStatuses": "All Statuses",
"stateRunning": "Running",
"statePaused": "Paused",
"stateExited": "Exited",
"stateRestarting": "Restarting",
"noContainersMatchFilters": "No containers match your filters",
"noContainersMatchFiltersHint": "Try adjusting your search or filter criteria",
"failedToFetchStats": "Failed to fetch container statistics",
"containerNotRunning": "Container not running",
"startContainerToViewStats": "Start the container to view statistics",
"loadingStats": "Loading statistics...",
"errorLoadingStats": "Error loading statistics",
"noStatsAvailable": "No statistics available",
"cpuUsage": "CPU Usage",
"current": "Current",
"memoryUsage": "Memory Usage",
"networkIo": "Network I/O",
"input": "Input",
"output": "Output",
"blockIo": "Block I/O",
"read": "Read",
"write": "Write",
"pids": "PIDs",
"containerInformation": "Container Information",
"name": "Name",
"id": "ID",
"state": "State",
"containerMustBeRunning": "Container must be running to access console",
"verificationCodePrompt": "Enter verification code",
"totpVerificationFailed": "TOTP verification failed. Please try again.",
"warpgateVerificationFailed": "Warpgate authentication failed. Please try again.",
"connectedTo": "Connected to {{containerName}}",
"disconnected": "Disconnected",
"consoleError": "Console error",
"errorMessage": "Error: {{message}}",
"failedToConnect": "Failed to connect to container",
"console": "Console",
"selectShell": "Select shell",
"bash": "Bash",
"sh": "sh",
"ash": "ash",
"connect": "Connect",
"disconnect": "Disconnect",
"notConnected": "Not connected",
"clickToConnect": "Click connect to start a shell session",
"connectingTo": "Connecting to {{containerName}}...",
"containerNotFound": "Container not found",
"backToList": "Back to List",
"logs": "Logs",
"stats": "Stats",
"consoleTab": "Console",
"startContainerToAccess": "Start the container to access the console"
},
"admin": {
"sectionGeneral": "General",
"sectionOidc": "OIDC",
"sectionSso": "SSO Providers",
"ssoAddProvider": "Add Provider",
"ssoDocsLink": "View docs",
"ssoProviderDocsLink": "View docs",
"ssoNoProviders": "No SSO providers configured.",
"ssoProviderName": "Display Name",
"ssoProviderType": "Provider Type",
"ssoDeleteProvider": "Delete Provider",
"ssoDeleteConfirm": "Delete this provider? Associated users will be unable to login.",
"ssoTypeOidc": "OIDC",
"ssoTypeLdap": "LDAP",
"ssoTypeGithub": "GitHub",
"ssoTypeGoogle": "Google",
"ssoEnabled": "Enabled",
"ssoDisabled": "Disabled",
"ssoSaveProvider": "Save Provider",
"ssoTestConnection": "Test Connection",
"ssoEditProvider": "Edit Provider",
"ldapHost": "LDAP Host",
"ldapPort": "Port",
"ldapUseTls": "Use TLS (LDAPS)",
"ldapBindDn": "Bind DN",
"ldapBindPassword": "Bind Password",
"ldapUserSearchBase": "User Search Base",
"ldapUserSearchFilter": "User Search Filter",
"ldapUsernameAttr": "Username Attribute",
"ldapDisplayNameAttr": "Display Name Attribute",
"ldapGroupSearchBase": "Group Search Base",
"ldapAdminGroup": "Admin Group",
"ldapAllowedUsers": "Allowed Users",
"sectionUsers": "Users",
"sectionSessions": "Sessions",
"sectionRoles": "Roles",
"sectionDatabase": "Database",
"sectionApiKeys": "API Keys",
"sectionAuditLog": "Audit Log",
"sectionSsl": "SSL / Let's Encrypt",
"sslDescription": "Automatically issue and renew a trusted SSL certificate from Let's Encrypt. Requires a public domain and port 80 or DNS access.",
"sslDocsLink": "View SSL docs",
"sslDomain": "Domain",
"sslDomainPlaceholder": "termix.example.com",
"sslDomainDesc": "The public domain name for the certificate.",
"sslEmail": "Email",
"sslEmailPlaceholder": "admin@example.com",
"sslEmailDesc": "Contact email for Let's Encrypt notifications and account.",
"sslChallengeType": "Challenge Type",
"sslChallengeTypeDesc": "How to prove domain ownership to Let's Encrypt.",
"sslChallengeHttp": "HTTP (webroot) - requires port 80 accessible from the internet",
"sslChallengeDns": "DNS (Cloudflare) - requires a Cloudflare API token",
"sslCloudflareToken": "Cloudflare API Token",
"sslCloudflareTokenPlaceholder": "Enter token...",
"sslCloudflareTokenDesc": "Scoped token with Zone:DNS:Edit permission for your domain.",
"sslCertStatus": "Certificate Status",
"sslCertStatusNone": "No certificate",
"sslCertStatusValid": "Valid",
"sslCertStatusExpiring": "Expiring soon",
"sslCertStatusExpired": "Expired",
"sslCertExpiresAt": "Expires {{date}}",
"sslLastIssued": "Last issued {{date}}",
"sslRequestCert": "Issue / Renew Certificate",
"sslRequestCertLoading": "Requesting certificate...",
"sslRequestCertSuccess": "Certificate issued and installed successfully",
"sslRequestCertFailed": "Certificate request failed",
"sslSave": "Save Settings",
"sslSaved": "SSL settings saved",
"sslSaveFailed": "Failed to save SSL settings",
"sslRequiresDomain": "Domain and email are required",
"sslInfoNote": "After issuing a certificate, enable SSL in your environment variables (ENABLE_SSL=true) and restart Termix.",
"sslManualOption": "Manual (upload certificate)",
"sslManualCert": "Certificate (PEM)",
"sslManualCertPlaceholder": "-----BEGIN CERTIFICATE-----",
"sslManualKey": "Private Key (PEM)",
"sslManualKeyPlaceholder": "-----BEGIN PRIVATE KEY-----",
"sslManualDesc": "Paste your existing certificate and private key, including a full chain if required by your CA.",
"sslManualUpload": "Upload & Install Certificate",
"sslManualUploadLoading": "Uploading certificate...",
"sslManualUploadSuccess": "Certificate uploaded and installed successfully",
"sslManualUploadFailed": "Certificate upload failed",
"sslManualRequiresFields": "Certificate and private key are required",
"auditLogTotal": "{{total}} total entries",
"auditLogEmpty": "No audit log entries found",
"auditLogSuccess": "Success",
"auditLogFailed": "Failed",
"auditLogClearFilters": "Clear Filters",
"auditLogPage": "Page {{page}} of {{totalPages}} ({{total}} total)",
"auditLogIp": "IP",
"auditLogResourceId": "Resource ID",
"auditLogFilterUser": "User",
"auditLogFilterAction": "Action",
"auditLogFilterResourceType": "Resource Type",
"auditLogFilterStatus": "Status",
"auditLogFilterFrom": "From",
"auditLogFilterTo": "To",
"auditLogFilterAll": "All",
"allowRegistration": "Allow User Registration",
"allowRegistrationDesc": "Let new users self-register with a username and password",
"allowPasswordLogin": "Allow Password Login",
"allowPasswordLoginDesc": "Username/password login",
"oidcAutoProvision": "OIDC Auto-Provision",
"oidcAutoProvisionDesc": "Auto-create accounts for OIDC/SSO users on first login (independent of the registration toggle)",
"oidcSilentLoginDefault": "Silent OIDC Login by Default",
"oidcSilentLoginDefaultDesc": "Automatically redirect to OIDC login on every visit, skipping the login form entirely",
"allowPasswordReset": "Allow Password Reset",
"allowPasswordResetDesc": "Reset code via Docker logs",
"commandHistoryEnabled": "Command History",
"commandHistoryEnabledDesc": "Allow command history recording. When disabled, history is not saved regardless of per-host settings.",
"updateCommandHistoryFailed": "Failed to update command history setting",
"analyticsEnabled": "Share Anonymous Usage Statistics",
"analyticsEnabledDesc": "Sends an anonymous daily count of users, hosts, and feature usage to help improve Termix. No personal data or connection details are ever included.",
"analyticsEnabledLockedDesc": "This setting is locked by the ENABLE_TELEMETRY environment variable and cannot be changed here.",
"updateAnalyticsFailed": "Failed to update analytics setting",
"sessionSharingGloballyEnabled": "Allow Session Sharing",
"sessionSharingGloballyEnabledDesc": "Allow live terminal, RDP, VNC, and Telnet sessions to be shared instance-wide. Overrides every per-host sharing toggle when disabled.",
"updateSessionSharingFailed": "Failed to update session sharing setting",
"sessionTimeout": "Session Timeout",
"hours": "hours",
"sessionTimeoutRange": "Min 1h · Max 720h",
"monitoringDefaults": "Monitoring Defaults",
"statusCheck": "Status Check",
"metrics": "Metrics",
"sec": "sec",
"logLevel": "Log Level",
"enableGuacamole": "Enable Guacamole",
"enableGuacamoleDesc": "RDP/VNC remote desktop",
"enableGuacamoleDocsLink": "View docs",
"guacdUrl": "guacd URL",
"tailscaleApiKey": "Tailscale API Key",
"tailscaleApiKeyDescription": "Used for device discovery in the host editor. Generate a key at tailscale.com/admin/settings/keys.",
"tailscaleApiKeyDocsLink": "View docs",
"oidcDescription": "Configure OpenID Connect for SSO. Fields marked * are required.",
"oidcDocsLink": "View docs",
"oidcClientId": "Client ID",
"oidcClientSecret": "Client Secret",
"oidcAuthUrl": "Authorization URL",
"oidcIssuerUrl": "Issuer URL",
"oidcTokenUrl": "Token URL",
"oidcUserIdentifier": "User Identifier Path",
"oidcDisplayName": "Display Name Path",
"oidcScopes": "Scopes",
"oidcUserinfoUrl": "Override Userinfo URL",
"oidcAllowedUsers": "Allowed Users",
"oidcAllowedUsersDesc": "One email per line. Leave empty to allow all.",
"oidcAdminGroup": "Admin Group",
"oidcAdminGroupDesc": "Users in this group are granted admin. Leave empty to disable group sync.",
"oidcGroupClaim": "Group Claim",
"oidcGroupClaimDesc": "Optional. The claim path that contains the user's groups. Defaults to groups, roles, then group. Use this for providers with a custom claim (e.g. Zitadel).",
"oidcCaCert": "Custom CA Certificate",
"oidcCaCertDesc": "Optional. PEM-encoded CA certificate for OIDC providers using a private or self-signed CA. Leave empty to use the system trust store.",
"removeOidc": "Remove",
"usersCount": "{{count}} users",
"createUser": "Create",
"newRole": "New Role",
"roleName": "Name",
"roleDisplayName": "Display Name",
"roleDescription": "Description",
"rolesCount": "{{count}} roles",
"createRole": "Create",
"creating": "Creating...",
"exportDatabase": "Export Database",
"exportDatabaseDesc": "Download a backup of all hosts, credentials, and settings",
"export": "Export",
"exporting": "Exporting...",
"importDatabase": "Import Database",
"importDatabaseDesc": "Restore from a .sqlite backup file",
"importDatabaseSelected": "Selected: {{name}}",
"selectFile": "Select File",
"changeFile": "Change",
"import": "Import",
"importing": "Importing...",
"apiKeysCount": "{{count}} keys",
"apiKeysDocsLink": "View docs",
"newApiKey": "New API Key",
"apiKeyCreatedWarning": "Key created - copy it now, it won't be shown again.",
"apiKeyName": "Name",
"apiKeyUser": "User",
"apiKeySelectUser": "Select a user...",
"apiKeyExpiresAt": "Expires At",
"createKey": "Create Key",
"apiKeyNoExpiry": "No expiry",
"revokedBadge": "REVOKED",
"authTypeDual": "Dual Auth",
"authTypeOidc": "OIDC",
"authTypeLocal": "Local",
"adminStatusAdministrator": "Administrator",
"adminStatusRegularUser": "Regular User",
"adminBadge": "ADMIN",
"systemBadge": "SYS",
"customBadge": "CUSTOM",
"youBadge": "YOU",
"sessionsActive": "{{count}} active",
"sessionActive": "Active: {{time}}",
"sessionExpires": "Exp: {{time}}",
"revokeAll": "All",
"revokeAllSessionsSuccess": "All sessions for user revoked",
"revokeAllSessionsFailed": "Failed to revoke sessions",
"revokeSessionFailed": "Failed to revoke session",
"addRole": "Add role",
"noCustomRoles": "No custom roles defined",
"removeRoleFailed": "Failed to remove role",
"assignRoleFailed": "Failed to assign role",
"deleteRoleFailed": "Failed to delete role",
"userAdminAccess": "Administrator",
"userAdminAccessDesc": "Full access to all admin settings",
"userRoles": "Roles",
"revokeAllUserSessions": "Revoke All Sessions",
"revokeAllUserSessionsDesc": "Force re-login on all devices",
"revoke": "Revoke",
"deleteUserWarning": "Deleting this user is permanent.",
"deleteUser": "Delete {{username}}",
"deleting": "Deleting...",
"deleteUserFailed": "Failed to delete user",
"deleteUserSuccess": "User \"{{username}}\" deleted",
"deleteRoleSuccess": "Role \"{{name}}\" deleted",
"revokeKeySuccess": "Key \"{{name}}\" revoked",
"revokeKeyFailed": "Failed to revoke key",
"copiedToClipboard": "Copied to clipboard",
"done": "Done",
"createUserTitle": "Create User",
"createUserDesc": "Create a new local account.",
"createUserUsername": "Username",
"createUserPassword": "Password",
"createUserPasswordHint": "Minimum 6 characters.",
"createUserEnterUsername": "Enter username",
"createUserEnterPassword": "Enter password",
"createUserSubmit": "Create User",
"editUserTitle": "Manage User: {{username}}",
"editUserDesc": "Edit roles, admin status, sessions, and account settings.",
"editUserUsername": "Username",
"editUserAuthType": "Auth Type",
"editUserAdminStatus": "Admin Status",
"editUserUserId": "User ID",
"linkAccountTitle": "Link Accounts",
"linkAccountDesc": "Merge the OIDC account {{username}} with an existing local account.",
"linkAccountDescLocal": "Link a local account {{username}} with an existing OIDC-only account.",
"linkAccountWarningTitle": "This will:",
"linkAccountEffect1": "Delete the OIDC-only account",
"linkAccountEffect2": "Add OIDC login to the target account",
"linkAccountEffect3": "Allow both OIDC and password login",
"linkAccountTargetUsername": "Local Account Username",
"linkAccountTargetPlaceholder": "Enter the local account username to link to",
"linkAccountOidcUsername": "OIDC Account Username",
"linkAccountOidcPlaceholder": "Enter the OIDC-only account username to merge in",
"linkAccountOidcNotFound": "No OIDC-only account found with that username",
"linkAccounts": "Link Accounts",
"linkAccountSuccess": "Accounts linked successfully",
"linkAccountFailed": "Failed to link accounts",
"linkAccountInProgress": "Linking...",
"unlinkAccountTitle": "Unlink OIDC",
"unlinkAccountDesc": "Remove OIDC authentication from {{username}}. They will only be able to log in with their password.",
"unlinkAccountWarning": "This will remove OIDC login from this account. The user must have a password set to continue logging in.",
"unlinkAccount": "Unlink OIDC",
"unlinkAccountInProgress": "Unlinking...",
"unlinkAccountSuccess": "OIDC unlinked successfully",
"unlinkAccountFailed": "Failed to unlink OIDC",
"saving": "Saving...",
"updateRegistrationFailed": "Failed to update registration setting",
"updatePasswordLoginFailed": "Failed to update password login setting",
"cannotDisablePasswordLoginWithTotp": "Cannot disable password login while 2FA is enabled for one or more users. Disable 2FA first.",
"updateOidcAutoProvisionFailed": "Failed to update OIDC auto-provision setting",
"updateOidcSilentLoginDefaultFailed": "Failed to update silent OIDC login setting",
"updatePasswordResetFailed": "Failed to update password reset setting",
"sessionTimeoutRange2": "Session timeout must be between 1 and 720 hours",
"sessionTimeoutSaved": "Session timeout saved",
"sessionTimeoutSaveFailed": "Failed to save session timeout",
"monitoringIntervalInvalid": "Invalid interval values",
"monitoringSaved": "Monitoring settings saved",
"monitoringSaveFailed": "Failed to save monitoring settings",
"metricsHistoryRetention": "Metrics History Retention",
"metricsHistoryRetentionRange": "1 to 90 days",
"days": "days",
"guacamoleSaved": "Guacamole settings saved",
"guacamoleSaveFailed": "Failed to save Guacamole settings",
"guacamoleUpdateFailed": "Failed to update Guacamole setting",
"tailscaleSettingsSaved": "Tailscale settings saved",
"tailscaleSettingsSaveFailed": "Failed to save Tailscale settings",
"logLevelUpdateFailed": "Failed to update log level",
"oidcSaved": "OIDC configuration saved",
"oidcSaveFailed": "Failed to save OIDC config",
"oidcRemoved": "OIDC configuration removed",
"oidcRemoveFailed": "Failed to remove OIDC config",
"createUserRequired": "Username and password are required",
"createUserPasswordTooShort": "Password must be at least 6 characters",
"createUserSuccess": "User \"{{username}}\" created",
"createUserFailed": "Failed to create user",
"updateAdminStatusFailed": "Failed to update admin status",
"allSessionsRevoked": "All sessions revoked",
"revokeSessionsFailed": "Failed to revoke sessions",
"manageUserData": "Manage user data",
"backToUsers": "Back to users",
"manageTabAccount": "Account",
"manageTabHosts": "Hosts",
"manageTabCredentials": "Credentials",
"manageTabSnippets": "Snippets",
"manageTabSessions": "Sessions",
"manageTabDanger": "Danger",
"manageEditorBack": "Back to {{username}}'s data",
"dataLockedBadge": "LOCKED",
"dataLockedNotice": "{{username}}'s data stays locked until they next log in. Their hosts, credentials and snippets cannot be viewed or edited until then.",
"resetPasswordTitle": "Reset Password",
"resetPasswordOidcOnly": "This user signs in through an external provider and has no password.",
"resetPasswordPlaceholder": "New password",
"resetPasswordBtn": "Reset",
"resetPasswordWorking": "Resetting...",
"resetPasswordSuccess": "Password reset",
"resetPasswordSuccessWiped": "Password reset. The user's encrypted data was wiped.",
"resetPasswordFailed": "Failed to reset password",
"resetPasswordConfirmWipe": "{{username}} has not logged in since the encryption upgrade, so their data cannot be recovered. Resetting now will delete their hosts, credentials and snippets. Continue?",
"totpSectionTitle": "Two-Factor Authentication",
"totpStatusEnabled": "TOTP is enabled for this user",
"totpStatusDisabled": "TOTP is not enabled for this user",
"disableTotp": "Disable",
"disableTotpConfirm": "Disable two-factor authentication for {{username}}? They will be able to log in with only their password.",
"totpDisabledSuccess": "TOTP disabled",
"totpDisableFailed": "Failed to disable TOTP",
"manageApiKeys": "API Keys",
"apiKeyNamePlaceholder": "Key name",
"apiKeyCopyNotice": "Copy this key now, it won't be shown again.",
"noApiKeysForUser": "No API keys",
"apiKeyDeleteFailed": "Failed to delete API key",
"exportUserData": "Data Export",
"exportUserDataDesc": "Download this user's hosts, credentials and file manager data as JSON. Secrets are decrypted.",
"exportUserDataSuccess": "User data exported",
"exportUserDataFailed": "Failed to export user data",
"hostsCount": "{{count}} hosts",
"addHostForUser": "Add Host",
"noHostsForUser": "This user has no hosts",
"connectToHost": "Connect",
"deleteHostConfirm": "Delete host \"{{name}}\" belonging to {{username}}?",
"hostDeletedSuccess": "Host deleted",
"hostDeleteFailed": "Failed to delete host",
"credentialsCount": "{{count}} credentials",
"addCredentialForUser": "Add Credential",
"noCredentialsForUser": "This user has no credentials",
"deleteCredentialConfirm": "Delete credential \"{{name}}\" belonging to {{username}}?",
"credentialDeletedSuccess": "Credential deleted",
"credentialDeleteFailed": "Failed to delete credential",
"snippetsCount": "{{count}} snippets",
"addSnippetForUser": "Add Snippet",
"noSnippetsForUser": "This user has no snippets",
"snippetRequiredFields": "Snippet name and content are required",
"snippetNamePlaceholder": "Snippet name",
"snippetContentPlaceholder": "Command content",
"snippetFolderPlaceholder": "Folder (optional)",
"snippetSaved": "Snippet saved",
"snippetSaveFailed": "Failed to save snippet",
"deleteSnippetConfirm": "Delete snippet \"{{name}}\" belonging to {{username}}?",
"snippetDeletedSuccess": "Snippet deleted",
"snippetDeleteFailed": "Failed to delete snippet",
"noSessionsForUser": "No active sessions",
"deleteUserDangerDesc": "Permanently delete {{username}} and all of their data (hosts, credentials, snippets, history). This cannot be undone.",
"deleteUserConfirm": "Permanently delete {{username}} and all of their data?",
"deleteUserAdminBlocked": "Remove admin status before deleting this user.",
"createRoleRequired": "Name and display name are required",
"createRoleSuccess": "Role \"{{name}}\" created",
"createRoleFailed": "Failed to create role",
"apiKeyNameRequired": "Key name is required",
"apiKeyUserRequired": "User ID is required",
"apiKeyCreatedSuccess": "API key \"{{name}}\" created",
"apiKeyCreateFailed": "Failed to create API key",
"exportSuccess": "Database exported successfully",
"exportFailed": "Database export failed",
"importSelectFile": "Please select a file first",
"importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped",
"importFailed": "Import failed: {{error}}",
"importError": "Database import failed",
"sectionHostDefaults": "Host Defaults",
"hostDefaultsDesc": "Settings applied automatically when creating a new host. Individual hosts can still override these.",
"hostDefaultsSocks5": "SOCKS5 Proxy",
"hostDefaultsUseSocks5": "Enable SOCKS5 Proxy",
"hostDefaultsUseSocks5Desc": "Pre-fill SOCKS5 proxy on all new hosts",
"hostDefaultsSocks5Host": "Proxy Host / Port",
"hostDefaultsSocks5Username": "Proxy Username",
"hostDefaultsSocks5Password": "Proxy Password",
"hostDefaultsMetrics": "Host Metrics",
"hostDefaultsMetricsEnabled": "Enable Metrics",
"hostDefaultsMetricsEnabledDesc": "Collect CPU, memory, and other stats on new hosts by default",
"hostDefaultsStatusCheckEnabled": "Enable Status Check",
"hostDefaultsStatusCheckEnabledDesc": "Poll online/offline status on new hosts by default",
"hostDefaultsTerminal": "Terminal",
"hostDefaultsSessionLogging": "Session Logging",
"hostDefaultsSessionLoggingDesc": "Record terminal sessions on new hosts by default",
"hostDefaultsCommandHistory": "Command History",
"hostDefaultsCommandHistoryDesc": "Track command history on new hosts by default",
"hostDefaultsSaved": "Host defaults saved",
"hostDefaultsSaveFailed": "Failed to save host defaults",
"rolePermissions": {
"count": "{{count}} permissions",
"editAction": "Edit permissions",
"loadError": "Failed to load the permissions catalog",
"saved": "Role permissions saved",
"saveError": "Failed to save role permissions",
"save": "Save",
"saving": "Saving..."
}
},
"newUi": {
"sidebar": {
"quickConnect": {
"hostLabel": "Host",
"hostPlaceholder": "192.168.1.1 or example.com",
"portLabel": "Port",
"portPlaceholder": "22",
"usernameLabel": "Username",
"usernamePlaceholder": "username",
"authLabel": "Auth",
"passwordLabel": "Password",
"passwordPlaceholder": "password",
"privateKeyLabel": "Private Key",
"privateKeyPlaceholder": "Paste private key...",
"credentialLabel": "Credential",
"credentialPlaceholder": "Select a saved credential",
"connectToTerminal": "Connect to Terminal",
"connectToFiles": "Connect to Files"
},
"history": {
"noTerminalSelected": "No terminal selected",
"noTerminalSelectedHint": "Open an SSH terminal tab to view its command history",
"searchPlaceholder": "Search history...",
"clearAll": "Clear All",
"noHistoryEntries": "No history entries",
"trackingDisabled": "History tracking is disabled",
"trackingDisabledHint": "Enable it in the host's terminal settings."
},
"sshTools": {
"keyRecordingTitle": "Key Recording",
"recordToTerminals": "Record to terminals",
"selectAll": "All",
"selectNone": "None",
"noTerminalTabsOpen": "No terminal tabs open",
"selectTerminalsAbove": "Select terminals above",
"broadcastInputPlaceholder": "Type here to broadcast keystrokes...",
"fillPassword": "Fill password",
"fillPasswordSuccess": "Filled password into {{count}} terminal(s)",
"fillPasswordMissing": "No saved password for {{count}} selected terminal(s)",
"stopRecording": "Stop Recording",
"startRecording": "Start Recording",
"settingsTitle": "Settings",
"enableRightClickCopyPaste": "Enable right-click copy/paste"
},
"splitScreen": {
"layoutTitle": "Layout",
"selectLayoutAbove": "Select a layout above",
"selectLayoutHint": "Choose how many panes to display",
"panesTitle": "Panes",
"openTabsTitle": "Open Tabs",
"dragTabsHint": "Drag tabs into panes above, or use Quick Assign",
"dropHere": "Drop here",
"emptyPane": "Empty",
"dashboard": "Dashboard",
"clearSplitScreen": "Clear Split Screen",
"quickAssign": "Quick Assign",
"alreadyAssigned": "Pane {{index}}",
"splitTab": "Split Tab",
"addToSplit": "Add to Split",
"removeFromSplit": "Remove from Split",
"assignToPane": "Assign to pane",
"hotkeysTitle": "Keyboard Shortcuts",
"hotkeysSplitRight": "Toggle 2-way split",
"hotkeysSplitBelow": "Toggle 3-way split",
"hotkeysNavigatePane": "Navigate panes",
"hotkeysNextTab": "Next tab",
"hotkeysPrevTab": "Previous tab"
},
"snippets": {
"title": "Snippets",
"createSnippetTitle": "Create Snippet",
"createSnippetDescription": "Create a new command snippet for quick execution",
"nameLabel": "Name",
"namePlaceholder": "e.g., Restart Nginx",
"descriptionLabel": "Description",
"descriptionPlaceholder": "Optional description",
"optional": "Optional",
"folderLabel": "Folder",
"noFolder": "No folder (Uncategorized)",
"commandLabel": "Command",
"commandPlaceholder": "e.g., sudo systemctl restart nginx",
"cancel": "Cancel",
"createSnippetButton": "Create Snippet",
"createFolderTitle": "Create Folder",
"createFolderDescription": "Organize your snippets into folders",
"folderNameLabel": "Folder Name",
"folderNamePlaceholder": "e.g., System Commands, Docker Scripts",
"folderColorLabel": "Folder Color",
"folderIconLabel": "Folder Icon",
"previewLabel": "Preview",
"folderNameFallback": "Folder Name",
"createFolderButton": "Create Folder",
"targetTerminals": "Target Terminals",
"selectAll": "All",
"selectNone": "None",
"noTerminalTabsOpen": "No terminal tabs open",
"searchPlaceholder": "Search snippets...",
"newSnippet": "New Snippet",
"newFolder": "New Folder",
"run": "Run",
"noSnippetsInFolder": "No snippets in this folder",
"uncategorized": "Uncategorized",
"editSnippetTitle": "Edit Snippet",
"editSnippetDescription": "Update this command snippet",
"saveSnippetButton": "Save Changes",
"createSuccess": "Snippet created successfully",
"createFailed": "Failed to create snippet",
"updateSuccess": "Snippet updated successfully",
"updateFailed": "Failed to update snippet",
"deleteFailed": "Failed to delete snippet",
"folderCreateSuccess": "Folder created successfully",
"folderCreateFailed": "Failed to create folder",
"editFolderTitle": "Edit Folder",
"editFolderDescription": "Rename or change the appearance of this folder",
"saveFolderButton": "Save Changes",
"editFolder": "Edit folder",
"deleteFolder": "Delete folder",
"folderDeleteSuccess": "Folder \"{{name}}\" deleted",
"folderDeleteFailed": "Failed to delete folder",
"folderEditSuccess": "Folder updated successfully",
"folderEditFailed": "Failed to update folder",
"confirmRunMessage": "Run \"{{name}}\"?",
"confirmRunButton": "Run",
"runSuccess": "Ran \"{{name}}\" in {{count}} terminal(s)",
"copySuccess": "Copied \"{{name}}\" to clipboard",
"shareTitle": "Share Snippet",
"shareUser": "User",
"shareRole": "Role",
"selectUser": "Select a user...",
"selectRole": "Select a role...",
"shareSuccess": "Snippet shared successfully",
"shareFailed": "Failed to share snippet",
"revokeSuccess": "Access revoked",
"revokeFailed": "Failed to revoke access",
"currentAccess": "Current Access",
"shareLoadError": "Failed to load share data",
"loading": "Loading...",
"close": "Close",
"reorderFailed": "Failed to save snippet order",
"importExport": "Import / Export",
"exportBtn": "Export JSON",
"importBtn": "Import JSON",
"exportSuccess": "Snippets exported successfully",
"exportFailed": "Failed to export snippets",
"importTitle": "Import Snippets",
"importDescription": "Import snippets and folders from a JSON file exported by Termix",
"importDropOrClick": "Drop a JSON file here or click to browse",
"importSelectedFile": "Selected: {{name}}",
"importOverwrite": "Overwrite existing snippets with the same name and folder",
"importStartBtn": "Import",
"importSuccess": "Import complete: {{snippets}} snippet(s) added, {{updated}} updated, {{skipped}} skipped, {{folders}} folder(s) added",
"importFailed": "Failed to import snippets",
"importInvalidFile": "Invalid file: expected a JSON object with snippets or folders arrays",
"targetHostsLabel": "Target Hosts",
"targetHostsHint": "Assign hosts to run this snippet directly without an open terminal.",
"noHostsAvailable": "No hosts configured",
"clearTargetHosts": "Clear all",
"hasTargetHosts": "Has target hosts",
"runOnTargets": "Run on Targets",
"directRunSuccess": "Ran \"{{name}}\" on {{count}} host(s)",
"directRunPartialFail": "\"{{name}}\" failed on one or more hosts",
"executionResultTitle": "Execution Results: {{name}}",
"executionResultDescription": "Output from running the snippet on each target host.",
"executionSuccess": "Success",
"executionFailed": "Failed"
},
"keybindings": {
"title": "Keyboard Shortcuts",
"description": "Customize terminal copy, paste, and control shortcuts, or bind keys to send text or run a snippet.",
"defaultsHeading": "Built-in Shortcuts",
"customHeading": "Custom Shortcuts",
"addBinding": "Add Shortcut",
"addBindingTitle": "Add Shortcut",
"editBindingTitle": "Edit Shortcut",
"loading": "Loading...",
"noCustomBindings": "No custom shortcuts yet",
"defaultBadge": "Default",
"customizedBadge": "Customized",
"customize": "Customize",
"resetToDefault": "Reset to default",
"resetAllToDefaults": "Reset all to defaults",
"close": "Close",
"cancel": "Cancel",
"saveBinding": "Save Shortcut",
"saveError": "Failed to save keyboard shortcuts",
"pressKeysToRecord": "Key combination",
"pressKeysPlaceholder": "Click and press keys",
"recording": "Press keys...",
"comboRequiredError": "Press a key combination first",
"textRequiredError": "Enter the text to send",
"controlCodeRequiredError": "Enter a single letter for the control code",
"snippetRequiredError": "Select a snippet",
"conflictWarning": "This combination is already bound to {{combo}}. Saving will make both fire.",
"actionLabel": "Action",
"actionCopy": "Copy selection",
"actionPaste": "Paste",
"actionSendControlCode": "Send Ctrl+letter signal",
"actionSendText": "Send text",
"actionRunSnippet": "Run existing snippet",
"controlCodeLabel": "Letter",
"textLabel": "Text to send",
"appendEnterLabel": "Press Enter after sending",
"snippetLabel": "Snippet",
"selectSnippetPlaceholder": "Select a snippet",
"orphanedSnippetWarning": "snippet not found",
"clipboardPermissionNote": "May prompt for clipboard permission on first use in some browsers."
},
"userProfile": {
"donateTitle": "Keep Termix alive",
"donateDescription": "Termix is free, self-hosted, and built by only a few people in their spare time. If it's saved you time or money, a crypto donation helps keep it going.",
"donateMilestones": "Donations help fund the time to research and learn what's needed to build SAML, Kubernetes, and Agent support. See the progress and donate.",
"donateButton": "Donate crypto",
"storageModeLocal": "Browser",
"storageModeCloud": "Database",
"storageModeDescription": "Browser stores settings in this browser only. Database syncs to the server and loads on any device.",
"resetToDefaults": "Reset to Defaults",
"resetToDefaultsSuccess": "Settings reset to defaults.",
"storageModeSwitch": "Preference Storage",
"sectionAccount": "Account",
"desktopProfileTitle": "Automatic local desktop profile",
"desktopProfileDescription": "This profile is restricted to the embedded backend and signs in automatically. It has no login password; Remote Sync below uses a separate server account.",
"sectionAppearance": "Appearance",
"sectionSecurity": "Security",
"sectionApiKeys": "API Keys",
"sectionData": "Data",
"sectionC2sTunnels": "C2S Tunnels",
"usernameLabel": "Username",
"roleLabel": "Role",
"roleAdministrator": "Administrator",
"authMethodLabel": "Auth Method",
"authMethodLocal": "Local",
"twoFaLabel": "2FA",
"twoFaOn": "On",
"twoFaOff": "Off",
"versionLabel": "Version",
"betaProgramTitle": "Beta Program",
"betaProgramDescription": "Try upcoming features early with the weekly rolling :beta Docker tag. Unstable, not for production.",
"betaProgramFeedback": "Found a bug? Report it here.",
"betaProgramLearnMore": "Learn More",
"deleteAccount": "Delete Account",
"deleteAccountDescription": "Permanently delete your account",
"changeServerDescription": "Switch to a different Termix backend server",
"deleteButton": "Delete",
"deleteAccountPermanent": "This action is permanent and cannot be undone.",
"deleteAccountWarning": "All sessions, hosts, credentials, and settings will be permanently deleted.",
"confirmPasswordDeletePlaceholder": "Enter your password to confirm",
"languageLabel": "Language",
"themeLabel": "Theme",
"fontSizeLabel": "Font Size",
"accentColorLabel": "Accent Color",
"settingsTerminal": "Terminal",
"commandAutocomplete": "Command Autocomplete",
"commandAutocompleteDesc": "Show autocomplete while typing",
"keyboardShortcuts": "Keyboard Shortcuts",
"keyboardShortcutsDescription": "Customize terminal copy, paste, and command shortcuts",
"manageShortcuts": "Manage",
"terminalLinkBehavior": "Terminal Link Click",
"terminalLinkBehaviorDesc": "Default behavior when clicking links in the terminal",
"historyTracking": "History Tracking",
"historyTrackingDesc": "Track terminal commands",
"commandPalette": "Command Palette",
"commandPaletteDesc": "Enable keyboard shortcut",
"reopenTabsOnLogin": "Reopen Tabs on Login",
"reopenTabsOnLoginDesc": "Restore your open tabs when you log in or refresh the page, even from another device",
"confirmTabClose": "Confirm Tab Close",
"confirmTabCloseDesc": "Ask before closing terminal tabs",
"settingsSidebar": "Sidebar",
"showHostTags": "Show Host Tags",
"showHostTagsDesc": "Display tags in host list",
"hostTrayOnClick": "Click to Expand Host Actions",
"hostTrayOnClickDesc": "Always show connection buttons; click to expand management options instead of hover",
"compactHostView": "Compact Host View",
"compactHostViewDesc": "Collapse each host to a single line showing only its name and address",
"statusColors": "Real Status Colors",
"statusColorsDesc": "Use green/red for online/offline status instead of the accent color",
"pinAppRail": "Pin App Rail",
"pinAppRailDesc": "Keep the left sidebar app rail always expanded instead of expanding on hover",
"openFullscreenSettings": "Open settings full screen",
"exitFullscreenSettings": "Exit full-screen settings",
"expandAppRailOnHover": "Expand App Rail on Hover",
"expandAppRailOnHoverDesc": "Allow the left sidebar app rail to expand when the pointer moves over it",
"settingsNavigation": "Navigation",
"navigationTabsDesc": "Choose which tabs appear in the app rail",
"settingsSnippets": "Snippets",
"foldersCollapsed": "Folders Collapsed",
"foldersCollapsedDesc": "Collapse folders by default",
"confirmExecution": "Confirm Execution",
"confirmExecutionDesc": "Confirm before running snippets",
"settingsUpdates": "Updates",
"disableUpdateChecks": "Disable Update Checks",
"disableUpdateChecksDesc": "Stop checking for updates",
"totpAuthenticator": "TOTP Authenticator",
"totpEnabled": "2FA is enabled",
"totpDisabled": "Add extra login security",
"disable": "Disable",
"enable": "Enable",
"setupTotp": "Setup TOTP",
"qrCode": "QR Code",
"totpInstructions": "Scan QR code or enter secret in your authenticator app, then enter the 6-digit code",
"totpCodePlaceholder": "000000",
"verify": "Verify",
"changePassword": "Change Password",
"currentPasswordLabel": "Current Password",
"currentPasswordPlaceholder": "Current password",
"newPasswordLabel": "New Password",
"newPasswordPlaceholder": "New password",
"confirmPasswordLabel": "Confirm New Password",
"confirmPasswordPlaceholder": "Confirm new password",
"updatePassword": "Update Password",
"createApiKeyTitle": "Create API Key",
"createApiKeyDescription": "Generate a new API key for programmatic access.",
"apiKeyNameLabel": "Name",
"apiKeyNamePlaceholder": "e.g. CI Pipeline",
"expiryDateLabel": "Expiry Date",
"optional": "optional",
"cancel": "Cancel",
"createKey": "Create Key",
"apiKeyCount": "{{count}} keys",
"newKey": "New Key",
"noApiKeys": "No API keys yet.",
"apiKeyActive": "Active",
"apiKeyUsageHint": "Include your key in the",
"apiKeyUsageHintHeader": "header.",
"apiKeyPermissionsHint": "Keys inherit the permissions of the creating user.",
"exportData": "Export My Data",
"exportDataDesc": "Download a backup of your hosts, credentials, and settings to transfer to another device",
"export": "Export",
"exporting": "Exporting...",
"importData": "Import My Data",
"importDataDesc": "Restore your hosts, credentials, and settings from a .sqlite backup file",
"importDataSelected": "Selected: {{name}}",
"selectFile": "Select File",
"changeFile": "Change",
"import": "Import",
"importing": "Importing...",
"exportSuccess": "Data exported successfully",
"exportFailed": "Data export failed",
"importSelectFile": "Please select a file first",
"importCompleted": "Import completed: {{total}} items imported, {{skipped}} skipped",
"importFailed": "Import failed: {{error}}",
"roleUser": "User",
"authMethodDual": "Dual Auth",
"authMethodOidc": "OIDC",
"totpSetupFailed": "Failed to start TOTP setup",
"totpEnter6Digits": "Enter a 6-digit code",
"totpEnabledSuccess": "Two-factor authentication enabled",
"totpInvalidCode": "Invalid code, please try again",
"totpDisableInputRequired": "Enter your TOTP code or password",
"totpDisabledSuccess": "Two-factor authentication disabled",
"totpDisableFailed": "Failed to disable 2FA",
"totpDisableTitle": "Disable 2FA",
"totpDisablePlaceholder": "Enter TOTP code or password",
"totpDisableConfirm": "Disable 2FA",
"totpContinueVerify": "Continue to Verify",
"totpVerifyTitle": "Verify Code",
"totpBackupTitle": "Backup Codes",
"totpDownloadBackup": "Download Backup Codes",
"passkeys": "Passkeys",
"passkeysDesc": "Use WebAuthn/FIDO2 credentials to sign in without a password",
"passkeyName": "Passkey name",
"passkeyUvPreferred": "Preferred",
"passkeyUvRequired": "Required",
"passkeyUvDiscouraged": "Discouraged",
"addPasskey": "Add Passkey",
"noPasskeys": "No passkeys registered.",
"passkeyAdded": "Passkey added",
"passkeyAddFailed": "Failed to add passkey",
"passkeyDeleted": "Passkey deleted",
"passkeyDeleteFailed": "Failed to delete passkey",
"done": "Done",
"secretCopied": "Secret copied to clipboard",
"apiKeyNameRequired": "Key name is required",
"apiKeyCreated": "API key \"{{name}}\" created",
"apiKeyCreateFailed": "Failed to create API key",
"apiKeyUser": "User",
"apiKeyExpires": "Expires",
"apiKeyRevoked": "API key \"{{name}}\" revoked",
"apiKeyRevokeFailed": "Failed to revoke API key",
"passwordFieldsRequired": "Current and new passwords are required",
"passwordMismatch": "Passwords do not match",
"passwordTooShort": "Password must be at least 6 characters",
"passwordUpdated": "Password updated successfully",
"passwordUpdateFailed": "Failed to update password",
"deletePasswordRequired": "Password is required to delete your account",
"deleteFailed": "Failed to delete account",
"deleting": "Deleting...",
"colorPickerTooltip": "Open color picker",
"themeSystem": "System",
"themeLight": "Light",
"themeDark": "Dark",
"themeDracula": "Dracula",
"themeCatppuccin": "Catppuccin",
"themeNord": "Nord",
"themeSolarized": "Solarized",
"themeTokyoNight": "Tokyo Night",
"themeOneDark": "One Dark",
"themeGruvbox": "Gruvbox"
}
}
},
"tmuxMonitor": {
"title": "Tmux Monitor",
"failedToLoadHosts": "Failed to load hosts",
"failedToLoad": "Failed to load tmux sessions",
"tmuxUnavailable": "tmux is not installed on this host",
"noSessions": "No tmux sessions on this host",
"noHostSelected": "No host selected",
"attached": "Attached",
"detached": "Detached",
"attach": "Attach",
"editTags": "Edit tags",
"tagsHint": "Comma-separated tags (e.g. YOLO, lab, training)",
"tagsSaved": "Tags saved",
"tagsSaveFailed": "Failed to save tags",
"searchPlaceholder": "Search output across sessions...",
"searchResults": "{{count}} matches",
"searchFailed": "Search failed",
"selectPaneHint": "Select a pane to preview its output",
"closePreview": "Close preview",
"newSession": "New session",
"newSessionHint": "Session name (letters, digits, _ @ % + = -)",
"newSessionPlaceholder": "my-session",
"create": "Create",
"sessionCreated": "Session \"{{name}}\" created",
"sessionCreateFailed": "Failed to create session",
"splitRight": "Split right",
"splitDown": "Split down",
"splitFailed": "Failed to split pane",
"newWindow": "New window",
"windowCreateFailed": "Failed to create window",
"attachSessionTooltip": "Attach to {{session}}",
"refresh": "Refresh",
"refreshFailed": "Failed to refresh sessions",
"collapseAll": "Collapse all",
"expandAll": "Expand all",
"moreActions": "More actions",
"sessionStats": "Session stats",
"renameSessionTitle": "Rename session \"{{name}}\"",
"rename": "Rename",
"sessionRenamed": "Session renamed to \"{{name}}\"",
"sessionRenameFailed": "Failed to rename session",
"editTagsTitle": "Edit tags for \"{{name}}\"",
"killPane": "Kill pane",
"resizeTree": "Drag to resize — double-click to reset",
"reattach": "Re-attach (fixes a garbled view)",
"killWindow": "Kill window",
"killWindowTitle": "Kill window {{index}} of \"{{session}}\"?",
"killWindowBody": "Every pane and process in this window will be terminated. Killing the last window ends the session.",
"windowKillFailed": "Failed to kill window",
"statusActivity": "Activity",
"statusWindows": "Windows",
"statusPanes": "panes",
"statusTags": "Tags",
"killPaneTitle": "Kill pane {{id}}?",
"killPaneBody": "The process running in this pane will be terminated. Killing the last pane closes its window.",
"paneKillFailed": "Failed to kill pane",
"killSessionTitle": "Kill session \"{{name}}\"?",
"killSessionBody": "All windows and running processes in this session will be terminated. This cannot be undone.",
"kill": "Kill",
"sessionKilled": "Session \"{{name}}\" killed",
"sessionKillFailed": "Failed to kill session",
"hostUnreachable": "Could not connect to the host. Check that it is online and reachable.",
"noServer": "No tmux server is running on this host.",
"searchTruncated": "Partial results — search covers the last {{lines}} lines of each pane and at most {{panes}} panes.",
"closeSearchResults": "Close search results",
"retry": "Retry",
"noHosts": "No SSH hosts available",
"noHostsHint": "Enable the Tmux Monitor option on an SSH host (Host Manager → Terminal tab) to monitor its tmux sessions.",
"tmuxInstallHint": "Install it on the host with:",
"attachTooltip": "Open a terminal to {{host}}",
"attachTooltipPane": "Open a terminal to {{host}} — tmux session {{session}}",
"timeJustNow": "just now",
"timeMinutes": "{{count}}m ago",
"timeHours": "{{count}}h ago",
"timeDays": "{{count}}d ago"
},
"mobileKeyboard": {
"shift": "Shift",
"ctrl": "Ctrl",
"esc": "Esc",
"tab": "Tab",
"backTab": "⇥",
"arrowUp": "Arrow Up",
"arrowDown": "Arrow Down",
"arrowLeft": "Arrow Left",
"arrowRight": "Arrow Right",
"home": "Home",
"end": "End",
"pageUp": "PgUp",
"pageDown": "PgDn",
"delete": "Del",
"paste": "Paste",
"editQuickKeys": "Edit quick keys",
"quickKeysTitle": "Quick Keys",
"quickKeysDesc": "Tap × to remove. Supports up to 8 characters.",
"quickKeyPlaceholder": "e.g. sudo ",
"addQuickKey": "Add",
"removeQuickKey": "Remove",
"resetDefaults": "Reset to defaults",
"done": "Done"
},
"serial": {
"title": "Serial",
"portLabel": "Port",
"portPlaceholder": "/dev/ttyUSB0 or COM3",
"baudRateLabel": "Baud Rate",
"dataBitsLabel": "Data",
"stopBitsLabel": "Stop",
"parityLabel": "Parity",
"parityNone": "None",
"parityEven": "Even",
"parityOdd": "Odd",
"connect": "Connect to Serial",
"disconnect": "Disconnect",
"refreshPorts": "Refresh ports",
"connected": "Connected to {{path}} at {{baud}} baud",
"disconnected": "Serial port disconnected",
"connectionError": "Failed to open serial port",
"wsError": "WebSocket error",
"errorNoServerUrl": "No server URL configured",
"notSupportedTitle": "Serial Not Supported",
"notSupported": "Serial connections require a browser with Web Serial API support (Chrome, Edge, or Firefox 151+), or the Termix desktop app.",
"hideHint": "You can hide the Serial tab under User Profile > Appearance > Sidebar > Navigation.",
"browserPickerHint": "Click Connect and your browser will open a port picker to choose the device."
},
"metricsHistory": {
"title": "Metrics History",
"historySuffix": "History",
"cpuMemoryDisk": "CPU / Memory / Disk",
"network": "Network",
"download": "RX",
"upload": "TX",
"noData": "No history data available for this time range.",
"custom": "Custom",
"to": "to",
"apply": "Apply",
"viewHistory": "View History"
},
"alerts": {
"tabFirings": "Alerts",
"tabRules": "Rules",
"tabChannels": "Channels",
"noFirings": "No unacknowledged alerts",
"noRules": "No alert rules configured",
"noChannels": "No notification channels configured",
"rulesDesc": "Alert rules trigger notifications",
"channelsDesc": "Where alert notifications are sent",
"acknowledge": "Acknowledge",
"ackAll": "Ack All",
"allAcknowledged": "All alerts acknowledged",
"ackFailed": "Failed to acknowledge alert",
"ackAllFailed": "Failed to acknowledge all alerts",
"showAcknowledged": "Show All",
"hideAcknowledged": "Hide Acked",
"addChannel": "Add Channel",
"editChannel": "Edit Channel",
"channelName": "Name",
"channelType": "Type",
"channelNameRequired": "Name is required",
"webhookUrl": "URL",
"webhookUrlRequired": "Webhook URL is required",
"webhookDesc": "POST JSON payload to this URL on each alert firing",
"ntfyServer": "Server URL",
"ntfyTopic": "Topic",
"ntfyTopicRequired": "Topic is required",
"ntfyToken": "Access Token (optional)",
"channelSaveFailed": "Failed to save channel",
"test": "Test",
"testSent": "Test notification sent",
"testFailed": "Test notification failed",
"addRule": "Add Alert Rule",
"editRule": "Edit Alert Rule",
"ruleName": "Rule Name",
"ruleNameRequired": "Name is required",
"triggerType": "Trigger",
"thresholdValue": "Threshold (%)",
"durationSeconds": "Duration (seconds, 0 = fire immediately)",
"cooldownMinutes": "Cooldown (minutes)",
"channels": "Notification Channels",
"noChannelsHint": "Add channels in the Channels tab first",
"ruleSaveFailed": "Failed to save rule"
}
}