using System; namespace VanadiumGuard.Protocol { /// POST /v1/session/open. Authenticated with the player's normal game token. public sealed class SessionOpenRequest { public int ProtocolVersion { get; set; } = ProtocolConstants.Version; /// Opaque player id from the game's own auth system. public string PlayerId { get; set; } = string.Empty; /// Game build identifier. Selects the reference images used for challenges. public string GameBuild { get; set; } = string.Empty; /// Assembly version of the guard client, so old builds can be refused. public string GuardVersion { get; set; } = string.Empty; /// Windows build string, for triaging platform-specific false positives. public string Platform { get; set; } = string.Empty; /// /// Correlates this guard session with the match/room session the game server knows /// about, so the server can tell "guard went quiet" from "player quit". /// public string GameSessionId { get; set; } = string.Empty; /// /// Hashed machine identity, used for hardware bans. Absent when the client could /// not collect one; the server decides what an unidentifiable machine is worth /// rather than the client deciding for it. /// public MachineFingerprint? Machine { get; set; } /// /// Device key and proof of possession. Null when the machine has no usable key, which /// is a supported state: the session then relies on the HMAC alone, exactly as before. /// public DeviceAttestation? Attestation { get; set; } } public sealed class SessionOpenResponse { public string SessionId { get; set; } = string.Empty; /// Base64 HMAC key for signing subsequent requests. Session-scoped. public string SessionKey { get; set; } = string.Empty; public GuardPolicy Policy { get; set; } = new GuardPolicy(); /// Server time, so a client with a skewed clock can still sign correctly. public DateTimeOffset ServerTimeUtc { get; set; } } /// POST /v1/session/report. Signed. Carries a batch of detections. public sealed class ReportRequest { public Detection[] Detections { get; set; } = Array.Empty(); /// Policy revision the client is running, so drift can be spotted. public string PolicyVersion { get; set; } = "0"; /// Detections dropped because the client queue was full. public int DroppedCount { get; set; } } public sealed class ReportResponse { public Verdict Verdict { get; set; } = new Verdict(); /// Number of detections the server accepted, for client-side log parity. public int Accepted { get; set; } } /// POST /v1/session/heartbeat. Signed. Liveness plus challenge answers. public sealed class HeartbeatRequest { /// Answers to challenges issued in previous responses. public ChallengeAnswer[] Answers { get; set; } = Array.Empty(); /// Milliseconds since the guard started. Monotonic; a reset means a reload. public long UptimeMs { get; set; } /// Detections currently queued but not yet reported. public int QueueDepth { get; set; } } public sealed class HeartbeatResponse { public Verdict Verdict { get; set; } = new Verdict(); /// Challenges to answer on the next heartbeat. public MemoryChallenge[] Challenges { get; set; } = Array.Empty(); /// Set when the server has a newer policy; the client re-opens its session. public string? NewPolicyVersion { get; set; } } /// /// GET /v1/internal/session/{gameSessionId}/status, called by the game server rather /// than the client. This is the join point that makes the whole thing worth running: /// gameplay authority asks the anti-cheat service what it thinks, instead of the /// client deciding its own fate. /// public sealed class GuardStatusResponse { public bool GuardPresent { get; set; } public bool GuardHealthy { get; set; } /// /// True when every request this session has made was co-signed by a TPM-backed key. /// Lets the gameplay authority treat an unattested session differently without this /// service deciding for it. /// public bool Attested { get; set; } /// Accumulated risk score, 0-100. public int RiskScore { get; set; } public DateTimeOffset? LastHeartbeatUtc { get; set; } /// Detection codes seen this session, most severe first. public int[] Codes { get; set; } = Array.Empty(); public VerdictAction RecommendedAction { get; set; } } }