mirror of
https://github.com/coder/coder.git
synced 2025-07-03 16:13:58 +00:00
feat: adds device_id, device_os, and coder_desktop_version to telemetry (#17086)
Records the Device ID, Device OS and Coder Desktop version to telemetry. These values are provided by the Coder Desktop client in the StartRequest method of the VPN protocol. We render them as an HTTP header to transmit to Coderd, where they are decoded and added to telemetry.
This commit is contained in:
@ -1652,6 +1652,8 @@ func (api *API) tailnetRPCConn(rw http.ResponseWriter, r *http.Request) {
|
||||
DeviceOS: nil,
|
||||
CoderDesktopVersion: nil,
|
||||
}
|
||||
|
||||
fillCoderDesktopTelemetry(r, &connectionTelemetryEvent, api.Logger)
|
||||
api.Telemetry.Report(&telemetry.Snapshot{
|
||||
UserTailnetConnections: []telemetry.UserTailnetConnection{connectionTelemetryEvent},
|
||||
})
|
||||
@ -1681,6 +1683,34 @@ func (api *API) tailnetRPCConn(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// fillCoderDesktopTelemetry fills out the provided event based on a Coder Desktop telemetry header on the request, if
|
||||
// present.
|
||||
func fillCoderDesktopTelemetry(r *http.Request, event *telemetry.UserTailnetConnection, logger slog.Logger) {
|
||||
// Parse desktop telemetry from header if it exists
|
||||
desktopTelemetryHeader := r.Header.Get(codersdk.CoderDesktopTelemetryHeader)
|
||||
if desktopTelemetryHeader != "" {
|
||||
var telemetryData codersdk.CoderDesktopTelemetry
|
||||
if err := telemetryData.FromHeader(desktopTelemetryHeader); err == nil {
|
||||
// Only set fields if they aren't empty
|
||||
if telemetryData.DeviceID != "" {
|
||||
event.DeviceID = &telemetryData.DeviceID
|
||||
}
|
||||
if telemetryData.DeviceOS != "" {
|
||||
event.DeviceOS = &telemetryData.DeviceOS
|
||||
}
|
||||
if telemetryData.CoderDesktopVersion != "" {
|
||||
event.CoderDesktopVersion = &telemetryData.CoderDesktopVersion
|
||||
}
|
||||
logger.Debug(r.Context(), "received desktop telemetry",
|
||||
slog.F("device_id", telemetryData.DeviceID),
|
||||
slog.F("device_os", telemetryData.DeviceOS),
|
||||
slog.F("desktop_version", telemetryData.CoderDesktopVersion))
|
||||
} else {
|
||||
logger.Warn(r.Context(), "failed to parse desktop telemetry header", slog.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// createExternalAuthResponse creates an ExternalAuthResponse based on the
|
||||
// provider type. This is to support legacy `/workspaceagents/me/gitauth`
|
||||
// which uses `Username` and `Password`.
|
||||
|
@ -51,6 +51,7 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/jwtutils"
|
||||
"github.com/coder/coder/v2/coderd/rbac"
|
||||
"github.com/coder/coder/v2/coderd/telemetry"
|
||||
"github.com/coder/coder/v2/coderd/util/ptr"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||
"github.com/coder/coder/v2/codersdk/workspacesdk"
|
||||
@ -2135,12 +2136,8 @@ func TestOwnedWorkspacesCoordinate(t *testing.T) {
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
logger := testutil.Logger(t)
|
||||
|
||||
fTelemetry := newFakeTelemetryReporter(ctx, t, 200)
|
||||
fTelemetry.enabled = false
|
||||
firstClient, _, api := coderdtest.NewWithAPI(t, &coderdtest.Options{
|
||||
Coordinator: tailnet.NewCoordinator(logger),
|
||||
TelemetryReporter: fTelemetry,
|
||||
})
|
||||
firstUser := coderdtest.CreateFirstUser(t, firstClient)
|
||||
member, memberUser := coderdtest.CreateAnotherUser(t, firstClient, firstUser.OrganizationID, rbac.RoleTemplateAdmin())
|
||||
@ -2148,17 +2145,12 @@ func TestOwnedWorkspacesCoordinate(t *testing.T) {
|
||||
// Create a workspace with an agent
|
||||
firstWorkspace := buildWorkspaceWithAgent(t, member, firstUser.OrganizationID, memberUser.ID, api.Database, api.Pubsub)
|
||||
|
||||
// enable telemetry now that workspace is built; we don't care about snapshots before this.
|
||||
fTelemetry.enabled = true
|
||||
|
||||
u, err := member.URL.Parse("/api/v2/tailnet")
|
||||
require.NoError(t, err)
|
||||
q := u.Query()
|
||||
q.Set("version", "2.0")
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
predialTime := time.Now()
|
||||
|
||||
//nolint:bodyclose // websocket package closes this for you
|
||||
wsConn, resp, err := websocket.Dial(ctx, u.String(), &websocket.DialOptions{
|
||||
HTTPHeader: http.Header{
|
||||
@ -2173,15 +2165,6 @@ func TestOwnedWorkspacesCoordinate(t *testing.T) {
|
||||
}
|
||||
defer wsConn.Close(websocket.StatusNormalClosure, "done")
|
||||
|
||||
// Check telemetry
|
||||
snapshot := testutil.RequireRecvCtx(ctx, t, fTelemetry.snapshots)
|
||||
require.Len(t, snapshot.UserTailnetConnections, 1)
|
||||
telemetryConnection := snapshot.UserTailnetConnections[0]
|
||||
require.Equal(t, memberUser.ID.String(), telemetryConnection.UserID)
|
||||
require.GreaterOrEqual(t, telemetryConnection.ConnectedAt, predialTime)
|
||||
require.LessOrEqual(t, telemetryConnection.ConnectedAt, time.Now())
|
||||
require.NotEmpty(t, telemetryConnection.PeerID)
|
||||
|
||||
rpcClient, err := tailnet.NewDRPCClient(
|
||||
websocket.NetConn(ctx, wsConn, websocket.MessageBinary),
|
||||
logger,
|
||||
@ -2229,9 +2212,116 @@ func TestOwnedWorkspacesCoordinate(t *testing.T) {
|
||||
NumAgents: 0,
|
||||
},
|
||||
})
|
||||
err = stream.Close()
|
||||
}
|
||||
|
||||
func TestUserTailnetTelemetry(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
telemetryData := &codersdk.CoderDesktopTelemetry{
|
||||
DeviceOS: "Windows",
|
||||
DeviceID: "device001",
|
||||
CoderDesktopVersion: "0.22.1",
|
||||
}
|
||||
fullHeader, err := json.Marshal(telemetryData)
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
headers map[string]string
|
||||
// only used for DeviceID, DeviceOS, CoderDesktopVersion
|
||||
expected telemetry.UserTailnetConnection
|
||||
}{
|
||||
{
|
||||
name: "no header",
|
||||
headers: map[string]string{},
|
||||
expected: telemetry.UserTailnetConnection{},
|
||||
},
|
||||
{
|
||||
name: "full header",
|
||||
headers: map[string]string{
|
||||
codersdk.CoderDesktopTelemetryHeader: string(fullHeader),
|
||||
},
|
||||
expected: telemetry.UserTailnetConnection{
|
||||
DeviceOS: ptr.Ref("Windows"),
|
||||
DeviceID: ptr.Ref("device001"),
|
||||
CoderDesktopVersion: ptr.Ref("0.22.1"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty header",
|
||||
headers: map[string]string{
|
||||
codersdk.CoderDesktopTelemetryHeader: "",
|
||||
},
|
||||
expected: telemetry.UserTailnetConnection{},
|
||||
},
|
||||
{
|
||||
name: "invalid header",
|
||||
headers: map[string]string{
|
||||
codersdk.CoderDesktopTelemetryHeader: "{\"device_os",
|
||||
},
|
||||
expected: telemetry.UserTailnetConnection{},
|
||||
},
|
||||
}
|
||||
|
||||
// nolint: paralleltest // no longer need to reinitialize loop vars in go 1.22
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
logger := testutil.Logger(t)
|
||||
|
||||
fTelemetry := newFakeTelemetryReporter(ctx, t, 200)
|
||||
fTelemetry.enabled = false
|
||||
firstClient := coderdtest.New(t, &coderdtest.Options{
|
||||
Logger: &logger,
|
||||
TelemetryReporter: fTelemetry,
|
||||
})
|
||||
firstUser := coderdtest.CreateFirstUser(t, firstClient)
|
||||
member, memberUser := coderdtest.CreateAnotherUser(t, firstClient, firstUser.OrganizationID, rbac.RoleTemplateAdmin())
|
||||
|
||||
headers := http.Header{
|
||||
"Coder-Session-Token": []string{member.SessionToken()},
|
||||
}
|
||||
for k, v := range tc.headers {
|
||||
headers.Add(k, v)
|
||||
}
|
||||
|
||||
// enable telemetry now that user is created.
|
||||
fTelemetry.enabled = true
|
||||
|
||||
u, err := member.URL.Parse("/api/v2/tailnet")
|
||||
require.NoError(t, err)
|
||||
q := u.Query()
|
||||
q.Set("version", "2.0")
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
predialTime := time.Now()
|
||||
|
||||
//nolint:bodyclose // websocket package closes this for you
|
||||
wsConn, resp, err := websocket.Dial(ctx, u.String(), &websocket.DialOptions{
|
||||
HTTPHeader: headers,
|
||||
})
|
||||
if err != nil {
|
||||
if resp != nil && resp.StatusCode != http.StatusSwitchingProtocols {
|
||||
err = codersdk.ReadBodyAsError(resp)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
}
|
||||
defer wsConn.Close(websocket.StatusNormalClosure, "done")
|
||||
|
||||
// Check telemetry
|
||||
snapshot := testutil.RequireRecvCtx(ctx, t, fTelemetry.snapshots)
|
||||
require.Len(t, snapshot.UserTailnetConnections, 1)
|
||||
telemetryConnection := snapshot.UserTailnetConnections[0]
|
||||
require.Equal(t, memberUser.ID.String(), telemetryConnection.UserID)
|
||||
require.GreaterOrEqual(t, telemetryConnection.ConnectedAt, predialTime)
|
||||
require.LessOrEqual(t, telemetryConnection.ConnectedAt, time.Now())
|
||||
require.NotEmpty(t, telemetryConnection.PeerID)
|
||||
requireEqualOrBothNil(t, telemetryConnection.DeviceID, tc.expected.DeviceID)
|
||||
requireEqualOrBothNil(t, telemetryConnection.DeviceOS, tc.expected.DeviceOS)
|
||||
requireEqualOrBothNil(t, telemetryConnection.CoderDesktopVersion, tc.expected.CoderDesktopVersion)
|
||||
|
||||
beforeDisconnectTime := time.Now()
|
||||
err = wsConn.Close(websocket.StatusNormalClosure, "done")
|
||||
require.NoError(t, err)
|
||||
@ -2246,6 +2336,11 @@ func TestOwnedWorkspacesCoordinate(t *testing.T) {
|
||||
require.NotNil(t, telemetryDisconnection.DisconnectedAt)
|
||||
require.GreaterOrEqual(t, *telemetryDisconnection.DisconnectedAt, beforeDisconnectTime)
|
||||
require.LessOrEqual(t, *telemetryDisconnection.DisconnectedAt, time.Now())
|
||||
requireEqualOrBothNil(t, telemetryConnection.DeviceID, tc.expected.DeviceID)
|
||||
requireEqualOrBothNil(t, telemetryConnection.DeviceOS, tc.expected.DeviceOS)
|
||||
requireEqualOrBothNil(t, telemetryConnection.CoderDesktopVersion, tc.expected.CoderDesktopVersion)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func buildWorkspaceWithAgent(
|
||||
@ -2414,3 +2509,12 @@ func (f *fakeTelemetryReporter) Enabled() bool {
|
||||
|
||||
// Close implements the telemetry.Reporter interface.
|
||||
func (*fakeTelemetryReporter) Close() {}
|
||||
|
||||
func requireEqualOrBothNil[T any](t testing.TB, a, b *T) {
|
||||
t.Helper()
|
||||
if a != nil && b != nil {
|
||||
require.Equal(t, *a, *b)
|
||||
return
|
||||
}
|
||||
require.Equal(t, a, b)
|
||||
}
|
||||
|
@ -76,6 +76,10 @@ const (
|
||||
// only.
|
||||
CLITelemetryHeader = "Coder-CLI-Telemetry"
|
||||
|
||||
// CoderDesktopTelemetryHeader contains a JSON-encoded representation of Desktop telemetry
|
||||
// fields, including device ID, OS, and Desktop version.
|
||||
CoderDesktopTelemetryHeader = "Coder-Desktop-Telemetry"
|
||||
|
||||
// ProvisionerDaemonPSK contains the authentication pre-shared key for an external provisioner daemon
|
||||
ProvisionerDaemonPSK = "Coder-Provisioner-Daemon-PSK"
|
||||
|
||||
@ -523,6 +527,28 @@ func (e ValidationError) Error() string {
|
||||
|
||||
var _ error = (*ValidationError)(nil)
|
||||
|
||||
// CoderDesktopTelemetry represents the telemetry data sent from Coder Desktop clients.
|
||||
// @typescript-ignore CoderDesktopTelemetry
|
||||
type CoderDesktopTelemetry struct {
|
||||
DeviceID string `json:"device_id"`
|
||||
DeviceOS string `json:"device_os"`
|
||||
CoderDesktopVersion string `json:"coder_desktop_version"`
|
||||
}
|
||||
|
||||
// FromHeader parses the desktop telemetry from the provided header value.
|
||||
// Returns nil if the header is empty or if parsing fails.
|
||||
func (t *CoderDesktopTelemetry) FromHeader(headerValue string) error {
|
||||
if headerValue == "" {
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal([]byte(headerValue), t)
|
||||
}
|
||||
|
||||
// IsEmpty returns true if all fields in the telemetry data are empty.
|
||||
func (t *CoderDesktopTelemetry) IsEmpty() bool {
|
||||
return t.DeviceID == "" && t.DeviceOS == "" && t.CoderDesktopVersion == ""
|
||||
}
|
||||
|
||||
// IsConnectionError is a convenience function for checking if the source of an
|
||||
// error is due to a 'connection refused', 'no such host', etc.
|
||||
func IsConnectionError(err error) bool {
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
|
||||
"cdr.dev/slog"
|
||||
"cdr.dev/slog/sloggers/sloghuman"
|
||||
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
|
3
site/src/api/typesGenerated.ts
generated
3
site/src/api/typesGenerated.ts
generated
@ -290,6 +290,9 @@ export interface ChangePasswordWithOneTimePasscodeRequest {
|
||||
readonly one_time_passcode: string;
|
||||
}
|
||||
|
||||
// From codersdk/client.go
|
||||
export const CoderDesktopTelemetryHeader = "Coder-Desktop-Telemetry";
|
||||
|
||||
// From codersdk/insights.go
|
||||
export interface ConnectionLatency {
|
||||
readonly p50: number;
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
|
||||
"cdr.dev/slog"
|
||||
"cdr.dev/slog/sloggers/slogtest"
|
||||
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
@ -47,7 +48,7 @@ func TestSpeaker_RawPeer(t *testing.T) {
|
||||
errCh <- err
|
||||
}()
|
||||
|
||||
expectedHandshake := "codervpn tunnel 1.0\n"
|
||||
expectedHandshake := "codervpn tunnel 1.1\n"
|
||||
|
||||
b := make([]byte, 256)
|
||||
n, err := mp.Read(b)
|
||||
@ -155,7 +156,7 @@ func TestSpeaker_OversizeHandshake(t *testing.T) {
|
||||
errCh <- err
|
||||
}()
|
||||
|
||||
expectedHandshake := "codervpn tunnel 1.0\n"
|
||||
expectedHandshake := "codervpn tunnel 1.1\n"
|
||||
|
||||
b := make([]byte, 256)
|
||||
n, err := mp.Read(b)
|
||||
@ -177,12 +178,12 @@ func TestSpeaker_HandshakeInvalid(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name, handshake string
|
||||
}{
|
||||
{name: "preamble", handshake: "ssh manager 1.0\n"},
|
||||
{name: "preamble", handshake: "ssh manager 1.1\n"},
|
||||
{name: "2components", handshake: "ssh manager\n"},
|
||||
{name: "newmajors", handshake: "codervpn manager 2.0,3.0\n"},
|
||||
{name: "0version", handshake: "codervpn 0.1 manager\n"},
|
||||
{name: "unknown_role", handshake: "codervpn 1.0 supervisor\n"},
|
||||
{name: "unexpected_role", handshake: "codervpn 1.0 tunnel\n"},
|
||||
{name: "unknown_role", handshake: "codervpn 1.1 supervisor\n"},
|
||||
{name: "unexpected_role", handshake: "codervpn 1.1 tunnel\n"},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
@ -208,7 +209,7 @@ func TestSpeaker_HandshakeInvalid(t *testing.T) {
|
||||
_, err = mp.Write([]byte(tc.handshake))
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedHandshake := "codervpn tunnel 1.0\n"
|
||||
expectedHandshake := "codervpn tunnel 1.1\n"
|
||||
b := make([]byte, 256)
|
||||
n, err := mp.Read(b)
|
||||
require.NoError(t, err)
|
||||
@ -246,7 +247,7 @@ func TestSpeaker_CorruptMessage(t *testing.T) {
|
||||
errCh <- err
|
||||
}()
|
||||
|
||||
expectedHandshake := "codervpn tunnel 1.0\n"
|
||||
expectedHandshake := "codervpn tunnel 1.1\n"
|
||||
|
||||
b := make([]byte, 256)
|
||||
n, err := mp.Read(b)
|
||||
|
@ -26,8 +26,10 @@ import (
|
||||
"tailscale.com/wgengine/router"
|
||||
|
||||
"cdr.dev/slog"
|
||||
"github.com/coder/coder/v2/tailnet"
|
||||
"github.com/coder/quartz"
|
||||
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/tailnet"
|
||||
)
|
||||
|
||||
// netStatusInterval is the interval at which the tunnel sends network status updates to the manager.
|
||||
@ -236,6 +238,24 @@ func (t *Tunnel) start(req *StartRequest) error {
|
||||
for _, h := range req.GetHeaders() {
|
||||
header.Add(h.GetName(), h.GetValue())
|
||||
}
|
||||
|
||||
// Add desktop telemetry if any fields are provided
|
||||
telemetryData := codersdk.CoderDesktopTelemetry{
|
||||
DeviceID: req.GetDeviceId(),
|
||||
DeviceOS: req.GetDeviceOs(),
|
||||
CoderDesktopVersion: req.GetCoderDesktopVersion(),
|
||||
}
|
||||
if !telemetryData.IsEmpty() {
|
||||
headerValue, err := json.Marshal(telemetryData)
|
||||
if err == nil {
|
||||
header.Set(codersdk.CoderDesktopTelemetryHeader, string(headerValue))
|
||||
t.logger.Debug(t.ctx, "added desktop telemetry header",
|
||||
slog.F("data", telemetryData))
|
||||
} else {
|
||||
t.logger.Warn(t.ctx, "failed to marshal telemetry data")
|
||||
}
|
||||
}
|
||||
|
||||
var networkingStack NetworkStack
|
||||
if t.networkingStackFn != nil {
|
||||
networkingStack, err = t.networkingStackFn(t, req, t.clientLogger)
|
||||
|
@ -16,10 +16,11 @@ import (
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"tailscale.com/util/dnsname"
|
||||
|
||||
"github.com/coder/quartz"
|
||||
|
||||
"github.com/coder/coder/v2/tailnet"
|
||||
"github.com/coder/coder/v2/tailnet/proto"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
"github.com/coder/quartz"
|
||||
)
|
||||
|
||||
func newFakeClient(ctx context.Context, t *testing.T) *fakeClient {
|
||||
@ -103,6 +104,9 @@ func TestTunnel_StartStop(t *testing.T) {
|
||||
Headers: []*StartRequest_Header{
|
||||
{Name: "X-Test-Header", Value: "test"},
|
||||
},
|
||||
DeviceOs: "macOS",
|
||||
DeviceId: "device001",
|
||||
CoderDesktopVersion: "0.24.8",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -12,7 +12,11 @@ import (
|
||||
// implementation of the VPN RPC protocol.
|
||||
var CurrentSupportedVersions = RPCVersionList{
|
||||
Versions: []RPCVersion{
|
||||
{Major: 1, Minor: 0},
|
||||
// 1.1 adds telemetry fields to StartRequest:
|
||||
// - device_id: Coder Desktop device ID
|
||||
// - device_os: Coder Desktop OS information
|
||||
// - coder_desktop_version: Coder Desktop version
|
||||
{Major: 1, Minor: 1},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -957,6 +957,12 @@ type StartRequest struct {
|
||||
CoderUrl string `protobuf:"bytes,2,opt,name=coder_url,json=coderUrl,proto3" json:"coder_url,omitempty"`
|
||||
ApiToken string `protobuf:"bytes,3,opt,name=api_token,json=apiToken,proto3" json:"api_token,omitempty"`
|
||||
Headers []*StartRequest_Header `protobuf:"bytes,4,rep,name=headers,proto3" json:"headers,omitempty"`
|
||||
// Device ID from Coder Desktop
|
||||
DeviceId string `protobuf:"bytes,5,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"`
|
||||
// Device OS from Coder Desktop
|
||||
DeviceOs string `protobuf:"bytes,6,opt,name=device_os,json=deviceOs,proto3" json:"device_os,omitempty"`
|
||||
// Coder Desktop version
|
||||
CoderDesktopVersion string `protobuf:"bytes,7,opt,name=coder_desktop_version,json=coderDesktopVersion,proto3" json:"coder_desktop_version,omitempty"`
|
||||
}
|
||||
|
||||
func (x *StartRequest) Reset() {
|
||||
@ -1019,6 +1025,27 @@ func (x *StartRequest) GetHeaders() []*StartRequest_Header {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *StartRequest) GetDeviceId() string {
|
||||
if x != nil {
|
||||
return x.DeviceId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *StartRequest) GetDeviceOs() string {
|
||||
if x != nil {
|
||||
return x.DeviceOs
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *StartRequest) GetCoderDesktopVersion() string {
|
||||
if x != nil {
|
||||
return x.CoderDesktopVersion
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type StartResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@ -1839,7 +1866,7 @@ var file_vpn_vpn_proto_rawDesc = []byte{
|
||||
0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73,
|
||||
0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f,
|
||||
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65,
|
||||
0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xe6, 0x01, 0x0a, 0x0c,
|
||||
0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xd4, 0x02, 0x0a, 0x0c,
|
||||
0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x16,
|
||||
0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x63,
|
||||
0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x74, 0x75,
|
||||
@ -1851,25 +1878,32 @@ var file_vpn_vpn_proto_rawDesc = []byte{
|
||||
0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e,
|
||||
0x76, 0x70, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73,
|
||||
0x1a, 0x32, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14,
|
||||
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76,
|
||||
0x61, 0x6c, 0x75, 0x65, 0x22, 0x4e, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12,
|
||||
0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73,
|
||||
0x73, 0x61, 0x67, 0x65, 0x22, 0x0d, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x22, 0x4d, 0x0a, 0x0c, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a,
|
||||
0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6f, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x6f,
|
||||
0x64, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x5f, 0x76, 0x65, 0x72, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x6f, 0x64, 0x65, 0x72,
|
||||
0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x32,
|
||||
0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05,
|
||||
0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x22, 0x4e, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x23, 0x0a,
|
||||
0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61,
|
||||
0x67, 0x65, 0x42, 0x39, 0x5a, 0x1d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f,
|
||||
0x76, 0x70, 0x6e, 0xaa, 0x02, 0x17, 0x43, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x44, 0x65, 0x73, 0x6b,
|
||||
0x74, 0x6f, 0x70, 0x2e, 0x56, 0x70, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x67, 0x65, 0x22, 0x0d, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x22, 0x4d, 0x0a, 0x0c, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x65,
|
||||
0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
|
||||
0x42, 0x39, 0x5a, 0x1d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
|
||||
0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x76, 0x70,
|
||||
0x6e, 0xaa, 0x02, 0x17, 0x43, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f,
|
||||
0x70, 0x2e, 0x56, 0x70, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -185,6 +185,12 @@ message StartRequest {
|
||||
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 {
|
||||
|
Reference in New Issue
Block a user