Nutshell is a lightweight native C SSH client for Windows. The goal is to produce a lightweight, high-performance SSH client with a tabbed interface, mirroring the functionality of the original Go application but with a minimal memory footprint and zero runtime dependencies (other than the OS and essential crypto libraries).
- SSH Connection: Establish secure shell connections to remote servers.
- Authentication: Password and SSH private key authentication with passphrase prompt and retry.
- Password Encryption: AES-256-GCM via OpenSSL; PBKDF2-SHA256 key derived from Windows MachineGuid. Auto-encrypts on save, decrypts on load. Plaintext passwords auto-migrated.
- Host Key Verification: TOFU (Trust On First Use) via libssh2 known_hosts API. First-connect fingerprint dialog, mismatch warning with block.
- PTY Management: Request and resize pseudo-terminals on window resize and zoom. Deduplicates identical resize requests.
- VT100/ANSI Parsing: Custom terminal state machine — CSI, OSC, SGR, cursor movement, line editing.
- 256-Colour and Truecolor: SGR 38;5;n / 48;5;n (256-colour) and 38;2;r;g;b / 48;2;r;g;b (truecolor). Full xterm 6×6×6 cube + grayscale ramp.
- Extended VT Sequences: OSC 0/2 (window title), DECTCEM (cursor show/hide), alternate screen buffer (?1049), application cursor keys (?1), insert mode (4).
- Buffer Management: Scrollback ring buffer, 10,000 lines default (configurable 100–50,000). Resize reflow respects actual content width.
- Native Windows UI: Win32 API directly (user32, gdi32, kernel32, comctl32). No GTK/Qt/Fyne.
- Tabbed Interface: Owner-drawn tab strip with multi-session support, status indicators, close buttons, Ctrl+T/Ctrl+W.
- Zoom: Ctrl+=/-, Ctrl+Scroll. Font snapped to discrete sizes (6–20 pt). Gutter-free fit with fallback. Persisted to settings.
- Paste Confirmation: Dialog for pastes >64 bytes or containing newlines. Truncated preview, line count. Inter-line delay from settings.
- Session File Logging: ANSI-stripped output to timestamped files. Configurable directory (default: exe directory) and strftime name format.
- Configuration: JSON profiles and settings via
nutshell.configin the executable's directory.
- Initial size: 1024 x 680 px, centred on screen.
- Title: "Nutshell".
- Layout: Header (toolbar + tab strip) → separator → content area (terminal or error).
- Default foreground: #0C0C0C (near-black). Default background: #F2F2F2 (light gray).
- Auto light/dark: UI theme chosen by BT.709 luminance (> 0.5 = light). Title bar set via
DwmSetWindowAttribute(DWMWA_USE_IMMERSIVE_DARK_MODE). - Palette (user-selectable): Light Gray, Black, Dark Green, Bright Green, Dark Blue, Cyan Blue, Dark Red, Dark Yellow, Dark Magenta, Medium Gray, White, Bright Yellow, Bright Cyan, Bright Red.
- Default: Consolas 10 pt. Range: 6–20 pt (discrete sizes: 6, 8, 10, 12, 14, 16, 18, 20).
- Options: Consolas, Courier New, Lucida Console, Cascadia Code, Cascadia Mono.
- Zoom: Ctrl+=/-, Ctrl+ScrollUp/Down adjusts font size ±1 pt, persisted to settings. Gutter-free fit with fallback across full 6–72 range.
- Layout:
[+] [<] [tab1] [tab2] … [>] [⚙]— horizontally scrollable. - Active tab: primary-colour background, contrasting text. Inactive: transparent, foreground text.
- Tab border: 1 px stroke, foreground colour, 4 px corner radius.
- Per-tab indicators: Status dot (12×H, yellow=connecting, green=connected, red=error, gray=idle) and logging button (12×H, "L", green=active, gray=inactive). Full inner tab height, equidistant 3 px gaps.
- Tooltip on hover: Table format — Name, Host, User, Status (duration), Logging status. Footnote:
[L] = toggle session logging.
- Default PTY: 80 × 24, xterm-256color.
- Scrollback: 10,000 lines default (configurable 100–50,000).
- Monospace rendering with the selected font.
- Resize: PTY resized on window resize.
- Size: ~705 × 300 px (470 × 185 dialog units). Two-column layout.
- Left: Saved Sessions list (single-click selects, double-click connects), [New], [Edit], [Delete].
- Right: Connection form — Name, Host, Port (default 22), Username, Auth type (Password/Key), Password field, Key file path with "..." browse button (
GetOpenFileNameA), [Save], [Connect], [Cancel].
- Size: 480 × 530 px, fixed.
- Fields: Font, Font size, Foreground colour (swatch + ChooseColor), Background colour (swatch + ChooseColor), Paste delay (350 ms), Session logging toggle, Log directory, Log name format (with strftime tooltip on hover listing specifiers), Scrollback lines (10,000).
- Validation:
settings_validate()snaps font_size to nearest allowed discrete size (6–20), clamps scrollback [100–50,000], paste_delay [0–5,000]. - Buttons: [Save] (primary), [Cancel]. Footer: copyright.
- Connection/PTY errors displayed as multi-line message with [Reconnect] and [Close Tab] buttons.
In order of precedence:
- Security — no buffer overflows, no format-string bugs, validated inputs at every boundary, secrets zeroed after use.
- Safety — crash-free operation; every allocation checked, every error path handled.
- Performance — fast startup, low CPU at idle, dirty-rect rendering, minimal allocations in hot paths.
- File Size — static link only what is needed; strip symbols in release builds; no bundled runtimes.
- Visual Fidelity — match the Go/Fyne version's layout and behaviour (see §2.4).
- Language: C11 (ISO/IEC 9899:2011).
- Compiler: GCC (MinGW-w64) or MSVC.
- Strict Limit: Import as few external modules as possible.
- Crypto: Link against
OpenSSL(AES-256-GCM password encryption, PBKDF2) andlibssh2(SSH protocol, known_hosts). Avoid high-level frameworks. - UI: Native Win32 API (user32.dll, gdi32.dll, kernel32.dll). No GTK/Qt/Fyne.
- TDD: Write tests before implementation.
- Custom Modules: Prefer writing custom implementations for:
- Data Structures (Vectors, HashMaps).
- String Handling.
- Config Parsing.
- UI Widget abstraction.
- Linter:
cppcheck(--enable=warning,style,performance,portability --std=c11). - Compiler Warnings:
-Wall -Wextra -Werror -Wpedantic -Wshadow -Wformat=2 -Wconversion. - Runtime Sanitizers: AddressSanitizer + UndefinedBehaviorSanitizer (
-fsanitize=address,undefined) enabled for all debug/test builds. - Windows Validation: Dr. Memory for final release testing on Windows.
Decisions locked in during implementation. Do not revisit without a clear reason.
xmalloc/xcalloc/xreallocabort the process on OOM rather than returning NULL. All callers treat the return value as unconditionally valid.- OOM error is reported via
fputs(notfprintf) to avoid any format-string processing when the heap may be corrupt. - Callers set freed pointers to NULL manually; ASan catches any dangling access.
log_write()accepts a pre-formattedconst char *string — no variadic format string. Callers usesnprintfbefore logging. Rationale: eliminates format-string vulnerabilities at every log call site.- File open failure is non-fatal — falls back to stderr-only. The logger must never crash the application.
log_close()is idempotent (safe to call multiple times).localtime()is used in Phase 1 (not thread-safe). To be replaced withlocaltime_s(Windows) /localtime_r(POSIX) in Phase 6.
- Stores
void *— ownership of pointed-to data is not managed by the vector. vec_free()zeroes the struct, leaving it in the init-empty state so it can be reused without a secondvec_init()call.- Initial backing capacity: 8 elements; doubles on overflow.
- Out-of-bounds
vec_get()returns NULL. Out-of-boundsvec_set()/vec_remove()are no-ops.
str_cat()usesstrncatwith an explicitremainingcalculation; always null-terminates even on truncation.str_trim()operates in-place withmemmovefor the leading-whitespace shift — no allocation.str_dup()returns NULL for a NULL input (not an abort); callers that require a non-NULL result must check.
- ASSERT macros set a local
_tf_localflag (notabort()/return), so all failures within a test function are reported before the function returns. - Each test function returns
int(0 = pass, 1 = fail). The runner records pass/fail counts. - Global counters (
_tf_total,_tf_passed,_tf_failed) are defined once inrunner.cand declaredexternintest_framework.h. - Test runner (
tests/runner.c) uses explicit forward declarations — no test-discovery magic.
make→ cross-compiles core tobuild/win/libnutshell_core.a(Windows static lib). Will becomenutshell.exein Phase 5.make test→ compiles and runs the test suite natively on Linux with ASan + UBSan.make debug→ builds the test runner without running it (for use withgdb).make lint→ cppcheck with--error-exitcode=1; a warning is a build failure.
- Foundation: Build system, test runner, and core data structures.
- Terminal Core: ANSI parser and render buffer.
- SSH Layer: Network socket handling and SSH protocol integration.
- UI Shell: Main window, tabs, and rendering loop.
- Integration: Wiring the terminal output to the UI.
- Unit Tests: Custom test harness (865 tests) for logic (parsing, config, data structures, tooltips, themes, paste, zoom, ANSI strip, settings validation, AI prompt, display buffer, edit scroll, tabs, fonts, and more).
- Integration Tests: Mock SSH server for connection testing.
- Reference: Use
../golang/tests as logic references. - Server Compatibility Matrix: OpenSSH 8.x (Ubuntu 22.04), OpenSSH 9.x (Debian 12), Dropbear, Windows OpenSSH Server — both password auth and Ed25519/RSA-4096 key auth.
- Security: Passwords and API keys encrypted at rest with AES-256-GCM. TOFU host key verification.
- DPAPI for password encryption, thread safety improvements.
- Dr. Memory audit on Windows release build.
- Port to Linux (X11/Wayland) using platform-specific UI layers.
- SFTP support.