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; }
}
}