Skip to main content

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}