// Identity messages for the unified identity model.
//
// TARS represents every authenticated principal -- human user, service
// account, machine workload -- as one Identity value. The shape mirrors
// IA's identity.v1 to keep wire formats congruent across platforms.
//
// See plan: check-ia-and-plan-optimized-cook.md item A1.
syntax = "proto3";

package tars.identity.v1;

import "google/api/field_behavior.proto";
import "google/protobuf/timestamp.proto";

option go_package = "github.com/tetrateio/fraser/api/tars/identity/v1;identityv1";

// IdentityType discriminates the principal kind. The value selects which
// of user / service_account / client is populated on an Identity.
enum IdentityType {
  IDENTITY_TYPE_UNSPECIFIED = 0;

  // Human user authenticated via OIDC.
  IDENTITY_TYPE_USER = 1;

  // Long-lived service account authenticated via API key.
  IDENTITY_TYPE_SERVICE_ACCOUNT = 2;

  // Machine workload authenticated via short-lived JWT signed with a
  // pre-registered JWK.
  IDENTITY_TYPE_CLIENT = 3;
}

// IdentityState is the lifecycle state of an Identity.
enum IdentityState {
  IDENTITY_STATE_UNSPECIFIED = 0;

  // Identity is active and may authenticate.
  IDENTITY_STATE_ACTIVE = 1;

  // Identity is suspended; authentication is rejected, data is retained.
  IDENTITY_STATE_SUSPENDED = 2;

  // Identity is soft-deleted; authentication is rejected and the record
  // is retained only for audit purposes.
  IDENTITY_STATE_DELETED = 3;
}

// UserDetails carries fields specific to human users.
message UserDetails {
  // Stable identifier from the upstream IdP (sub claim on the OIDC token).
  string oidc_subject = 1 [(google.api.field_behavior) = REQUIRED];

  // Issuer URL of the upstream IdP (matches iss on the OIDC token).
  string oidc_issuer = 2 [(google.api.field_behavior) = REQUIRED];

  // Email address from the OIDC profile.
  string email = 3;

  // Display name from the OIDC profile.
  string name = 4;

  // True when the IdP asserted email ownership (the email_verified claim).
  bool email_verified = 5;
}

// ServiceAccountDetails carries fields specific to service accounts.
message ServiceAccountDetails {
  // Human-friendly description of the account's purpose.
  string description = 1;
}

// ClientDetails carries fields specific to machine workloads.
message ClientDetails {
  // Description of the workload's purpose.
  string description = 1;
}

// Identity is the unified principal record.
//
// Every TARS token resolves to exactly one Identity. The facade injects
// the id into the X-Tetrate-Identity header on backend calls.
message Identity {
  // Resource name id_<ulid>. Server-assigned; immutable.
  string id = 1 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Customer this identity belongs to. Immutable. Empty for platform
  // identities (cross-customer operators).
  string customer_id = 2 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Discriminator selecting the populated details oneof branch.
  IdentityType type = 3 [(google.api.field_behavior) = REQUIRED];

  // Lifecycle state.
  IdentityState state = 4 [(google.api.field_behavior) = OUTPUT_ONLY];

  // Type-specific details.
  oneof details {
    UserDetails user = 10;
    ServiceAccountDetails service_account = 11;
    ClientDetails client = 12;
  }

  // Role bundles assigned to this identity. Expanded into the flat
  // permission set at JWT-issue time.
  repeated string bundles = 20;

  // Customer memberships -- the set of customer ids this identity may
  // operate against. Populated by the facade from the identity's bindings
  // at JWT-issue time; surfaced on GetMe for the CLI's whoami output.
  repeated string customer_memberships = 21 [(google.api.field_behavior) = OUTPUT_ONLY];

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

  // Optimistic concurrency token. Echo the value back on update; a stale
  // value yields concurrent_update.
  int64 version = 52;
}
