pub struct CapabilityVersion(/* private fields */);Expand description
Indicates the capability level of a Tailscale node. This must be kept in-sync with the
CapabilityVersion constants in the Golang codebase (tailcfg/tailcfg.go).
It can be thought of as a node’s simple version number; a single monotonically increasing integer, rather than the complex x.y.z-cccccc semver+hash(es) versioning scheme. Whenever a node gains a capability or wants to negotiate a change in semantics with the control plane, peers, or an official frontend (such as LocalAPI in the Golang codebase), bump this number and document what’s changed.
The capability versions 0, 1, 2, and 35 are undefined; you cannot create a
CapabilityVersion with these values.
Note: Prior to 2022-03-06, this value was known as the “MapRequest version”, mapVer, or “map
cap”; you’ll still see that name used in comments throughout the Golang codebase.
Implementations§
Source§impl CapabilityVersion
impl CapabilityVersion
Sourcepub const V4: Self
pub const V4: Self
2020-??-??: opt-in keep-alives via KeepAlive field, opt-in compression via Compress
Sourcepub const V5: Self
pub const V5: Self
2020-10-19: implies IncludeIPv6; delta Peers/UserProfiles, supports MagicDNS
Sourcepub const V7: Self
pub const V7: Self
2020-12-15: FilterRule.SrcIPs accepts CIDRs+ranges, doesn’t warn about 0.0.0.0/::
Sourcepub const V8: Self
pub const V8: Self
2020-12-19: client can buggily receive IPv6 addresses and routes if beta enabled server-side
Sourcepub const V9: Self
pub const V9: Self
2020-12-30: client doesn’t auto-add implicit search domains from peers; only DNSConfig.Domains
Sourcepub const V11: Self
pub const V11: Self
2021-03-03: client understands IPv6; multiple default routes, and goroutine dumping
Sourcepub const V18: Self
pub const V18: Self
2021-04-19: MapResponse.Node nil means unchanged (all fields now omitempty)
Sourcepub const V20: Self
pub const V20: Self
2021-06-11: MapResponse.LastSeen used even less (https://github.com/tailscale/tailscale/issues/2107)
Sourcepub const V23: Self
pub const V23: Self
2021-08-25: DNSConfig.Routes values may be empty (for ExtraRecords support in 1.14.1+)
Sourcepub const V24: Self
pub const V24: Self
2021-09-18: MapResponse.Health from control to node; node shows in “tailscale status”
Sourcepub const V36: Self
pub const V36: Self
2022-08-02: added PeersChangedPatch.{Key,DiscoKey,Online,LastSeen,KeyExpiry,Capabilities}
Sourcepub const V37: Self
pub const V37: Self
2022-08-09: added Debug.{SetForceBackgroundSTUN,SetRandomizeClientPort}; Debug are sticky
Sourcepub const V41: Self
pub const V41: Self
2022-08-30: uses 100.100.100.100 for route-less ExtraRecords if global nameservers is set
Sourcepub const V42: Self
pub const V42: Self
2022-09-06: NextDNS DoH support; see https://github.com/tailscale/tailscale/pull/5556
Sourcepub const V53: Self
pub const V53: Self
2023-01-18: client respects explicit Node.Expired + auto-sets based on Node.KeyExpiry
Sourcepub const V54: Self
pub const V54: Self
2023-01-19: Node.Cap added, PeersChangedPatch.Cap, uses Node.Cap for ExitDNS before Hostinfo.Services fallback
Sourcepub const V62: Self
pub const V62: Self
2023-05-05: Client can notify control over noise for SSHEventNotificationRequest recording failure events
Sourcepub const V64: Self
pub const V64: Self
2023-07-11: Client understands s/CapabilityTailnetLockAlpha/CapabilityTailnetLock
Sourcepub const V65: Self
pub const V65: Self
2023-07-12: Client understands DERPMap.HomeParams + incremental DERPMap updates with params
Sourcepub const V68: Self
pub const V68: Self
2023-08-09: Client has dedicated updateRoutine; MapRequest.Stream true means ignore Hostinfo+Endpoints
Sourcepub const V69: Self
pub const V69: Self
2023-08-16: removed Debug.LogHeap* + GoroutineDumpURL; added c2n /debug/logheap
Sourcepub const V70: Self
pub const V70: Self
2023-08-16: removed most Debug fields; added NodeAttrDisable*, NodeAttrDebug* instead
Sourcepub const V76: Self
pub const V76: Self
2023-09-20: Client understands ExitNodeDNSResolvers for IsWireGuardOnly nodes
Sourcepub const V82: Self
pub const V82: Self
2023-12-01: Client understands NodeAttrLinuxMustUseIPTables, NodeAttrLinuxMustUseNfTables, c2n /netfilter-kind
Sourcepub const V89: Self
pub const V89: Self
2024-03-23: Client no longer respects deleted PeerChange.Capabilities (use CapMap)
Sourcepub const V95: Self
pub const V95: Self
2024-05-06: Client uses NodeAttrUserDialUseRoutes to change DNS dialing behavior.
Sourcepub const V97: Self
pub const V97: Self
2024-06-06: Client understands NodeAttrDisableSplitDNSWhenNoCustomResolvers
Sourcepub const V98: Self
pub const V98: Self
2024-06-13: iOS/tvOS clients may provide serial number as part of posture information
Sourcepub const V100: Self
pub const V100: Self
2024-06-18: Initial support for filtertype.Match.SrcCaps - actually usable in capver 109 (issue #12542)
Sourcepub const V101: Self
pub const V101: Self
2024-07-01: Client supports SSH agent forwarding when handling connections with /bin/su
Sourcepub const V106: Self
pub const V106: Self
2024-09-03: fix panic regression from cryptokey routing change (65fe0ba7b5)
Sourcepub const V108: Self
pub const V108: Self
2024-11-08: Client sends ServicesHash in Hostinfo, understands c2n GET /vip-services.
Sourcepub const V110: Self
pub const V110: Self
2024-12-12: removed never-before-used Tailscale SSH public key support (#14373)
Sourcepub const V112: Self
pub const V112: Self
2025-01-14: Client interprets AllowedIPs of nil as meaning same as Addresses
Sourcepub const V113: Self
pub const V113: Self
2025-01-20: Client communicates to control whether funnel is enabled by sending Hostinfo.IngressEnabled (#14688)
Sourcepub const V114: Self
pub const V114: Self
2025-01-30: NodeAttrMaxKeyDuration CapMap defined, clients might use it (no tailscaled code change) (#14829)
Sourcepub const V116: Self
pub const V116: Self
2025-05-05: Client serves MagicDNS “AAAA” if NodeAttrMagicDNSPeerAAAA set on self node
Sourcepub const V117: Self
pub const V117: Self
2025-05-28: Client understands DisplayMessages (structured health messages), but not necessarily PrimaryAction.
Sourcepub const V118: Self
pub const V118: Self
2025-07-01: Client sends Hostinfo.StateEncrypted to report whether the state file is encrypted at rest (#15830)
Sourcepub const V119: Self
pub const V119: Self
2025-07-10: Client uses Hostinfo.Location.Priority to prioritize one route over another.
Sourcepub const V120: Self
pub const V120: Self
2025-07-15: Client understands peer relay disco messages, and implements peer client and relay server functions
Sourcepub const V121: Self
pub const V121: Self
2025-07-19: Client understands peer relay endpoint alloc with
disco.AllocateUDPRelayEndpointRequest & disco.AllocateUDPRelayEndpointResponse
Sourcepub const V122: Self
pub const V122: Self
2025-07-21: Client sends Hostinfo.ExitNodeID to report which exit node it has selected, if any.
Sourcepub const V123: Self
pub const V123: Self
2025-07-28: fix deadlock regression from cryptokey routing change (issue #16651)
Sourcepub const V124: Self
pub const V124: Self
2025-08-08: removed NodeAttrDisableMagicSockCryptoRouting support, crypto routing is now mandatory
Sourcepub const V126: Self
pub const V126: Self
2025-09-17: Client uses seamless key renewal unless disabled by control (tailscale/corp#31479)
Sourcepub const V129: Self
pub const V129: Self
2025-10-04: Fixed sleep/wake deadlock in magicsock when using peer relay (PR #17449)
Sourcepub const V130: Self
pub const V130: Self
2025-10-06: client can send key.HardwareAttestationPublic and key.HardwareAttestationKeySignature in MapRequest
Sourcepub const V133: Self
pub const V133: Self
2026-02-17: client understands NodeAttrForceRegisterMagicDNSIPv4Only; MagicDNS IPv6 registered w/ OS by default
Sourcepub const CURRENT: Self = Self::V125
pub const CURRENT: Self = Self::V125
The capability version this node declares to control.
This is a claim control and peers act on, so it is pinned to what this tree actually implements — not to the newest constant defined above. The constants above are a transcription of upstream’s list (they document what each version means); this one is a promise. Declaring a version whose behaviour is missing does not gain a feature, it just tells control to stop sending the compatibility path for it.
The value is bracketed from both sides, and both bounds are load-bearing. See
Self::is_relay_capable for the lower one.
§Upper bound: below 126
The declaration is held at 125 because the next three versions each name a behaviour this tree does not have:
- 126 — seamless key renewal. Go renews the node key ahead of expiry without breaking
connections (
controlknobs.Knobs.SeamlessKeyRenewal). This tree’s expiry recovery is a node-key rotation plus a full re-register with the stored auth key (ts_control::Config::reauth_on_expiry, GodoLogin) — that is upstream’s non-seamless path, so connections do break across a renewal. - 127 — c2n
GET|POST /debug/netmap(GohandleC2NDebugNetMap,ipn/ipnlocal/c2n.go). Needs anetmap.NetworkMapaggregate to marshal; this tree has none — the netmap arrives as a stream of deltas accumulated by the runtime’s peer tracker. - 128 — c2n
/debug/health(GohandleC2NDebugHealth,ipn/ipnlocal/c2n.go). Needs ahealth.Tracker; this fork has no health subsystem.
and 138 (c2n /debug/tka/log, Go handleC2NDebugTKALog in
feature/tailnetlock/tailnetlock.go) is unimplemented for the same reason: the AUM chain
lives in ts_runtime, and the c2n responder in ts_control cannot reach it. All three
paths take the responder’s 400 Bad Request / unknown c2n path fallthrough, which is
exactly what Go’s handleC2N writes for an unregistered path.
125 is also a version a real client shipped (Tailscale v1.88.0 declares it), which keeps
this coherent with the IPNVersion advertised in ts_control::hostinfo.
§Lower bound: at or above 121 — and the one exception it leaves standing
A capability version is a single integer, so declaring 125 also asserts 120 (“Client
understands peer relay disco messages, and implements peer client and relay server
functions”) and 121 (“Client understands peer relay endpoint alloc with
disco.AllocateUDPRelayEndpointRequest & disco.AllocateUDPRelayEndpointResponse”).
Neither is fully implemented here: this tree ports the peer-relay client half only —
it answers an inbound CallMeMaybeVia and brings the relayed path up (ts_magicsock’s
relay module) — but it never serves as a relay (Go net/udprelay.Server,
feature/relayserver) and never originates an allocation request of its own.
That is a real overclaim, and it is deliberately not resolved the way 126, 127, 128 and 138 were, because here the two directions are not symmetric:
- Lowering would be unconditionally destructive. An upstream peer decides whether we may
be offered a relay path from our declared version alone, with
magicsock.capVerIsRelayCapable(n.Cap())— ported here asSelf::is_relay_capable. It gates bothendpoint.wantUDPRelayPathDiscoveryLocked(no discovery is started toward a peer below the floor) and the inboundCallMeMaybeViahandler (the message is ignored outright). Declaring 119 would mean no Go peer ever sends us aCallMeMaybeVia, making the ported client half unreachable in production. 122–125 would go with it, including 125’sdnstype.Resolver.UseWithExitNode, whichts_runtime::magic_dnsdoes implement. - The overclaim is inert unless an operator opts in. The half we do not implement —
serving as a relay — is additionally gated on tailnet policy: a peer treats us as a
relay-server candidate only if it also holds
peercap.RelayTarget(tailscale.com/cap/relay-target) on us (Conn.updateRelayServersSet), a grant an operator writes into the policy file. Absent it no peer ever asks us to allocate, and anAllocateUDPRelayEndpointRequestthat does arrive is decoded and dropped. Contrast 127/128/138, which control acts on unilaterally the moment the version is declared — which is why those were resolved by lowering and this one is not.
Closing it for real means porting the relay server, which was ruled out of scope for an embedded client when the peer-relay client half landed. Until then it is a recorded, bounded exception rather than an oversight.
Before raising this, check that every version between the old value and the new one is
implemented here, not just the topmost one — capability versions are monotone, so declaring
N asserts everything at or below N. Before lowering it, check it against
Self::is_relay_capable first.
Sourcepub const fn is_relay_capable(self) -> bool
pub const fn is_relay_capable(self) -> bool
The capability version at and above which an upstream Go peer will treat a node as
peer-relay capable: 121.
Faithful port of Go magicsock.capVerIsRelayCapable (wgengine/magicsock/magicsock.go,
pinned at 49e148c4a30b4f8098f69468fd27a7021d85ea02), which is exactly
version >= 121. Go evaluates it over a peer’s netmap tailcfg.Node.Cap and stores the
result as endpoint.relayCapable, which gates two things:
endpoint.wantUDPRelayPathDiscoveryLocked— a peer below the floor never has UDP relay path discovery started toward it, so it is never sent aCallMeMaybeVia.- the inbound
CallMeMaybeViapath inConn.handleDiscoMessage— aCallMeMaybeViafrom a peer below the floor is ignored (“is not known to be relay capable”).
So this is the floor Self::CURRENT must stay at or above for this tree’s ported
peer-relay client half to be reachable at all by a real Tailscale peer; see that constant
for why the tree sits above the floor despite not implementing all of 120/121.
Sourcepub const fn new(value: u16) -> Option<Self>
pub const fn new(value: u16) -> Option<Self>
Create a new CapabilityVersion instance from a u16. Note that the versions 0, 1, 2,
and 35 are undefined and will result in an error.
Trait Implementations§
Source§impl Clone for CapabilityVersion
impl Clone for CapabilityVersion
Source§fn clone(&self) -> CapabilityVersion
fn clone(&self) -> CapabilityVersion
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreimpl Copy for CapabilityVersion
Source§impl Debug for CapabilityVersion
impl Debug for CapabilityVersion
Source§impl Default for CapabilityVersion
impl Default for CapabilityVersion
Source§impl Display for CapabilityVersion
impl Display for CapabilityVersion
impl Eq for CapabilityVersion
Source§impl From<CapabilityVersion> for u16
Convert CapabilityVersion into a native-endian u16; little-endian on x86_64.
impl From<CapabilityVersion> for u16
Convert CapabilityVersion into a native-endian u16; little-endian on x86_64.