summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/web/web.go2
-rw-r--r--cmd/k8s-operator/ingress-for-pg.go2
-rw-r--r--cmd/tailscale/cli/configure-synology-cert.go2
-rw-r--r--cmd/vet/jsontags_allowlist98
-rw-r--r--health/state.go2
-rw-r--r--internal/client/tailscale/vip_service.go2
-rw-r--r--ipn/auditlog/auditlog.go2
-rw-r--r--ipn/backend.go2
-rw-r--r--ipn/conf.go40
-rw-r--r--ipn/ipnstate/ipnstate.go12
-rw-r--r--ipn/prefs.go6
-rw-r--r--kube/ingressservices/ingressservices.go4
-rw-r--r--kube/k8s-proxy/conf/conf.go34
-rw-r--r--kube/kubeclient/client.go2
-rw-r--r--kube/kubetypes/grants.go2
-rw-r--r--kube/kubetypes/types_test.go2
-rw-r--r--sessionrecording/header.go2
-rw-r--r--tailcfg/derpmap.go2
-rw-r--r--tailcfg/tailcfg.go64
-rw-r--r--tsweb/log.go2
-rw-r--r--types/opt/bool_test.go2
-rw-r--r--types/views/views_test.go4
-rw-r--r--wgengine/magicsock/endpoint.go4
23 files changed, 98 insertions, 196 deletions
diff --git a/client/web/web.go b/client/web/web.go
index dbd3d5df0..39d85ce1f 100644
--- a/client/web/web.go
+++ b/client/web/web.go
@@ -706,7 +706,7 @@ func (s *Server) serveAPI(w http.ResponseWriter, r *http.Request) {
type authResponse struct {
ServerMode ServerMode `json:"serverMode"`
Authorized bool `json:"authorized"` // has an authorized management session
- ViewerIdentity *viewerIdentity `json:"viewerIdentity,omitempty"`
+ ViewerIdentity *viewerIdentity `json:"viewerIdentity,omitzero"`
NeedsSynoAuth bool `json:"needsSynoAuth,omitempty"`
}
diff --git a/cmd/k8s-operator/ingress-for-pg.go b/cmd/k8s-operator/ingress-for-pg.go
index 3afeb528f..f1414a8bd 100644
--- a/cmd/k8s-operator/ingress-for-pg.go
+++ b/cmd/k8s-operator/ingress-for-pg.go
@@ -860,7 +860,7 @@ type ownerAnnotationValue struct {
type OwnerRef struct {
// OperatorID is the stable ID of the operator's Tailscale device.
OperatorID string `json:"operatorID,omitempty"`
- Resource *Resource `json:"resource,omitempty"` // optional, used to identify the ProxyGroup that owns this Tailscale Service.
+ Resource *Resource `json:"resource,omitzero"` // optional, used to identify the ProxyGroup that owns this Tailscale Service.
}
type Resource struct {
diff --git a/cmd/tailscale/cli/configure-synology-cert.go b/cmd/tailscale/cli/configure-synology-cert.go
index b5168ef92..e219e75f3 100644
--- a/cmd/tailscale/cli/configure-synology-cert.go
+++ b/cmd/tailscale/cli/configure-synology-cert.go
@@ -194,7 +194,7 @@ type synoAPICaller interface {
type apiResponse struct {
Success bool `json:"success"`
- Error *apiError `json:"error,omitempty"`
+ Error *apiError `json:"error,omitzero"`
Data json.RawMessage `json:"data"`
}
diff --git a/cmd/vet/jsontags_allowlist b/cmd/vet/jsontags_allowlist
index 060a81b05..68f05f3ef 100644
--- a/cmd/vet/jsontags_allowlist
+++ b/cmd/vet/jsontags_allowlist
@@ -1,39 +1,3 @@
-OmitEmptyShouldBeOmitZero tailscale.com/client/web.authResponse.ViewerIdentity
-OmitEmptyShouldBeOmitZero tailscale.com/cmd/k8s-operator.OwnerRef.Resource
-OmitEmptyShouldBeOmitZero tailscale.com/cmd/tailscale/cli.apiResponse.Error
-OmitEmptyShouldBeOmitZero tailscale.com/health.UnhealthyState.PrimaryAction
-OmitEmptyShouldBeOmitZero tailscale.com/internal/client/tailscale.VIPService.Name
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.AcceptDNS
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.AcceptRoutes
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.AllowLANWhileUsingExitNode
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.AppConnector
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.AuthKey
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.AutoUpdate
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.DisableSNAT
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.Enabled
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.ExitNode
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.Hostname
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.Locked
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.NetfilterMode
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.NoStatefulFiltering
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.OperatorUser
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.PostureChecking
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.RunSSHServer
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.RunWebClient
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.ServeConfigTemp
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.ServerURL
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.ShieldsUp
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.OutgoingFile.PeerID
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.Prefs.AutoExitNode
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.Prefs.NoStatefulFiltering
-OmitEmptyShouldBeOmitZero tailscale.com/ipn.Prefs.RelayServerPort
-OmitEmptyShouldBeOmitZero tailscale.com/ipn/auditlog.transaction.Action
-OmitEmptyShouldBeOmitZero tailscale.com/ipn/ipnstate.PeerStatus.AllowedIPs
-OmitEmptyShouldBeOmitZero tailscale.com/ipn/ipnstate.PeerStatus.Location
-OmitEmptyShouldBeOmitZero tailscale.com/ipn/ipnstate.PeerStatus.PrimaryRoutes
-OmitEmptyShouldBeOmitZero tailscale.com/ipn/ipnstate.PeerStatus.Tags
-OmitEmptyShouldBeOmitZero tailscale.com/ipn/ipnstate.Status.ExitNodeStatus
-OmitEmptyShouldBeOmitZero tailscale.com/ipn/ipnstate.UpdateProgress.Status
OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.ConnectorSpec.AppConnector
OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.ConnectorSpec.Hostname
OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.ConnectorSpec.HostnamePrefix
@@ -65,71 +29,9 @@ OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.RecorderPod.A
OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.RecorderPod.SecurityContext
OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.StatefulSet.Pod
OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.Storage.S3
-OmitEmptyShouldBeOmitZero tailscale.com/kube/ingressservices.Config.IPv4Mapping
-OmitEmptyShouldBeOmitZero tailscale.com/kube/ingressservices.Config.IPv6Mapping
-OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.APIServerProxyConfig.Enabled
-OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.APIServerProxyConfig.IssueCerts
-OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.APIServerProxyConfig.Mode
-OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.APIServerProxyConfig.ServiceName
-OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.AcceptRoutes
-OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.APIServerProxy
-OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.App
-OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.AuthKey
-OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.HealthCheckEnabled
-OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.Hostname
-OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.LocalAddr
-OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.LocalPort
-OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.LogLevel
-OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.MetricsEnabled
-OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.ServerURL
-OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.State
-OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.VersionedConfig.V1Alpha1
OmitEmptyShouldBeOmitZero tailscale.com/kube/kubeapi.ObjectMeta.DeletionGracePeriodSeconds
OmitEmptyShouldBeOmitZero tailscale.com/kube/kubeapi.Status.Details
-OmitEmptyShouldBeOmitZero tailscale.com/kube/kubeclient.JSONPatch.Value
-OmitEmptyShouldBeOmitZero tailscale.com/kube/kubetypes.*.Mode
-OmitEmptyShouldBeOmitZero tailscale.com/kube/kubetypes.KubernetesCapRule.Impersonate
-OmitEmptyShouldBeOmitZero tailscale.com/sessionrecording.CastHeader.Kubernetes
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.AuditLogRequest.Action
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Debug.Exit
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.DERPMap.HomeParams
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.DisplayMessage.PrimaryAction
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Hostinfo.AppConnector
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Hostinfo.Container
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Hostinfo.Desktop
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Hostinfo.Location
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Hostinfo.NetInfo
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Hostinfo.StateEncrypted
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Hostinfo.TPM
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Hostinfo.Userspace
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Hostinfo.UserspaceRouter
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.ClientVersion
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.CollectServices
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.ControlDialPlan
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.Debug
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.DefaultAutoUpdate
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.DERPMap
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.DNSConfig
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.Node
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.PingRequest
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.SSHPolicy
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.TKAInfo
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.NetPortRange.Bits
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Node.Online
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Node.SelfNodeV4MasqAddrForThisPeer
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Node.SelfNodeV6MasqAddrForThisPeer
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.PeerChange.Online
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.RegisterRequest.Auth
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.RegisterResponseAuth.Oauth2Token
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.SSHAction.OnRecordingFailure
-OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.SSHPrincipal.Node
OmitEmptyShouldBeOmitZero tailscale.com/tempfork/acme.*.ExternalAccountBinding
-OmitEmptyShouldBeOmitZero tailscale.com/tsweb.AccessLogRecord.RequestID
-OmitEmptyShouldBeOmitZero tailscale.com/types/opt.*.Unset
-OmitEmptyShouldBeOmitZero tailscale.com/types/views.viewStruct.AddrsPtr
-OmitEmptyShouldBeOmitZero tailscale.com/types/views.viewStruct.StringsPtr
-OmitEmptyShouldBeOmitZero tailscale.com/wgengine/magicsock.EndpointChange.From
-OmitEmptyShouldBeOmitZero tailscale.com/wgengine/magicsock.EndpointChange.To
OmitEmptyShouldBeOmitZeroButHasIsZero tailscale.com/types/persist.Persist.AttestationKey
OmitEmptyUnsupportedInV1 tailscale.com/client/tailscale.KeyCapabilities.Devices
OmitEmptyUnsupportedInV1 tailscale.com/client/tailscale/apitype.ExitNodeSuggestionResponse.Location
diff --git a/health/state.go b/health/state.go
index e6d937b6a..3ce78c653 100644
--- a/health/state.go
+++ b/health/state.go
@@ -41,7 +41,7 @@ type UnhealthyState struct {
Args Args `json:",omitempty"`
DependsOn []WarnableCode `json:",omitempty"`
ImpactsConnectivity bool `json:",omitempty"`
- PrimaryAction *UnhealthyStateAction `json:",omitempty"`
+ PrimaryAction *UnhealthyStateAction `json:",omitzero"`
// ETag identifies a specific version of an UnhealthyState. If the contents
// of the other fields of two UnhealthyStates are the same, the ETags will
diff --git a/internal/client/tailscale/vip_service.go b/internal/client/tailscale/vip_service.go
index 48c59ce45..e47c60654 100644
--- a/internal/client/tailscale/vip_service.go
+++ b/internal/client/tailscale/vip_service.go
@@ -17,7 +17,7 @@ import (
// VIPService is a Tailscale VIPService with Tailscale API JSON representation.
type VIPService struct {
// Name is a VIPService name in form svc:<leftmost-label-of-service-DNS-name>.
- Name tailcfg.ServiceName `json:"name,omitempty"`
+ Name tailcfg.ServiceName `json:"name,omitzero"`
// Addrs are the IP addresses of the VIP Service. There are two addresses:
// the first is IPv4 and the second is IPv6.
// When creating a new VIP Service, the IP addresses are optional: if no
diff --git a/ipn/auditlog/auditlog.go b/ipn/auditlog/auditlog.go
index 0460bc4e2..6fb3e4242 100644
--- a/ipn/auditlog/auditlog.go
+++ b/ipn/auditlog/auditlog.go
@@ -30,7 +30,7 @@ type transaction struct {
Retries int `json:",omitempty"`
// Action is the action to be logged. It must correspond to a known action in the control plane.
- Action tailcfg.ClientAuditAction `json:",omitempty"`
+ Action tailcfg.ClientAuditAction `json:",omitzero"`
// Details is an opaque string specific to the action being logged. Empty strings may not
// be valid depending on the action being logged.
Details string `json:",omitempty"`
diff --git a/ipn/backend.go b/ipn/backend.go
index 91cf81ca5..2b1ee6c78 100644
--- a/ipn/backend.go
+++ b/ipn/backend.go
@@ -234,7 +234,7 @@ type PartialFile struct {
// OutgoingFile represents an in-progress outgoing file transfer.
type OutgoingFile struct {
ID string `json:",omitempty"` // unique identifier for this transfer (a type 4 UUID)
- PeerID tailcfg.StableNodeID `json:",omitempty"` // identifier for the peer to which this is being transferred
+ PeerID tailcfg.StableNodeID `json:",omitzero"` // identifier for the peer to which this is being transferred
Name string `json:",omitempty"` // e.g. "foo.jpg"
Started time.Time // time transfer started
DeclaredSize int64 // or -1 if unknown
diff --git a/ipn/conf.go b/ipn/conf.go
index 2c9fb2fd1..f75c8bdfe 100644
--- a/ipn/conf.go
+++ b/ipn/conf.go
@@ -14,37 +14,37 @@ import (
// ConfigVAlpha is the config file format for the "alpha0" version.
type ConfigVAlpha struct {
Version string // "alpha0" for now
- Locked opt.Bool `json:",omitempty"` // whether the config is locked from being changed by 'tailscale set'; it defaults to true
+ Locked opt.Bool `json:",omitzero"` // whether the config is locked from being changed by 'tailscale set'; it defaults to true
- ServerURL *string `json:",omitempty"` // defaults to https://controlplane.tailscale.com
- AuthKey *string `json:",omitempty"` // as needed if NeedsLogin. either key or path to a file (if prefixed with "file:")
- Enabled opt.Bool `json:",omitempty"` // wantRunning; empty string defaults to true
+ ServerURL *string `json:",omitzero"` // defaults to https://controlplane.tailscale.com
+ AuthKey *string `json:",omitzero"` // as needed if NeedsLogin. either key or path to a file (if prefixed with "file:")
+ Enabled opt.Bool `json:",omitzero"` // wantRunning; empty string defaults to true
- OperatorUser *string `json:",omitempty"` // local user name who is allowed to operate tailscaled without being root or using sudo
- Hostname *string `json:",omitempty"`
+ OperatorUser *string `json:",omitzero"` // local user name who is allowed to operate tailscaled without being root or using sudo
+ Hostname *string `json:",omitzero"`
- AcceptDNS opt.Bool `json:"acceptDNS,omitempty"` // --accept-dns
- AcceptRoutes opt.Bool `json:"acceptRoutes,omitempty"` // --accept-routes defaults to true
+ AcceptDNS opt.Bool `json:"acceptDNS,omitzero"` // --accept-dns
+ AcceptRoutes opt.Bool `json:"acceptRoutes,omitzero"` // --accept-routes defaults to true
- ExitNode *string `json:"exitNode,omitempty"` // IP, StableID, or MagicDNS base name
- AllowLANWhileUsingExitNode opt.Bool `json:"allowLANWhileUsingExitNode,omitempty"`
+ ExitNode *string `json:"exitNode,omitzero"` // IP, StableID, or MagicDNS base name
+ AllowLANWhileUsingExitNode opt.Bool `json:"allowLANWhileUsingExitNode,omitzero"`
AdvertiseRoutes []netip.Prefix `json:",omitempty"`
- DisableSNAT opt.Bool `json:",omitempty"`
+ DisableSNAT opt.Bool `json:",omitzero"`
AdvertiseServices []string `json:",omitempty"`
- AppConnector *AppConnectorPrefs `json:",omitempty"` // advertise app connector; defaults to false (if nil or explicitly set to false)
+ AppConnector *AppConnectorPrefs `json:",omitzero"` // advertise app connector; defaults to false (if nil or explicitly set to false)
- NetfilterMode *string `json:",omitempty"` // "on", "off", "nodivert"
- NoStatefulFiltering opt.Bool `json:",omitempty"`
+ NetfilterMode *string `json:",omitzero"` // "on", "off", "nodivert"
+ NoStatefulFiltering opt.Bool `json:",omitzero"`
- PostureChecking opt.Bool `json:",omitempty"`
- RunSSHServer opt.Bool `json:",omitempty"` // Tailscale SSH
- RunWebClient opt.Bool `json:",omitempty"`
- ShieldsUp opt.Bool `json:",omitempty"`
- AutoUpdate *AutoUpdatePrefs `json:",omitempty"`
- ServeConfigTemp *ServeConfig `json:",omitempty"` // TODO(bradfitz,maisem): make separate stable type for this
+ PostureChecking opt.Bool `json:",omitzero"`
+ RunSSHServer opt.Bool `json:",omitzero"` // Tailscale SSH
+ RunWebClient opt.Bool `json:",omitzero"`
+ ShieldsUp opt.Bool `json:",omitzero"`
+ AutoUpdate *AutoUpdatePrefs `json:",omitzero"`
+ ServeConfigTemp *ServeConfig `json:",omitzero"` // TODO(bradfitz,maisem): make separate stable type for this
// StaticEndpoints are additional, user-defined endpoints that this node
// should advertise amongst its wireguard endpoints.
diff --git a/ipn/ipnstate/ipnstate.go b/ipn/ipnstate/ipnstate.go
index e7ae2d62b..ec9ac6356 100644
--- a/ipn/ipnstate/ipnstate.go
+++ b/ipn/ipnstate/ipnstate.go
@@ -51,7 +51,7 @@ type Status struct {
// ExitNodeStatus describes the current exit node.
// If nil, an exit node is not in use.
- ExitNodeStatus *ExitNodeStatus `json:"ExitNodeStatus,omitempty"`
+ ExitNodeStatus *ExitNodeStatus `json:"ExitNodeStatus,omitzero"`
// Health contains health check problems.
// Empty means everything is good. (or at least that no known
@@ -239,16 +239,16 @@ type PeerStatus struct {
// TailscaleIPs are the IP addresses assigned to the node.
TailscaleIPs []netip.Addr
// AllowedIPs are IP addresses allowed to route to this node.
- AllowedIPs *views.Slice[netip.Prefix] `json:",omitempty"`
+ AllowedIPs *views.Slice[netip.Prefix] `json:",omitzero"`
// Tags are the list of ACL tags applied to this node.
// See tailscale.com/tailcfg#Node.Tags for more information.
- Tags *views.Slice[string] `json:",omitempty"`
+ Tags *views.Slice[string] `json:",omitzero"`
// PrimaryRoutes are the routes this node is currently the primary
// subnet router for, as determined by the control plane. It does
// not include the IPs in TailscaleIPs.
- PrimaryRoutes *views.Slice[netip.Prefix] `json:",omitempty"`
+ PrimaryRoutes *views.Slice[netip.Prefix] `json:",omitzero"`
// Endpoints:
Addrs []string
@@ -327,7 +327,7 @@ type PeerStatus struct {
// will expire.
KeyExpiry *time.Time `json:",omitempty"`
- Location *tailcfg.Location `json:",omitempty"`
+ Location *tailcfg.Location `json:",omitzero"`
}
type TaildropTargetStatus int
@@ -796,7 +796,7 @@ const (
)
type UpdateProgress struct {
- Status SelfUpdateStatus `json:"status,omitempty"`
+ Status SelfUpdateStatus `json:"status,omitzero"`
Message string `json:"message,omitempty"`
Version string `json:"version,omitempty"`
}
diff --git a/ipn/prefs.go b/ipn/prefs.go
index 81dd1c1c3..b7e622d6c 100644
--- a/ipn/prefs.go
+++ b/ipn/prefs.go
@@ -114,7 +114,7 @@ type Prefs struct {
// As of 2025-07-02, the only supported value is [AnyExitNode].
// It's a string rather than a boolean to allow future extensibility
// (e.g., AutoExitNode = "mullvad" or AutoExitNode = "geo:us").
- AutoExitNode ExitNodeExpression `json:",omitempty"`
+ AutoExitNode ExitNodeExpression `json:",omitzero"`
// InternalExitNodePrior is the most recently used ExitNodeID in string form. It is set by
// the backend on transition from exit node on to off and used by the
@@ -231,7 +231,7 @@ type Prefs struct {
// removed since then, but the field remains an opt.Bool.
//
// Linux-only.
- NoStatefulFiltering opt.Bool `json:",omitempty"`
+ NoStatefulFiltering opt.Bool `json:",omitzero"`
// NetfilterMode specifies how much to manage netfilter rules for
// Tailscale, if at all.
@@ -280,7 +280,7 @@ type Prefs struct {
// should be disabled. This field is currently experimental, and therefore
// no guarantees are made about its current naming and functionality when
// non-nil/enabled.
- RelayServerPort *int `json:",omitempty"`
+ RelayServerPort *int `json:",omitzero"`
// AllowSingleHosts was a legacy field that was always true
// for the past 4.5 years. It controlled whether Tailscale
diff --git a/kube/ingressservices/ingressservices.go b/kube/ingressservices/ingressservices.go
index f79410761..7aeccc7ed 100644
--- a/kube/ingressservices/ingressservices.go
+++ b/kube/ingressservices/ingressservices.go
@@ -41,8 +41,8 @@ type Status struct {
// Config is an ingress service configuration.
type Config struct {
- IPv4Mapping *Mapping `json:"IPv4Mapping,omitempty"`
- IPv6Mapping *Mapping `json:"IPv6Mapping,omitempty"`
+ IPv4Mapping *Mapping `json:"IPv4Mapping,omitzero"`
+ IPv6Mapping *Mapping `json:"IPv6Mapping,omitzero"`
}
// Mapping describes a rule that forwards traffic from Tailscale Service IP to a
diff --git a/kube/k8s-proxy/conf/conf.go b/kube/k8s-proxy/conf/conf.go
index 529495243..a607449e8 100644
--- a/kube/k8s-proxy/conf/conf.go
+++ b/kube/k8s-proxy/conf/conf.go
@@ -44,35 +44,35 @@ type VersionedConfig struct {
// Backwards compatibility version(s) of the config. Fields and sub-fields
// from here should only be added to, never changed in place.
- V1Alpha1 *ConfigV1Alpha1 `json:",omitempty"`
+ V1Alpha1 *ConfigV1Alpha1 `json:",omitzero"`
// V1Beta1 *ConfigV1Beta1 `json:",omitempty"` // Not yet used.
}
type ConfigV1Alpha1 struct {
- AuthKey *string `json:",omitempty"` // Tailscale auth key to use.
- State *string `json:",omitempty"` // Path to the Tailscale state.
- LogLevel *string `json:",omitempty"` // "debug", "info". Defaults to "info".
- App *string `json:",omitempty"` // e.g. kubetypes.AppProxyGroupKubeAPIServer
- ServerURL *string `json:",omitempty"` // URL of the Tailscale coordination server.
- LocalAddr *string `json:",omitempty"` // The address to use for serving HTTP health checks and metrics (defaults to all interfaces).
- LocalPort *uint16 `json:",omitempty"` // The port to use for serving HTTP health checks and metrics (defaults to 9002).
- MetricsEnabled opt.Bool `json:",omitempty"` // Serve metrics on <LocalAddr>:<LocalPort>/metrics.
- HealthCheckEnabled opt.Bool `json:",omitempty"` // Serve health check on <LocalAddr>:<LocalPort>/metrics.
+ AuthKey *string `json:",omitzero"` // Tailscale auth key to use.
+ State *string `json:",omitzero"` // Path to the Tailscale state.
+ LogLevel *string `json:",omitzero"` // "debug", "info". Defaults to "info".
+ App *string `json:",omitzero"` // e.g. kubetypes.AppProxyGroupKubeAPIServer
+ ServerURL *string `json:",omitzero"` // URL of the Tailscale coordination server.
+ LocalAddr *string `json:",omitzero"` // The address to use for serving HTTP health checks and metrics (defaults to all interfaces).
+ LocalPort *uint16 `json:",omitzero"` // The port to use for serving HTTP health checks and metrics (defaults to 9002).
+ MetricsEnabled opt.Bool `json:",omitzero"` // Serve metrics on <LocalAddr>:<LocalPort>/metrics.
+ HealthCheckEnabled opt.Bool `json:",omitzero"` // Serve health check on <LocalAddr>:<LocalPort>/metrics.
// TODO(tomhjp): The remaining fields should all be reloadable during
// runtime, but currently missing most of the APIServerProxy fields.
- Hostname *string `json:",omitempty"` // Tailscale device hostname.
- AcceptRoutes opt.Bool `json:",omitempty"` // Accepts routes advertised by other Tailscale nodes.
+ Hostname *string `json:",omitzero"` // Tailscale device hostname.
+ AcceptRoutes opt.Bool `json:",omitzero"` // Accepts routes advertised by other Tailscale nodes.
AdvertiseServices []string `json:",omitempty"` // Tailscale Services to advertise.
- APIServerProxy *APIServerProxyConfig `json:",omitempty"` // Config specific to the API Server proxy.
+ APIServerProxy *APIServerProxyConfig `json:",omitzero"` // Config specific to the API Server proxy.
StaticEndpoints []netip.AddrPort `json:",omitempty"` // StaticEndpoints are additional, user-defined endpoints that this node should advertise amongst its wireguard endpoints.
}
type APIServerProxyConfig struct {
- Enabled opt.Bool `json:",omitempty"` // Whether to enable the API Server proxy.
- Mode *kubetypes.APIServerProxyMode `json:",omitempty"` // "auth" or "noauth" mode.
- ServiceName *tailcfg.ServiceName `json:",omitempty"` // Name of the Tailscale Service to advertise.
- IssueCerts opt.Bool `json:",omitempty"` // Whether this replica should issue TLS certs for the Tailscale Service.
+ Enabled opt.Bool `json:",omitzero"` // Whether to enable the API Server proxy.
+ Mode *kubetypes.APIServerProxyMode `json:",omitzero"` // "auth" or "noauth" mode.
+ ServiceName *tailcfg.ServiceName `json:",omitzero"` // Name of the Tailscale Service to advertise.
+ IssueCerts opt.Bool `json:",omitzero"` // Whether this replica should issue TLS certs for the Tailscale Service.
}
// Load reads and parses the config file at the provided path on disk.
diff --git a/kube/kubeclient/client.go b/kube/kubeclient/client.go
index 0ed960f4d..89b14da5d 100644
--- a/kube/kubeclient/client.go
+++ b/kube/kubeclient/client.go
@@ -287,7 +287,7 @@ func (c *client) UpdateSecret(ctx context.Context, s *kubeapi.Secret) error {
type JSONPatch struct {
Op string `json:"op"`
Path string `json:"path"`
- Value any `json:"value,omitempty"`
+ Value any `json:"value,omitzero"`
}
// JSONPatchResource updates a resource in the Kubernetes API using a JSON patch.
diff --git a/kube/kubetypes/grants.go b/kube/kubetypes/grants.go
index 4dc278ff1..18642f2dc 100644
--- a/kube/kubetypes/grants.go
+++ b/kube/kubetypes/grants.go
@@ -14,7 +14,7 @@ import "net/netip"
type KubernetesCapRule struct {
// Impersonate is a list of rules that specify how to impersonate the caller
// when proxying to the Kubernetes API.
- Impersonate *ImpersonateRule `json:"impersonate,omitempty"`
+ Impersonate *ImpersonateRule `json:"impersonate,omitzero"`
// Recorders defines a tag of a tsrecorder instance(s) that a recording
// of a 'kubectl exec' session, matching `src` of this grant, to an API
// server proxy, matching `dst` of this grant, should be sent to.
diff --git a/kube/kubetypes/types_test.go b/kube/kubetypes/types_test.go
index ea1846b32..9d328e0b2 100644
--- a/kube/kubetypes/types_test.go
+++ b/kube/kubetypes/types_test.go
@@ -22,7 +22,7 @@ func TestUnmarshalAPIServerProxyMode(t *testing.T) {
for _, tc := range tests {
var s struct {
- Mode *APIServerProxyMode `json:",omitempty"`
+ Mode *APIServerProxyMode `json:",omitzero"`
}
err := json.Unmarshal([]byte(tc.data), &s)
if tc.expected == "" {
diff --git a/sessionrecording/header.go b/sessionrecording/header.go
index 220852216..816f54f76 100644
--- a/sessionrecording/header.go
+++ b/sessionrecording/header.go
@@ -62,7 +62,7 @@ type CastHeader struct {
ConnectionID string `json:"connectionID"`
// Fields that are only set for Kubernetes API server proxy session recordings:
- Kubernetes *Kubernetes `json:"kubernetes,omitempty"`
+ Kubernetes *Kubernetes `json:"kubernetes,omitzero"`
}
// Kubernetes contains 'kubectl exec/attach' session specific information for
diff --git a/tailcfg/derpmap.go b/tailcfg/derpmap.go
index e05559f3e..97c10640b 100644
--- a/tailcfg/derpmap.go
+++ b/tailcfg/derpmap.go
@@ -15,7 +15,7 @@ type DERPMap struct {
// HomeParams, if non-nil, is a change in home parameters.
//
// The rest of the DEPRMap fields, if zero, means unchanged.
- HomeParams *DERPHomeParams `json:",omitempty"`
+ HomeParams *DERPHomeParams `json:",omitzero"`
// Regions is the set of geographic regions running DERP node(s).
//
diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go
index a95d0559c..e45f7b8c2 100644
--- a/tailcfg/tailcfg.go
+++ b/tailcfg/tailcfg.go
@@ -412,7 +412,7 @@ type Node struct {
// Online is whether the node is currently connected to the
// coordination server. A value of nil means unknown, or the
// current node doesn't have permission to know.
- Online *bool `json:",omitempty"`
+ Online *bool `json:",omitzero"`
MachineAuthorized bool `json:",omitempty"` // TODO(crawshaw): replace with MachineStatus
@@ -486,7 +486,7 @@ type Node struct {
// This only applies to traffic originating from the current node to the
// peer or any of its subnets. Traffic originating from subnet routes will
// not be masqueraded (e.g. in case of --snat-subnet-routes).
- SelfNodeV4MasqAddrForThisPeer *netip.Addr `json:",omitempty"`
+ SelfNodeV4MasqAddrForThisPeer *netip.Addr `json:",omitzero"`
// SelfNodeV6MasqAddrForThisPeer is the IPv6 that this peer knows the current node as.
// It may be empty if the peer knows the current node by its native
@@ -501,7 +501,7 @@ type Node struct {
// This only applies to traffic originating from the current node to the
// peer or any of its subnets. Traffic originating from subnet routes will
// not be masqueraded (e.g. in case of --snat-subnet-routes).
- SelfNodeV6MasqAddrForThisPeer *netip.Addr `json:",omitempty"`
+ SelfNodeV6MasqAddrForThisPeer *netip.Addr `json:",omitzero"`
// IsWireGuardOnly indicates that this is a non-Tailscale WireGuard peer, it
// is not expected to speak Disco or DERP, and it must have Endpoints in
@@ -844,7 +844,7 @@ type Hostinfo struct {
// "5.10.0-17-amd64".
OSVersion string `json:",omitempty"`
- Container opt.Bool `json:",omitempty"` // best-effort whether the client is running in a container
+ Container opt.Bool `json:",omitzero"` // best-effort whether the client is running in a container
Env string `json:",omitempty"` // a hostinfo.EnvType in string form
Distro string `json:",omitempty"` // "debian", "ubuntu", "nixos", ...
DistroVersion string `json:",omitempty"` // "20.04", ...
@@ -853,7 +853,7 @@ type Hostinfo struct {
// App is used to disambiguate Tailscale clients that run using tsnet.
App string `json:",omitempty"` // "k8s-operator", "golinks", ...
- Desktop opt.Bool `json:",omitempty"` // if a desktop was detected on Linux
+ Desktop opt.Bool `json:",omitzero"` // if a desktop was detected on Linux
Package string `json:",omitempty"` // Tailscale package to disambiguate ("choco", "appstore", etc; "" for unknown)
DeviceModel string `json:",omitempty"` // mobile phone model ("Pixel 3a", "iPhone12,3")
PushDeviceToken string `json:",omitempty"` // macOS/iOS APNs device token for notifications (and Android in the future)
@@ -879,27 +879,27 @@ type Hostinfo struct {
RequestTags []string `json:",omitempty"` // set of ACL tags this node wants to claim
WoLMACs []string `json:",omitempty"` // MAC address(es) to send Wake-on-LAN packets to wake this node (lowercase hex w/ colons)
Services []Service `json:",omitempty"` // services advertised by this machine
- NetInfo *NetInfo `json:",omitempty"`
+ NetInfo *NetInfo `json:",omitzero"`
SSH_HostKeys []string `json:"sshHostKeys,omitempty"` // if advertised
Cloud string `json:",omitempty"`
- Userspace opt.Bool `json:",omitempty"` // if the client is running in userspace (netstack) mode
- UserspaceRouter opt.Bool `json:",omitempty"` // if the client's subnet router is running in userspace (netstack) mode
- AppConnector opt.Bool `json:",omitempty"` // if the client is running the app-connector service
+ Userspace opt.Bool `json:",omitzero"` // if the client is running in userspace (netstack) mode
+ UserspaceRouter opt.Bool `json:",omitzero"` // if the client's subnet router is running in userspace (netstack) mode
+ AppConnector opt.Bool `json:",omitzero"` // if the client is running the app-connector service
ServicesHash string `json:",omitempty"` // opaque hash of the most recent list of tailnet services, change in hash indicates config should be fetched via c2n
ExitNodeID StableNodeID `json:",omitzero"` // the client’s selected exit node, empty when unselected.
// Location represents geographical location data about a
// Tailscale host. Location is optional and only set if
// explicitly declared by a node.
- Location *Location `json:",omitempty"`
+ Location *Location `json:",omitzero"`
- TPM *TPMInfo `json:",omitempty"` // TPM device metadata, if available
+ TPM *TPMInfo `json:",omitzero"` // TPM device metadata, if available
// StateEncrypted reports whether the node state is stored encrypted on
// disk. The actual mechanism is platform-specific:
// * Apple nodes use the Keychain
// * Linux and Windows nodes use the TPM
// * Android apps use EncryptedSharedPreferences
- StateEncrypted opt.Bool `json:",omitempty"`
+ StateEncrypted opt.Bool `json:",omitzero"`
// NOTE: any new fields containing pointers in this type
// require changes to Hostinfo.Equal.
@@ -1234,7 +1234,7 @@ type RegisterResponseAuth struct {
// At most one of Oauth2Token or AuthKey is set.
- Oauth2Token *Oauth2Token `json:",omitempty"` // used by pre-1.66 Android only
+ Oauth2Token *Oauth2Token `json:",omitzero"` // used by pre-1.66 Android only
AuthKey string `json:",omitempty"`
}
@@ -1256,7 +1256,7 @@ type RegisterRequest struct {
NodeKey key.NodePublic
OldNodeKey key.NodePublic
NLKey key.NLPublic
- Auth *RegisterResponseAuth `json:",omitempty"`
+ Auth *RegisterResponseAuth `json:",omitzero"`
// Expiry optionally specifies the requested key expiry.
// The server policy may override.
// As a special case, if Expiry is in the past and NodeKey is
@@ -1501,7 +1501,7 @@ func (pr PortRange) String() string {
type NetPortRange struct {
_ structs.Incomparable
IP string // IP, CIDR, Range, or "*" (same formats as FilterRule.SrcIPs)
- Bits *int `json:",omitempty"` // deprecated; the 2020 way to turn IP into a CIDR. See FilterRule.SrcBits.
+ Bits *int `json:",omitzero"` // deprecated; the 2020 way to turn IP into a CIDR. See FilterRule.SrcBits.
Ports PortRange
}
@@ -1978,7 +1978,7 @@ type MapResponse struct {
// provided URL. No auth headers are necessary.
// PingRequest may be sent on any MapResponse (ones with
// KeepAlive true or false).
- PingRequest *PingRequest `json:",omitempty"`
+ PingRequest *PingRequest `json:",omitzero"`
// PopBrowserURL, if non-empty, is a URL for the client to
// open to complete an action. The client should dup suppress
@@ -1989,11 +1989,11 @@ type MapResponse struct {
// Node describes the node making the map request.
// Starting with MapRequest.Version 18, nil means unchanged.
- Node *Node `json:",omitempty"`
+ Node *Node `json:",omitzero"`
// DERPMap describe the set of DERP servers available.
// A nil value means unchanged.
- DERPMap *DERPMap `json:",omitempty"`
+ DERPMap *DERPMap `json:",omitzero"`
// Peers, if non-empty, is the complete list of peers.
// It will be set in the first MapResponse for a long-polled request/response.
@@ -2030,7 +2030,7 @@ type MapResponse struct {
// DNSConfig contains the DNS settings for the client to use.
// A nil value means no change from an earlier non-nil value.
- DNSConfig *DNSConfig `json:",omitempty"`
+ DNSConfig *DNSConfig `json:",omitzero"`
// Domain is the name of the network that this node is
// in. It's either of the form "example.com" (for user
@@ -2045,7 +2045,7 @@ type MapResponse struct {
// requested that info about services be included in HostInfo.
// If unset, the most recent non-empty MapResponse value in
// the HTTP response stream is used.
- CollectServices opt.Bool `json:",omitempty"`
+ CollectServices opt.Bool `json:",omitzero"`
// PacketFilter are the firewall rules.
//
@@ -2121,7 +2121,7 @@ type MapResponse struct {
// SSHPolicy, if non-nil, updates the SSH policy for how incoming
// SSH connections should be handled.
- SSHPolicy *SSHPolicy `json:",omitempty"`
+ SSHPolicy *SSHPolicy `json:",omitzero"`
// ControlTime, if non-zero, is the current timestamp according to the control server.
ControlTime *time.Time `json:",omitempty"`
@@ -2134,7 +2134,7 @@ type MapResponse struct {
// indicates the control plane believes TKA should be enabled.
// A nil TKAInfo in a mapresponse stream (i.e. a 'delta' mapresponse)
// indicates no change from the value sent earlier.
- TKAInfo *TKAInfo `json:",omitempty"`
+ TKAInfo *TKAInfo `json:",omitzero"`
// DomainDataPlaneAuditLogID, if non-empty, is the per-tailnet log ID to be
// used when writing data plane audit logs.
@@ -2142,24 +2142,24 @@ type MapResponse struct {
// Debug is normally nil, except for when the control server
// is setting debug settings on a node.
- Debug *Debug `json:",omitempty"`
+ Debug *Debug `json:",omitzero"`
// ControlDialPlan tells the client how to connect to the control
// server. An initial nil is equivalent to new(ControlDialPlan).
// A subsequent streamed nil means no change.
- ControlDialPlan *ControlDialPlan `json:",omitempty"`
+ ControlDialPlan *ControlDialPlan `json:",omitzero"`
// ClientVersion describes the latest client version that's available for
// download and whether the client is using it. A nil value means no change
// or nothing to report.
- ClientVersion *ClientVersion `json:",omitempty"`
+ ClientVersion *ClientVersion `json:",omitzero"`
// DefaultAutoUpdate is the default node auto-update setting for this
// tailnet. The node is free to opt-in or out locally regardless of this
// value. This value is only used on first MapResponse from control, the
// auto-update setting doesn't change if the tailnet admin flips the
// default after the node registered.
- DefaultAutoUpdate opt.Bool `json:",omitempty"`
+ DefaultAutoUpdate opt.Bool `json:",omitzero"`
}
// DisplayMessage represents a health state of the node from the control plane's
@@ -2197,7 +2197,7 @@ type DisplayMessage struct {
// take when interacting with this message. For example, if the
// DisplayMessage is shown via a notification, the action label might be a
// button on that notification and clicking the button would open the URL.
- PrimaryAction *DisplayMessageAction `json:",omitempty"`
+ PrimaryAction *DisplayMessageAction `json:",omitzero"`
}
// DisplayMessageAction represents an action (URL and link) to be presented to
@@ -2334,7 +2334,7 @@ type Debug struct {
// Exit optionally specifies that the client should os.Exit
// with this code. This is a safety measure in case a client is crash
// looping or in an unsafe state and we need to remotely shut it down.
- Exit *int `json:",omitempty"`
+ Exit *int `json:",omitzero"`
}
func (id ID) String() string { return fmt.Sprintf("id:%d", int64(id)) }
@@ -2878,7 +2878,7 @@ type SSHPrincipal struct {
// Matching any one of the following four field causes a match.
// It must also match Certs, if non-empty.
- Node StableNodeID `json:"node,omitempty"`
+ Node StableNodeID `json:"node,omitzero"`
NodeIP string `json:"nodeIP,omitempty"`
UserLogin string `json:"userLogin,omitempty"` // email-ish: foo@example.com, bar@github
Any bool `json:"any,omitempty"` // if true, match any connection
@@ -2951,7 +2951,7 @@ type SSHAction struct {
// OnRecorderFailure is the action to take if recording fails.
// If nil, the default action is to fail open.
- OnRecordingFailure *SSHRecorderFailureAction `json:"onRecordingFailure,omitempty"`
+ OnRecordingFailure *SSHRecorderFailureAction `json:"onRecordingFailure,omitzero"`
}
// SSHRecorderFailureAction is the action to take if recording fails.
@@ -3196,7 +3196,7 @@ type PeerChange struct {
DiscoKey *key.DiscoPublic `json:",omitempty"`
// Online, if non-nil, means that the NodeID's online status changed.
- Online *bool `json:",omitempty"`
+ Online *bool `json:",omitzero"`
// LastSeen, if non-nil, means that the NodeID's online status changed.
LastSeen *time.Time `json:",omitempty"`
@@ -3284,7 +3284,7 @@ type AuditLogRequest struct {
// NodeKey is the client's current node key.
NodeKey key.NodePublic `json:",omitzero"`
// Action is the action to be logged. It must correspond to a known action in the control plane.
- Action ClientAuditAction `json:",omitempty"`
+ Action ClientAuditAction `json:",omitzero"`
// Details is an opaque string, specific to the action being logged. Empty strings may not
// be valid depending on the action being logged.
Details string `json:",omitempty"`
diff --git a/tsweb/log.go b/tsweb/log.go
index 51f95e95f..59dc29224 100644
--- a/tsweb/log.go
+++ b/tsweb/log.go
@@ -50,7 +50,7 @@ type AccessLogRecord struct {
// client immediately after the error text, as well as logged here. This
// makes it easier to correlate support requests with server logs. If a
// RequestID generator is not configured, RequestID will be empty.
- RequestID RequestID `json:"request_id,omitempty"`
+ RequestID RequestID `json:"request_id,omitzero"`
}
// String returns m as a JSON string.
diff --git a/types/opt/bool_test.go b/types/opt/bool_test.go
index dddbcfc19..dc2935683 100644
--- a/types/opt/bool_test.go
+++ b/types/opt/bool_test.go
@@ -44,7 +44,7 @@ func TestBool(t *testing.T) {
in: struct {
True Bool
False Bool
- Unset Bool `json:",omitempty"`
+ Unset Bool `json:",omitzero"`
}{
True: "true",
False: "false",
diff --git a/types/views/views_test.go b/types/views/views_test.go
index 5a30c11a1..7e49ea4a1 100644
--- a/types/views/views_test.go
+++ b/types/views/views_test.go
@@ -39,8 +39,8 @@ type viewStruct struct {
Int int
Addrs Slice[netip.Prefix]
Strings Slice[string]
- AddrsPtr *Slice[netip.Prefix] `json:",omitempty"`
- StringsPtr *Slice[string] `json:",omitempty"`
+ AddrsPtr *Slice[netip.Prefix] `json:",omitzero"`
+ StringsPtr *Slice[string] `json:",omitzero"`
}
type noPtrStruct struct {
diff --git a/wgengine/magicsock/endpoint.go b/wgengine/magicsock/endpoint.go
index 2010775a1..48982aae7 100644
--- a/wgengine/magicsock/endpoint.go
+++ b/wgengine/magicsock/endpoint.go
@@ -428,8 +428,8 @@ type pongReply struct {
type EndpointChange struct {
When time.Time // when the change occurred
What string // what this change is
- From any `json:",omitempty"` // information about the previous state
- To any `json:",omitempty"` // information about the new state
+ From any `json:",omitzero"` // information about the previous state
+ To any `json:",omitzero"` // information about the new state
}
// shouldDeleteLocked reports whether we should delete this endpoint.