pub struct NodeState {
pub disco_keys: DiscoKeyPair,
pub machine_keys: MachineKeyPair,
pub network_lock_keys: NetworkLockKeyPair,
pub node_keys: NodeKeyPair,
pub old_node_key: Option<NodePublicKey>,
pub acme_account_key: Option<Zeroizing<Vec<u8>>>,
}Expand description
The complete runtime key state for a Tailscale node.
Fields§
§disco_keys: DiscoKeyPairThe DiscoKeyPair this Tailnet peer uses for the Disco protocol.
These should be randomly generated for each run of a Tailscale device.
machine_keys: MachineKeyPairThe MachineKeyPair for the hardware this Tailnet peer runs on.
network_lock_keys: NetworkLockKeyPairThe NetworkLockKeyPair for this Tailnet peer, for use with Tailnet Lock.
node_keys: NodeKeyPairThe NodeKeyPair for this Tailnet peer.
old_node_key: Option<NodePublicKey>The node’s previous node public key during a rotation (see
PersistState::old_node_key). Threaded to registration as RegisterRequest.OldNodeKey.
acme_account_key: Option<Zeroizing<Vec<u8>>>The persisted ACME account key (PKCS#8 DER), threaded from
PersistState::acme_account_key. The acme cert-issuance path reads this to reuse the
same Let’s Encrypt account across renewals. None when no ACME account is provisioned.
Wrapped in Zeroizing so the DER private-key bytes are wiped from
memory on drop; serializes transparently via the inner Vec (unchanged JSON shape).
Implementations§
Source§impl NodeState
impl NodeState
Sourcepub fn rotate_node_key(&mut self)
pub fn rotate_node_key(&mut self)
Rotate the node key for re-registration, the runtime twin of
PersistState::rotate_node_key: record the current node public key as
old_node_key and replace the node_keys
pair with a freshly-generated one. The next registration built from this state sends the prior
key as RegisterRequest.OldNodeKey, so control links the new node key to the node’s existing
identity instead of treating it as a brand-new node (Go’s regen/doLogin flow).
Only the node key rotates. The disco and machine keys are deliberately left untouched: the data plane (magicsock/WireGuard sessions, disco) keys on those and on per-peer keys, never on the self node key, so a node-key rotation does not re-key or flap an established tunnel. The node key is a control-plane identity. (The disco ping packet does carry the self node key as a claimed-sender identity — a caller that rotates at runtime should refresh magicsock’s copy so outbound pings advertise the new key, but that is a magicsock concern, not a key-state one.)
old_node_key anchor lifecycle. This records the prior key as old_node_key only if no
rotation anchor is already held (old_node_key is currently
None). A second rotation before a successful re-register therefore preserves the
original pre-expiry key as the anchor, rather than overwriting it with the intermediate
rotated key — control links a rotation against the key it already knows, so the anchor must
stay pinned to the last registered key, not a transient one. The anchor is released back to
None by clear_old_node_key, which the re-register path
calls on a successful register: once control has accepted the rotated key, that key is now
the node’s known identity, so a subsequent genuine expiry cycle correctly captures it as the
new anchor. (A runtime caller that drives repeated rotations is additionally expected not to
rotate again while a re-register is unconfirmed; this guard is the belt-and-suspenders.)
Sourcepub fn clear_old_node_key(&mut self)
pub fn clear_old_node_key(&mut self)
Release the rotation anchor: drop any old_node_key back to None.
Called by the re-register path on a successful register. After control accepts a rotated
node key, that key is the node’s known identity, so the next genuine node-key rotation should
anchor on it (capture it fresh as old_node_key) rather than re-sending a now-stale prior
key. Paired with rotate_node_key’s preserve-if-Some guard:
the anchor is captured once per rotation episode and cleared once the episode is confirmed.
A no-op when no anchor is held (the steady-state case — every successful map-poll re-register
calls this and it harmlessly does nothing while old_node_key is already None).
Trait Implementations§
Source§impl<'de> Deserialize<'de> for NodeState
impl<'de> Deserialize<'de> for NodeState
Source§fn deserialize<__D>(
__deserializer: __D,
) -> Result<NodeState, <__D as Deserializer<'de>>::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(
__deserializer: __D,
) -> Result<NodeState, <__D as Deserializer<'de>>::Error>where
__D: Deserializer<'de>,
Source§impl From<&NodeState> for PersistState
impl From<&NodeState> for PersistState
Source§fn from(value: &NodeState) -> PersistState
fn from(value: &NodeState) -> PersistState
Source§impl From<&PersistState> for NodeState
impl From<&PersistState> for NodeState
Source§fn from(value: &PersistState) -> NodeState
fn from(value: &PersistState) -> NodeState
Source§impl From<NodeState> for PersistState
impl From<NodeState> for PersistState
Source§fn from(value: NodeState) -> PersistState
fn from(value: NodeState) -> PersistState
Source§impl From<PersistState> for NodeState
impl From<PersistState> for NodeState
Source§fn from(value: PersistState) -> NodeState
fn from(value: PersistState) -> NodeState
Auto Trait Implementations§
impl Freeze for NodeState
impl RefUnwindSafe for NodeState
impl Send for NodeState
impl Sync for NodeState
impl Unpin for NodeState
impl UnsafeUnpin for NodeState
impl UnwindSafe for NodeState
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> DisplayExt for Twhere
T: Display,
impl<T> DisplayExt for Twhere
T: Display,
Source§fn as_display_debug(&self) -> DisplayToDebug<'_>
fn as_display_debug(&self) -> DisplayToDebug<'_>
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<A, T> DynMessage<A> for T
impl<A, T> DynMessage<A> for T
Source§fn handle_dyn<'a>(
self: Box<T>,
state: &'a mut A,
actor_ref: ActorRef<A>,
tx: Option<Sender<Result<Box<dyn Any + Send>, SendError<Box<dyn Any + Send>, Box<dyn Any + Send>>>>>,
stop: &'a mut bool,
) -> Pin<Box<dyn Future<Output = Result<(), Box<dyn ReplyError>>> + Send + 'a>>
fn handle_dyn<'a>( self: Box<T>, state: &'a mut A, actor_ref: ActorRef<A>, tx: Option<Sender<Result<Box<dyn Any + Send>, SendError<Box<dyn Any + Send>, Box<dyn Any + Send>>>>>, stop: &'a mut bool, ) -> Pin<Box<dyn Future<Output = Result<(), Box<dyn ReplyError>>> + Send + 'a>>
impl<T> ErasedDestructor for Twhere
T: 'static,
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreimpl<T> ReplyError for T
Source§impl<T> ToCompactString for Twhere
T: Display,
impl<T> ToCompactString for Twhere
T: Display,
Source§fn try_to_compact_string(&self) -> Result<CompactString, ToCompactStringError>
fn try_to_compact_string(&self) -> Result<CompactString, ToCompactStringError>
ToCompactString::to_compact_string() Read moreSource§fn to_compact_string(&self) -> CompactString
fn to_compact_string(&self) -> CompactString
CompactString. Read more