Documentation
Single Go binary on the host, Flutter client on the remote machine. One environment variable enables mock mode for testing. Production requires FFmpeg and the NTK access point.
Quickstart
1 — Prerequisites
# Go 1.24+
sudo apt install golang-go
# FFmpeg dependencies (for screen capture / encode)
sudo apt install libavcodec-dev libavformat-dev libswscale-dev \
libavdevice-dev libavfilter-dev pkg-config
# PipeWire (Linux capture backend)
sudo apt install libpipewire-0.3-dev
2 — Build FFmpeg (first time, ~15 min)
./scripts/build-ffmpeg.sh --prefix /opt/ntkvdi/ffmpeg export PKG_CONFIG_PATH=/opt/ntkvdi/ffmpeg/lib/pkgconfig:$PKG_CONFIG_PATH
3 — Build and run server
make server # Dev / testing (mock mode — no real NTK required) NTV_MOCK_NTK=true ./bin/ntkVDI-server # Production NTV_QUIC_ADDR=0.0.0.0:7443 \ NTV_MAX_SESSIONS=100 \ NTV_TARGET_LATENCY_MS=50 \ ./bin/ntkVDI-server
1 — Prerequisites
# Homebrew brew install go pkg-config # FFmpeg dependencies brew install ffmpeg nasm yasm
2 — Build FFmpeg
./scripts/build-ffmpeg.sh --prefix /opt/ntkvdi/ffmpeg export PKG_CONFIG_PATH=/opt/ntkvdi/ffmpeg/lib/pkgconfig:$PKG_CONFIG_PATH
3 — Build and run server
make server # Dev mode (ScreenCaptureKit — requires macOS 13+) NTV_MOCK_NTK=true ./bin/ntkVDI-server # Grant screen recording permission when prompted
1 — Prerequisites
# Install Go 1.24+ from https://go.dev/dl/
# Install MSYS2 for CGo (https://www.msys2.org/)
# In MSYS2 MINGW64 shell:
pacman -S mingw-w64-x86_64-go mingw-w64-x86_64-pkg-config \
mingw-w64-x86_64-ffmpeg
2 — Build and run server
# In MSYS2 MINGW64: make server # Output: bin/ntkVDI-server.exe # Dev mode (DXGI capture — Windows 8+) NTV_MOCK_NTK=true .\bin\ntkVDI-server.exe # Run as Administrator for DXGI Desktop Duplication
Client
make client # Builds client/native/ Go bridge, then Flutter app # Output: client/build/
host:7443 in the client. On first connect the server auto-generates a self-signed TLS certificate.
NTV_MOCK_NTK=true), any credentials are accepted. For production, configure the NTK access point with real Kyber/Dilithium keys.
Configuration
All configuration is via environment variables. No config files. The server validates required vars at startup and fails fast with a clear error.
| Variable | Default | Notes |
|---|---|---|
NTV_MOCK_NTK |
false |
Set true for all local dev / tests. Real NTK (Kyber/Dilithium) is skipped. |
NTV_QUIC_ADDR |
0.0.0.0:7443 |
QUIC listen address. Ensure UDP 7443 is open in firewall. |
NTV_ENCODER_PRESET |
ultrafast |
x264 encode preset. Trade CPU for quality: ultrafast → medium → slow. |
NTV_TARGET_LATENCY_MS |
50 |
p95 target for adaptive quality manager. RTT above this triggers quality reduction. |
NTV_MAX_SESSIONS |
100 |
Concurrent session cap (server-wide). Returns error on exceed. |
NTV_MAX_SESSIONS_PER_USER |
3 |
Per-authenticated-user session cap. |
NTV_SESSION_TIMEOUT_SEC |
28800 |
Idle timeout before graceful SESSION_CLOSE (8 hours default). |
NTV_DISPLAY_INDEX |
0 |
0-based display index for capture. Multi-monitor: set 1, 2, etc. |
NTV_KEEPALIVE_INTERVAL_S |
30 |
Control stream KEEPALIVE send interval (seconds). |
NTV_KEEPALIVE_TIMEOUT_S |
60 |
Disconnect threshold when KEEPALIVE replies go stale. |
Protocol Reference
| Stream ID | Direction | Purpose |
|---|---|---|
0 |
Bidirectional | Control — auth handshake, keepalive, RTT feedback, session close |
2 |
Server → Client | H.264 video frames |
4 |
Server → Client | Audio (stub, reserved) |
6 |
Bidirectional | Input events — mouse, keyboard from client to server |
Control Frame Format (8-byte header)
[version:1][msgType:1][flags:2][payloadLen:4] + payload protocolVersion = 1
Testing
All tests (lint + unit + integration + Flutter)
NTV_MOCK_NTK=true make test
Go unit tests only (race detector + coverage)
make test-server # go test -race -count=1 -timeout 120s ./server/... ./pkg/...
Single test
go test -run TestControlFrame_RoundTrip ./server/internal/quic/
Real NTK (Kyber-1024/Dilithium5) is not wired in the test environment. Tests fail without mock mode. Coverage gate: 80% enforced by scripts/test-all.sh.