mirror of
https://github.com/binaricat/Netcatty.git
synced 2026-10-09 15:58:34 +00:00
1d91c8fbf4e22d48fe2ff0130784fadfe2062853
15 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1b965ac6ad | fix port forwarding jump host verification | ||
|
|
73e615cb64 | fix: isolate restored peer session windows | ||
|
|
92ecd84edf |
Fix #939: per-host SSH keepalive override + cloud-friendly defaults (#947)
* fix(ssh): per-host keepalive override + cloud-friendly defaults (#939, #581)
Issues #939 (cloud / Aliyun sessions silently freezing after 15-20 min idle
because no SSH keepalive packets are sent) and #581 (older routers like
NOKIA / ALCATEL being killed by ssh2 after a few unanswered keepalives) are
in direct tension at the global-setting level: cloud users want keepalive
ON, embedded-device users want it OFF, and any single global default hurts
the other group.
Resolves the conflict by moving keepalive to a per-host setting (mirroring
the existing `legacyAlgorithms` per-host pattern), with cloud-friendly
global defaults:
Domain:
- Host gains `keepaliveOverride?: boolean` + `keepaliveInterval?: number`
+ `keepaliveCountMax?: number`. When override is true, the host's
values are used; otherwise the global TerminalSettings values apply.
Per-field fallback so a host can override interval only or countMax only.
- TerminalSettings gains `keepaliveCountMax: number` so the second knob
(number of unanswered keepalives before declaring dead) is no longer
hardcoded at 3 in the bridge.
- DEFAULT_TERMINAL_SETTINGS: keepaliveInterval bumped from 0 to 30, and
keepaliveCountMax = 10. Cloud LBs / NAT tables stay populated; brief
network glitches don't trip the dead-connection check; an actually
dead session is detected within ~5 minutes. Existing users with 0
saved keep their value (no migration) — they were the #581 router
cohort and their setup still works untouched.
Plumbing:
- domain/host.ts adds resolveHostKeepalive(host, globalSettings) with
five unit tests covering both directions of the override flag and
per-field fallback.
- components/terminal/runtime/createTerminalSessionStarters.ts uses the
resolver when building startSSHSession options.
- electron/bridges/sshBridge.cjs reads keepaliveCountMax from options
(defaulting to 10) at both connection sites (direct + jump host) and
still routes interval=0 through to a fully disabled keepalive
(preserving #581's escape hatch).
UI:
- Settings → Terminal → Connection grows a second input next to the
existing interval: "Max unanswered keepalives".
- Host details panel gains a Keepalive section with a "Override global
keepalive" toggle that, when on, exposes per-host interval +
countMax inputs and an inline hint when interval = 0 (explaining
the implications). Same visual pattern as the existing Legacy
Algorithms section.
Sync:
- keepaliveCountMax added to SYNCABLE_TERMINAL_KEYS so the new global
field rides existing sync infrastructure. Per-host fields ride the
hosts array passthrough automatically (older clients receiving them
ignore unknown fields, per the existing lenient sync contract).
i18n: en + zh-CN strings for the new settings row, the host section
header, and the override toggle / inputs / disabled hint.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ssh): resolve keepalive per jump host, not just the final target
Addresses codex review on PR #947:
https://github.com/binaricat/Netcatty/pull/947#discussion_r3217027xxx
The first cut only resolved keepalive for the final target host and
forwarded a single interval/countMax pair across the whole start-SSH
call. connectThroughChain in sshBridge.cjs then applied that one pair
to every hop, so a chain like:
router (bastion, needs keepalive=0) → cloud target (needs 30s)
would either kill the router (with cloud-friendly defaults) or fail
to keep the target alive (with router-friendly 0). The per-host
override was effectively useless for bastion hosts.
Fix:
- NetcattyJumpHost gains optional keepaliveInterval / keepaliveCountMax.
- createTerminalSessionStarters runs resolveHostKeepalive() per
jumpHost when building the chain, so each hop carries its own
resolved pair.
- sshBridge.cjs's chain connector reads jump.keepaliveInterval /
jump.keepaliveCountMax for each hop, falling back to the call's
target-level options for backward compatibility with older
serializers that don't yet populate the per-hop fields.
The final target's keepalive path is unchanged — it still reads
options.keepaliveInterval / options.keepaliveCountMax that the
session starter resolves from the target host.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ssh): per-host keepalive for SFTP + port forwarding too
Follow-up to the maintainer review on PR #947 — terminal SSH was the
only path that honored per-host keepalive overrides. SFTP and port
forwarding share the same NetcattyJumpHost type but their builders
weren't resolving keepalive per-hop, and their bridges hardcoded the
old 10s/3 defaults. Net result: a router-as-bastion in a chain still
got killed when reached via the SFTP file panel or a port-forwarding
tunnel, even though the user had toggled per-host override.
Plumbing:
- useSftpHostCredentials / buildSftpHostCredentials: accept optional
terminalSettings; call resolveHostKeepalive() for the target and
each jump entry; emit keepaliveInterval / keepaliveCountMax in the
returned NetcattySSHOptions.
- useSftpConnections + useSftpState + SftpStateOptions thread the
setting down. SftpSidePanel passes the global terminalSettings prop
it already has from TerminalLayer.
- portForwardingService.startPortForward: accepts terminalSettings
as an 8th argument, resolves per-host (target + each jump), and
populates the bridge payload.
- usePortForwardingState.startTunnel and usePortForwardingAutoStart
forward the new parameter; App.tsx supplies terminalSettings (via
a ref in the once-on-launch auto-start effect so changing global
keepalive later doesn't re-fire it).
Bridges:
- sftpBridge.cjs target connect: now also reads keepaliveCountMax
from options (was hardcoded 3). 10s/3 stays as the bridge-level
fallback to preserve the #669 protection when the renderer hasn't
supplied a value.
- sftpBridge.cjs jump hop: reads jump.keepaliveInterval /
jump.keepaliveCountMax, then falls back to the target-call options
(matches the symmetric SSH bridge change).
- portForwardingBridge.cjs: reads keepaliveInterval /
keepaliveCountMax from the IPC payload; same 10s/3 fallback.
Types:
- NetcattyJumpHost already grew keepalive fields earlier; this
commit also adds them to PortForwardOptions so the IPC contract
is explicit.
End-to-end: a chain `[router-as-bastion, cloud-host]` with the
router host's keepaliveOverride=true / interval=0 now correctly
disables keepalive on the router hop for terminal SSH AND SFTP AND
port forwarding, while the cloud target still gets the resolved
30s/10 default for each path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ssh): honor explicit keepalive=0 in SFTP + port forwarding bridges
Addresses codex review on PR #947:
- https://github.com/binaricat/Netcatty/pull/947#discussion_r3217448xxx
- https://github.com/binaricat/Netcatty/pull/947#discussion_r3217449xxx
The previous follow-up commit (
|
||
|
|
72e305fb7a | Add reusable proxy profiles | ||
|
|
155463f77c | add reusable proxy profiles | ||
|
|
33f8221d5c |
refactor: remove immersive mode toggle remnants — always enabled
Immersive mode was already hardcoded to true with a no-op setter. Clean up all dead code: - Remove isImmersive param from useImmersiveMode hook - Remove immersiveMode/setImmersiveMode from useSettingsState - Remove toggle from SettingsPage and SettingsAppearanceTab - Remove sync read/write of immersiveMode setting - Remove i18n keys for the removed toggle - Simplify App.tsx conditionals Kept: useImmersiveMode hook (core logic), CSS classes (fade overlay), sync type field (backward compat), storage key. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
2f314c3588 |
feat: group configuration inheritance (#220) (#593)
* feat(i18n): add translations for group config panel * feat(models): add GroupConfig data model, resolution logic, and encryption Add the GroupConfig interface for group-level default settings that hosts inherit. Includes ancestor-chain resolution (A/B/C merges from A, A/B, A/B/C), host-level application logic, storage key, and secure field encryption/decryption for sensitive GroupConfig fields. Part of #220. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(state): add groupConfigs state management with encryption Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(ui): create GroupDetailsPanel with full config editing Side panel for editing group-level default configuration using AsidePanel. Includes General, SSH, Telnet, Advanced, Mosh, and Appearance sections with sub-panel navigation for Proxy, Chain, EnvVars, and Theme selection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(vault): wire GroupDetailsPanel, replace rename dialog with full config panel Replace all group rename dialog triggers with the new GroupDetailsPanel sidebar. The hover edit button, context menu, and tree view edit callbacks now open the full group configuration panel instead of a simple rename dialog. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(connect): apply group config defaults at connection time When connecting to a host, merge group-level default configuration so hosts inherit their group's settings for auth, protocol, appearance, and other inheritable fields. Connection logs still reference the original host's label/hostname. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(sync): include groupConfigs in sync and export payloads Add groupConfigs to SyncPayload, SyncableVaultData, buildSyncPayload, and applySyncPayload so group connection defaults are preserved during cloud sync and data import/export. Also wire groupConfigs into the vault object in SettingsPage so it flows through to the sync payload builder. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(vault): update group configs on move and delete * feat(host-panel): show inherited group defaults as placeholders When editing a host that belongs to a group with configuration, group default values now appear as placeholder text in username, startup command, and charset fields where the host doesn't have its own value. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: clean up unused imports in GroupDetailsPanel Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(group-panel): add/remove protocol sections, editable parent group - SSH and Telnet sections are now add/remove — click "Add Protocol" to enable, "..." menu to remove. Only enabled protocols override hosts. - Parent Group is now editable via Combobox dropdown for quick group moving. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: move SSH-specific fields into SSH protocol section Startup Command, Legacy Algorithms, Proxy, Host Chaining, Environment Variables, and Mosh are all SSH-specific and now only visible when SSH protocol is added. Only Charset remains as a shared field in the Advanced section. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: hide charset and appearance when no protocol is added Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: close Add Protocol dropdown after selection Use controlled open state to explicitly close the dropdown when a protocol is selected, preventing residual content from overlapping the newly rendered section. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: apply group defaults in TerminalLayer sessionHostsMap Terminal component was re-reading the original host from the hosts array by hostId, bypassing the group defaults applied in handleConnectToHost. Now sessionHostsMap applies resolveGroupDefaults + applyGroupDefaults when building the host object for each session, so Terminal sees the merged credentials/settings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: move Add Protocol to bottom, fix i18n for protocol/font labels - Add Protocol button moved below Appearance section - Added i18n keys: addProtocol, removeProtocol, fontFamily, fontSize - All hardcoded English strings replaced with t() calls Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: replace font family text input with TerminalFontSelect dropdown Use the same font selector component as settings, showing available terminal fonts with preview. Includes "Use Global" reset button. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(group-panel): match HostDetailsPanel key/certificate selection pattern Replace the simple Combobox key selector with the same credential selection flow used in HostDetailsPanel: a popover with Key/Certificate options, inline combobox per type, and proper badge display with certificate icon. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(group-panel): add Local Key File option to credential selection Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(group-panel): add identityFilePaths to GroupConfig and Local Key File option - Added identityFilePaths to GroupConfig interface and INHERITABLE_KEYS - GroupDetailsPanel now supports Key, Certificate, and Local Key File credential selection, matching HostDetailsPanel's full credential flow Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: prevent local key file input from overflowing panel width Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: constrain local key file input width with w-0 flex-1 Native input elements have a large default min-width. Using w-0 with flex-1 forces the input to shrink within the flex container. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add overflow-hidden to SSH Card to contain local key file input Matches HostDetailsPanel's Card which uses overflow-hidden on the credentials section to prevent long file paths from overflowing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add min-w-0 to key file path row for proper text truncation Flex children need min-w-0 for truncate to work correctly, otherwise the text pushes the container wider. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: force key file path text truncation with inline max-width calc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use fixed 320px max-width on key file path text to force truncation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add overflow-hidden to AsidePanelContent to prevent content overflow The root cause was the inner div of AsidePanelContent only had overflow-x-hidden which was being overridden by ScrollArea's viewport. Changed to full overflow-hidden with w-full box-border. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: override Radix ScrollArea viewport's display:table in AsidePanel Radix ScrollArea Viewport wraps content in a div with display:table and min-width:100%, causing content to expand beyond the panel width. Override this on AsidePanelContent's ScrollArea to use display:block and min-width:0 instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: critical issues — seed new hosts from group defaults, validate group names, fix empty import - HostDetailsPanel: When groupDefaults has values for port/username/charset, new hosts start with undefined/empty so group defaults take effect via applyGroupDefaults() instead of being blocked by hardcoded values - GroupDetailsPanel: Validate group name in handleSubmit to reject '/' and '\' characters, matching the old rename dialog behavior, with visual error - useVaultState: Check groupConfigs !== undefined instead of truthy so that importing an empty array [] properly clears all group configs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: safe prefix replacement, remove dead code, extract shared resolveEffectiveHost - Replace all .replace(oldPath, newPath) / .replace(sourcePath, newPath) with explicit prefix slicing (newPath + str.slice(oldPath.length)) in handleSaveGroupConfig and moveGroup for more robust path renaming - Remove dead c.path === oldPath branch in finalConfigs mapping since updatedConfigs already contains the config with newPath - Extract resolveEffectiveHost helper in App.tsx to deduplicate group defaults resolution in _handleTrayPanelConnect and handleConnectToHost Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: preserve undefined port on save when group has port default form.port || 22 was forcing port to 22 even when intentionally left undefined for group inheritance. Now uses nullish coalescing and only defaults to 22 when no group port default exists. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: SSH-adjacent field detection, chain host defaults, telnet inheritance, theme clear - hasSshFields() now checks proxyConfig, hostChain, startupCommand, legacyAlgorithms, environmentVariables, moshEnabled, moshServerPath, and identityFilePaths so the SSH section auto-opens when editing - Chain hosts in sessionChainHostsMap now get group defaults applied via resolveGroupDefaults + applyGroupDefaults - Added telnetEnabled to GroupConfig interface and INHERITABLE_KEYS; save handler sets telnetEnabled: true when Telnet section is on - Theme/font "Use global" clear now sets override to false instead of undefined, preventing parent group theme from leaking through Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: review round 4 — sync, SFTP, port forwarding, type safety, UX - Scan groupConfigs in encrypted credential guard (P1 security) - Add groupConfigs to auto-sync payload and three-way merge (P1 sync) - Apply group defaults in SFTP connections (P1 SFTP) - Apply group defaults in all port forwarding paths (P1 port forwarding) - Make Host.port optional to fix unsafe type cast (P1 type safety) - Fix port input empty → 0 instead of undefined (P2) - Add port placeholder showing inherited value (P2) - Mutual exclusion of group/host detail panels (P2) - Fix sub-panel width jump 420px → 380px (P2) - Validate duplicate group path on rename/reparent (P2) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: review round 5 — null guard, empty array inheritance, memo comparator, form reset - Guard groupConfigs import against null payload (P1 crash) - Validate duplicate path on moveGroup drag-drop (P2 data corruption) - Clear empty environmentVariables to undefined for group inheritance (P1) - Clear empty hostChain to undefined for group inheritance (P2) - Add groupConfigs to SftpView memo comparator (P1 stale defaults) - Add key={editingGroupPath} to GroupDetailsPanel for form reset (P1) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: review round 6 — copy credentials, protocol dialog use effective host - Apply group defaults in handleCopyCredentials (P2) - Apply group defaults in hasMultipleProtocols check (P2) - Pass effective host to ProtocolSelectDialog (P2) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: serialize protocol:'ssh' marker to persist SSH section in group config - Add protocol:'ssh' as marker field in handleSubmit SSH block - Detect protocol:'ssh' in hasSshFields() to preserve section on reopen - Clean up protocol field in removeSsh() Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
859d4b8156 | Fix auto-start auth readiness checks | ||
|
|
c6e07cf149 | Clean up port forwarding auto-start lint | ||
|
|
0ab18ce186 | Fix port forwarding startup and cleanup races | ||
|
|
f814719b32 | Fix jump-host port forwarding edge cases | ||
|
|
ee6b05892d | Support jump hosts for port forwarding | ||
|
|
746c336ee1 | fix: no passphrase given error on port forwarding launch | ||
|
|
be44f38911 |
fix: prevent syncWithBackend spam when opening Settings
- Move autoStartExecutedRef assignment before async work to ensure effect only runs once regardless of auto-start rules - Memoize keys array in App.tsx to prevent new references on each render Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
f6e67b6edb |
fix: move auto-start to app level and remove indicator icon
Co-authored-by: binaricat <16399091+binaricat@users.noreply.github.com> |