// Proposed contract, version 1. Status: proposal for review, not confirmed design.
//
// Synchronous calls other systems make to access about Organizations and identities. Rights are read by every
// system from its own cached copy of memberships, fed by the events of access; this service bootstraps and
// repairs that copy, and lets farm mint the identity of a farm sync client.
// Callers: every system (StreamSnapshot, at start-up or after a gap in the events; req 5, 8),
// farm (RegisterServiceAccount from its backend when an enrolment code is redeemed; DisableServiceAccount from its
// backend when a sync client is retired or reinstalled, and from its sweeper when it retries a disable the backend
// could not finish; req 67, 1.*.2).
// The callers listed are authentication only (decided 2026-11-11): each RPC below names who may call it and what
// it sees, and refuses anyone else with PERMISSION_DENIED.
// Deadline: StreamSnapshot 60 s, RegisterServiceAccount 5 s, DisableServiceAccount 5 s.
// x-moonitoring-callers: farm, moochat, grant, releases, ai-model-platform, ai-gateway, news
// x-moonitoring-requirements: 5, 8, 6, 1.*.2, 67
syntax = "proto3";

package moonitoring.access.organizations.v1;

import "google/protobuf/timestamp.proto";

service OrganizationsService {
  // Streams the current image of Organizations, memberships, delegations and roles, as of one write of access.
  // The caller replaces its cache with the snapshot, then applies the events published after `as_of`.
  // Callers: every system of the service's list, from any of its components (backend, consumer, sweeper).
  // Sees: Organizations, memberships, delegations and Organization roles. Staff roles and staff memberships
  // (SNAPSHOT_KIND_STAFF) only to the backend of a system, the component that serves its staff operations, and to its sweeper, which fills and repairs the copies; empty
  // `kinds` means every kind the caller may receive, and asking for staff without that right is PERMISSION_DENIED.
  rpc StreamSnapshot(StreamSnapshotRequest) returns (stream StreamSnapshotResponse);
  // Creates a service account of an Organization with a public key and gives it a role, for a program that
  // calls our API on the Organization's behalf. Used for farm sync clients at enrolment.
  // x-moonitoring-callers: farm
  // Callers: farm only, its backend, which redeems enrolment codes. Sees: the account it created. access refuses a
  // role other than Sync Client (key sync_client) and an Organization that is not a Farm.
  rpc RegisterServiceAccount(RegisterServiceAccountRequest) returns (RegisterServiceAccountResponse);
  // Disables a service account that the caller registered, with its credential and its memberships, so no token is
  // issued to it again and its refresh tokens stop working (decided 2026-11-05). Used by farm when a sync client is
  // retired, and for the previous credential when a client is reinstalled. Idempotent: disabling a disabled account
  // returns it unchanged. Refused with PERMISSION_DENIED for an account another system registered.
  // x-moonitoring-callers: farm
  // Callers: farm only, its backend, which retires and reinstalls sync clients, and its sweeper, which retries a
  // disable the backend could not finish (decided 2026-11-13); any other component of farm is refused with
  // PERMISSION_DENIED. Sees: only the accounts farm registered.
  rpc DisableServiceAccount(DisableServiceAccountRequest) returns (DisableServiceAccountResponse);
}

message StreamSnapshotRequest {
  // Only this Organization; empty streams every Organization.
  string organization_id = 1;
  // Which records to include; empty means all.
  repeated SnapshotKind kinds = 2;
}

enum SnapshotKind {
  SNAPSHOT_KIND_UNSPECIFIED = 0;
  SNAPSHOT_KIND_ORGANIZATIONS = 1;
  SNAPSHOT_KIND_MEMBERSHIPS = 2;
  SNAPSHOT_KIND_DELEGATIONS = 3;
  SNAPSHOT_KIND_ROLES = 4;
  // Staff roles and the staff memberships that give them: every system checks staff capabilities against its copy.
  SNAPSHOT_KIND_STAFF = 5;
}

message StreamSnapshotResponse {
  // The point in time the snapshot is consistent with: the event id of the last event included.
  string as_of_event_id = 1;
  google.protobuf.Timestamp as_of = 2;
  oneof record {
    Organization organization = 3;
    Membership membership = 4;
    Delegation delegation = 5;
    OrganizationRole role = 6;
    StaffRole staff_role = 7;
    StaffMembership staff_membership = 8;
  }
}

// A staff role (MoonitoringRoleAccessControl), as staff_role.updated.v1 carries it.
message StaffRole {
  string id = 1;
  string name = 2;
  repeated string capabilities = 3;
  int32 priority = 4;
}

// An active staff membership (MoonitoringRoleMembership), as staff_membership.changed.v1 carries it.
message StaffMembership {
  string id = 1;
  // The MoonitoringUser.
  string user_id = 2;
  string role_id = 3;
  // A person or a MoonitoringServiceAccount, so that rules that need a person, such as the second approver of a
  // release, can tell them apart (decided 2026-11-05).
  AccountKind account_kind = 4;
}

enum AccountKind {
  ACCOUNT_KIND_UNSPECIFIED = 0;
  ACCOUNT_KIND_PERSON = 1;
  ACCOUNT_KIND_SERVICE_ACCOUNT = 2;
}

message Organization {
  string id = 1;
  string name = 2;
  OrganizationType type = 3;
  // IANA time zone of a farm, e.g. Europe/Rome.
  string timezone = 4;
  // The cooperative of a farm, when it has one.
  string cooperative_id = 5;
  string owner_user_id = 6;
  // Its lifecycle (req 2.7), so a copy knows which Organizations take writes (decided 2026-11-05).
  OrganizationStatus status = 7;
}

enum OrganizationStatus {
  ORGANIZATION_STATUS_UNSPECIFIED = 0;
  ORGANIZATION_STATUS_PENDING = 1;
  ORGANIZATION_STATUS_ACTIVE = 2;
  ORGANIZATION_STATUS_SUSPENDED = 3;
  ORGANIZATION_STATUS_CLOSING = 4;
  ORGANIZATION_STATUS_CLOSED = 5;
}

enum OrganizationType {
  ORGANIZATION_TYPE_UNSPECIFIED = 0;
  ORGANIZATION_TYPE_FARM = 1;
  ORGANIZATION_TYPE_COOPERATIVE = 2;
  ORGANIZATION_TYPE_INSURER = 3;
  ORGANIZATION_TYPE_PROFESSIONAL = 4;
}

message Membership {
  string id = 1;
  string user_id = 2;
  // The Organization the membership gives access to: its own for a direct membership, the delegator for a delegated one.
  string organization_id = 3;
  string role_id = 4;
  MembershipType type = 5;
  // Set for a delegated membership.
  string delegation_id = 6;
  google.protobuf.Timestamp created_at = 7;
  // Empty while active.
  google.protobuf.Timestamp revoked_at = 8;
  // Set for a role through a user group; id is then the stable id derived from the group membership and the group's
  // role assignment, the same as in membership.granted.v1.
  string user_group_id = 9;
}

enum MembershipType {
  MEMBERSHIP_TYPE_UNSPECIFIED = 0;
  MEMBERSHIP_TYPE_DIRECT = 1;
  MEMBERSHIP_TYPE_DELEGATED = 2;
  // A role given to a user group the user belongs to, in the group's Organization.
  MEMBERSHIP_TYPE_DIRECT_GROUP_ROLE = 3;
  // A role given to a user group through a delegation, in the delegator.
  MEMBERSHIP_TYPE_DELEGATED_GROUP_ROLE = 4;
}

message Delegation {
  string id = 1;
  string delegator_id = 2;
  string delegatee_id = 3;
  string role_id = 4;
  google.protobuf.Timestamp revoked_at = 5;
}

message OrganizationRole {
  string id = 1;
  string name = 2;
  repeated string capabilities = 3;
  int32 priority = 4;
  // The stable key of a predefined role (req 6.6, 1028), such as owner or sync_client; empty for any other role.
  // Systems find a predefined role by this key, never by its name.
  string key = 5;
}

message RegisterServiceAccountRequest {
  string organization_id = 1;
  string description = 2;
  // The Person responsible for it.
  string owner_user_id = 3;
  // ID of the OrganizationRoleAccessControl it holds; for a sync client the Sync Client role.
  string role_id = 4;
  // JSON Web Key of the public key the program signs its token requests with.
  string public_key_jwk = 5;
}

message RegisterServiceAccountResponse {
  // The User (ServiceAccount) created; the program authenticates as this id.
  string user_id = 1;
  string membership_id = 2;
}

message DisableServiceAccountRequest {
  string organization_id = 1;
  // The User (ServiceAccount) to disable, as RegisterServiceAccount returned it.
  string user_id = 2;
  DisableReason reason = 3;
  // Who asked, for the audit event: a Person of the Organization or a staff member. Neither, when the calling system
  // decided it alone; the audit event then names the system as its actor (req 70.14).
  oneof requested_by {
    string requested_by_user_id = 4;
    // The MoonitoringUser, when staff retired the client.
    string requested_by_staff_user_id = 5;
  }
}

enum DisableReason {
  DISABLE_REASON_UNSPECIFIED = 0;
  // The client was retired by a person or by staff.
  DISABLE_REASON_RETIRED = 1;
  // The client was installed again and enrolled with a new credential.
  DISABLE_REASON_REINSTALLED = 2;
}

message DisableServiceAccountResponse {
  string user_id = 1;
  google.protobuf.Timestamp disabled_at = 2;
}
