Streamer Service
The Streamer is the per-session workhorse: one instance of webstream.exe (no flag — this is the default mode) runs inside each pooled RDP session, captures the Windows application, relays input, streams audio, and enforces the workspace policy at the point where the user actually interacts with files, printing, and the clipboard.
Function
- Screen capture and encoding. Captures dirty regions of the application window or desktop, PNG-encodes them (with region CRC caching and adaptive frame rates), and streams them over the
/captureWebSocket. - Input relay. Receives mouse, keyboard, and resize events over
/controland replays them into the session. - Audio. Captures session audio (per-app, per-session, or host loopback) and streams Opus packets over
/audio. - Policy enforcement. Applies the workspace policy delivered via the gateway's
X-Policy-JSONheader — file dialog interception, print-to-PDF, clipboard rules, child-process control, network isolation, and session recording. - Telemetry. Aggregates performance counters and user activity locally and POSTs them to the Metrics Engine on a fixed cadence — the streamer never touches a database directly.
Ports and endpoints
Ports are derived from the pool account: basePortNumber + userNumber × 10. With the default base of 9000, webs01 listens on 9010, webs02 on 9020, and so on.
| Listener | Port (webs01 example) | Routes |
|---|---|---|
| WebSocket | 9010 | /capture (frames + input), /control (session control, clipboard, resize), /audio (Opus stream) |
| HTTP | 9011 | Session-scoped HTTP (file transfer endpoints, dialog assets) |
The streamer serves plain WS/HTTP; browsers reach it only through the Gateway proxy, which terminates SSL and holds the session affinity.
Message contracts
Local — to the Session Manager (startup)
| Message | Direction | When |
|---|---|---|
REQUEST_SESSION_INFO:{username} | Streamer → Session Manager (/control WebSocket) | Startup, to obtain its session identity. |
SESSION_INFO:{sessionUuid}|{commandLineParams} | Session Manager → Streamer | Reply; the streamer then binds its ports and begins capture. |
Outbound — to the Metrics Engine
| Endpoint | Method | Frequency | Payload |
|---|---|---|---|
/metrics/streamer/flush | POST | Every 10, 30, or 60 s (metrics.server.flushIntervalSeconds) | { nodeId, hostName, environment, region, userName, sessionId, tsUtc, intervalSecs, summary{…}, events[], instanceType, processorCount, totalMemoryGB } plus a one-time sessionContext block carrying { iamUsername, iamUserId, orgId, workspaceId, workspaceName, workspaceType }. |
/activity/log | POST | Session, file, clipboard, and print events: immediate. Input activity: batched every 10 s (activityLogging.batchIntervalSeconds). | { type, nodeId, sessionId, username, timestamp, iamUsername?, workspaceName?, data } where type is session, input, clipboard, etc. |
Both POSTs are asynchronous fire-and-forget with a 5-second timeout — telemetry never blocks the capture loop, and a down Metrics Engine does not affect streaming.
Outbound — to the Gateway (licence registry)
| Endpoint | Method | Frequency | Payload / response |
|---|---|---|---|
/gateway/license/streamer-checkin | POST | Startup, then every 15 s | { streamerGuid } → 200 OK, 403 (licence invalid), or 409 (session cap reached) |
/gateway/license/streamer-checkout | POST | Logoff / exit | { streamerGuid } → { success, activeStreamers } |
Configuration
The Streamer consumes the largest share of app.config.xml:
| Section / property | Purpose |
|---|---|
webstreamSettings.streamerMode | performance (fps priority), balanced, or lean (optimal for large regions). |
webstreamSettings.remoteCompressionLevel | PNG compression: 0 fastest (default), 1 optimal, 2 smallest. |
webstreamSettings.remoteMaxDesktopWidth / Height | Upper bound on session resolution (default 3840×2160). |
webstreamSettings.elevateStreamerPriority | Run at High priority for improved latency on dedicated streaming servers. |
webstreamSettings.appRemoteStartExe / appRemoteAppMode / appRemoteStartArgs | The application to project, with {SESSION_ID} and {USERNAME} placeholders. The special value remotedesktop activates full desktop mode. |
webstreamSettings.remoteDesktopSettings | Desktop features, session termination (maxSessionDurationMinutes, idleTimeoutMinutes), process allow/block lists, audio capture mode, isolation tiers (hklmShadow, identityHooks, jobContainment, alternateShell). |
activityDetection | Adaptive frame rate: frameRateTarget during activity, frameRateIdle when idle, with activityThresholdKB and activityIdleDelaySeconds hysteresis. |
regionCrcCache, regionOptimizer | Bandwidth optimisation: CRC references (~50 bytes) instead of re-sent regions; edge-scan region tightening. |
trust, headlessDialogs, fileSharing, printing | Trust level, file dialog interception, virtual shares, and print-to-PDF tunables. |
metrics | Telemetry mode (server recommended), server.baseUrl, flushIntervalSeconds, optional process metrics, node identity. |
activityLogging | Audit trail POST target (serverUrl), batching, and timeout. |
sessionRecording | Recording enable, storage path, format, retention. Policy hierarchy: user setting > workspace policy > organization default > this global config. |
Aligned settings on peer services
metrics.server.baseUrlandactivityLogging.serverUrlmust both point at the Metrics Engine (metricsEngine.httpPort, default 9009).gateway.backend.gatewayUrl(shared with the session host) is where licence check-ins go; if unset, the streamer runs without a gateway registry.- Workspace policies set in the Admin Console override many of these globals per session via the
X-Policy-JSONcontract.