Skip to main content

a3s_memory/repository/
change_token.rs

1use super::MemoryRepositoryError;
2use serde::{Deserialize, Serialize};
3
4/// Stable identity of the namespace change-token contract.
5pub const MEMORY_NAMESPACE_CHANGE_TOKEN_PROFILE_V1: &str =
6    "a3s.memory.namespace-change-token.sequence.v1";
7
8/// Bounded, content-free evidence that one namespace did not change.
9///
10/// A token is meaningful only for repeated reads of the same exact namespace
11/// from the same repository history. Equal tokens prove that no novel,
12/// successful repository apply changed that namespace between the reads.
13/// Sequences may jump, but must never repeat after a change. Admission and use
14/// events do not change the token.
15///
16/// The token is not a namespace or backend identity, a snapshot, a lock, or a
17/// distributed lease. Consumers must retain their ordinary snapshot and
18/// revision-CAS proofs whenever the token is unavailable or changes.
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
20#[serde(rename_all = "camelCase")]
21pub struct MemoryNamespaceChangeToken {
22    profile: String,
23    sequence: u64,
24}
25
26impl MemoryNamespaceChangeToken {
27    /// Construct a token for a backend-owned monotonic sequence.
28    pub fn new(sequence: u64) -> Self {
29        Self {
30            profile: MEMORY_NAMESPACE_CHANGE_TOKEN_PROFILE_V1.to_string(),
31            sequence,
32        }
33    }
34
35    pub fn profile(&self) -> &str {
36        &self.profile
37    }
38
39    pub fn sequence(&self) -> u64 {
40        self.sequence
41    }
42
43    /// Verify that a deserialized or backend-provided token uses this contract.
44    pub fn verify(&self) -> Result<(), MemoryRepositoryError> {
45        if self.profile != MEMORY_NAMESPACE_CHANGE_TOKEN_PROFILE_V1 {
46            return Err(MemoryRepositoryError::invariant(
47                "namespace change token uses an unsupported profile",
48            ));
49        }
50        Ok(())
51    }
52}