syntax = "proto3";

package cinch.v1;

option go_package = "github.com/cinchcli/cinch-core/go/cinch/v1;cinchv1";

// AuthService handles account creation and device authentication.
service AuthService {
  // Login creates a new anonymous account and returns its credentials.
  // Mirrors POST /auth/login — no auth required.
  rpc Login(LoginRequest) returns (LoginResponse);

  // DeviceCodeStart initiates the device-code OAuth flow.
  // Mirrors POST /auth/device-code — no auth required.
  rpc DeviceCodeStart(DeviceCodeStartRequest) returns (DeviceCodeStartResponse);

  // DeviceCodePoll polls a device code for completion.
  // Mirrors GET /auth/device-code/poll — no auth required.
  rpc DeviceCodePoll(DeviceCodePollRequest) returns (DeviceCodePollResponse);

  // DeviceCodeComplete marks a device code as approved.
  // Mirrors POST /auth/device-code/complete — auth required.
  rpc DeviceCodeComplete(DeviceCodeCompleteRequest) returns (DeviceCodeCompleteResponse);

  // DeviceCodeDeny rejects a pending device-code from an already-signed-in
  // device. The remote CLI's next DeviceCodePoll returns status="denied".
  // Mirrors POST /auth/device-code/deny — auth required.
  rpc DeviceCodeDeny(DeviceCodeDenyRequest) returns (DeviceCodeDenyResponse);

  // RevokeDevice revokes a paired device by ID.
  // Mirrors POST /auth/device/revoke — auth required.
  rpc RevokeDevice(RevokeDeviceRequest) returns (RevokeDeviceResponse);

  // KeyBundlePut stores an encrypted key bundle for a target device.
  // Mirrors POST /auth/key-bundle — auth required.
  rpc KeyBundlePut(KeyBundlePutRequest) returns (KeyBundlePutResponse);

  // KeyBundleGet retrieves the encrypted key bundle for the calling device.
  // Mirrors GET /auth/key-bundle — auth required.
  rpc KeyBundleGet(KeyBundleGetRequest) returns (KeyBundleGetResponse);

  // KeyBundleRetry asks the relay to re-broadcast key_exchange_requested
  // for the calling device. Used by `cinch auth retry-key`.
  // Mirrors POST /auth/key-bundle/retry — auth required.
  rpc KeyBundleRetry(KeyBundleRetryRequest) returns (KeyBundleRetryResponse);

  // RegisterDevicePublicKey stores the X25519 public key for the calling
  // device so the relay can include it in ListPendingKeyExchanges sweeps
  // and broadcast key_exchange_requested for it. Called once after the
  // OAuth-only login flow finishes installing local credentials.
  // Mirrors POST /auth/device/public-key — auth required.
  rpc RegisterDevicePublicKey(RegisterDevicePublicKeyRequest) returns (RegisterDevicePublicKeyResponse);
}

// ─── Login ──────────────────────────────────────────────────

message LoginRequest {
  optional string hostname = 1;
}

message LoginResponse {
  string token = 1;
  reserved 2; // was: pair_token (removed in OAuth-only)
  reserved "pair_token";
  string user_id = 3;
  string device_id = 4;
}

// ─── Device Code ────────────────────────────────────────────
//
// `status` is one of: "pending", "complete", "expired" — kept as a string
// for legacy REST wire compatibility.

message DeviceCodeStartRequest {
  optional string hostname = 1;
  // Stable per-machine identifier (opaque hash). When set, the relay reuses
  // an existing device row for the same (user_id, machine_id) instead of
  // creating a duplicate when the same Mac signs in via CLI and desktop.
  optional string machine_id = 2;
  // Email or user_id — relay broadcasts device_code_pending to this user's
  // WS sessions so already-signed-in devices can approve/deny.
  optional string user_hint = 3;
}

message DeviceCodeStartResponse {
  string device_code = 1;
  string user_code = 2;
  string verification_uri = 3;
  int64 expires_in = 4;
  int64 interval = 5;
  // Suggested polling interval in milliseconds. When non-zero, clients use
  // this in place of `interval` (seconds) for tighter UX. Older clients
  // ignore the field and fall back to `interval`.
  optional int64 interval_ms = 6;
}

message DeviceCodePollRequest {
  string code = 1;
}

message DeviceCodePollResponse {
  string status = 1;
  optional string token = 2;
  optional string user_id = 3;
  optional string device_id = 4;
  optional string email = 5;
  optional string identity_provider = 6;
}

message DeviceCodeCompleteRequest {
  string user_code = 1;
  string user_id = 2;
  string device_id = 3;
  string token = 4;
}

message DeviceCodeCompleteResponse {
  string status = 1;
}

message DeviceCodeDenyRequest {
  string user_code = 1;
}

message DeviceCodeDenyResponse {
  bool ok = 1;
}

// ─── Revoke Device ──────────────────────────────────────────
//
// `revoked_at` is RFC3339 — string for the same reason as Clip.created_at.

message RevokeDeviceRequest {
  string device_id = 1;
}

message RevokeDeviceResponse {
  bool ok = 1;
  string device_id = 2;
  string revoked_at = 3;
}

// ─── Key Bundle ─────────────────────────────────────────────

message KeyBundlePutRequest {
  string device_id = 1;
  string ephemeral_public_key = 2;
  string encrypted_bundle = 3;
}

message KeyBundlePutResponse {
  bool ok = 1;
}

message KeyBundleGetRequest {}

message KeyBundleGetResponse {
  string ephemeral_public_key = 1;
  string encrypted_bundle = 2;
  // RFC3339 timestamp of when this device first registered its
  // public key. Empty if the bundle is already present.
  string pending_since = 3;
}

message KeyBundleRetryRequest {}

message KeyBundleRetryResponse {
  bool ok = 1;
}

// ─── Register Device Public Key ─────────────────────────────

message RegisterDevicePublicKeyRequest {
  string public_key = 1; // base64url X25519 pubkey
  string fingerprint = 2; // 8-hex-char SHA-256 prefix of raw pubkey bytes
}

message RegisterDevicePublicKeyResponse {
  bool ok = 1;
}

// ─── Error ──────────────────────────────────────────────────
//
// Standard relay error JSON body, used by every legacy REST handler when
// returning a non-2xx status. Mirrors `protocol.ErrorResponse`.

message ErrorResponse {
  string error = 1;
  string message = 2;
  string fix = 3;
}
