Files
rdp-proxy/docs/architecture/RDP_SERVICE_CSHARP_TARGET.md
T
2026-05-18 21:33:39 +03:00

10 KiB

RDP Service C# Target Architecture

Archived scope note: this document is retained as historical RDP runtime research and is not the current source of truth for node-to-node transport. Fabric transport is now QUIC-only between nodes; use docs/architecture/DISTRIBUTED_FABRIC_NODE_PROTOCOL_PLAN.md, docs/architecture/FABRIC_FIRST_TRANSPORT_AND_STRESS_PLAN.md, and docs/architecture/SECURE_ACCESS_FABRIC_TARGET.md for the active transport model.

Status

Superseded.

The active direction is now documented in:

  • docs/architecture/RDP_SERVICE_CPP_PERFORMANCE_TARGET.md

The C++ worker remains the primary RDP runtime. This C# document is retained only as historical/research context and must not be used for implementation unless explicitly re-approved.

Problem Statement

The current RDP MVP proved the platform lifecycle, but its rendering model is not production-grade:

  • FreeRDP connects to the target RDP server.
  • The worker reads the GDI framebuffer.
  • The worker publishes full or cropped BGRA frames through RAP direct WSS.
  • The Windows client renders those frames as a custom viewer.

This is not how high-performance RDP clients work. On a fast LAN, the network is not the main bottleneck. The bottleneck is that the service is repeatedly copying and publishing screen images instead of consuming the RDP graphics protocol as a graphics protocol.

Observable symptoms:

  • delayed visual feedback after input
  • unreliable first-click behavior
  • poor hover behavior
  • high CPU/memory pressure from framebuffer copies
  • unnecessary 1280x720 BGRA full-frame payloads
  • fragile coupling between input, render snapshots, and UI timing

External Reference Model

Microsoft RDP performance is based on graphics protocol features rather than screen scraping:

  • RDP Graphics Pipeline Extension (MS-RDPEGFX) uses a dynamic virtual channel for graphics pipeline updates.
  • RDP supports adaptive graphics, delta detection, caching, mixed-mode encoding, RemoteFX Progressive, H.264/AVC, AVC444, and HEVC in modern environments.
  • FreeRDP documentation describes the RDP GFX Pipeline (rdpgfx) and codecs such as RemoteFX Progressive, H.264 AVC420/AVC444, ClearCodec, and ZGFX.

References:

Target Decision

Replace the internal RDP engine with a C# implementation owned by this project.

The new service:

  • is a RAP RDP service adapter, not a generic local RDP client UI
  • speaks standard RDP to the target Windows RDP server
  • keeps RDP protocol details inside the RDP service boundary
  • preserves current backend and cluster data-plane contracts
  • does not use FreeRDP as the runtime RDP engine
  • does not require the local Windows desktop client to become mstsc

The local Windows client remains a RAP client. It receives RAP display/input/ clipboard/file messages over the existing direct worker WSS data-plane.

What Must Not Change

The following are outside this rewrite:

  • backend organization/auth/session lifecycle
  • PostgreSQL source-of-truth model
  • Redis live coordination model
  • worker registration and lease semantics
  • data_plane_token model
  • direct_worker_wss transport contract
  • backend gateway fallback
  • clipboard/file policy semantics
  • file upload policy semantics
  • session attach/detach/reattach/takeover/terminate semantics

Only the RDP service adapter internals change.

Service Boundary

flowchart LR
    Client["Windows RAP Client"]
    Backend["Backend Control Plane"]
    Worker["RDP Service Node"]
    Engine["C# RDP Protocol Engine"]
    Target["Target Windows RDP Server"]

    Client <--> |"direct_worker_wss RAP channels"| Worker
    Backend <--> |"assignments, leases, audit"| Worker
    Worker --> Engine
    Engine <--> |"standard RDP"| Target

The RDP service owns:

  • RDP negotiation and transport
  • NLA/CredSSP/TLS integration
  • input translation to RDP fast-path input
  • graphics channel parsing
  • virtual channel handling for clipboard and future file features
  • conversion from RDP graphics units to RAP render messages
  • session runtime ownership and reconnect/takeover binding

The data-plane layer owns:

  • data_plane_token validation
  • direct WSS connection binding
  • logical channel priority
  • reliable/droppable semantics
  • fallback compatibility

New RDP Service Components

Rap.Rdp.Service

Host process.

Responsibilities:

  • load worker/RDP service configuration
  • register worker capabilities with existing coordination layer later
  • expose the existing direct WSS endpoint later
  • create and supervise RDP sessions
  • keep the current C++ worker active until cutover

Rap.Rdp.Core

Pure C# protocol and runtime boundaries.

Responsibilities:

  • define RDP session lifecycle interfaces
  • define protocol engine interfaces
  • define graphics/input/clipboard/file abstractions
  • avoid any dependency on WPF or backend repositories

Rap.Rdp.Protocol

Future implementation module.

Responsibilities:

  • implement RDP connection sequence from Microsoft Open Specifications
  • implement security/NLA/CredSSP/TLS
  • implement core channels and fast-path input
  • implement graphics pipeline negotiation
  • implement virtual channel framing

This module must not depend on the Windows desktop UI.

Rap.Rdp.DataPlane

Future adapter module.

Responsibilities:

  • map RAP direct WSS JSON/binary envelopes to the protocol engine
  • keep input highest priority
  • keep render latest-frame or latest-update droppable
  • keep clipboard/file reliable and policy-gated

Graphics Strategy

The new render path must not use framebuffer screen scraping as the primary production path.

Priority order:

  1. RDPGFX graphics pipeline channel.
  2. Surface/dirty-region updates.
  3. Encoded graphics payloads where available.
  4. Raw bitmap fallback only for compatibility/debug.

Target RAP render message classes:

  • surface.create
  • surface.delete
  • surface.map
  • surface.region
  • surface.codec_frame
  • cursor.update
  • frame.ack

The first usable implementation may still decode some graphics to BGRA, but only as a controlled fallback. It must not become the permanent production model.

Input Strategy

Input must be independent from render.

Rules:

  • mouse down/up, wheel, and keyboard down/up are reliable and ordered
  • pointer move is coalesced latest-only
  • pointer position is explicitly sent before button-down when needed
  • input never waits behind render
  • no UI focus event may be inserted into the same ordered sequence in a way that consumes the first remote click

The current double-click regression is treated as a bug caused by the RAP-side focus/input sequencing, not as a normal RDP behavior.

Clipboard And File Strategy

Existing policy semantics remain:

  • clipboard modes stay enforced in backend, gateway/data-plane, and RDP service
  • file transfer modes stay enforced in backend, gateway/data-plane, and RDP service
  • text clipboard maps to RDP clipboard virtual channel
  • restricted drive visibility remains a separate policy-controlled feature

The C# rewrite must not expand clipboard/file scope while replacing render/input.

Staged Migration Plan

RDP-C#-0: Documentation And Skeleton

Create a buildable C# RDP service skeleton with interfaces only.

No runtime cutover.

RDP-C#-1: Control-Plane Compatible Worker Shell

Implement worker registration, heartbeats, lease renewal, assignment consumption, and direct WSS token validation in C# using existing contracts.

The C++ worker remains default.

RDP-C#-2: RDP Handshake Probe

Implement a non-viewing RDP connection probe:

  • TCP/TLS
  • basic RDP negotiation
  • NLA/CredSSP if required
  • connect/disconnect lifecycle
  • failure reporting

No rendering yet.

RDP-C#-3: Input-Only Protocol Path

After a connected session, send fast-path keyboard/mouse input to the RDP server.

Use diagnostic-only graphics or no graphics.

RDP-C#-4: Basic Graphics Protocol Path

Implement the simplest RDP graphics path needed to display a desktop without FreeRDP.

Allowed as temporary fallback:

  • raw bitmap updates
  • dirty-region bitmap updates

Not acceptable as final production:

  • repeated full-frame screenshot capture

RDP-C#-5: RDPGFX Foundation

Implement RDPGFX channel negotiation and surface update handling.

RDP-C#-6: Codec Path

Implement or relay supported encoded graphics modes:

  • RemoteFX Progressive where practical
  • H.264/AVC420/AVC444 where negotiated
  • client-side decode through platform APIs where possible

RDP-C#-7: Runtime Cutover

Enable the C# RDP service per worker/resource via feature flag.

Rollback must switch back to the current C++ worker without changing backend contracts.

Performance Requirements

Target for LAN:

  • first frame under 2 seconds after successful RDP login
  • click to visible response under 150 ms for normal UI
  • keypress to visible response under 150 ms for text input
  • pointer hover response under 100 ms where the target OS emits hover changes
  • no unbounded frame queue
  • no render work on UI thread except final apply
  • no full-frame publish loop for static desktops

Risks

  • Implementing RDP from specs is substantial.
  • NLA/CredSSP correctness is security-sensitive.
  • Graphics codecs are complex.
  • Some target servers may negotiate older bitmap paths.
  • AVC/AVC444 decode support differs by client platform.
  • A partial RDP engine must not be switched into production before smoke proof.

Proceed with RDP-C#-0 only.

Goal: Create a buildable C# RDP service skeleton and protocol boundaries, without switching runtime traffic away from the current worker.

Strict rules:

  • do not change backend contracts
  • do not change cluster transport
  • do not remove C++ worker
  • do not use FreeRDP in the new C# service
  • do not use third-party RDP libraries
  • do not claim the C# engine is runtime-ready

Deliver:

  • buildable workers/rdp-service-csharp
  • interfaces for protocol engine, data-plane bridge, graphics sink, input source
  • README with migration stages
  • docs update marking current C++/FreeRDP path as legacy MVP runtime