Internal
Public Access
Implements RFC 6143's Tight encoding: a compression-control byte (low 4 bits reset one of 4 persistent zlib streams; high nibble selects Fill/JPEG/Basic mode) followed by Fill's 3-byte solid color, JPEG's compact-length-prefixed baseline JPEG covering the whole rectangle (decoded via libjpeg-turbo directly, not QImage's plugin, to avoid a packaging-dependent runtime failure mode), or Basic mode's compact-length-prefixed zlib payload plus a filter (Copy, Palette, or Gradient) applied after decompression. Unlike Hextile/ZRLE, Tight has no internal tiling -- one rectangle is one filtered/compressed unit. The three filters live in vnc_pixel_codecs.h/.cpp alongside the Hextile/ZRLE decoders. Adds find_package(JPEG REQUIRED) + JPEG::JPEG as a new build dependency (confirmed available via libjpeg-turbo on this dev machine). 5 new tests cover Fill, Basic+Copy, Basic+Palette, JPEG (round-tripped through a real libjpeg-turbo-encoded fixture, compared with tolerance since JPEG is lossy), and the stream-reset flag correctly tearing down and reinitializing a targeted stream rather than erroring on stale state. Known, documented gap: this decoder always treats Basic-mode payloads as zlib-compressed; the real protocol allows very small payloads to skip compression, which couldn't be verified with confidence against the RFC text alone and is narrow enough in practice (tiny solid areas are virtually always sent as Fill instead) to leave unhandled for now -- it fails that one rectangle's decode cleanly rather than misinterpreting it silently. The Gradient filter is implemented from the spec description but is the least exercised of the three in this pass. Live-verified against the TightVNC test server that nothing regressed; that server still consistently chose Raw for actual framebuffer content regardless of announced encodings, so Tight's live decode path isn't independently confirmed against a real server here either -- the unit tests are the primary evidence. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
306 lines
17 KiB
Markdown
306 lines
17 KiB
Markdown
# OrbitHub Progress
|
|
|
|
## Milestone 0 - Restart in C++/Qt Widgets
|
|
|
|
Status: Completed
|
|
|
|
Delivered:
|
|
- Fresh C++17/Qt6 Widgets scaffold with CMake
|
|
- `ProfilesWindow` (`QMainWindow`) with search, profile list, and New/Edit/Delete controls
|
|
- Double-click in Profiles opens a `SessionWindow`
|
|
- `SessionWindow` (`QMainWindow`) with `QTabWidget`
|
|
- Placeholder tab content showing `OrbitHub Native Surface`
|
|
- `main.cpp` wiring for application startup
|
|
- Cross-platform build command guide in `docs/BUILDING.md`
|
|
|
|
Git:
|
|
- Tag: `v0-m0-done`
|
|
|
|
## Milestone 1 - Storage and CRUD
|
|
|
|
Status: Completed
|
|
|
|
Delivered:
|
|
- SQLite integration via Qt SQL (`QSQLITE`)
|
|
- Persistent profile database bootstrap (`profiles` table)
|
|
- Profiles CRUD (New / Edit / Delete) in `ProfilesWindow`
|
|
- Search-backed profile listing from storage
|
|
- Double-click connect opens `SessionWindow` tab with selected profile name
|
|
|
|
Git:
|
|
- Tag: `v0-m1-done`
|
|
|
|
## Milestone 2 - Profile Details and Connect Lifecycle
|
|
|
|
Status: Completed
|
|
|
|
Delivered:
|
|
- SQLite schema migration for profile details (`host`, `port`, `username`, `protocol`, `auth_mode`)
|
|
- New `ProfileDialog` form for New/Edit profile workflows
|
|
- Profiles list now shows endpoint metadata and supports search by name or host
|
|
- Connect now loads complete profile details into `SessionWindow`
|
|
- Session tab lifecycle status updates (`Connecting`, `Connected`, `Failed`) via non-blocking timer flow
|
|
|
|
Git:
|
|
- Tag: `v0-m2-done`
|
|
|
|
## Milestone 3 - Real SSH Backend and Session Controls
|
|
|
|
Status: Completed
|
|
|
|
Delivered:
|
|
- Backend architecture introduced (`SessionBackend` + protocol-specific implementations)
|
|
- Worker-thread backend execution for connection lifecycle operations
|
|
- Real SSH process backend (`ssh`) with connect/disconnect/reconnect
|
|
- Unsupported protocol backend with explicit not-implemented messaging (RDP/VNC)
|
|
- Session tab controls: `Connect`, `Disconnect`, `Reconnect`, `Copy Error`
|
|
- Connect-time credential flow (password prompt / private-key path selection)
|
|
- Session event log pane with timestamps and user-friendly error mapping
|
|
- SQLite profile schema migration for `private_key_path` and `known_hosts_policy`
|
|
|
|
Git:
|
|
- Tag: `v0-m3-done`
|
|
|
|
## Milestone 4 - Interactive SSH Session UX
|
|
|
|
Status: Completed
|
|
|
|
Delivered:
|
|
- Embedded interactive SSH terminal using `KodoTerm` + vendored `libvterm`
|
|
- Native in-terminal typing for SSH sessions (no separate input box)
|
|
- ANSI/color rendering with selectable terminal themes (`Dark`, `Light`, `Solarized Dark`)
|
|
- Cross-platform SSH auth path improvements (`ssh-askpass` handling and host-key policy wiring)
|
|
- Session UX simplification: auto-connect on tab open, disconnect on tab close
|
|
- Tab-state indicators via tab color and state suffix (`Connecting`, `Connected`, `Disconnected`, `Failed`)
|
|
- Right-click tab menu for `Disconnect`, `Reconnect`, `Theme`, and `Clear`
|
|
- Collapsible events panel retained as primary diagnostics surface; inline detail/status banners removed
|
|
- Terminal behavior polish: better fixed-width font selection, cursor visibility, backspace handling, and terminal-size negotiation stability
|
|
|
|
Git:
|
|
- Tag: `v0-m4-done`
|
|
|
|
## Milestone 5 - RDP Fully Working
|
|
|
|
Status: Completed
|
|
|
|
Delivered:
|
|
- Added `RdpSessionBackend` and wired protocol selection so `RDP` no longer routes to unsupported backend
|
|
- Pivoted RDP design to embedded-only integration (no external RDP process launches)
|
|
- Implemented embedded FreeRDP client thread with connect/disconnect lifecycle and event-loop handling
|
|
- Added in-window `RdpDisplayWidget` rendering surface with frame updates from FreeRDP GDI
|
|
- Wired direct keyboard/mouse input from the embedded RDP surface to the backend
|
|
- Added RDP connect-time password prompt flow and settings wiring (host/port/user/password, desktop size)
|
|
- Added explicit profile `Domain` support for RDP auth (with `DOMAIN\username` fallback parsing)
|
|
- Updated session tab/context-menu behavior so terminal-only actions are hidden on RDP tabs
|
|
- Implemented dynamic in-session RDP resolution renegotiation from viewport resize events
|
|
- Enabled minimal FreeRDP client-channel build (`drdynvc` + `disp`) and channel loading for runtime resize support
|
|
- Added RDP profile-level security mode and performance profile options, wired into FreeRDP connection settings
|
|
- Hardened RDP lifecycle handling for disconnect/reconnect/abort flows to avoid false failure states on user-initiated stops
|
|
- Expanded RDP error/disconnect diagnostics with richer FreeRDP code mapping and raw disconnect detail events
|
|
- Pulled FreeRDP source for integration planning and API review
|
|
|
|
Git:
|
|
- Tag: `v0-m5-done`
|
|
|
|
## Milestone 6 - VNC Working (initial scope)
|
|
|
|
Status: Completed (initial scope; see gaps below)
|
|
|
|
Delivered:
|
|
- `VncSessionBackend`: an original RFB (RFC 6143) client implementation
|
|
against `QTcpSocket` -- no permissively licensed VNC client library
|
|
exists to vendor the way FreeRDP was for RDP (LibVNCClient is GPLv2,
|
|
gtk-vnc is LGPL but GTK-tied), so this is from-scratch protocol code,
|
|
threaded like `SshSessionBackend` (a `QObject` on its own `QThread`
|
|
driven by Qt's own async socket signals) rather than `RdpSessionBackend`'s
|
|
manual worker-thread/blocking-loop pattern
|
|
- Full connect/disconnect/reconnect lifecycle, RFB protocol-version
|
|
negotiation (3.3/3.7/3.8 handshake differences handled explicitly),
|
|
VNC Authentication (DES challenge-response, using OpenSSL's classic DES
|
|
API) and no-auth security types, Raw + CopyRect framebuffer decoding,
|
|
keyboard (Qt key -> X11 keysym mapping) and mouse/wheel input forwarding
|
|
- `VncDisplayWidget` mirroring `RdpDisplayWidget`'s scale-to-fit rendering
|
|
and input-forwarding shape
|
|
- 41 unit tests (`tests/test_vnc_session_backend.cpp`): pure-function
|
|
coverage (DES key prep verified against an independently documented test
|
|
vector, keysym mapping, socket-error mapping) plus state-machine
|
|
coverage against a scripted in-process fake RFB server (all three
|
|
protocol-version handshake shapes, auth success/failure, unsupported
|
|
security types, pixel-accurate Raw/Hextile/ZRLE/Tight decoding including
|
|
a ZRLE zlib-stream-persistence test across two separate
|
|
`FramebufferUpdate` messages, a Tight stream-reset-flag test, cursor/
|
|
clipboard round trips) -- caught and fixed a real re-entrancy bug
|
|
(`abort()` synchronously re-firing `disconnected()` mid-
|
|
`failConnection()`, silently overwriting a specific error with a
|
|
generic one)
|
|
- Verified live against a real, independently implemented VNC server
|
|
(TightVNC on Windows): connect with VNC Authentication, correct
|
|
framebuffer dimensions and pixel data, clean disconnect, reconnect,
|
|
live cursor-shape and clipboard-send checks. That particular server
|
|
consistently chose Raw for actual framebuffer content regardless of
|
|
which compression encodings were announced, so Hextile/ZRLE/Tight's
|
|
real-world decode path isn't independently confirmed live -- the unit
|
|
tests are the primary correctness evidence for those
|
|
- Per-tab VNC-only display mode toggle (tab-bar right-click ->
|
|
`Display Mode`): `Scale to Fit` (default, matches RDP's behavior) or
|
|
`Actual Size (Scrollbars)` -- renders the remote framebuffer at its
|
|
native pixel size inside a `QScrollArea` so text isn't shrunk, at the
|
|
cost of needing to scroll to see the whole screen. Reuses
|
|
`VncDisplayWidget::renderRect()`'s existing scale-to-fit math unchanged:
|
|
it degenerates to an exact 1:1 mapping once the widget's own bounds are
|
|
fixed to the remote's size, so no separate rendering path was needed.
|
|
Persisted across sessions like the terminal theme preference.
|
|
- Robustness fix: an unrecognized `FramebufferUpdate` rectangle encoding
|
|
used to abort the connection generically; `kAnnouncedEncodings` is now
|
|
the single source of truth for what `SetEncodings` announces and what
|
|
the rectangle-dispatch `switch` can decode, with a regression test
|
|
pinning that every announced encoding has a working case
|
|
- Clipboard sync (`ServerCutText`/`ClientCutText`, Latin-1 only -- no
|
|
Unicode extension) in both directions
|
|
- Remote cursor shape sync via RFB's Cursor pseudo-encoding, mirroring
|
|
`RdpDisplayWidget`'s cursor handling in `VncDisplayWidget`
|
|
- Hextile, ZRLE, and Tight compression encodings, in addition to Raw +
|
|
CopyRect -- meaningfully reduces bandwidth over slower links versus Raw
|
|
alone; Tight in particular is the encoding most real VNC servers prefer
|
|
when the client offers it. Pure tile/pixel decode logic (including
|
|
Tight's three filters -- Copy, Palette, Gradient) lives in
|
|
`src/vnc_pixel_codecs.h/.cpp`, kept separate from the wire-sequencing
|
|
state machine so it's unit-testable without a socket. ZRLE and Tight's
|
|
Basic mode share the same "persistent zlib stream(s), decompress
|
|
in-memory, decode synchronously" approach (Tight has 4 independent
|
|
streams selected per-rectangle, individually reset via the
|
|
compression-control byte's low 4 bits). Tight's JPEG sub-mode decodes
|
|
via libjpeg-turbo directly (`find_package(JPEG REQUIRED)` ->
|
|
`JPEG::JPEG`), not `QImage`'s own JPEG plugin, to avoid a
|
|
packaging-dependent runtime failure mode. ZRLE/Tight link `ZLIB::ZLIB`
|
|
(found via a fresh top-level `find_package(ZLIB REQUIRED)`, independent
|
|
of whether vendored FreeRDP's own internal zlib usage stays enabled)
|
|
- 41 unit tests total, 15 of them for Hextile/ZRLE/Tight specifically,
|
|
including a ZRLE zlib-stream-persistence test across two separate
|
|
`FramebufferUpdate` messages and a Tight stream-reset-flag test proving
|
|
the low 4 control-byte bits actually tear down and reinitialize the
|
|
targeted stream rather than erroring out on stale state
|
|
|
|
Known gaps (explicit scope decisions, not oversights -- see issue #3 for
|
|
follow-up tracking):
|
|
- Apple's Screen Sharing authentication (Diffie-Hellman + AES, security
|
|
type 30) isn't implemented, so this can't yet reach macOS's built-in VNC
|
|
server -- only standard VNC Authentication (type 2) and no-auth (type 1)
|
|
- Tight's Basic compression mode always assumes zlib-compressed payloads;
|
|
the real protocol permits the server to skip compression for very small
|
|
(filtered byte count under ~12) payloads, which this decoder doesn't
|
|
special-case (the exact trigger/wire-signaling for that couldn't be
|
|
verified with confidence against the RFC text alone). In practice this
|
|
only affects rare, tiny rectangles -- solid or near-solid tiny areas are
|
|
virtually always sent as Fill instead -- and fails that one rectangle's
|
|
decode cleanly (disconnects with a clear error) rather than silently
|
|
misinterpreting it
|
|
- Tight's Gradient filter is implemented from RFC 6143's description but
|
|
is the least exercised/confirmed of the three filters against a real
|
|
server in this pass (most real-world Tight traffic uses Copy or
|
|
Palette)
|
|
- No dynamic resize (connects at the server's native resolution; the
|
|
`Scale to Fit`/`Actual Size` toggle changes how that fixed resolution is
|
|
displayed locally, not what resolution is requested from the guest --
|
|
VNC has no equivalent of RDP's MS-RDPEDISP for that)
|
|
|
|
## Milestone 7 - Cross-Platform Protocol Hardening
|
|
|
|
Status: Completed
|
|
|
|
Delivered:
|
|
- Validated SSH and RDP workflows on Linux, macOS, and Windows 11 (VNC is out
|
|
of scope while Milestone 6 remains deferred)
|
|
- Windows: validated the full `docs/BUILDING.md` toolchain end-to-end
|
|
(Git/CMake/Ninja/VS Build Tools with the C++ workload/vcpkg for
|
|
Qt6-OpenSSL-zlib); documented a VS Build Tools installer gotcha where the
|
|
actual compiler is a "recommended", not "required", component of the
|
|
VCTools workload
|
|
- Fixed RDP keyboard input misreading punctuation keys on Linux (X11 keycode
|
|
numbering was being treated as a PC/AT scancode; now uses FreeRDP's
|
|
authoritative X11-keycode-to-scancode table)
|
|
- Added RDP clipboard sync (bidirectional, plain text) and RDP cursor/pointer
|
|
shape sync (resize handles, text I-beam, etc. instead of a static arrow)
|
|
- Fixed Tab/Shift+Tab being intercepted by local UI focus navigation instead
|
|
of reaching SSH/RDP sessions
|
|
- Fixed RDP key auto-repeat being dropped, so holding a key only ever sent a
|
|
single keystroke to the remote machine
|
|
- Windows-specific RDP fixes: a crash on every connect attempt (FreeRDP's
|
|
signal-handling critical section was never initialized) and a host
|
|
resolution failure on every connect, including literal IP addresses
|
|
(Winsock was never initialized via `WSAStartup`)
|
|
- Windows: automatic build-time deployment of Qt's platform/SQL-driver
|
|
plugins and their runtime DLL dependencies, and marked the executable as a
|
|
GUI (`WIN32`) app to remove a stray console window behind the UI
|
|
- Windows and macOS: proper native app icon embedding (Windows `.rc`/`.ico`
|
|
resource; macOS `.app` bundle with `.icns`), replacing the generic default
|
|
icon previously shown for the built executable/bundle
|
|
- macOS: fixed Edit/New Profile dialog form fields collapsing to `sizeHint`
|
|
width due to the platform-default `QFormLayout` field growth policy
|
|
|
|
Git:
|
|
- Tag: Pending user approval (`v0-m7-done`)
|
|
|
|
## Milestone 8 - Profile and Session UX Completion
|
|
|
|
Status: Completed
|
|
|
|
Delivered:
|
|
- Added profile `tags` field to storage + schema migration and profile editor UX
|
|
- Added profile `folder_path` field + nested folder/subfolder profile view mode
|
|
- Added profile tree context actions (`New Folder`, `New Connection`) and drag-to-folder profile moves with persistence
|
|
- Added `Help -> About OrbitHub` dialog with third-party library inventory and MIT/Apache-2.0 license links
|
|
- Extended profile search to include tags/folder path and added profile sort controls (`Name`, `Protocol`, `Host`)
|
|
- Persisted profile list UX preferences (`search text`, `view mode`, protocol/tag filters, `sort order`) across app restarts
|
|
- Added protocol-aware profile validation/normalization for SSH/RDP/VNC (repository + dialog)
|
|
- Improved profile form protocol UX hints and SSH private-key path validation
|
|
- Added session events filtering and tab-context actions (`Show/Hide Events`, `Copy Events`, `Clear Events`)
|
|
- Added session diagnostics QoL: severity quick-filter (`All/Warnings/Errors`) and `Export Events` action
|
|
- Persisted session UI defaults (`terminal theme`, `events panel visibility`) for new tabs/windows
|
|
- Added profile quick filters (`Protocol`, `Tag`) with persistence to speed profile browsing
|
|
|
|
Validation:
|
|
- Local build verification passed (`cmake --build build`)
|
|
- No automated tests are currently configured in CTest
|
|
|
|
Git:
|
|
- Tag: Pending user approval (`v0-m8-done`)
|
|
|
|
## Milestone 9 - Packaging and Distribution
|
|
|
|
Status: Completed
|
|
|
|
Delivered:
|
|
- Linux `.deb` (`packaging/linux/build-deb.sh`) and Flatpak (`packaging/flatpak/build-flatpak.sh`) packages, both verified installed and launched with working SSH/RDP
|
|
- Renamed the app's reverse-DNS identity from `io.orbithub.OrbitHub` to `org.darksingularity.OrbitHub` (desktop file, AppStream metainfo, Flatpak manifest, macOS bundle identifier) to reflect a domain actually owned by the project
|
|
- Fixed Linux taskbar/panel pin matching (`StartupWMClass`) so a pinned launcher merges with its running window instead of creating a duplicate entry
|
|
- Replaced the stale, mismatched hand-authored launcher SVG with PNG icons rendered directly from the app's own `createOrbitHubAppIcon()` at each hicolor theme size
|
|
- Windows installer via Inno Setup (`packaging/windows/orbithub.iss`, `packaging/windows/build-installer.ps1`), verified installed silently and launched cleanly with no crash events
|
|
- macOS `.dmg` via `cmake --install` + `macdeployqt` + `hdiutil` (`packaging/macos/build-dmg.sh`), verified installed and launched after fixing:
|
|
- a launch crash caused by `macdeployqt` invalidating the code signature (fixed with ad hoc re-signing)
|
|
- a missing-library crash caused by the Linux-only `$ORIGIN` rpath token and vendored dylibs installing outside the `.app` bundle (fixed with `APPLE`-specific `@executable_path`/`Contents/Frameworks` layout)
|
|
- the dmg showing the generic disk icon instead of the app icon
|
|
- README and `docs/BUILDING.md` Packaging sections documented for all three platforms
|
|
|
|
Validation:
|
|
- All three installers built, installed, and launched successfully with working SSH/RDP sessions
|
|
- Code signing is ad hoc only (no purchased Apple Developer ID or Windows code-signing certificate), so macOS Gatekeeper and Windows SmartScreen still show first-run warnings by design
|
|
|
|
Git:
|
|
- Tag: `v0-m9-done`
|
|
- Release: [v2026.9.8](https://git.darksingularity.org/DarkSingularity/orbithub/releases/tag/v2026.9.8) (`v2026.9.8` tag, installers for Windows/Linux/macOS)
|
|
- Release: [v2026.9.8.2](https://git.darksingularity.org/DarkSingularity/orbithub/releases/tag/v2026.9.8.2) — same-day patch fixing RDP TLS certificate verification (was fully disabled) and preparing Flatpak packaging for Flathub submission
|
|
- Release: [v2026.9.8.3](https://git.darksingularity.org/DarkSingularity/orbithub/releases/tag/v2026.9.8.3) — same-day patch adding an in-app User Guide and standalone User Guide PDF
|
|
- Release: [v2026.9.14](https://git.darksingularity.org/DarkSingularity/orbithub/releases/tag/v2026.9.14) — fixes distorted RDP text on HiDPI monitors and reduces RDP resize-related display glitches
|
|
- Release: [v2026.9.14.2](https://git.darksingularity.org/DarkSingularity/orbithub/releases/tag/v2026.9.14.2) — same-day patch fixing RDP display corruption (missing/misplaced taskbar) after resizing the session window (the client never resized its own display buffer for channel-driven RDP resizes)
|
|
- Release: [v2026.9.15](https://git.darksingularity.org/DarkSingularity/orbithub/releases/tag/v2026.9.15) — adds Import/Export for profile lists (File menu)
|
|
|
|
## Milestone 10 - v1.0 Stabilization
|
|
|
|
Status: Planned
|
|
|
|
Planned Scope:
|
|
- Run final regression and acceptance testing across all protocols
|
|
- Resolve release-blocking defects
|
|
- Finalize docs and publish v1.0 release notes/checklist
|