Skip to main content

krabka_metadata/
write_freeze.rs

1//! Topic write-freeze registry entries, replicated through the raft quorum
2//! with `MetadataRecord::V1TopicFreeze`.
3//!
4//! A freeze is a cluster state in which a topic stays readable and stays
5//! replicated, but the broker refuses every append that puts new
6//! externally-authored data into its log. Incident response, migrations, and
7//! disaster-recovery promotion all need that state, and an ACL edit cannot
8//! express it.
9//!
10//! The record stays pure data. The produce-path gate and the signature checks
11//! live in `krabka-broker`, and the resolution index lives on
12//! [`MetadataImage::topic_freeze`](crate::MetadataImage::topic_freeze).
13
14use serde::{Deserialize, Serialize};
15use uuid::Uuid;
16
17use crate::acl::PatternType;
18
19/// One entry in the write-freeze registry.
20///
21/// The entry is authoritative for its `(pattern_type, scope)` key: a later
22/// record with the same key replaces the earlier one. [`Self::frozen`] is the
23/// removal sentinel, in the same shape as `FeatureLevelRecord.level == 0`, so
24/// a thaw stays attributed in the raft log rather than vanishing as a
25/// tombstone.
26#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
27pub struct TopicFreezeRecord {
28    /// A literal topic name, or a topic-name prefix, as
29    /// [`Self::pattern_type`] selects.
30    pub scope: String,
31    /// The vocabulary that `kafka-acls` already gives an operator:
32    /// [`PatternType::Literal`] names one topic and
33    /// [`PatternType::Prefixed`] names a namespace.
34    pub pattern_type: PatternType,
35    /// `true` adds or replaces the entry. `false` removes it, and is the
36    /// thaw.
37    pub frozen: bool,
38    /// Free text that says why the freeze exists. It goes to the audit log
39    /// and to the producer's error message.
40    pub reason: String,
41    /// The principal that set the entry, in `KafkaPrincipal` string form,
42    /// for example `"User:alice"`.
43    pub set_by: String,
44    /// Epoch milliseconds at which the operator built the record. It is part
45    /// of the signed bytes, so an old signed thaw cannot be replayed.
46    pub set_at_ms: i64,
47    /// The break-glass approval that authorized a thaw. It is
48    /// [`Uuid::nil`] on a freeze, because a freeze needs no approval.
49    pub proposal_id: Uuid,
50    /// The operator key that signed the record. It is empty when the record
51    /// is unsigned.
52    pub key_id: String,
53    /// Detached Ed25519 signature over the canonical bytes of the record,
54    /// made on the machine of the operator. It is empty when the record is
55    /// unsigned. This crate keeps the bytes opaque; `krabka-broker` builds
56    /// and verifies them.
57    pub signature: Vec<u8>,
58}