syntax = "proto3"; option go_package = "github.com/coder/coder/v2/vpn"; option csharp_namespace = "Coder.Desktop.Vpn.Proto"; import "google/protobuf/timestamp.proto"; import "google/protobuf/duration.proto"; package vpn; // The CoderVPN protocol operates over a bidirectional stream between a "manager" and a "tunnel." // The manager is part of the Coder Desktop application and written in OS native code. It handles // configuring the VPN and displaying status to the end user. The tunnel is written in Go and // handles operating the actual tunnel, including reading and writing packets, & communicating with // the Coder server control plane. // RPC allows a very simple unary request/response RPC mechanism. The requester generates a unique // msg_id which it sets on the request, the responder sets response_to that msg_id on the response // message message RPC { uint64 msg_id = 1; uint64 response_to = 2; } // ManagerMessage is a message from the manager (to the tunnel). message ManagerMessage { RPC rpc = 1; oneof msg { GetPeerUpdate get_peer_update = 2; NetworkSettingsResponse network_settings = 3; StartRequest start = 4; StopRequest stop = 5; } } // TunnelMessage is a message from the tunnel (to the manager). message TunnelMessage { RPC rpc = 1; oneof msg { Log log = 2; PeerUpdate peer_update = 3; NetworkSettingsRequest network_settings = 4; StartResponse start = 5; StopResponse stop = 6; } } // ClientMessage is a message from the client (to the service). Windows only. message ClientMessage { RPC rpc = 1; oneof msg { StartRequest start = 2; StopRequest stop = 3; StatusRequest status = 4; } } // ServiceMessage is a message from the service (to the client). Windows only. message ServiceMessage { RPC rpc = 1; oneof msg { StartResponse start = 2; StopResponse stop = 3; Status status = 4; // either in reply to a StatusRequest or broadcasted StartProgress start_progress = 5; // broadcasted during startup (used exclusively by Windows) } } // Log is a log message generated by the tunnel. The manager should log it to the system log. It is // one-way tunnel -> manager with no response. message Log { enum Level { // these are designed to match slog levels DEBUG = 0; INFO = 1; WARN = 2; ERROR = 3; CRITICAL = 4; FATAL = 5; } Level level = 1; string message = 2; repeated string logger_names = 3; message Field { string name = 1; string value = 2; } repeated Field fields = 4; } // GetPeerUpdate asks for a PeerUpdate with a full set of data. message GetPeerUpdate {} // PeerUpdate is an update about workspaces and agents connected via the tunnel. It is generated in // response to GetPeerUpdate (which dumps the full set). It is also generated on any changes (not in // response to any request). message PeerUpdate { repeated Workspace upserted_workspaces = 1; repeated Agent upserted_agents = 2; repeated Workspace deleted_workspaces = 3; repeated Agent deleted_agents = 4; } message Workspace { bytes id = 1; // UUID string name = 2; enum Status { UNKNOWN = 0; PENDING = 1; STARTING = 2; RUNNING = 3; STOPPING = 4; STOPPED = 5; FAILED = 6; CANCELING = 7; CANCELED = 8; DELETING = 9; DELETED = 10; } Status status = 3; } message Agent { bytes id = 1; // UUID string name = 2; bytes workspace_id = 3; // UUID repeated string fqdn = 4; repeated string ip_addrs = 5; // last_handshake is the primary indicator of whether we are connected to a peer. Zero value or // anything longer than 5 minutes ago means there is a problem. google.protobuf.Timestamp last_handshake = 6; // If unset, a successful ping has not yet been made. optional LastPing last_ping = 7; } message LastPing { // latency is the RTT of the ping to the agent. google.protobuf.Duration latency = 1; // did_p2p indicates whether the ping was sent P2P, or over DERP. bool did_p2p = 2; // preferred_derp is the human readable name of the preferred DERP region, // or the region used for the last ping, if it was sent over DERP. string preferred_derp = 3; // preferred_derp_latency is the last known latency to the preferred DERP // region. Unset if the region does not appear in the DERP map. optional google.protobuf.Duration preferred_derp_latency = 4; } // NetworkSettingsRequest is based on // https://developer.apple.com/documentation/networkextension/nepackettunnelnetworksettings for // macOS. It is a request/response message with response NetworkSettingsResponse message NetworkSettingsRequest { uint32 tunnel_overhead_bytes = 1; uint32 mtu = 2; message DNSSettings { repeated string servers = 1; repeated string search_domains = 2; // domain_name is the primary domain name of the tunnel string domain_name = 3; repeated string match_domains = 4; // match_domains_no_search specifies if the domains in the matchDomains list should not be // appended to the resolver’s list of search domains. bool match_domains_no_search = 5; } DNSSettings dns_settings = 3; string tunnel_remote_address = 4; message IPv4Settings { repeated string addrs = 1; repeated string subnet_masks = 2; // router is the next-hop router in dotted-decimal format string router = 3; message IPv4Route { string destination = 1; string mask = 2; // router is the next-hop router in dotted-decimal format string router = 3; } repeated IPv4Route included_routes = 4; repeated IPv4Route excluded_routes = 5; } IPv4Settings ipv4_settings = 5; message IPv6Settings { repeated string addrs = 1; repeated uint32 prefix_lengths = 2; message IPv6Route { string destination = 1; uint32 prefix_length = 2; // router is the address of the next-hop string router = 3; } repeated IPv6Route included_routes = 3; repeated IPv6Route excluded_routes = 4; } IPv6Settings ipv6_settings = 6; } // NetworkSettingsResponse is the response from the manager to the tunnel for a // NetworkSettingsRequest message NetworkSettingsResponse { bool success = 1; string error_message = 2; } // StartRequest is a request from the manager to start the tunnel. The tunnel replies with a // StartResponse. message StartRequest { int32 tunnel_file_descriptor = 1; string coder_url = 2; string api_token = 3; // Additional HTTP headers added to all requests message Header { string name = 1; string value = 2; } repeated Header headers = 4; // Device ID from Coder Desktop string device_id = 5; // Device OS from Coder Desktop string device_os = 6; // Coder Desktop version string coder_desktop_version = 7; } message StartResponse { bool success = 1; string error_message = 2; } // StartProgress is sent from the manager to the client to indicate the // download/startup progress of the tunnel. This will be sent during the // processing of a StartRequest before the StartResponse is sent. // // Note: this is currently a broadcasted message to all clients due to the // inability to easily send messages to a specific client in the Speaker // implementation. If clients are not expecting these messages, they // should ignore them. enum StartProgressStage { Initializing = 0; Downloading = 1; Finalizing = 2; } message StartProgressDownloadProgress { uint64 bytes_written = 1; optional uint64 bytes_total = 2; // unknown in some situations } message StartProgress { StartProgressStage stage = 1; optional StartProgressDownloadProgress download_progress = 2; // only set when stage == Downloading } // StopRequest is a request from the manager to stop the tunnel. The tunnel replies with a // StopResponse. message StopRequest {} // StopResponse is a response to stopping the tunnel. After sending this response, the tunnel closes // its side of the bidirectional stream for writing. message StopResponse { bool success = 1; string error_message = 2; } // StatusRequest is a request to get the status of the tunnel. The manager // replies with a Status. message StatusRequest {} // Status is sent in response to a StatusRequest or broadcasted to all clients // when the status changes. message Status { enum Lifecycle { UNKNOWN = 0; STARTING = 1; STARTED = 2; STOPPING = 3; STOPPED = 4; } Lifecycle lifecycle = 1; string error_message = 2; // This will be a FULL update with all workspaces and agents, so clients // should replace their current peer state. Only the Upserted fields will // be populated. PeerUpdate peer_update = 3; }