Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ express the boundary.
workarounds. Handle errors inline or extract a named function.
- Use identity types (`Name`, `NamePattern`, `Imid`) instead of raw strings
once values cross a boundary.
- Prefer `BTreeMap` / `BTreeSet` over `HashMap` / `HashSet` unless a hot
point-lookup path specifically needs hash-table behavior. Ordered collections
give deterministic iteration and are usually smaller for Intermesh's small,
iteration-heavy state sets.

## Imports

Expand Down
46 changes: 0 additions & 46 deletions context/interfaces/src/message.md

This file was deleted.

20 changes: 20 additions & 0 deletions context/interfaces/src/state/messages.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# `src/state/messages.rs`


## Responsible for
- Maintaining the daemon's observed signed mesh message set.
- Collapsing endorsements to the latest message per endorsement base.
- Retaining this node's older self-endorsements so they can be re-gossiped.
- Storing revocations and suppressing endorsements with revoked signatures.
- Tracking a monotonic generation for meaningful observed-message changes.

## Public interface
```rust
/// Atomic update to the observed message set.
pub(crate) struct Update {
pub(crate) rm_endors: Vec<endor::Endor>,
pub(crate) add_endors: Vec<endor::Endor>,
pub(crate) rm_revocations: Vec<revocation::Revocation>,
pub(crate) add_revocations: Vec<revocation::Revocation>,
}
```
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# `src/state.rs`
# `src/state/mod.rs`


## Responsible for
Expand All @@ -8,7 +8,8 @@
when they need to read or update their persisted slice of daemon state.
- Owning durable node identity, static daemon configuration, and persisted
module state.
- Saving coherent state-file snapshots, including the observed message store.
- Saving coherent state-file snapshots, including the observed message set.
- Notifying subscribers when mutable persisted state changes.

## Public interface
```rust
Expand All @@ -26,8 +27,8 @@ pub(crate) struct Args {
/// Durable daemon state plus constructed runtime handles.
///
/// Static configuration and identity live directly on `State`; runtime-mutable
/// persisted data is owned behind typed accessors or helper stores so
/// state-file snapshots are taken coherently.
/// persisted data is owned behind typed accessors so state-file snapshots are
/// taken coherently.
pub(crate) struct State {
pub(crate) keypair: ImidKeypair,
pub(crate) imid: Imid,
Expand All @@ -38,7 +39,6 @@ pub(crate) struct State {
pub(crate) local_ip: IpAddr,
pub(crate) endorse_local_ip: bool,
pub(crate) intercept: bool,
pub(crate) message_store: Arc<message::Store>,
}

impl State {
Expand All @@ -48,9 +48,27 @@ impl State {
/// Return the current ad-hoc membership, if any.
pub(crate) async fn adhoc_membership(&self) -> Option<adhoc::Membership>;

/// Replace ad-hoc membership in memory. Call `save()` to persist it.
/// Replace ad-hoc membership in memory and notify subscribers when changed.
pub(crate) async fn set_adhoc_membership(&self, membership: Option<adhoc::Membership>);

/// Return latest endorsements and revocations from one coherent state read.
pub(crate) async fn messages(&self) -> (BTreeSet<endor::Endor>, BTreeSet<Revocation>);

/// Return messages plus retained self-endorsement history from one coherent
/// state read.
pub(crate) async fn messages_with_retained(
&self,
) -> (BTreeSet<endor::Endor>, BTreeSet<Revocation>);

/// Apply removals and additions as one observed message set update.
pub(crate) async fn update_messages(&self, update: MessageUpdate) -> anyhow::Result<()>;

/// Return the current monotonic message generation.
pub(crate) async fn message_generation(&self) -> u64;

/// Subscribe to mutable state changes.
pub(crate) fn subscribe(&self) -> watch::Receiver<()>;

/// Persist durable daemon state atomically.
pub(crate) async fn save(&self) -> anyhow::Result<()>;
}
Expand Down
14 changes: 7 additions & 7 deletions src/connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ mod tests {
use crate::proto::intermesh::GossipUpdate;
use crate::test_utils::TestFixture;
use futures::TryStreamExt;
use std::collections::HashMap;
use std::collections::BTreeMap;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::sync::mpsc;
use tokio::time::{timeout, Duration};
Expand Down Expand Up @@ -417,7 +417,7 @@ mod tests {
.contains("failed to find IP"));

// Test connect_imid: IMID → IP resolution via trust engine
trust_engine.set_ip_map(HashMap::from([(
trust_engine.set_ip_map(BTreeMap::from([(
server_imid.clone(),
vec![server_addr.ip()],
)]));
Expand All @@ -435,7 +435,7 @@ mod tests {
}

// Test connect with hostname: name → IMID → IP resolution
trust_engine.set_name_map(HashMap::from([(
trust_engine.set_name_map(BTreeMap::from([(
"server.local".parse().assert(),
vec![server_imid.clone()],
)]));
Expand All @@ -462,7 +462,7 @@ mod tests {

// Test wrong IMID rejection: server presents different IMID than expected
let wrong_imid = fix.imid("wrong");
trust_engine.set_ip_map(HashMap::from([(
trust_engine.set_ip_map(BTreeMap::from([(
wrong_imid.clone(),
vec![server_addr.ip()],
)]));
Expand Down Expand Up @@ -521,11 +521,11 @@ mod tests {
let client_imid = client_keypair.to_imid();
let trust_engine = Arc::new(TrustEngine::new(client_imid.clone()));

trust_engine.set_ip_map(HashMap::from([(
trust_engine.set_ip_map(BTreeMap::from([(
server_imid.clone(),
vec![server_addr.ip()],
)]));
trust_engine.set_name_map(HashMap::from([(
trust_engine.set_name_map(BTreeMap::from([(
"server.local".parse().assert(),
vec![server_imid],
)]));
Expand Down Expand Up @@ -627,7 +627,7 @@ mod tests {
});

let trust_engine = Arc::new(TrustEngine::new(client_imid));
trust_engine.set_ip_map(HashMap::from([(
trust_engine.set_ip_map(BTreeMap::from([(
server_imid.clone(),
vec![server_addr.ip()],
)]));
Expand Down
2 changes: 1 addition & 1 deletion src/endor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
//! Core types ([`Base`], [`Endor`]) live in the `types` submodule.
//! Lifecycle coordination lives in [`crate::manager::Manager`].
//! Observed endorsement storage lives in [`crate::message::Store`].
//! Observed endorsement storage lives in [`crate::state::State`].

mod types;

Expand Down
8 changes: 1 addition & 7 deletions src/endor/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,6 @@ impl Base {
.duration_since(UNIX_EPOCH)
.assert()
.as_secs();
self.sign_at(keypair, issued)
}

fn sign_at(&self, keypair: &ImidKeypair, issued: u64) -> Endor {
assert!(self.endorser == keypair.to_imid());

let expires = issued + ENDORSEMENT_EXPIRATION_SECS;

let mut data: proto::EndorsementData = self.into();
Expand Down Expand Up @@ -114,7 +108,7 @@ impl fmt::Display for Base {
/// represents an endorsement that has been verified. The assurance of
/// verification is lost when we read an endorsement from an external source.
/// Convert to proto and re-verify when de-serializing.
#[derive(Clone, PartialEq, Eq, Hash)]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Endor {
base: Base,
issued: u64,
Expand Down
Loading
Loading