// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

syntax = "proto3";

package hashicorp.consul.internal.peerstream;

import "annotations/ratelimit/ratelimit.proto";
import "google/protobuf/any.proto";
import "private/pbpeering/peering.proto";
import "private/pbservice/node.proto";
// TODO(peering): Handle this some other way
import "private/pbstatus/status.proto";

// TODO(peering): comments

// TODO(peering): also duplicate the pbservice, some pbpeering, and ca stuff.

service PeerStreamService {
  // StreamResources opens an event stream for resources to share between peers, such as services.
  // Events are streamed as they happen.
  // buf:lint:ignore RPC_REQUEST_STANDARD_NAME
  // buf:lint:ignore RPC_RESPONSE_STANDARD_NAME
  // buf:lint:ignore RPC_REQUEST_RESPONSE_UNIQUE
  rpc StreamResources(stream ReplicationMessage) returns (stream ReplicationMessage) {
    option (hashicorp.consul.internal.ratelimit.spec) = {
      operation_type: OPERATION_TYPE_READ,
      operation_category: OPERATION_CATEGORY_PEER_STREAM
    };
  }

  // ExchangeSecret is a unary RPC for exchanging the one-time establishment secret
  // for a long-lived stream secret.
  rpc ExchangeSecret(ExchangeSecretRequest) returns (ExchangeSecretResponse) {
    option (hashicorp.consul.internal.ratelimit.spec) = {
      operation_type: OPERATION_TYPE_WRITE,
      operation_category: OPERATION_CATEGORY_PEER_STREAM
    };
  }
}

message ReplicationMessage {
  oneof Payload {
    Open open = 1;
    Request request = 2;
    Response response = 3;
    Terminated terminated = 4;
    Heartbeat heartbeat = 5;
  }

  // Open is the initial message send by a dialing peer to establish the peering stream.
  message Open {
    // An identifier for the peer making the request.
    // This identifier is provisioned by the serving peer prior to the request from the dialing peer.
    string PeerID = 1;

    // StreamSecretID contains the long-lived secret from stream authn/authz.
    string StreamSecretID = 2;

    // Remote contains metadata about the remote peer.
    hashicorp.consul.internal.peering.RemoteInfo Remote = 3;
  }

  // A Request requests to subscribe to a resource of a given type.
  message Request {
    // An identifier for the peer making the request.
    // This identifier is provisioned by the serving peer prior to the request from the dialing peer.
    string PeerID = 1;

    // ResponseNonce corresponding to that of the response being ACKed or NACKed.
    // Initial subscription requests will have an empty nonce.
    // The nonce is generated and incremented by the exporting peer.
    // TODO
    string ResponseNonce = 2;

    // The type URL for the resource being requested or ACK/NACKed.
    string ResourceURL = 3;

    // The error if the previous response was not applied successfully.
    // This field is empty in the first subscription request.
    status.Status Error = 5;
  }

  // A Response contains resources corresponding to a subscription request.
  message Response {
    // Nonce identifying a response in a stream.
    string Nonce = 1;

    // The type URL of resource being returned.
    string ResourceURL = 2;

    // An identifier for the resource being returned.
    // This could be the SPIFFE ID of the service.
    string ResourceID = 3;

    // The resource being returned.
    google.protobuf.Any Resource = 4;

    // REQUIRED. The operation to be performed in relation to the resource.
    Operation operation = 5;
  }

  // Terminated is sent when a peering is deleted locally.
  // This message signals to the peer that they should clean up their local state about the peering.
  message Terminated {}

  // Heartbeat is sent to verify that the connection is still active.
  message Heartbeat {}
}

// Operation enumerates supported operations for replicated resources.
enum Operation {
  OPERATION_UNSPECIFIED = 0;

  // UPSERT represents a create or update event.
  OPERATION_UPSERT = 1;
}

// LeaderAddress is sent when the peering service runs on a consul node
// that is not a leader. The node either lost leadership, or never was a leader.
message LeaderAddress {
  // address is an ip:port best effort hint at what could be the cluster leader's address
  string address = 1;
}

// ExportedService is one of the types of data returned via peer stream replication.
message ExportedService {
  repeated hashicorp.consul.internal.service.CheckServiceNode Nodes = 1;
}

// ExportedServiceList is one of the types of data returned via peer stream replication.
message ExportedServiceList {
  // The identifiers for the services being exported.
  repeated string Services = 1;
}

message ExchangeSecretRequest {
  // PeerID is the ID of the peering, as determined by the cluster that generated the
  // peering token.
  string PeerID = 1;

  // EstablishmentSecret is the one-time-use secret encoded in the received peering token.
  string EstablishmentSecret = 2;
}

message ExchangeSecretResponse {
  // StreamSecret is the long-lived secret to be used for authentication with the
  // peering stream handler.
  string StreamSecret = 1;
}
