Files
cc-ci/tests/rustdesk-server/PARITY.md
T

3.8 KiB

Parity — rustdesk-server

The recipe-maintainer corpus has no recipe-info/rustdesk-server/ directory — rustdesk-server was never in the recipe-maintainer parity suite (this is a fresh 2026-10-30 enrollment, requested as a one-off upgrade of a single-client production deploy). So this PARITY.md documents the cc-ci-authored suite as the baseline.

The recipe's shape (why the suite looks like this)

rustdesk-server is not HTTP-native: hbbs (rendezvous/registry) + hbbr (relay) speak a hand-rolled protobuf protocol on host-published ports 21115-21119 (21116 also UDP; 21118/21119 websocket). There is no web UI, no HTTP API, and the stock compose's traefik labels point at a port nothing serves. The cc-ci ENVIRONMENTAL overlay (compose.ccci.yml) adds a status sidecar serving exactly hbbs's generated identity PUBLIC key at /id_ed25519.pub — the HTTP health gate then asserts real app state (the key only exists because hbbs minted it), never a canned page. The private key + sqlite peer DB in the same volume are asserted UNREACHABLE. Host ports need no overlay: the stock compose publishes them mode:host, so protocol tests run on-host at 127.0.0.1 (the mumble pattern).

Recipe-specific tests (≥2 beyond the generic floor)

cc-ci file what's verified why non-vacuous
custom/test_rendezvous_ports.py All 5 stock host-published ports (21115-21119) accept TCP; 21116 holds 3 consecutive connects. The recipe's entire function is serving those ports; a missing/failed bind (e.g. a compose port change upstream) is named per-port.
custom/test_websocket_handshake.py Real 101 Switching Protocols upgrades on hbbs :21118 and hbbr :21119. These are the app's own client-facing protocol listeners; a bare GET gets NO response, so 101 can only come from a genuinely serving hbbs/hbbr (verified live on 1.1.12 and 1.1.16).
custom/test_identity_key.py hbbs's ed25519 identity key read back through THREE independent surfaces — HTTPS status route (via Traefik), hbbs's own Key: startup log, the volume file — asserted EQUAL and a valid 32-byte key; plus the private key / DB paths asserted 404. The §4.3-style round-trip of the recipe's defining generated state; a crash-looping hbbs (no key) fails, a routing gap fails, a surface mismatch (reading another deployment) fails, and an exposure fails.
custom/test_punch_hole_no_amplification.py An unauthenticated UDP punch-hole request gets NO response on ≥1.1.16, while a register_peer sanity probe on the same socket IS answered. The security regression test for the 1.1.16 amplification fix (GH #670, actively exploited pre-fix even with -k _). The in-test register_peer response proves silence means "fixed", not "broken"; a regression turns this RED naming the layer. Empirical baseline recorded in the file (1.1.12 answered 3/3, 1.1.16 silent 3/3). Version-gated per STYLE §2.

Backup data-integrity

The stock recipe declares no backupbot.backup labels — backup/restore tiers auto-skip (intentional non-run, auto-detected). Nothing here weakens that; if the recipe grows backup support upstream, the generic tiers pick it up automatically.

Deferred

  • A full register_pk/punch-hole EXCHANGE between two registered peers (proving the happy-path connection flow, not just the refusal) — needs the client-side crypto handshake (signed pk registration); the register_peer + ws + punch-hole surfaces already cover the server's externally observable behavior. Revisit if upstream changes the rendezvous flow.
  • Host-port contention: 21115-21119 are unique to this recipe, but two CONCURRENT rustdesk deploys (e.g. a cold run racing a canonical reattach) would collide on them — same exposure mailu (25/465/587) and lasuite-meet (7882) already carry as host-port recipes. Cadence makes a collision rare; a failed deploy is loud and re-runnable.