syntax = "proto3";
option go_package = "github.com/coder/coder/v2/tailnet/proto";

package coder.tailnet.v2;

import "google/protobuf/timestamp.proto"; // Importing for time.Time
import "google/protobuf/duration.proto"; // Importing for time.Duration
import "google/protobuf/wrappers.proto"; // Importing for nullable types


message DERPMap {
	message HomeParams {
		map<int64, double> region_score = 1;
	}
	HomeParams home_params = 1;

	message Region {
		int64 region_id = 1;
		bool embedded_relay = 2;
		string region_code = 3;
		string region_name = 4;
		bool avoid = 5;

		message Node {
			string name = 1;
			int64 region_id = 2;
			string host_name = 3;
			string cert_name = 4;
			string ipv4 = 5;
			string ipv6 = 6;
			int32 stun_port = 7;
			bool stun_only = 8;
			int32 derp_port = 9;
			bool insecure_for_tests = 10;
			bool force_http = 11;
			string stun_test_ip = 12;
			bool can_port_80 = 13;
		}
		repeated Node nodes = 6;
	}
	map<int64, Region> regions = 2;
}

message StreamDERPMapsRequest {}

// defined in tailnet/coordinator.go
message Node {
	int64 id = 1;
	google.protobuf.Timestamp as_of = 2;
	bytes key = 3;
	string disco = 4;
	int32 preferred_derp = 5;
	map<string, double> derp_latency = 6;
	map<int32, string> derp_forced_websocket = 7;
	repeated string addresses = 8;
	repeated string allowed_ips = 9;
	repeated string endpoints = 10;
}

message RefreshResumeTokenRequest {}

message RefreshResumeTokenResponse {
	string token = 1;
	google.protobuf.Duration refresh_in = 2;
	google.protobuf.Timestamp expires_at = 3;
}

message CoordinateRequest {
	message UpdateSelf {
		Node node = 1;
	}
	UpdateSelf update_self = 1;

	message Disconnect {}
	Disconnect disconnect = 2;

	message Tunnel {
		bytes id = 1;
	}
	Tunnel add_tunnel = 3;
	Tunnel remove_tunnel = 4;

	// ReadyForHandskales are sent from destinations back to the source,
	// acknowledging receipt of the source's node. If the source starts pinging
	// before a ReadyForHandshake, the Wireguard handshake will likely be
	// dropped.
	message ReadyForHandshake {
		bytes id = 1;
	}
	repeated ReadyForHandshake ready_for_handshake = 5;
}

message CoordinateResponse {
	message PeerUpdate {
		bytes id = 1;
		Node node = 2;

		enum Kind {
			KIND_UNSPECIFIED = 0;
			NODE = 1;
			DISCONNECTED = 2;
			LOST = 3;
			READY_FOR_HANDSHAKE = 4;
		}
		Kind kind = 3;

		string reason = 4;
	}
	repeated PeerUpdate peer_updates = 1;
	string error = 2;
}

message IPFields {
	int32 version = 1;
	enum IPClass {
		PUBLIC = 0;
		PRIVATE = 1;
		LINK_LOCAL = 2;
		LOOPBACK = 3;
	}
	IPClass class = 2;
}

message Netcheck {
	bool UDP = 1;
	bool IPv6 = 2;
	bool IPv4 = 3;
	bool IPv6CanSend = 4;
	bool IPv4CanSend = 5;
	bool ICMPv4 = 6;

	google.protobuf.BoolValue OSHasIPv6 = 7;
	google.protobuf.BoolValue MappingVariesByDestIP = 8;
	google.protobuf.BoolValue HairPinning = 9;
	google.protobuf.BoolValue UPnP = 10;
	google.protobuf.BoolValue PMP = 11;
	google.protobuf.BoolValue PCP = 12;

	int64 PreferredDERP = 13;  // 0 for unknown

	map<int64, google.protobuf.Duration> RegionV4Latency = 14;
	map<int64, google.protobuf.Duration> RegionV6Latency = 15;

	message NetcheckIP {
		string hash = 1;
		IPFields fields = 2;
	}
	NetcheckIP GlobalV4 = 17;
	NetcheckIP GlobalV6 = 18;
}

message TelemetryEvent {
	enum Status {
		CONNECTED = 0;
		DISCONNECTED = 1;
	}

	enum ClientType {
		CLI = 0;
		AGENT = 1;
		CODERD = 2;
		WSPROXY = 3;
	}

	message P2PEndpoint {
		string hash = 1;
		int32 port = 2;
		IPFields fields = 3;
	}

	bytes id = 1;
	google.protobuf.Timestamp time = 2;
	string application = 3;
	Status status = 4;
	// Previously the unused DisconnectionReason,
	// reserved to ensure it's not re-used.
	reserved 5;
	ClientType client_type = 6;
	string client_version = 19;
	uint64 node_id_self = 7;
	uint64 node_id_remote = 8;
	P2PEndpoint p2p_endpoint = 9;
	int32 home_derp = 10;
	DERPMap derp_map = 11;
	Netcheck latest_netcheck = 12;

	google.protobuf.Duration connection_age = 13;
	google.protobuf.Duration connection_setup = 14;
	google.protobuf.Duration p2p_setup = 15;
	google.protobuf.Duration derp_latency = 16;
	google.protobuf.Duration p2p_latency = 17;
	google.protobuf.FloatValue throughput_mbits = 18;
}

message TelemetryRequest {
	repeated TelemetryEvent events = 1;
}

message TelemetryResponse {}

service Tailnet {
	rpc PostTelemetry(TelemetryRequest) returns (TelemetryResponse);
	rpc StreamDERPMaps(StreamDERPMapsRequest) returns (stream DERPMap);
	rpc RefreshResumeToken(RefreshResumeTokenRequest) returns (RefreshResumeTokenResponse);
	rpc Coordinate(stream CoordinateRequest) returns (stream CoordinateResponse);
}
