feature,client: add serviceclientprefs for desktop client service launch (#20501)

Add serviceclientprefs, an optional feature that stores and loads the
desktop clients' saved service launch preferences, one file per login
profile.

- Add GET|POST /localapi/v0/prefs/service-clients to load and save the
  current profile's service client prefs.
- Add local client GetServiceClientPrefs and SetServiceClientPref that
  call the new local api endpoint.
- Store the prefs with the ipn/store FileStore at
  TailscaleVarRoot()/profile-data/<profileID>/service-client-prefs/<hex-encoded-key>,
  so DeleteProfile cleans them up for free. Fall back to an in-memory
  store when there's no var root.
- Register the feature and its local api route from build tagged files
  so the whole thing drops out under ts_omit_serviceclientprefs.
- Add the serviceclient package holding Pref and Prefs (saved client,
  username, database name, and last used time), so the local api client
  and desktop apps can import the types without the feature machinery.

Change-Id: I340a99c1b332d181fb1556fbf3e8003bb3b95a08
Updates: https://github.com/tailscale/tailscale/issues/20429

Signed-off-by: Rollie Ma <rollie@tailscale.com>
This commit is contained in:
Rollie Ma
2026-07-22 20:36:05 -07:00
committed by GitHub
parent 66bb4ac61f
commit f4978b4b9b
20 changed files with 597 additions and 3 deletions
+1
View File
@@ -91,6 +91,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
tailscale.com/envknob from tailscale.com/client/local+
tailscale.com/feature from tailscale.com/tsweb+
tailscale.com/feature/buildfeatures from tailscale.com/feature+
tailscale.com/feature/serviceclientprefs/serviceclient from tailscale.com/client/local
tailscale.com/health from tailscale.com/net/tlsdial+
tailscale.com/hostinfo from tailscale.com/net/netmon+
tailscale.com/ipn from tailscale.com/client/local
+1
View File
@@ -741,6 +741,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
tailscale.com/feature/netlog from tailscale.com/feature/condregister/netlog
tailscale.com/feature/oauthkey from tailscale.com/feature/condregister/oauthkey
tailscale.com/feature/portmapper from tailscale.com/feature/condregister/portmapper
tailscale.com/feature/serviceclientprefs/serviceclient from tailscale.com/client/local
tailscale.com/feature/useproxy from tailscale.com/feature/condregister/useproxy
tailscale.com/health from tailscale.com/control/controlclient+
tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal
+1
View File
@@ -211,6 +211,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/feature/identityfederation from tailscale.com/feature/condregister/identityfederation
tailscale.com/feature/oauthkey from tailscale.com/feature/condregister/oauthkey
tailscale.com/feature/portmapper from tailscale.com/feature/condregister/portmapper
tailscale.com/feature/serviceclientprefs/serviceclient from tailscale.com/client/local
tailscale.com/feature/syspolicy from tailscale.com/cmd/tailscale/cli
tailscale.com/feature/useproxy from tailscale.com/feature/condregister/useproxy
tailscale.com/gokrazy/mkfs from tailscale.com/cmd/tailscale/cli
+2
View File
@@ -322,6 +322,8 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/feature/routecheck from tailscale.com/feature/condregister
tailscale.com/feature/runtimemetrics from tailscale.com/feature/condregister
L tailscale.com/feature/sdnotify from tailscale.com/feature/condregister
tailscale.com/feature/serviceclientprefs from tailscale.com/feature/condregister
tailscale.com/feature/serviceclientprefs/serviceclient from tailscale.com/client/local+
LD tailscale.com/feature/ssh from tailscale.com/cmd/tailscaled
L tailscale.com/feature/syslog from tailscale.com/feature/condregister
tailscale.com/feature/syspolicy from tailscale.com/feature/condregister
+12
View File
@@ -13,6 +13,18 @@ import (
"tailscale.com/tstest/deptest"
)
func TestOmitServiceClientPrefs(t *testing.T) {
const msg = "unexpected with ts_omit_serviceclientprefs"
deptest.DepChecker{
GOOS: "linux",
GOARCH: "amd64",
Tags: "ts_omit_serviceclientprefs,ts_include_cli",
BadDeps: map[string]string{
"tailscale.com/feature/serviceclientprefs": msg,
},
}.Check(t)
}
func TestOmitSSH(t *testing.T) {
const msg = "unexpected with ts_omit_ssh"
deptest.DepChecker{
+1
View File
@@ -158,6 +158,7 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
tailscale.com/feature/netlog from tailscale.com/feature/condregister/netlog
tailscale.com/feature/oauthkey from tailscale.com/feature/condregister/oauthkey
tailscale.com/feature/portmapper from tailscale.com/feature/condregister/portmapper
tailscale.com/feature/serviceclientprefs/serviceclient from tailscale.com/client/local
tailscale.com/feature/useproxy from tailscale.com/feature/condregister/useproxy
tailscale.com/health from tailscale.com/control/controlclient+
tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal