// SecretsService -- TARS KMS-fronted signing surface.
//
// Milestone A only needs Sign / Verify for the session JWT (purpose
// JWKS_SIGNING). PutSecret / GetSecret / RotateResourceDek / EraseSecret
// and the KEK-rotation RPCs (RotatePurposeKek / RotateMasterKek) land
// alongside this proto for shape parity with IA, but only Sign and
// Verify are required for `ia login` end-to-end. The day-one shortcut
// is a static EC P-256 key configured via env, replacing the multi-tier
// envelope flow until milestone C.
//
// All RPCs here are internal-only -- services consume secrets-svc over
// the mTLS-protected internal subdomain, not the public facade.
//
// Plan reference: A2 in check-ia-and-plan-optimized-cook.md.
syntax = "proto3";

package tars.kms.v1;

import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
import "google/protobuf/timestamp.proto";
import "tars/auth/v1/auth_options.proto";
import "tars/cli/v1/cli.proto";
import "tars/docs/v1/docs.proto";

option go_package = "github.com/tetrateio/fraser/api/tars/kms/v1;kmsv1";

// Purpose enumerates the closed set of purpose-scoped KEKs.
//
// A purpose-KEK wraps every DEK created for that purpose. Adding a
// purpose is a versioned change; removal is breaking.
enum Purpose {
  PURPOSE_UNSPECIFIED = 0;

  // Audit log signing.
  PURPOSE_AUDIT_SIGNING = 1;

  // BYOK material wrapping.
  PURPOSE_BYOK = 2;

  // Session JWT signing for identity-svc.
  PURPOSE_JWKS_SIGNING = 3;

  // Outbound webhook secret wrapping.
  PURPOSE_WEBHOOK_SECRET = 4;

  // Data-plane service-account JWT signing.
  PURPOSE_DATAPLANE_SA = 5;
}

// SecretsService is the gRPC surface every TARS service uses for envelope
// encryption, signing, and key rotation. Internal-only.
service SecretsService {
  // PutSecret stores a caller-supplied secret encrypted with a fresh
  // resource-DEK wrapped by the requested purpose-KEK.
  rpc PutSecret(PutSecretRequest) returns (PutSecretResponse) {
    option (tars.docs.v1.title) = "Put a KMS secret";
    option (google.api.http) = {
      post: "/v1/kms/secrets"
      body: "*"
    };
    option (tars.auth.v1.auth) = {
      auth_types: [AUTH_TYPE_SERVICE_ACCOUNT]
      scopes: ["internal"]
    };
    option (tars.cli.v1.command) = {
      path: [
        "kms",
        "put-secret"
      ]
      hidden: true
    };
  }

  // GetSecret returns the plaintext for a stored secret. The requesting
  // identity is captured on every call and forms the audit trail.
  rpc GetSecret(GetSecretRequest) returns (GetSecretResponse) {
    option (tars.docs.v1.title) = "Get a KMS secret";
    option (google.api.http) = {get: "/v1/kms/secrets/{id}"};
    option idempotency_level = NO_SIDE_EFFECTS;
    option (tars.auth.v1.auth) = {
      auth_types: [AUTH_TYPE_SERVICE_ACCOUNT]
      scopes: ["internal"]
    };
    option (tars.cli.v1.command) = {
      path: [
        "kms",
        "get-secret"
      ]
      primary_flag: "id"
      hidden: true
    };
  }

  // RotateResourceDek re-wraps a stored secret with a fresh DEK under
  // the active purpose-KEK version. Idempotent. Synchronous today; a
  // future milestone may move this to operations.v1 for a staged
  // long-running-operation surface.
  rpc RotateResourceDek(RotateResourceDekRequest) returns (RotateResourceDekResponse) {
    option (tars.docs.v1.title) = "Rotate the resource DEK on a stored secret";
    option (google.api.http) = {
      post: "/v1/kms/secrets/{id}/rotate-dek"
      body: "*"
    };
    option (tars.auth.v1.auth) = {
      auth_types: [AUTH_TYPE_SERVICE_ACCOUNT]
      scopes: ["internal"]
    };
    option (tars.cli.v1.command) = {
      path: [
        "kms",
        "rotate-dek"
      ]
      primary_flag: "id"
      hidden: true
    };
  }

  // EraseSecret deletes the wrapped DEK and ciphertext.
  rpc EraseSecret(EraseSecretRequest) returns (EraseSecretResponse) {
    option (tars.docs.v1.title) = "Erase a KMS secret";
    option (google.api.http) = {delete: "/v1/kms/secrets/{id}"};
    option (tars.auth.v1.auth) = {
      auth_types: [AUTH_TYPE_SERVICE_ACCOUNT]
      scopes: ["internal"]
    };
    option (tars.cli.v1.command) = {
      path: [
        "kms",
        "erase-secret"
      ]
      primary_flag: "id"
      hidden: true
    };
  }

  // RotatePurposeKek mints a new active version for a purpose-KEK and
  // re-wraps every row of that purpose under the new active version.
  // Synchronous today; a future milestone may move this to operations.v1
  // for a staged long-running-operation surface.
  rpc RotatePurposeKek(RotatePurposeKekRequest) returns (RotatePurposeKekResponse) {
    option (tars.docs.v1.title) = "Rotate the purpose KEK";
    option (google.api.http) = {
      post: "/v1/kms/purpose-keks/rotate"
      body: "*"
    };
    option (tars.auth.v1.auth) = {
      auth_types: [AUTH_TYPE_SERVICE_ACCOUNT]
      scopes: ["internal"]
    };
    option (tars.cli.v1.command) = {
      path: [
        "kms",
        "rotate-purpose-kek"
      ]
      hidden: true
    };
  }

  // RotateMasterKek rotates the operator's master KEK and re-wraps every
  // purpose-KEK under the new master. Synchronous today; a future
  // milestone may move this to operations.v1 for a staged
  // long-running-operation surface.
  rpc RotateMasterKek(RotateMasterKekRequest) returns (RotateMasterKekResponse) {
    option (tars.docs.v1.title) = "Rotate the master KEK";
    option (google.api.http) = {
      post: "/v1/kms/master-kek/rotate"
      body: "*"
    };
    option (tars.auth.v1.auth) = {
      auth_types: [AUTH_TYPE_SERVICE_ACCOUNT]
      scopes: ["internal"]
    };
    option (tars.cli.v1.command) = {
      path: [
        "kms",
        "rotate-master-kek"
      ]
      hidden: true
    };
  }

  // Sign produces a signature over the supplied payload using the
  // active purpose-KEK. Used by identity-svc for session JWT signing.
  // This is the milestone-A entrypoint.
  rpc Sign(SignRequest) returns (SignResponse) {
    option (tars.auth.v1.auth) = {
      auth_types: [AUTH_TYPE_SERVICE_ACCOUNT]
      scopes: ["internal"]
    };
  }

  // Verify checks a signature against the active or any prior version
  // of the purpose-KEK. Used by the auth middleware to verify session
  // JWTs presented on inbound requests.
  rpc Verify(VerifyRequest) returns (VerifyResponse) {
    option idempotency_level = NO_SIDE_EFFECTS;
    option (tars.auth.v1.auth) = {
      auth_types: [AUTH_TYPE_SERVICE_ACCOUNT]
      scopes: ["internal"]
    };
  }
}

// SecretRef is the stored handle returned by PutSecret. Service callers
// persist this in their own tables.
message SecretRef {
  // Resource name secret_<ulid>. Server-assigned; immutable.
  string id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Purpose this secret was wrapped under.
  Purpose purpose = 2 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Active purpose-KEK version at wrap time.
  int32 kek_version = 3 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Caller-supplied tag for audit-trail readability.
  string label = 4;

  google.protobuf.Timestamp created_at = 50 [(google.api.field_behavior) = OUTPUT_ONLY];
  google.protobuf.Timestamp updated_at = 51 [(google.api.field_behavior) = OUTPUT_ONLY];
}

message PutSecretRequest {
  Purpose purpose = 1 [(google.api.field_behavior) = REQUIRED];

  // Plaintext bytes the store wraps under a fresh DEK. Wire type is
  // string so the proto-driven CLI generator can surface --plaintext as
  // a flag; callers passing truly binary material should base64-encode
  // outside the proto contract or use the byte-typed milestone-C
  // surface once operations.v1 lands.
  string plaintext = 2 [(google.api.field_behavior) = REQUIRED];

  string label = 3;
}

message PutSecretResponse {
  SecretRef ref = 1;
}

message GetSecretRequest {
  string id = 1 [(google.api.field_behavior) = REQUIRED];
}

message GetSecretResponse {
  SecretRef ref = 1;

  // Plaintext bytes; see PutSecretRequest.plaintext for the binary
  // round-trip caveat.
  string plaintext = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
}

message RotateResourceDekRequest {
  string id = 1 [(google.api.field_behavior) = REQUIRED];
}

message RotateResourceDekResponse {}

message EraseSecretRequest {
  string id = 1 [(google.api.field_behavior) = REQUIRED];
}

message EraseSecretResponse {}

message RotatePurposeKekRequest {
  Purpose purpose = 1 [(google.api.field_behavior) = REQUIRED];
}

message RotatePurposeKekResponse {
  // Operation id (op_<ulid>) the caller polls via OperationsService.
  string operation_id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
}

message RotateMasterKekRequest {}

message RotateMasterKekResponse {
  string operation_id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
}

message SignRequest {
  Purpose purpose = 1 [(google.api.field_behavior) = REQUIRED];
  bytes payload = 2 [(google.api.field_behavior) = REQUIRED];
}

message SignResponse {
  bytes signature = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
  int32 kek_version = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
}

message VerifyRequest {
  Purpose purpose = 1 [(google.api.field_behavior) = REQUIRED];
  bytes payload = 2 [(google.api.field_behavior) = REQUIRED];
  bytes signature = 3 [(google.api.field_behavior) = REQUIRED];
}

message VerifyResponse {
  bool valid = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
  int32 kek_version = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
}
