ts_control/tka.rs
1//! Owned domain view of the control-pushed Tailnet Lock (TKA) status.
2//!
3//! Control includes a `TKAInfo` in each [`MapResponse`][ts_control_serde::MapResponse] carrying the
4//! current authority head (a base32 `AUMHash`) and a disablement signal. This is the lightweight
5//! per-netmap signal; the full authority (the AUM chain + trusted keys) is synced via a separate
6//! RPC. The actual signature verification lives in the `ts_tka` crate; this type just carries the
7//! head/disabled fields off the netmap so the runtime and embedder can react (e.g. detect a head
8//! change and resync, or surface lock state).
9//!
10//! AUM-sync RPC deferred, see SECURITY.md.
11
12use alloc::string::{String, ToString};
13
14/// The control plane's view of this tailnet's Tailnet Lock state (Go `tailcfg.TKAInfo`).
15#[derive(Debug, Clone, PartialEq, Eq, Default)]
16pub struct TkaStatus {
17 /// The base32 (no-pad) `AUMHash` of the latest Authority Update Message control has applied. A
18 /// node whose locally-known head differs should resync the authority. Empty when control sends
19 /// no head.
20 pub head: String,
21 /// Whether control believes Tailnet Lock should be disabled (the node should fetch and verify a
22 /// disablement secret before disabling locally).
23 pub disabled: bool,
24}
25
26impl TkaStatus {
27 /// Build the owned status from the borrowed serde view parsed off the netmap.
28 pub fn from_serde(info: &ts_control_serde::TkaInfo<'_>) -> Self {
29 TkaStatus {
30 head: info.head.to_string(),
31 disabled: info.disabled,
32 }
33 }
34
35 /// Whether Tailnet Lock is in effect for this tailnet: a non-empty head and not disabled.
36 pub fn is_enabled(&self) -> bool {
37 !self.head.is_empty() && !self.disabled
38 }
39}