Files
guru-connect/proto/guruconnect.proto
Mike Swanson f9bdecbfdb
All checks were successful
Build and Test / Build Agent (Windows) (push) Successful in 6m57s
Build and Test / Build Server (Linux) (push) Successful in 10m23s
Build and Test / Security Audit (push) Successful in 4m15s
Build and Test / Build Summary (push) Successful in 9s
feat(agent,server): v2 secure-session-core Task 7 - HW H.264 + negotiated raw fallback
SPEC-002 Phase 1 Task 7 (the last), code-reviewed APPROVED, locally verified
(cargo fmt + clippy -D warnings exit 0 + cargo test --workspace 89 pass + build).

- Encoder trait + factory: RawEncoder (salvaged, UNCHANGED) and H264Encoder,
  selected by negotiation; factory falls back to raw on H.264 init failure.
- Negotiation: agent advertises supports_h264 (MFTEnumEx HW probe, cached) in
  AgentStatus; server picks the codec via select_video_codec(supports, prefer)
  and stamps StartStream.video_codec; agent re-guards on local HW. Policy
  constant DEFAULT_PREFER_H264 = false, so RAW is negotiated for every session
  today - H.264 stays dormant until live hardware validation (Task 8).
- MF H.264 encoder (h264.rs, FIRST-CUT / compile-verified-only): HW encoder MFT,
  BGRA->NV12 (color.rs, unit-tested), sync drain, fall-back-to-raw on any failure.
- Viewer H.264 decoder (decoder.rs, FIRST-CUT): MF decoder on a dedicated COM
  thread; drops+logs on failure, raw render path untouched.
- proto additive: VideoCodec enum, StartStream.video_codec=3,
  SessionResponse.video_codec=5, AgentStatus.supports_h264=11.
- Raw+Zstd path byte-for-byte unchanged; remains the guaranteed default/fallback.

Review confirmed unsafe impl Send for H264Encoder is sound (single-owned &mut on
the block_on thread; session future never spawned) and every MF failure degrades
to raw. H.264 is NOT claimed functional - compile/clippy/build-verified only;
live validation + force-IDR + the no-spawn-invariant doc are Task 8 go-live gates.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 10:35:04 -07:00

454 lines
14 KiB
Protocol Buffer

syntax = "proto3";
package guruconnect;
// ============================================================================
// Session Management
// ============================================================================
message SessionRequest {
string agent_id = 1;
string session_token = 2;
SessionType session_type = 3;
string client_version = 4;
}
message SessionResponse {
bool success = 1;
string session_id = 2;
string error = 3;
DisplayInfo display_info = 4;
// Negotiated video codec for this session (Task 7). The plan models the
// codec selection here; however the LIVE server->agent handshake in v2 is
// done over WebSocket query params + StartStream (SessionRequest/
// SessionResponse are not exchanged on the wire today), so the codec the
// agent actually applies is carried on StartStream.video_codec below. This
// field is kept for spec parity / future use; raw is the safe default.
VideoCodec video_codec = 5;
}
// Negotiated video codec (Task 7). RAW = salvaged BGRA+Zstd+dirty-rects, the
// guaranteed fallback and current default. H264 = hardware Media Foundation
// encode, the negotiated upgrade (compile-verified; validated on hardware in
// plan Task 8). H265/HEVC is future opt-in (TODO).
enum VideoCodec {
VIDEO_CODEC_RAW = 0; // Raw BGRA + Zstd (safe default / fallback)
VIDEO_CODEC_H264 = 1; // Hardware H.264 via Media Foundation
VIDEO_CODEC_H265 = 2; // Future / opt-in (not implemented)
}
enum SessionType {
SCREEN_CONTROL = 0;
VIEW_ONLY = 1;
BACKSTAGE = 2;
FILE_TRANSFER = 3;
}
// ============================================================================
// Display Information
// ============================================================================
message DisplayInfo {
repeated Display displays = 1;
int32 primary_display = 2;
}
message Display {
int32 id = 1;
string name = 2;
int32 x = 3;
int32 y = 4;
int32 width = 5;
int32 height = 6;
bool is_primary = 7;
}
message SwitchDisplay {
int32 display_id = 1;
}
// ============================================================================
// Video Frames
// ============================================================================
message VideoFrame {
int64 timestamp = 1;
int32 display_id = 2;
int32 sequence = 3;
oneof encoding {
RawFrame raw = 10;
EncodedFrame vp9 = 11;
EncodedFrame h264 = 12;
EncodedFrame h265 = 13;
}
}
message RawFrame {
int32 width = 1;
int32 height = 2;
bytes data = 3; // Zstd compressed BGRA
bool compressed = 4;
repeated DirtyRect dirty_rects = 5;
bool is_keyframe = 6; // Full frame vs incremental
}
message DirtyRect {
int32 x = 1;
int32 y = 2;
int32 width = 3;
int32 height = 4;
}
message EncodedFrame {
bytes data = 1;
bool keyframe = 2;
int64 pts = 3;
int64 dts = 4;
}
message VideoAck {
int32 sequence = 1;
int64 timestamp = 2;
}
// ============================================================================
// Cursor
// ============================================================================
message CursorShape {
uint64 id = 1;
int32 hotspot_x = 2;
int32 hotspot_y = 3;
int32 width = 4;
int32 height = 5;
bytes data = 6; // BGRA bitmap
}
message CursorPosition {
int32 x = 1;
int32 y = 2;
bool visible = 3;
}
// ============================================================================
// Input Events
// ============================================================================
message MouseEvent {
int32 x = 1;
int32 y = 2;
MouseButtons buttons = 3;
int32 wheel_delta_x = 4;
int32 wheel_delta_y = 5;
MouseEventType event_type = 6;
}
enum MouseEventType {
MOUSE_MOVE = 0;
MOUSE_DOWN = 1;
MOUSE_UP = 2;
MOUSE_WHEEL = 3;
}
message MouseButtons {
bool left = 1;
bool right = 2;
bool middle = 3;
bool x1 = 4;
bool x2 = 5;
}
message KeyEvent {
bool down = 1; // true = key down, false = key up
KeyEventType key_type = 2;
uint32 vk_code = 3; // Virtual key code (Windows VK_*)
uint32 scan_code = 4; // Hardware scan code
string unicode = 5; // Unicode character (for text input)
Modifiers modifiers = 6;
// True when the originating key is an "extended" key (right Ctrl/Alt, arrows,
// Insert/Home/End/PageUp/Down, NumLock, Win keys, numpad Divide). The viewer
// captures this from the low-level hook (KBDLLHOOKSTRUCT.flags & LLKHF_EXTENDED);
// the agent injects with KEYEVENTF_EXTENDEDKEY when set. Field added in Task 6
// (v2 full key fidelity); older agents that ignore it fall back to deriving the
// flag from vk_code/scan_code.
bool is_extended = 7;
}
enum KeyEventType {
KEY_VK = 0; // Virtual key code
KEY_SCAN = 1; // Scan code
KEY_UNICODE = 2; // Unicode character
}
message Modifiers {
bool ctrl = 1;
bool alt = 2;
bool shift = 3;
bool meta = 4; // Windows key
bool caps_lock = 5;
bool num_lock = 6;
}
message SpecialKeyEvent {
SpecialKey key = 1;
}
enum SpecialKey {
CTRL_ALT_DEL = 0;
LOCK_SCREEN = 1;
PRINT_SCREEN = 2;
}
// ============================================================================
// Clipboard
// ============================================================================
message ClipboardData {
ClipboardFormat format = 1;
bytes data = 2;
string mime_type = 3;
}
enum ClipboardFormat {
CLIPBOARD_TEXT = 0;
CLIPBOARD_HTML = 1;
CLIPBOARD_RTF = 2;
CLIPBOARD_IMAGE = 3;
CLIPBOARD_FILES = 4;
}
message ClipboardRequest {
// Request current clipboard content
}
// ============================================================================
// Quality Control
// ============================================================================
message QualitySettings {
QualityPreset preset = 1;
int32 custom_fps = 2; // 1-60
int32 custom_bitrate = 3; // kbps
CodecPreference codec = 4;
}
enum QualityPreset {
QUALITY_AUTO = 0;
QUALITY_LOW = 1; // Low bandwidth
QUALITY_BALANCED = 2;
QUALITY_HIGH = 3; // Best quality
}
enum CodecPreference {
CODEC_AUTO = 0;
CODEC_RAW = 1; // Raw + Zstd (LAN)
CODEC_VP9 = 2;
CODEC_H264 = 3;
CODEC_H265 = 4;
}
message LatencyReport {
int64 rtt_ms = 1;
int32 fps = 2;
int32 bitrate_kbps = 3;
}
// ============================================================================
// Chat Messages
// ============================================================================
message ChatMessage {
string id = 1; // Unique message ID
string sender = 2; // "technician" or "client"
string content = 3; // Message text
int64 timestamp = 4; // Unix timestamp
}
// ============================================================================
// Control Messages
// ============================================================================
message Heartbeat {
int64 timestamp = 1;
}
message HeartbeatAck {
int64 client_timestamp = 1;
int64 server_timestamp = 2;
}
message Disconnect {
string reason = 1;
}
// Server commands agent to start streaming video
message StartStream {
string viewer_id = 1; // ID of viewer requesting stream
int32 display_id = 2; // Which display to stream (0 = primary)
// Negotiated codec for this stream (Task 7). The server selects this from
// the agent's advertised capability (AgentStatus.supports_h264) and its
// policy (DEFAULT_PREFER_H264, currently false so we never ship unvalidated
// H.264 as the default). The agent builds its encoder from this value;
// VIDEO_CODEC_RAW (0, the default for older servers) keeps the salvaged
// raw+Zstd path. On any H.264 init failure the agent falls back to raw.
VideoCodec video_codec = 3;
}
// Server commands agent to stop streaming
message StopStream {
string viewer_id = 1; // Which viewer disconnected
}
// Agent reports its status periodically when idle
message AgentStatus {
string hostname = 1;
string os_version = 2;
bool is_elevated = 3;
int64 uptime_secs = 4;
int32 display_count = 5;
bool is_streaming = 6;
string agent_version = 7; // Agent version (e.g., "0.1.0-abc123")
string organization = 8; // Company/organization name
string site = 9; // Site/location name
repeated string tags = 10; // Tags for categorization
// HW-encode capability (Task 7). True when the agent enumerated a HARDWARE
// H.264 encoder MFT at startup (MFTEnumEx, MFT_CATEGORY_VIDEO_ENCODER,
// MFVideoFormat_H264, hardware flag). The server uses this for codec
// negotiation (see StartStream.video_codec). Detected once and cached;
// false on non-Windows / no HW encoder / MF unavailable.
bool supports_h264 = 11;
}
// Server commands agent to uninstall itself
message AdminCommand {
AdminCommandType command = 1;
string reason = 2; // Why the command was issued
}
enum AdminCommandType {
ADMIN_UNINSTALL = 0; // Uninstall agent and remove from startup
ADMIN_RESTART = 1; // Restart the agent process
ADMIN_UPDATE = 2; // Download and install update
}
// ============================================================================
// Attended-mode Consent (Task 5)
// ============================================================================
//
// For an ATTENDED (support-code) session, the end user on the managed machine
// must explicitly accept a consent prompt before the technician's session goes
// live. The server holds the session in `consent_state = pending` and does NOT
// surface it to the technician (no viewer join / no streaming) until the agent
// returns a ConsentResponse with granted = true. A denial or timeout tears the
// session down. Managed/unattended sessions are `not_required` and never see a
// ConsentRequest (Phase-1 default policy).
// Server/relay -> Agent: ask the end user to allow this support session.
message ConsentRequest {
string session_id = 1; // Session awaiting consent (UUID string)
string technician_name = 2; // Display name of the requesting technician
ConsentAccessMode access_mode = 3; // View vs. control, so the prompt is honest
int32 timeout_secs = 4; // How long the agent should wait before auto-deny
}
// Agent -> Server: the end user's decision.
message ConsentResponse {
string session_id = 1; // Echoes the ConsentRequest session_id
bool granted = 2; // true = allow, false = deny (incl. dialog closed)
string reason = 3; // Optional human-readable reason (e.g. "timeout")
}
// Whether the technician is requesting view-only or full control. Mirrors the
// viewer access mode the server already enforces; used only to phrase the prompt.
enum ConsentAccessMode {
CONSENT_VIEW = 0; // Technician will VIEW the screen
CONSENT_CONTROL = 1; // Technician will VIEW and CONTROL
}
// ============================================================================
// Auto-Update Messages
// ============================================================================
// Update command details (sent with AdminCommand or standalone)
message UpdateInfo {
string version = 1; // Target version (e.g., "0.2.0")
string download_url = 2; // HTTPS URL to download new binary
string checksum_sha256 = 3; // SHA-256 hash for verification
bool mandatory = 4; // If true, agent must update immediately
}
// Update status report (agent -> server)
message UpdateStatus {
string current_version = 1; // Current running version
UpdateState state = 2; // Current update state
string error_message = 3; // Error details if state is FAILED
int32 progress_percent = 4; // Download progress (0-100)
}
enum UpdateState {
UPDATE_IDLE = 0; // No update in progress
UPDATE_CHECKING = 1; // Checking for updates
UPDATE_DOWNLOADING = 2; // Downloading new binary
UPDATE_VERIFYING = 3; // Verifying checksum
UPDATE_INSTALLING = 4; // Installing (rename/copy)
UPDATE_RESTARTING = 5; // About to restart
UPDATE_COMPLETE = 6; // Update successful (after restart)
UPDATE_FAILED = 7; // Update failed
}
// ============================================================================
// Top-Level Message Wrapper
// ============================================================================
message Message {
oneof payload {
// Session
SessionRequest session_request = 1;
SessionResponse session_response = 2;
// Video
VideoFrame video_frame = 10;
VideoAck video_ack = 11;
SwitchDisplay switch_display = 12;
// Cursor
CursorShape cursor_shape = 15;
CursorPosition cursor_position = 16;
// Input
MouseEvent mouse_event = 20;
KeyEvent key_event = 21;
SpecialKeyEvent special_key = 22;
// Clipboard
ClipboardData clipboard_data = 30;
ClipboardRequest clipboard_request = 31;
// Quality
QualitySettings quality_settings = 40;
LatencyReport latency_report = 41;
// Control
Heartbeat heartbeat = 50;
HeartbeatAck heartbeat_ack = 51;
Disconnect disconnect = 52;
StartStream start_stream = 53;
StopStream stop_stream = 54;
AgentStatus agent_status = 55;
// Chat
ChatMessage chat_message = 60;
// Admin commands (server -> agent)
AdminCommand admin_command = 70;
// Auto-update messages
UpdateInfo update_info = 75; // Server -> Agent: update available
UpdateStatus update_status = 76; // Agent -> Server: update progress
// Attended-mode consent (Task 5)
ConsentRequest consent_request = 80; // Server/relay -> Agent: prompt end user
ConsentResponse consent_response = 81; // Agent -> Server: end user's decision
}
}