Hiatus Remote

Remote support where the relay sees nothing. Screen, files, shell and audio, end-to-end encrypted.

Version 1.0.0-rc1 · Release candidate

Windows · Linux (X11) · macOS · Android debugging

Hiatus Remote main window: own ID, one-time password and connecting to a partner

Architecture

Hiatus Remote consists of six clearly separated processes. The supporter works in the client, the target runs service and agent, and the relay server sees nothing but encrypted packets in either direction. Video decoding runs in its own sandboxed process.

hiatus-relay Docker · 443 TCP/UDP, 444 UDP sees only encrypted packets SUPPORTER COMPUTER hiatus-client Qt Quick · session, files, shell hiatus-decoder Sandbox: AppContainer / seccomp Android device adb · logcat, remote control TARGET COMPUTER hiatus-service System service · unattended hiatus-agent User session · screen, input hiatus-pty-host Remote shell Relay fallback · TLS 1.3 Relay fallback · TLS 1.3 Direct · UDP hole punching · QUIC · E2EE IPC adb IPC, hardened Terminal as the user End-to-end encrypted sees only metadata / encrypted packets Sandboxed process
Components and connections · UML deployment diagramOpen full size

Components

Process Runs on Task
hiatus-client Supporter and target User interface (Qt Quick): sessions, file browser, shell, Android debugging; the logic lives in Qt-free libraries
hiatus-decoder Supporter Decodes video, audio and clipboard images from the other side in its own process; AppContainer (Windows) or seccomp (Linux)
hiatus-service Target System service for unattended access; accepts connections even without a signed-in user and starts the agent in the user session
hiatus-agent Target Screen capture, input, files and audio in the respective user or login session
hiatus-pty-host Target (Windows) Holds terminal and shell for the remote shell as the signed-in user, not as SYSTEM
hiatus-relay Server (Docker, Windows service) Assigns IDs, brokers connections, rate-limits attempts and forwards packets when no direct path can be established

Connection setup

From the ID request to the first frame: the password is never transmitted, the relay never gets to see it.

ID request. Both sides sign in to the relay over TLS 1.3, whose key is pinned via the SPKI pin in the access code, and receive their ID. The supporter requests the partner ID.

SPAKE2+ handshake. Over the signalling channel a shared key K_shared is derived from the one-time password without sending the password. Both device keys, the IDs and the protocol version go into the transcript.

Hole punching. Both sides exchange address candidates and attempt a direct UDP connection, with a targeted port search behind symmetric NAT. If that fails, the relay path remains; via TCP if UDP is blocked.

QUIC and channel binding. QUIC handshake (ngtcp2, TLS 1.3); the TLS exporter is bound to K_shared, direction keys are derived via HKDF. If a session starts via the relay, it switches to the direct path without interruption as soon as one is found.

Connection setup: from the ID to the first frameSupporterRelay serverTargetSign-in, TLS 1.3 with SPKI pinSign-in, own IDConnection request to ID 482 917 305Request from the supporterSPAKE2+ (RFC 9383) over the signalling channel:both prove the password without sending it.Result: shared secret K_sharedSPAKE2+ message (pA)forwardedSPAKE2+ message (pB, confirmV)forwardedconfirmPConsent dialog, fingerprint, permissionsAddress candidatesAddress candidatesUDP hole punching, QUIC handshake (TLS 1.3)Channel binding: HMAC(K_bind, TLS exporter)Channel binding confirmedEnd-to-end from here: AES-256-GCM per stream.If the direct path fails, the same packets go via the relay.Screen, input, files, shell
Connection setup · UML sequence diagramOpen full size

Unattended access

With the Hiatus service a computer is reachable without anyone sitting in front of it, even at the login screen. The service checks the permanent password against a stored Argon2id verifier, matches the supporter's device key against the allowlist and starts the agent in the active session. Only an administrator can change the settings; on Linux through a polkit action.

Unattended access: service and agentSupporterhiatus-service (SYSTEM)hiatus-agent (session)Connection request with the permanent passwordSPAKE2+ against the stored Argon2id verifier;the password itself is stored nowhere on disk.Check the supporter's device key (allowlist, pinning)Optional: ask the signed-in user (30 s)Start the agent in the active session (start token)Ready, shared memory read-onlyEnd-to-end encrypted sessionScreen, input, files with the user's rightsFrame dataFrames, status bar shows route and RTTEnd sessionLock the computer (default)History: when, who, how long, which permissions
Unattended access · UML sequence diagramOpen full size

Layer model

The logic lives in Qt-free libraries, the Qt Quick layer is thin. A CMake rule (hiatus_forbid_qt()) aborts configuration if a logic library links against Qt.

  • Qt Quick · windows, dialogs, bindings, view models: no logic
  • support · adb · session logic, file transfer, shell, audit log, Android tools (file system, ADB shell, logcat)
  • session · connect · remote · session protocol, connection setup, hole punching, relay fallback, host and viewer side
  • protocol · net · message format (Protobuf), framing, QUIC (ngtcp2), TLS adapter with SPKI pinning
  • crypto · common · SPAKE2+, HKDF, AEAD, Argon2id, Ed25519; basic types, errors, logging

Network and relay

  • Ports on the relay: the same port for TCP (TLS 1.3, signalling) and UDP (hole punching, forwarding), default 443, plus UDP 444 for address probes that detect symmetric NATs.
  • Direct connections without a relay, for example inside a VPN: address instead of ID, default port 47443, certificate confirmed on first contact and pinned afterwards.
  • Relay file or access code hiatus://relay.example.org:443?pin=sha256:… for distribution to clients; the Docker image creates certificate and access code itself on first start.
  • Quality control: adaptive bitrate up to 16 Mbit/s and 60 frames/s, modes automatic, quality, speed, manual.

Test strategy

Unit

Every logic library without Qt and without network; CTest label unit.

Integration

Client, service and relay as real processes working together; NAT scenarios (cone, symmetric, UDP blocked, IPv6, VPN) in Docker.

Fuzzing · sanitizers

Protocol parsers with libFuzzer, ASan and UBSan; overnight runs over 26 targets, mutation tests against planted bugs.

BinSkim · clang-tidy

Hardening flags of the binaries (CFG, CET, ASLR, NX), static analysis with clang-tidy and MSVC /analyze, SBOM with CVE matching.

Technology stack

C++23 with CMake and vcpkg; Qt 6 for the client (LGPLv3, dynamically linked); OpenSSL for TLS and cryptographic primitives; ngtcp2 for QUIC; Protobuf for messages; OpenH264 and libyuv for video, Opus for audio, libvterm for the terminal; SQLite for the relay's device database; asio for network I/O. Packages: MSI (WiX) for Windows, systemd units and a polkit policy for Linux, a Docker image for the relay.