Hiatus Remote
Administrator Manual
Running the relay server, distributing the client, setting up the host service, installer parameters, hardening, session limits.
This manual is for administrators who run the relay server, distribute the client and set up the host service for unattended access. Day-to-day use is covered in the User Manual; building from source is described in the README.
1. Components and network
| Component | Program | Role |
|---|---|---|
| Relay server | hiatus-relay |
Assigns IDs and brokers connections. When no direct connection is possible, it forwards the traffic. It never sees content, because everything is end-to-end encrypted. |
| Client | hiatus-client |
User interface for both the supporter and the target. |
| Host service | hiatus-service + hiatus-agent (+ hiatus-pty-host on Windows) |
Optional. Makes the computer reachable without a signed-in user and adds the login screen, Ctrl+Alt+Del and user switching. The agent runs in the current user or login session; hiatus-pty-host runs the Remote Shell terminal and shell as the signed-in user (not as SYSTEM). |
Ports on the relay server: the same port for TCP (TLS 1.3, signaling) and UDP (hole punching and forwarding), 443 by default. In addition, a second UDP port answers address queries only (default: UDP port + 1, i.e. 444; --udp-probe-port, 0 = off). It lets clients detect whether their NAT assigns a new port per destination (symmetric NAT, e.g. company firewalls) and then search for a direct path. Clients need outgoing TCP and UDP to these ports. They try a direct connection first (UDP hole punching, also over IPv6) and use the relay otherwise. If both are on the same local network, its address takes priority over the detour through the router's public address (NAT loopback goes through the router's internet queue and becomes very slow when the line is busy); if the local address does not answer within 200 ms (e.g. guest Wi-Fi with client isolation), the public one is used. The relay runs over UDP or, if UDP is blocked, over the TLS connection (TCP). If a direct path appears during a relayed session, the session moves there without interruption (audit log: "Weg gewechselt: direkt"). The port search briefly sends up to ~1,200 small packets to different ports; where that is unwanted (intrusion detection in a company network), turn it off under Settings → Network → Establishing connections → Port search behind symmetric NAT – in service mode only as an administrator, with an audit log entry. Such connections then use the relay.
Port forwarding with different ports outside and inside (router "outside 18115 → inside 443", NAS container, Docker): the clients take the TCP port from the access code and convert the UDP ports themselves – the relay reports its local ports together with its own TCP port, and the client applies the same distance to its configured port (UDP = TCP port, address query = TCP port + 1 by default). Nothing needs to be configured on the relay; outside, only the same distances as inside must apply, e.g. forward TCP 18115, UDP 18115 and UDP 18116. Only if different distances are needed outside (e.g. TCP 18115 but UDP 20000), tell the relay the outside UDP ports explicitly: --public-udp-port <n> and --public-udp-probe-port <n> (default: public UDP port + 1) – the same for the relay as a program, Windows service and under systemd; in the Docker image HIATUS_RELAY_PUBLIC_UDP_PORT / HIATUS_RELAY_PUBLIC_PROBE_PORT. At startup the relay log states which distances or fixed ports apply; the client log shows the converted ports ("Relay-Server: UDP … (gemeldet …, umgerechnet auf Port …)").
Platforms: Windows 10 (1809+)/11 and Linux with X11. Wayland and macOS are not supported yet.
2. Running the relay server
Certificate and pin
hiatus-relay --create-certificate server.crt server.key --common-name relay.example.org
hiatus-relay --print-pin server.crt
The SPKI pin (sha256:…) is the fingerprint of the server's public key. Clients and services only accept a server whose pin they know (TLS pinning, no certificate authority needed). Protect the private key: whoever has it can pretend to be your server.
Starting
hiatus-relay --cert server.crt --key server.key [--listen 0.0.0.0] [--port 443] [--database devices.db]
--database stores the permanent device IDs in SQLite: each ID with its device's public key, no passwords. Without it, devices get new IDs whenever the server restarts.
As a Windows service
Run as administrator, with a virtual service account:
sc.exe create HiatusRelay start= auto obj= "NT SERVICE\HiatusRelay" binPath= "\"C:\Program Files\Hiatus\hiatus-relay.exe\" --service --cert \"C:\ProgramData\Hiatus\server.crt\" --key \"C:\ProgramData\Hiatus\server.key\" --database \"C:\ProgramData\Hiatus\relay\devices.db\""
icacls C:\ProgramData\Hiatus\server.key /inheritance:r /grant:r "NT SERVICE\HiatusRelay:R" "BUILTIN\Administrators:F"
mkdir C:\ProgramData\Hiatus\relay
icacls C:\ProgramData\Hiatus\relay /inheritance:r /grant:r "NT SERVICE\HiatusRelay:(OI)(CI)M" "BUILTIN\Administrators:(OI)(CI)F"
sc.exe start HiatusRelay
Firewall: allow incoming TCP and UDP on the chosen port, plus UDP on the second port (default: port + 1). Without it everything keeps working, just without the port search behind symmetric NAT.
On Linux (systemd)
- Copy
packaging/linux/hiatus-relay.serviceto/etc/systemd/system/. - Place the certificate and key in
/etc/hiatus-relay/. The key must be mode0600and owned by root. - Then run:
systemctl daemon-reload
systemctl enable --now hiatus-relay
journalctl -u hiatus-relay
The device database is stored in /var/lib/hiatus-relay/devices.db.
In Docker (Linux)
The hiatus-relay image contains the relay for Linux Docker hosts. Build it on Windows with scripts\build-relay-image.cmd (from the Linux build); afterwards build\hiatus-relay-docker\ contains hiatus-relay-image-docker.tar (current Docker), hiatus-relay-image-qnap.tar (see below) and compose.yaml to take along. On the Docker host:
docker load -i hiatus-relay-image-docker.tar
# edit compose.yaml: at least HIATUS_RELAY_PUBLIC_HOST
docker compose up -d
docker compose logs relay # access codes
docker compose cp relay:/data/hiatus.relay . # ready-made relay file (section 3)
hiatus-relay-image-qnap.tar contains the same image as a classic Docker archive (larger, layers uncompressed) for older Docker versions and NAS interfaces (for example QNAP Container Station: Images → Import). The image is built for x86-64; ARM devices are not supported.
On its first start the container creates the certificate and key in the /data volume, writes the access codes to the log and creates the relay file /data/hiatus.relay. Key and device IDs are kept across restarts. Back up the volume: without server.key every client needs a new access code; without devices.db devices get new IDs. When updating (new image), recreate the container with the same volume – in QNAP Container Station choose the existing volume for /data under Storage when recreating, not a new one. If the log shows "Erzeuge Zertifikat …" again, the volume was not carried over.
All parameters are explained in packaging/docker/relay/compose.yaml:
| Variable | Default | Meaning |
|---|---|---|
HIATUS_RELAY_PUBLIC_HOST |
– (required) | Name/address for the clients, part of the access code; several separated by commas |
HIATUS_RELAY_PORT |
443 | TCP port (sign-in, relay over TLS) |
HIATUS_RELAY_UDP_PORT |
same as TCP | UDP port (address query, relay) |
HIATUS_RELAY_PROBE_PORT |
UDP + 1 | second UDP port for detecting symmetric NAT; 0 = off |
HIATUS_RELAY_PUBLIC_PORT |
same as TCP | with port mapping: TCP port from outside – only for the access code in the log and the relay file |
HIATUS_RELAY_PUBLIC_UDP_PORT |
– | only with different distances outside than inside: fixed outside UDP port (--public-udp-port) |
HIATUS_RELAY_PUBLIC_PROBE_PORT |
public UDP + 1 | the same for the second UDP port (--public-udp-probe-port) |
HIATUS_RELAY_LISTEN |
0.0.0.0 | bind address in the container (:: for IPv6) |
HIATUS_RELAY_CERT_NAME |
first host | name in the certificate (first start only) |
HIATUS_RELAY_CERT_DAYS |
3650 | certificate validity (first start only) |
HIATUS_RELAY_EXTRA_PINS |
empty | additional pins in the access code (key rollover) |
HIATUS_RELAY_VERBOSE |
0 | 1 = verbose log |
Keep outside and inside ports identical (443:443/tcp, 443:443/udp, 444:444/udp): the relay tells the clients its UDP port. If the Docker host maps the ports differently (e.g. QNAP Container Station: 18115/18116 outside, 443/444 in the container), the clients convert the UDP ports themselves to the port in the access code (section 1, Port forwarding) – as long as the same distances apply outside. HIATUS_RELAY_PUBLIC_PORT=18115 only makes the access code and the relay file name the outside port. Forward all three on the router (TCP and UDP 18115, UDP 18116). The container runs without root, without extra capabilities and with a read-only file system (except /data).
Key rollover
Clients accept up to four pins. For an orderly rollover:
- Create a new certificate.
- Distribute its pin in addition to the old one (an access code with two
pin=entries). - Only then switch the server to the new certificate.
- Remove the old pin later.
3. Creating and distributing the access code
Users set up the client at first start with an access code:
hiatus://<server>:<port>?pin=<pin>[&pin=<second pin>]
Examples:
hiatus://relay.example.org:443?pin=sha256:3f9a…c21e
hiatus://[2001:db8::10]:443?pin=sha256:3f9a…c21e
<server>is a DNS name or IP address; put IPv6 addresses in square brackets. Without a port, 443 is used.<pin>is the output ofhiatus-relay --print-pin. Up to four pins are allowed.- No other parameters are allowed, and a code without a pin is rejected.
Distribute the code through a trusted channel: intranet, signed e-mail, or printout. A client that is already set up can pass it on via Settings → Network → Relay server → Copy access code.
Relay file (*.relay)
Instead of handing the code to every user, ship it as a file with the extension .relay (the Docker image creates it as hiatus.relay). Content: the access code on one line; blank lines and lines starting with # are ignored; 4 KB at most.
# Hiatus Remote – company relay
hiatus://relay.example.org:443?pin=sha256:3f9a…c21e
The search only runs while no relay server is set up. If there are several files, the newest wins (modification time; on a tie, the name).
- Client without service: searches the user's data directory and the program directory (
C:\Program Files\Hiatus) and prefills the setup dialog; the user confirms with Check and save. Continue without relay server closes the dialog (remembered; direct connections keep working, set up later under Settings → Network → Relay server). - Host service (Windows): searches only its data directory
C:\ProgramData\Hiatusand takes the server intoservice.confwithout asking – with an entry in the audit log and in Event Viewer. The file stays in place; onceservice.confexists, there is no more search. An invalid code is logged as an error in Event Viewer.
Whoever can place the file decides the server. C:\ProgramData\Hiatus and C:\Program Files\Hiatus are writable by administrators only. If you run the service with a custom --data-dir or place the client outside C:\Program Files, protect those folders the same way (only SYSTEM and administrators may write) – Hiatus does not check this. A file in a user's own data directory affects only that user, who confirms it in the dialog.
Before saving, the client checks reachability, TLS 1.3, the pin and the protocol version. It does not register while checking.
4. Distributing and preconfiguring the client
Windows: installer HiatusRemote-<version>-x64.msi (for all users, into C:\Program Files\Hiatus). It contains the client, hiatus-decoder, Qt, the Visual C++ runtime and THIRD-PARTY-NOTICES.txt. It adds a Start menu shortcut and allows the client direct connections on private and domain networks (firewall). The host service is an optional feature (section 5). The installer is not signed (internal use): on first start, SmartScreen asks ("More info → Run anyway"), and User Account Control shows "Unknown publisher". The installation folder gets its own, non-inherited permissions as under C:\Program Files (full control only for SYSTEM and Administrators, read and execute otherwise), even if you choose another folder in the installer, e.g. on a second drive.
msiexec /i HiatusRemote-<version>-x64.msi & rem with dialogs
msiexec /i HiatusRemote-<version>-x64.msi /qn & rem silent, client only
msiexec /i HiatusRemote-<version>-x64.msi /qn HIATUS_ACCESS_CODE="hiatus://relay.example.org:443?pin=sha256:…"
Installer parameters (MSI)
Properties are appended to msiexec as NAME=value (quote values containing spaces or &).
| Parameter | Values | Purpose |
|---|---|---|
ADDLOCAL |
Client or Client,Service |
Which parts are installed. Client is always included; Service = host service for unattended access (section 5). If omitted: client only on a new installation, the same parts as before on an upgrade. |
REMOVE |
Service |
Remove one part, e.g. only the service: msiexec /i … REMOVE=Service. The data in C:\ProgramData\Hiatus is kept. |
HIATUS_ACCESS_CODE |
hiatus://host:port?pin=sha256:… |
Preset the relay server (section 3). With service: the service takes it into service.conf on startup and replaces an existing server. Without service: the code is stored under HKLM\SOFTWARE\Hiatus\Remote\RelayAccessCode (writable by administrators only) and prefills the setup dialog on first start ("taken from the installation", takes precedence over relay files); the user confirms with Check and save. |
HIATUS_KEEP_DATA |
1 |
Uninstall only: keep data and settings (see below). Any non-empty value counts – even 0. |
INSTALLFOLDER |
path | Program directory other than C:\Program Files\Hiatus (command line only; the folder cannot be chosen in the dialog). The installer sets its own, non-inherited permissions there, as under Program Files: full control for SYSTEM and Administrators only, read/execute for users and app containers. Only specify a new folder, or an empty one created by an administrator: files already in it keep their permissions – if a user created the folder and placed, say, a DLL in it, it would stay modifiable by users and be loaded by the service as SYSTEM. If in doubt, delete the folder first. |
General msiexec switches:
| Switch | Purpose |
|---|---|
/i <msi> |
install or update |
/x <msi> |
uninstall (also via Settings → Apps) |
/qn |
fully silent, no dialogs (software distribution) |
/qb or /passive |
progress only, no questions |
/l*v <file> |
verbose log – include it with error reports |
/norestart |
never restart (Hiatus does not need a restart) |
Typical calls:
rem supporter workstation (client only, relay preset)
msiexec /i HiatusRemote-<version>-x64.msi /qn HIATUS_ACCESS_CODE="hiatus://relay.example.org:443?pin=sha256:…"
rem computer with unattended access (client and service, relay preset, log)
msiexec /i HiatusRemote-<version>-x64.msi /qn ADDLOCAL=Client,Service HIATUS_ACCESS_CODE="hiatus://relay.example.org:443?pin=sha256:…" /l*v hiatus-install.log
rem update: just install the new MSI (parts and data are kept)
msiexec /i HiatusRemote-<new version>-x64.msi /qn
rem uninstall and remove everything, or keep the data
msiexec /x HiatusRemote-<version>-x64.msi /qn
msiexec /x HiatusRemote-<version>-x64.msi /qn HIATUS_KEEP_DATA=1
Updating: the new MSI replaces the installed version – even with the same version number (a release candidate such as 1.0.0-rc1 and the final 1.0.0 share the installer version 1.0.0; the pre-release tag appears in the file name, the file properties and the client; every build is a separate product); REINSTALL/REINSTALLMODE are not needed. Installed parts, C:\ProgramData\Hiatus (ID, keys, unattended access, audit log, service.conf) and the users' settings are kept; the service is stopped and restarted. An older version cannot be installed over a newer one. Running the same MSI file a second time only offers Change, Repair and Remove.
Running programs: before replacing or deleting files (update, change, repair, uninstall) the installer ends all running Hiatus programs – the client (also in the notification area and in other user sessions), agent, pty host and decoder; the service is stopped. Open sessions are dropped, including one used to install remotely. No restart is needed.
Uninstalling (Settings → Apps, right-click on the MSI file → Uninstall, or msiexec /x) removes everything – whether started with or without administrator rights (the installer deletes the protected folder C:\ProgramData\Hiatus as SYSTEM):
- programs including the program folder, service, firewall rules, Start menu, event source and the Ctrl+Alt+Del policy (only if the installer set it),
C:\ProgramData\Hiatuswith device key and ID, unattended access, allowlist, audit log,service.confand relay files,HKLM\SOFTWARE\Hiatus,- for the uninstalling account
%LOCALAPPDATA%\Hiatus(the client's device key) andHKCU\Software\Hiatus(settings).
After a reinstallation the computer gets a new ID; partners see the "key changed" warning, unattended access has to be set up again, and the audit log is gone – if you have to prove access, back up C:\ProgramData\Hiatus\audit.log first or uninstall with HIATUS_KEEP_DATA=1. The installer cannot reach the data of other user accounts (their %LOCALAPPDATA%\Hiatus and HKCU\Software\Hiatus); delete it in each account if needed:
rmdir /s /q "%LOCALAPPDATA%\Hiatus"
reg delete "HKCU\Software\Hiatus" /f
Linux: the program with the distribution's Qt libraries (section 6); packages are postponed.
hiatus-decoder is part of it. The picture, sound and clipboard images from the other side are decoded by a separate process (hiatus-decoder.exe or hiatus-decoder), which the client starts from its own directory.
- It has no keys and no network connection, so a flaw in a decoder (H.264, Opus, PNG) cannot reach the client.
- If the file is missing, remote control shows no picture, plays no sound, and does not take over clipboard images from the other side (text still works).
On Windows, hiatus-decoder.exe runs in a sandbox:
- AppContainer
Hiatus.Decoder: no network, no access to the user's files and registry, low integrity. - A job: one process, at most 1 GiB, ends with the client.
- Strict process mitigations, e.g. no Win32k calls and only Microsoft-signed DLLs.
For this, the group "ALL APPLICATION PACKAGES" must be allowed to read and execute the file. Under C:\Program Files this is the default. If the client is installed elsewhere, it adds this entry itself on first start (only on hiatus-decoder.exe). If it may not change the permissions there, set the entry as an administrator: icacls hiatus-decoder.exe /grant *S-1-15-2-1:RX.
On Linux, hiatus-decoder restricts itself at startup, before it reads any data from the other side:
- seccomp filter: no opening of files, no network, no new processes, no executable memory.
no_new_privs, not inspectable by other processes, at most 1 GiB of memory, ends with the client.- Its own namespaces (user, network, mount), where the system allows unprivileged namespaces. Elsewhere (e.g. in containers, or with
kernel.apparmor_restrict_unprivileged_userns=1) it runs without them; the seccomp filter always applies.
Nothing needs to be set up for this.
The service's session agent uses it as well: an image the supporter puts on the clipboard is decoded by the decoder process, not by the agent with its SYSTEM rights. If hiatus-decoder.exe is missing next to the agent, the image only reaches the clipboard as PNG (programs without PNG support cannot paste it then).
Data directory
C:\ProgramData\Hiatus holds the device key, the verifier of the permanent password, the unattended-access settings with the allowlist, service.conf and relay files. The service checks the directory at every start, before reading anything from it: owner only SYSTEM or Administrators, access only for SYSTEM, Administrators and virtual service accounts (NT SERVICE\…), no links (junction, symlink). If it is missing, the service creates it with these permissions. If anything differs, the service does not start and names the file and the reason in the Event Viewer (Application, source HiatusRemote) – e.g. "Zugriff für S-1-1-0" (Everyone) or "fremder Besitzer" (foreign owner).
This happens if a user created the directory before installation or permissions were changed by hand. Since files may have been planted as well, do not just repair the permissions:
- Stop the service (
sc.exe stop HiatusRemote). - Check the contents. If in doubt, delete the directory (
rmdir /s /q C:\ProgramData\Hiatus) – the computer then gets a new ID and unattended access must be set up again; back upaudit.logfirst if needed. - Repair the installation (Settings → Apps → Hiatus Remote → Modify → Repair) or start the service – the directory is created again with the correct permissions.
Settings
The client stores its settings per user (QSettings, organization "Hiatus", application "Remote"):
- Windows: registry
HKEY_CURRENT_USER\Software\Hiatus\Remote - Linux:
~/.config/Hiatus/Remote.conf
| Key | Meaning |
|---|---|
server, port, pins |
Relay server (pins as a list). If these are preset, no setup dialog appears. With the host service installed, the client takes over the service's server and stores it here. |
language |
system, de or en |
rotationMinutes |
How often the one-time password changes, in minutes (default 10) |
newKeyPolicy |
0 = trust and mark "not verified"; 1 = unattended only after verification; 2 = verified immediately |
matchResolution |
Match the target's resolution (unattended sessions) |
trayHintShown |
true = the one-time hint "Hiatus Remote keeps running" was shown on the first minimize to the notification area |
TargetBanner/pinned |
true = the banner shown while this computer is accessed is pinned (does not slide to the edge) |
relaySkipped |
true = "Continue without relay server" was chosen: no setup dialog at startup (cleared once a server is set up) |
logcat/filters |
Your own saved filters of the Logcat viewer (name, query, levels; at most 30) |
logcat/window/…, logcat/sidebar…, logcat/details/height, logcat/columns/…, logcat/timeFormat |
Window position, sidebar, details height, columns and time format of the Logcat viewer |
Command line
| Option | Meaning |
|---|---|
--server <host>, --port <port>, --pin <sha256:…> (repeatable) |
Relay server; takes precedence over stored settings |
--data-dir <path> |
Data directory (device key). One instance runs per data directory. |
--no-service |
Work without the host service even if it is running |
--service-pipe, --service-owner |
Development only (the service's console mode) |
Data
The device key and verifier are stored in the user's data directory (on Windows typically %LOCALAPPDATA%\Hiatus\Hiatus Remote), protected by the system's secret store. The device key determines the ID. If the directory is deleted, the computer gets a new ID, and its partners see the "key changed" warning. Uninstalling with the MSI deletes this directory for the uninstalling account (keep it with HIATUS_KEEP_DATA=1, see Installer parameters).
5. Host service on Windows
The service:
- gives the computer its own ID,
- accepts all incoming connections,
- starts the session agent in the active console session. The agent follows the login screen, lock, UAC and user switching.
The client connects to the service automatically at startup and shows the service's ID. Outgoing connections are still made by the client itself. The one-time password and incoming requests appear only in the client of the console session (the computer's own screen); a client in another session, e.g. over Remote Desktop on a terminal server, shows a note instead. Settings for unattended access and direct connections, remembered devices and the history are visible only at the console or to an administrator. The permanent password is not affected.
Setup with the installer
Select the feature "Unbeaufsichtigter Zugriff (Dienst)" in the MSI (in the dialog under Custom), or silently:
msiexec /i HiatusRemote-<version>-x64.msi /qn ADDLOCAL=Client,Service HIATUS_ACCESS_CODE="hiatus://relay.example.org:443?pin=sha256:…"
The installer:
- creates the service HiatusRemote (automatic start, LocalSystem, restart after a crash) and starts it,
- creates
C:\ProgramData\Hiatusfor SYSTEM and administrators only (no inherited permissions), - sets the Ctrl+Alt+Del policy (
SoftwareSASGeneration= 1, services) – only if it is not set yet; an existing setting stays unchanged, - registers the event source "HiatusRemote" (entries with text in Event Viewer), and
- allows the service direct connections on private and domain networks.
Access code: with HIATUS_ACCESS_CODE (all parameters: section 4, Installer parameters), the service takes over the relay server into service.conf on its first start. For this, the code is briefly stored under HKLM\SOFTWARE\Hiatus\Remote\AccessCode and deleted afterwards; an invalid code is logged as an error in Event Viewer. Without a code, the service takes a relay file from C:\ProgramData\Hiatus (section 3); if there is none either, it starts without a server: start the client as administrator and set the server under Settings → Network → Relay server.
Switching from a manual setup: remove the old service first (sc.exe stop HiatusRemote and sc.exe delete HiatusRemote) and keep C:\ProgramData\Hiatus. ID, password and settings are then kept.
Manual setup (without installer)
Run as administrator, with the programs in C:\Program Files\Hiatus\ (hiatus-service.exe, hiatus-agent.exe, hiatus-pty-host.exe):
mkdir C:\ProgramData\Hiatus
icacls C:\ProgramData\Hiatus /inheritance:r /grant:r "NT AUTHORITY\SYSTEM:(OI)(CI)F" "BUILTIN\Administrators:(OI)(CI)F"
notepad C:\ProgramData\Hiatus\service.conf
sc.exe create HiatusRemote start= auto DisplayName= "Hiatus Remote" binPath= "\"C:\Program Files\Hiatus\hiatus-service.exe\" --service"
sc.exe description HiatusRemote "Hiatus Remote: unattended remote access (permanent password and allowlist only)"
sc.exe failure HiatusRemote reset= 86400 actions= restart/5000/restart/30000//
sc.exe start HiatusRemote
service.conf:
server=relay.example.org
port=443
pin=sha256:… # up to 4 lines
The data directory holds the device key, verifier, settings and audit log. Only SYSTEM and administrators have access to it.
Ctrl+Alt+Del
The installer sets the policy if it is not set yet. Manually or by group policy: for the supporter to be able to send Ctrl+Alt+Del, set the group policy "Disable or enable software Secure Attention Sequence" (Computer Configuration → Administrative Templates → Windows Components → Windows Logon Options) to "Services".
Updating and removing
With the installer: install the new MSI (the service is stopped and restarted during this; data and settings are kept), or remove Hiatus Remote under Settings → Apps. Uninstalling also deletes C:\ProgramData\Hiatus (ID, keys, unattended access, audit log) – keep it with msiexec /x … HIATUS_KEEP_DATA=1; remove only the service with msiexec /i … REMOVE=Service (data is kept). Details in section 4 under Installer parameters. The installer only removes the Ctrl+Alt+Del policy if it set it itself.
Manually:
sc.exe stop HiatusRemote
copy /y hiatus-service.exe "C:\Program Files\Hiatus\"
copy /y hiatus-agent.exe "C:\Program Files\Hiatus\"
copy /y hiatus-pty-host.exe "C:\Program Files\Hiatus\"
sc.exe start HiatusRemote
sc.exe stop HiatusRemote
sc.exe delete HiatusRemote
Delete the data directory by hand if needed. After that, the computer gets a new ID.
Changing settings
The client can change the unattended access settings and the relay server only when it runs as administrator; other users can only view them. Otherwise the settings page offers "Restart as administrator" (UAC): the new instance takes over and the old one exits. If a different administrator account signs in for this, the client runs with that account's user data (its own ID for outgoing connections). Every change, and every rejected attempt, is recorded in the audit log.
Changing the service's relay server
The client uses the service's server for its own connection too (one server per computer). To change it:
- Start the client as administrator.
- Go to Settings → Network → Relay server → Change … and enter the access code.
- Click Check and save.
What happens then:
- The client checks the server.
- The service rewrites
service.confand reconnects immediately; running sessions end. - The audit log records "Relay-Server: host:port" with its originator.
- When the file is rewritten, comments in
service.confare lost; all other entries are kept.
Alternatively, edit service.conf by hand and restart the service.
6. Host service on Linux
The service follows the active X11 session on seat0 (systemd-logind), both at the login screen and for signed-in users. Settings, including the relay server as on Windows, can be changed by anyone in the active session who enters an administrator password (polkit action de.hiatus.remote.manage). Files and shell run with the rights of the signed-in user.
install -d /usr/lib/hiatus
install -m 0755 hiatus-service hiatus-agent /usr/lib/hiatus/
install -m 0644 packaging/linux/de.hiatus.remote.policy /usr/share/polkit-1/actions/
install -d -m 0755 /etc/hiatus
install -m 0644 /dev/null /etc/hiatus/service.conf # add server=, port=, pin=
install -m 0644 packaging/linux/hiatus-service.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now hiatus-service
/etc/hiatus/service.conf must be owned by root and not writable by group or others. Otherwise the service refuses to start, because anyone who can change the file could redirect the service to a foreign relay.
- When an administrator changes the relay server through the client, the service rewrites the file (root,
0644). - Data:
/var/lib/hiatus(root only) - Sockets:
/run/hiatus - Logs:
journalctl -u hiatus-service
7. Setting up unattended access
Unattended access is off until it is set up. Recommended procedure:
Start the client on the target computer. With the service installed, start it as administrator.
Set a permanent password: at least 12 characters not counting spaces, and not a common password. Only an Argon2id verifier is stored. After three failed attempts, incoming connections are briefly blocked (the block doubles up to one hour, falls back to 30 seconds after 24 hours without a failed attempt, and covers internet and direct connections together). An attempt counts as soon as the other side can check it; at most two sign-ins run at the same time, a third is refused and can simply be retried.
Keep Allow only listed supporters (the default). Other supporters are then refused before the password check and cannot try the permanent password. To add a supporter:
- The supporter connects once attended.
- You compare the fingerprint.
- You confirm under Active sessions → Allow.
If you remove a supporter from the list again, their running sessions on this computer end immediately.
Limit the permissions to what is needed.
Depending on the environment:
| Setting | Recommendation |
|---|---|
| Ask the signed-in user | On where users work at the computer (privacy). |
| Allow without an answer | Only for computers that are not regularly staffed. |
| Lock after the session | Keep on (the default), so no open session is left behind. |
| Files and shell at the login screen (as SYSTEM) | Keep off unless it is really needed. |
| "Tunnel" permission | Only if supporters need to reach services of this computer (e.g. a remote debugger or a server application). |
| "Remote debugging" permission | Only for developer support. A debugger effectively runs code in the target process; grant it only to trusted supporters. |
Tunnels: with the "Tunnel" permission, the supporter reaches ports of this computer: only through localhost (127.0.0.1 or ::1), never other computers in the network, and only ports they have registered for the session. The connection to the service is made by the Hiatus service or the client on this computer, so services that listen on localhost only are reachable too. The firewall does not need any rule for this. The user sees the ports in the notification bar and can close all tunnels there. With the same permission the supporter may query the processes with a TCP listener on this computer (Remote process …, to choose ports): name, PID, service, user, path, ports and bind address – only processes with a listener, no full process list and no command lines. With the Hiatus service the service queries (all processes visible), without it the client with the user's rights. Ports of HTTP.sys (owner System) are assigned via netsh http show servicestate view=requestq to the programs that registered URLs there (Windows has no public interface for this; netsh is started from System32, for at most 5 s). Tunnels still go only to the registered ports on localhost; listeners bound to a network address only stay unreachable.
Remote debugging: with the "Remote debugging" permission, Hiatus starts this computer's Visual Studio Remote Debugger (msvsmon) – only if the Remote Tools for Visual Studio are installed. It runs, as chosen, under the signed-in user, as administrator, or under a given account (password encrypted only, used once for the start, not stored, not logged). Hiatus starts the 64-bit and 32-bit variants at once and reaches them through localhost only; the firewall needs no rule. It stops on the supporter's command, at session end, or when the permission is revoked. Because a debugger runs code in the target process, the permission is deliberately meant for trusted supporters only. Hiatus transfers symbols only if the supporter explicitly chooses to (an optional extra, off by default): the PDBs go into a temporary session folder readable only by the target user, and _NT_SYMBOL_PATH is set only in the msvsmon process environment – no system variable is changed and no administrator rights are needed; the folder is deleted again on stop.
At the login screen, files and shell are unavailable without that permission. With a signed-in user they run with that user's rights. If the user at the console changes, they follow the new user, and the previous user's shells are closed. This also applies during a session: if it starts at the login screen and someone signs in through the remote control, files and shell become available within a few seconds; after signing out they are locked again.
Note: a permanent password is bound to the ID. After switching to another relay server (which gives a new ID), it must be set again.
Direct connections (without a relay server)
For computers without internet access that the supporter reaches through a VPN (typically a customer's Windows server). The computer then accepts connections itself, through a built-in relay on its own port. Sign-in (one-time or permanent password), confirmation, allowlist, permissions, audit log and end-to-end encryption stay the same; the session runs directly over the VPN. The registration with the internet relay (if reachable) continues alongside.
Set it up under Settings → Network → Direct connections (off by default; in service mode only as an administrator, and every change is recorded in the audit log):
| Field | Meaning |
|---|---|
| Accept direct connections | On/off. Off ends running direct sessions. |
| Port (TCP and UDP) | 47443 by default; any port can be chosen. |
| Accept only on this address | Empty = all interfaces; otherwise an address of this computer (e.g. the one reachable through the VPN). If it is missing (VPN adapter disconnected), Hiatus retries every 10 seconds. |
| Only from these networks | Allowed senders, in CIDR notation or as single addresses, separated by commas (e.g. 10.20.0.0/16, 192.168.5.7). Empty = all. Others are disconnected before any sign-in. |
After Apply, the card shows "Reachable at 10.20.5.17:47443" and the computer's fingerprint (certificate, created once). Give the supporter the address and port; they enter it in the connect field instead of the partner ID (e.g. 10.20.5.17 or 10.20.5.17:5000), see the fingerprint on their first connection and should compare it with you. Their Hiatus remembers the address and fingerprint. If the port is in use, the card shows the reason.
Firewall: the installer allows incoming connections to the Hiatus service on private and domain networks – for any port, so a changed port needs no new rule. If the network connection the supporter comes through is classified as public, open the port (TCP and UDP) yourself or classify the network as private. Without the service, the client accepts the connections; the same rule applies to it.
IDs: the computer keeps its Hiatus ID even without internet access, and so do supporters. Permanent password and allowlist therefore keep working. If the computer has never been connected to an internet relay, the built-in relay assigns the ID.
8. Logs and accountability
Every session on a computer is recorded:
- start, duration, supporter ID and key,
- whether it was attended or unattended,
- permissions and the features used (files, shell, …),
- end and reason.
Rejected attempts, wrong passwords, lockouts and settings changes are recorded too, each with its originator. Folders are recorded with one entry per folder ("Ordner auf diesen Rechner übertragen: C:\Target\Logs (12 Dateien, … Byte)"), counted by the computer itself and written at the latest when the session ends; paused files do not count as failed. File operations of the supporter in the file browser are recorded individually ("Ordner angelegt: …", "Umbenannt: … → …", "Gelöscht: … (n Einträge)", "Dupliziert: … → …", "Verschoben: … → …", "Kopiert: … → …", "Archiv erstellt: … (n Einträge)", "Auf dem Zielrechner geöffnet: …" – the latter only with the Transfer files and Remote shell permissions, because it can start programs (e.g. an uploaded .exe); never at the sign-in screen); viewing properties and searching subfolders are not (a search only reads what opening the folders would show). Tunnels are recorded with their target when they are opened and closed ("Tunnel geöffnet: localhost:4026"), each listener query as "Prozesse mit Netzwerk-Listenern abgefragt". The remote debugger is recorded with account, authentication mode and ports ("Remote Debugger gestartet: … / beendet"), never with a password. On the supporter's side, the program log records the start and end of their tunnels.
| Location | Content |
|---|---|
| Windows Event Viewer, Application, source "HiatusRemote" | Service messages (the text currently appears under "Details", see section 11) |
journalctl -u hiatus-service |
The same on Linux |
audit.log in the data directory of the service or client |
Audit records (binary, 1 MiB, one rotation .1) |
| Client: Settings → Security → Access history | Readable overview of the sessions |
After unattended access, users see the "Access while you were away" notice at the next start.
Picture transmission measurements (diagnostics, off by default)
To analyse picture quality and frame rate (e.g. "jerky over mobile"), the supporter and the target can write measurements once per second to a separate file. In normal operation this is off; it is only enabled through the environment variable HIATUS_VIDEO_STATS. The file contains measurements only (frame rate, data rate, round-trip time, delay, capture and encoding time, quality control level and its reasons) – no picture content, file names or input.
- Value: path of the measurement file, e.g.
C:\Hiatus-Messung\target.log; the folder is created if needed, new lines are appended (delete the file yourself when needed). A value without\or/(e.g.1) writes tohiatus-video-stats.login the process's temp folder. The normal log names the file once when writing starts. - Supporter (client): set the variable and start the client from the same command prompt, e.g.
A client that is already running must be closed first.set HIATUS_VIDEO_STATS=C:\Hiatus-Messung\supporter.log "C:\Program Files\Hiatus\hiatus-client.exe" - Target with the host service (unattended) – as administrator:
Turn off again:reg add HKLM\SYSTEM\CurrentControlSet\Services\HiatusRemote /v Environment /t REG_MULTI_SZ /d HIATUS_VIDEO_STATS=C:\Hiatus-Messung\target.log /f sc stop HiatusRemote sc start HiatusRemotereg delete HKLM\SYSTEM\CurrentControlSet\Services\HiatusRemote /v Environment /f, then restart the service. (setx /Monly takes effect for services after a reboot.) On Linux:Environment=HIATUS_VIDEO_STATS=/var/tmp/hiatus-target.login a drop-in of the systemd unit, thensystemctl restart hiatus-service. An update (new MSI) recreates the service and removes the switch – set it again after an update if needed. - Target without the service: as for the supporter (set the variable before starting the client).
- Every line starts with date and time, so both files (supporter and target) can be lined up. The measurement costs hardly any CPU time, but the file grows by about 300 bytes per second and video – turn it off again after the analysis.
9. Security notes
- Keep the relay key and the service data directory accessible only to SYSTEM/root and administrators.
- Distribute access codes and pins through trusted channels only.
- Run unattended access only with the allowlist, and keep permissions minimal.
- Switch on direct connections only where needed, and restrict them to the VPN network (Only from these networks). Other senders are disconnected before any sign-in; the port is held exclusively.
- Review the audit log regularly.
- Clients verify partners by pinned device keys. Never dismiss the "key changed" warning without checking back.
- Keys are kept in memory that is locked against being paged out. If the system does not allow this (Linux:
RLIMIT_MEMLOCKbelow 64 KiB), the log shows "Schlüsselspeicher nicht gegen Auslagern gesperrt", and keys are only wiped when they are released. systemd allows services enough by default.
10. Troubleshooting
| Symptom | Cause / solution |
|---|---|
| Client: "No relay server set up" | Enter the access code, or preset server/port/pins. With the host service: is the service running, and is service.conf valid? |
| "The Hiatus service did not accept the server" | The reason is shown with it: no administrator rights, invalid input, or service.conf not writable. |
| Check: "The pin does not match this server" | Wrong pin, or another server answers (proxy, TLS inspection). Exempt the relay port from TLS inspection. |
| Check: "Server not reachable" | Check the TCP/UDP firewall and the port, and whether the server is running. |
| Relay log repeats "logged in elsewhere" | Two clients use the same data directory (e.g. one normal, one as administrator). Current clients prevent this; close older ones. |
| Client log: "Server antwortet nicht per UDP (host:port) – Sitzungen über TLS", also from outside | The port shown is the one the client uses after conversion (section 1, Port forwarding). If the UDP forwarding (router, firewall) is missing there or different distances apply outside than inside, add the forwarding or set --public-udp-port / HIATUS_RELAY_PUBLIC_UDP_PORT. Only inside your own network (relay in the same LAN reached via its public name): the router does not loop UDP back internally (hairpinning) – from outside it works anyway. |
| Session runs over the relay instead of directly | The log (client or service) has lines "Portsuche: …" and "Direkter QUIC-Handshake …" for each connection: whether a symmetric NAT was detected, when and after how many probes a hit came, whether the session started over the relay and moved ("Umzug vom Relay auf …"). Without these lines: no port search needed or switched off; both sides symmetric → stays on the relay. The port search needs the relay's second UDP port to be open (section 1). |
| Picture blurry or jerky, hint "Slow connection on the other side" | Usually the target's upload is not enough (mobile, DSL): the control sets the bitrate to what the line demonstrably carries and, with motion, first lowers frame rate or resolution (depending on the mode in the Quality menu). For each reduction the supporter's log has "Videoqualität vom Target gesenkt (reason): …" – rtt/queue/loss/throughput: the line; receiver-delay/receiver-drops: frames arrive late or are dropped; starvation: frames are held back; encoder: the content (heavy motion) needs more than the target. A large file transfer at the same time shares the line with the picture. |
| Display scaling of the target differs after a session (e.g. 125 % instead of 100 %) | Hiatus never changes the scaling itself; only in unattended sessions with "Unattended: match the target's resolution to my screen" does it temporarily change the resolution, and Windows sets the scaling per resolution. The service's event log (or the client's log without the service) shows "Auflösung angepasst: 1920×1080, Skalierung 125 % (vorher 100 %) …" and at the end of the session "Auflösung zurückgesetzt: …, Skalierung …" or "Auflösung nicht zurückgesetzt: …". If the reset line is missing or the scaling stays different afterwards, pass these lines to the developers; workaround: turn off resolution matching on the supporter. |
| Ctrl+Alt+Del has no effect | Group policy (section 5), or no service installed. |
| No picture after sign-out or lock | It returns after a few seconds; otherwise use Restart in the session. The service keeps the display on during a session. |
| Service does not start (Linux) | Check the permissions of service.conf (root-owned, not group- or world-writable). |
| Event Viewer: "Sitzungslogik reagiert seit … s nicht" (session logic not responding) | An internal call is stuck; no new connections are possible until the service (HiatusRemote) is restarted. Restart the service and pass the entries before it (e.g. "Android (USB): …") to the developers. If it recovers on its own, "reagiert wieder" follows. |
| Event Viewer: "description … cannot be found" | Expected until M8; the text is under "Details". |
| Remote control shows no picture, and the supporter's client reports "Decoder-Prozess …" | hiatus-decoder is missing next to hiatus-client, or is not allowed to start (e.g. application control/AppLocker; rules must also allow AppContainer processes). |
| The supporter's client reports "… für die Sandbox nicht freigebbar" or "AppContainer nicht verfügbar" | Set the permission on hiatus-decoder.exe (see distributing the client), or check the user profile (AppContainer profiles are stored in the user's profile). |
| Linux: remote control shows no picture, and the client's stderr shows "hiatus-decoder: Systemaufruf N gesperrt (Sandbox)" | The decoder attempted a system call that is not allowed (a different library version, or an attack) and was ended. Report the message with the distribution and version to the developers. |
| Linux: "hiatus-decoder: Sandbox nicht möglich …" | The kernel has no seccomp (very old or custom-built), or the architecture is neither x86-64 nor ARM64. Without a sandbox, Hiatus deliberately does not decode. |
11. Known limitations
- There is no Linux package (deb/rpm) yet; set up by following this manual. On Windows the MSI installs everything (section Installer parameters).
- When set up without the MSI, the Event Viewer shows the text only under "Details" (the event source is not registered).
- Linux works with X11 only; Wayland and macOS follow later.
- If an upload is paused and then cancelled, the hidden partial file (
.hiatus-*.part) stays in the target folder until the same file is transferred again. - The target's resolution can only be set to modes the graphics driver offers. A virtual monitor is planned for M8.
- Android devices on the customer's computer (USB bridge, Wi-Fi discovery) are only available when it runs Windows; the developer can work on Windows or Linux. Wi-Fi debugging requires Android 11 or later.
- After the terminal window is made narrower, Windows PowerShell 5.1 shifts lines as soon as a progress display appears (a PowerShell bug in the console buffer, not in Hiatus);
clscleans up,$ProgressPreference = 'SilentlyContinue'avoids it. - Video and file transfer share the line without priority; during a large transfer the picture quality drops.
- The Logcat viewer holds at most 50,000 lines or 64 MB per device (ring buffer); older lines are dropped. The reader thread's queue is limited to 50,000 lines or 32 MB.
12. Android debugging and Logcat
Developers debug Android apps on a device at the customer's site through a session (Visual Studio/.NET MAUI, Xamarin, adb tools) and read its Logcat. Usage is described in the user manual (Debugging Android devices, Logcat viewer).
How it works
- The developer's client uses the adb on the developer's own computer; ADB packets travel end-to-end encrypted through the session. Nothing is installed at the customer's site or on the phone. Logcat, the apps' files and the ADB shell also use this adb – log contents only reach the developer and are only stored when exported.
- USB (customer on Windows): Hiatus opens the phone's ADB interface (WinUSB) only while connecting and releases it afterwards, listens only on
127.0.0.1and only accepts connections from its own process; every packet header is checked. The phone is recognized by its serial number (also behind a vendor driver's child node) and reconnects by itself after re-plugging. If the Hiatus service is running, it provides the bridge. - Wi-Fi (wireless debugging): Hiatus at the customer's site finds devices on the local network via mDNS and only connects to local addresses (private IPv4 ranges, IPv6 ULA or link-local); the connection to the phone uses TCP keepalive (a dead connection is closed after about 8 s; load only while idle, ~50 bytes/s, LAN only).
Requirements
- Developer: Android SDK with adb (e.g. from Visual Studio). Hiatus uses the running adb server, otherwise the Visual Studio SDK,
%ProgramFiles(x86)%\Android\android-sdk,ANDROID_HOME/ANDROID_SDK_ROOT,%LOCALAPPDATA%\Android\Sdkor~/Android/Sdk, oradbon the path. - Customer: Windows 10/11; for USB a driver providing the ADB interface (vendor driver or Google USB Driver) and no local adb holding the phone (Android Studio, Visual Studio with Android tools, platform tools: close them or run
adb kill-server). For Wi-Fi, Android 11+ and the phone on the same network as the computer (mDNS not blocked by a firewall; otherwise add the device manually with IP address and port).
Permission and traceability
- Permission Debug an Android device in the request (off by default). The customer sees it in the banner ("Android off (…)") and can end it at any time; on the phone they also have to allow debugging (USB trust prompt or pairing).
- Additionally Add Android devices by address (off by default, only together with Android debugging, also configurable for unattended sessions): the developer may add devices on the customer's network by IP address and port and remove their own entries again – for devices that do not announce themselves via mDNS (Fire TV, Nvidia Shield with network debugging). Only private addresses (as for entries by the customer); Hiatus does not check whether a device answers there. Every entry and removal is recorded in the audit log ("Android-Gerät vom Supporter eingetragen/entfernt: address:port"). Allowing it enables ADB connections to any port on the customer's local network – only for trusted developers.
- Remembered Android devices: every address added by hand (by the customer or the developer) is remembered by the customer's computer in
android-devices.datin the data directory (service: the service's data; at most 16, private addresses only, forgotten after 90 days without use). They are offered only with both permissions, when the developer opens the Android window, and only if an adb answers there right now: Hiatus sends the adb greeting (CNXN) and disconnects after the reply – before the key exchange, so without a prompt on the device; no adb is needed at the customer's side. Audit log: "Gemerkte Android-Geräte angeboten (antworten): …". The device name is reported by the developer after connecting (read by adb); the customer's computer accepts it only for devices added by hand and checks it (at most 64 characters, no control characters) – it is for display only. List and Forget under Settings → Security; in service mode only for the user at the console. - The customer's audit log contains the Android connection (connected/disconnected), not every single file or log line.
- Allowing Android debugging gives the developer full ADB access to the device (install apps, read and write data of debuggable apps, read logs; Clear device buffer deletes the logs there after confirmation).
13. Session limits
Every session has fixed upper limits. They protect both computers from overload and from a hostile peer trying to open any number of connections. They apply per session (several sessions each have their own) and are built in, not configurable.
| What | Limit | Applies to | Reached – what the user sees |
|---|---|---|---|
| Tunnels created by the supporter | 32 | Add and Remote process … together; tunnels of the remote debugger and for Android do not count | Buttons greyed out, note "All 32 tunnels are in use – remove one …"; in the Tunnels to remote processes dialog "At most 32 tunnels – n ports too many selected" |
| Simultaneous connections through tunnels and the remote debugger | 64 | one shared pool: all tunnels of the supporter and the connections of Visual Studio to msvsmon | The connection is refused (the supporter's program sees the connection drop); note "Connection limit reached" with the limit and advice; at the tunnel "n refused (limit: 64 connections …)" in warning colour |
| Simultaneous connections to Android devices | 64 | separate pool (connect and pair, at most 16 devices) – does not affect tunnels and debugger | as above, text "Android: connection refused …" |
| Channels (streams) of the transport connection | 256 per direction | all functions together | Practically never: the per-function limits add up to less (see below). Otherwise note "no free channels" |
| Remote shells | 4 | per session | message "Zu viele Shells" (too many shells) |
| Simultaneous file transfers | 4 | per session (the partner accepts at most 16 as protection) | further ones wait in the queue (not refused) |
| Monitors with picture | 8 | per session | – |
| Processes in Tunnels to remote processes | 512 processes, 32 ports per process | per query | note "List incomplete" |
Feedback is mandatory: no connection is refused silently. When a connection hits a limit, the tunnel counts it as "refused" (visible in the tunnel list until the tunnel is removed) and a "Connection limit reached" note appears – also for the invisible tunnels of the remote debugger and Android. With many refusals in a row there is at most one note per session every 5 seconds; the counter at the tunnel stays exact. The program that tried to connect (e.g. Visual Studio, a browser, adb) only sees the connection drop.
What if 64 connections are not enough? Close connections you do not need (e.g. browser tabs keeping a tunnel open), stop tunnels you do not need right now, or open a second session for a second task – each session has its own limits. Visual Studio opens several connections itself when remote debugging; that is why tunnels and the debugger share the larger pool.
Technical background (for administrators): the supporter enforces the limits and reports every refusal. The partner computer checks them again independently – with a buffer of 8 connections, because closed connections still count there for a moment – and refuses anything beyond with "Limit"; the supporter shows that as a refusal too. The sum of all per-function limits (about 180) stays below the 256 channels of the transport connection, so no function can take the channels of another (checked by a test).