Skip to main content

TopicId

Struct TopicId 

Source
pub struct TopicId(/* private fields */);
Expand description

Topic identifier derived from a string via SHA512 hashing.

Used as the stable identifier for gossip subscriptions and DHT records.

§Example

let topic_id = TopicId::new("chat-room-1".to_string());

Implementations§

Source§

impl TopicId

Source

pub fn new(topic_name: impl Into<Vec<u8>>) -> Self

Create a new topic ID from a string.

String is hashed with SHA512; the first 32 bytes produce the identifier.

Examples found in repository?
examples/simple.rs (line 29)
10async fn main() -> Result<()> {
11    // Generate a new random secret key
12    let secret_key = SecretKey::generate();
13    let signing_key = SigningKey::from_bytes(&secret_key.to_bytes());
14
15    // Set up endpoint with discovery enabled
16    let endpoint = Endpoint::builder(iroh::endpoint::presets::N0)
17        .secret_key(secret_key.clone())
18        .bind()
19        .await?;
20
21    // Initialize gossip with auto-discovery
22    let gossip = Gossip::builder().spawn(endpoint.clone());
23
24    // Set up protocol router
25    let _router = iroh::protocol::Router::builder(endpoint.clone())
26        .accept(iroh_gossip::ALPN, gossip.clone())
27        .spawn();
28
29    let topic_id = TopicId::new("my-iroh-gossip-topic".to_string());
30    let initial_secret = b"my-initial-secret".to_vec();
31
32    let record_publisher = RecordPublisher::new(
33        topic_id.clone(),
34        signing_key.clone(),
35        None,
36        initial_secret,
37        Config::default(),
38    );
39
40    let topic = gossip
41        .subscribe_and_join_with_auto_discovery(record_publisher)
42        .await?;
43
44    println!("[joined topic]");
45
46    // Do something with the gossip topic
47    // (bonus: GossipSender and GossipReceiver are safely clonable)
48    let (_gossip_sender, _gossip_receiver) = topic.split().await?;
49
50    Ok(())
51}
More examples
Hide additional examples
examples/full_config.rs (line 100)
81async fn main() -> Result<()> {
82    // Generate a new random secret key
83    let secret_key = SecretKey::generate();
84    let signing_key = SigningKey::from_bytes(&secret_key.to_bytes());
85
86    // Set up endpoint with discovery enabled
87    let endpoint = Endpoint::builder(iroh::endpoint::presets::N0)
88        .secret_key(secret_key.clone())
89        .bind()
90        .await?;
91
92    // Initialize gossip with auto-discovery
93    let gossip = Gossip::builder().spawn(endpoint.clone());
94
95    // Set up protocol router
96    let _router = iroh::protocol::Router::builder(endpoint.clone())
97        .accept(iroh_gossip::ALPN, gossip.clone())
98        .spawn();
99
100    let topic_id = TopicId::new("my-iroh-gossip-topic".to_string());
101    let initial_secret = b"my-initial-secret".to_vec();
102
103    let record_publisher =
104        RecordPublisher::builder(topic_id.clone(), signing_key.clone(), initial_secret)
105            .config(config_builder())
106            .secret_rotation(RotationHandle::new(DefaultSecretRotation))
107            .build();
108
109    let topic = gossip
110        .subscribe_and_join_with_auto_discovery(record_publisher)
111        .await?;
112
113    println!("[joined topic]");
114
115    // Do something with the gossip topic
116    // (bonus: GossipSender and GossipReceiver are safely clonable)
117    let (_gossip_sender, _gossip_receiver) = topic.split().await?;
118
119    Ok(())
120}
examples/without_mergers.rs (line 32)
13async fn main() -> Result<()> {
14    // Generate a new random secret key
15    let secret_key = SecretKey::generate();
16    let signing_key = SigningKey::from_bytes(&secret_key.to_bytes());
17
18    // Set up endpoint with discovery enabled
19    let endpoint = Endpoint::builder(iroh::endpoint::presets::N0)
20        .secret_key(secret_key.clone())
21        .bind()
22        .await?;
23
24    // Initialize gossip with auto-discovery
25    let gossip = Gossip::builder().spawn(endpoint.clone());
26
27    // Set up protocol router
28    let _router = iroh::protocol::Router::builder(endpoint.clone())
29        .accept(iroh_gossip::ALPN, gossip.clone())
30        .spawn();
31
32    let topic_id = TopicId::new("my-iroh-gossip-topic".to_string());
33    let initial_secret = b"my-initial-secret".to_vec();
34
35    let record_publisher = RecordPublisher::new(
36        topic_id.clone(),
37        signing_key.clone(),
38        None,
39        initial_secret,
40        // [!] Disable merge workers (BubbleMerge and MessageOverlapMerge)
41        Config::builder()
42            .merge_config(
43                MergeConfig::builder()
44                    .bubble_merge(BubbleMergeConfig::Disabled)
45                    .message_overlap_merge(MessageOverlapMergeConfig::Disabled)
46                    .build(),
47            )
48            .build(),
49    );
50
51    let topic = gossip
52        .subscribe_and_join_with_auto_discovery(record_publisher)
53        .await?;
54
55    println!("[joined topic]");
56
57    // Do something with the gossip topic
58    // (bonus: GossipSender and GossipReceiver are safely clonable)
59    let (_gossip_sender, _gossip_receiver) = topic.split().await?;
60
61    Ok(())
62}
examples/e2e_test.rs (line 30)
11async fn main() -> Result<()> {
12    // Generate a new random secret key
13    let secret_key = SecretKey::generate();
14    let signing_key = mainline::SigningKey::from_bytes(&secret_key.to_bytes());
15
16    // Set up endpoint with address lookup enabled
17    let endpoint = Endpoint::builder(iroh::endpoint::presets::N0)
18        .secret_key(secret_key.clone())
19        .bind()
20        .await?;
21
22    // Initialize gossip with auto-discovery
23    let gossip = Gossip::builder().spawn(endpoint.clone());
24
25    // Set up protocol router
26    let _router = iroh::protocol::Router::builder(endpoint.clone())
27        .accept(iroh_gossip::ALPN, gossip.clone())
28        .spawn();
29
30    let topic_id = TopicId::new("my-iroh-gossip-topic".to_string());
31    let initial_secret = b"my-initial-secret".to_vec();
32
33    let record_publisher = RecordPublisher::new(
34        topic_id.clone(),
35        signing_key.clone(),
36        None,
37        initial_secret,
38        Config::default(),
39    );
40    let (gossip_sender, mut gossip_receiver) = gossip
41        .subscribe_and_join_with_auto_discovery(record_publisher)
42        .await?
43        .split()
44        .await?;
45
46    tokio::spawn(async move {
47        while let Ok(event) = gossip_receiver.next().await {
48            println!("event: {event:?}");
49        }
50        println!("\nGossip receiver stream ended");
51    });
52
53    tokio::time::sleep(std::time::Duration::from_secs(3)).await;
54    gossip_sender
55        .broadcast(format!("hi from {}", endpoint.id()).into())
56        .await?;
57
58    println!("[joined topic]");
59
60    tokio::time::sleep(std::time::Duration::from_secs(10)).await;
61
62    println!("[finished]");
63
64    // successfully joined
65    // exit with code 0
66    Ok(())
67}
examples/secret_rotation.rs (line 49)
30async fn main() -> Result<()> {
31    // Generate a new random secret key
32    let secret_key = SecretKey::generate();
33    let signing_key = SigningKey::from_bytes(&secret_key.to_bytes());
34
35    // Set up endpoint with discovery enabled
36    let endpoint = Endpoint::builder(iroh::endpoint::presets::N0)
37        .secret_key(secret_key.clone())
38        .bind()
39        .await?;
40
41    // Initialize gossip with auto-discovery
42    let gossip = Gossip::builder().spawn(endpoint.clone());
43
44    // Set up protocol router
45    let _router = iroh::protocol::Router::builder(endpoint.clone())
46        .accept(iroh_gossip::ALPN, gossip.clone())
47        .spawn();
48
49    let topic_id = TopicId::new("my-iroh-gossip-topic".to_string());
50    let initial_secret = b"my-initial-secret".to_vec();
51
52    let record_publisher = RecordPublisher::new(
53        topic_id.clone(),
54        signing_key.clone(),
55        Some(RotationHandle::new(MySecretRotation)),
56        initial_secret,
57        Config::default(),
58    );
59    let (gossip_sender, mut gossip_receiver) = gossip
60        .subscribe_and_join_with_auto_discovery(record_publisher)
61        .await?
62        .split()
63        .await?;
64
65    println!("Joined topic");
66
67    // Spawn listener for incoming messages
68    tokio::spawn(async move {
69        while let Ok(event) = gossip_receiver.next().await {
70            if let Event::Received(msg) = event {
71                println!(
72                    "\nMessage from {}: {}",
73                    &msg.delivered_from.to_string()[0..8],
74                    String::from_utf8(msg.content.to_vec()).unwrap()
75                );
76            } else if let Event::NeighborUp(peer) = event {
77                println!("\nJoined by {}", &peer.to_string()[0..8]);
78            }
79        }
80        println!("\nGossip receiver stream ended");
81    });
82
83    // Main input loop for sending messages
84    let mut buffer = String::new();
85    let stdin = std::io::stdin();
86    loop {
87        print!("\n> ");
88        stdin.read_line(&mut buffer).unwrap();
89        gossip_sender
90            .broadcast(buffer.clone().replace("\n", "").into())
91            .await
92            .unwrap();
93        println!(" - (sent)");
94        buffer.clear();
95    }
96}
examples/chat_no_wait.rs (line 40)
12async fn main() -> Result<()> {
13    tracing_subscriber::fmt()
14        .with_thread_ids(true)
15        .with_ansi(true)
16        .with_env_filter(
17            EnvFilter::try_from_default_env()
18                .unwrap_or_else(|_| EnvFilter::new("distributed_topic_tracker=debug")),
19        )
20        .init();
21
22    // Generate a new random secret key
23    let secret_key = SecretKey::generate();
24    let signing_key = SigningKey::from_bytes(&secret_key.to_bytes());
25
26    // Set up endpoint with discovery enabled
27    let endpoint = Endpoint::builder(iroh::endpoint::presets::N0)
28        .secret_key(secret_key.clone())
29        .bind()
30        .await?;
31
32    // Initialize gossip with auto-discovery
33    let gossip = Gossip::builder().spawn(endpoint.clone());
34
35    // Set up protocol router
36    let _router = iroh::protocol::Router::builder(endpoint.clone())
37        .accept(iroh_gossip::ALPN, gossip.clone())
38        .spawn();
39
40    let topic_id = TopicId::new("my-iroh-gossip-topic".to_string());
41    let initial_secret = b"my-initial-secret".to_vec();
42
43    let record_publisher = RecordPublisher::new(
44        topic_id.clone(),
45        signing_key.clone(),
46        None,
47        initial_secret,
48        Config::default(),
49    );
50    let (gossip_sender, mut gossip_receiver) = gossip
51        .subscribe_and_join_with_auto_discovery_no_wait(record_publisher)
52        .await?
53        .split()
54        .await?;
55
56    println!("Joined topic");
57
58    // Spawn listener for incoming messages
59    tokio::spawn(async move {
60        while let Ok(event) = gossip_receiver.next().await {
61            if let Event::Received(msg) = event {
62                println!(
63                    "\nMessage from {}: {}",
64                    &msg.delivered_from.to_string()[0..8],
65                    String::from_utf8(msg.content.to_vec()).unwrap()
66                );
67            } else if let Event::NeighborUp(peer) = event {
68                println!("\nJoined by {}", &peer.to_string()[0..8]);
69            }
70        }
71        println!("\nGossip receiver stream ended");
72    });
73
74    // Main input loop for sending messages
75    let mut buffer = String::new();
76    let stdin = std::io::stdin();
77    loop {
78        print!("\n> ");
79        stdin.read_line(&mut buffer).unwrap();
80        gossip_sender
81            .broadcast(
82                buffer
83                    .trim_end_matches(&['\r', '\n'][..])
84                    .as_bytes()
85                    .to_vec(),
86            )
87            .await
88            .unwrap();
89        println!(" - (sent)");
90        buffer.clear();
91    }
92}
Source

pub fn from_hash(bytes: &[u8; 32]) -> Self

Create from a pre-computed 32-byte hash.

Source

pub fn hash(&self) -> [u8; 32]

Get the hash bytes.

Trait Implementations§

Source§

impl Clone for TopicId

Source§

fn clone(&self) -> TopicId

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for TopicId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<&str> for TopicId

Source§

fn from(topic_name: &str) -> Self

Converts to this type from the input type.
Source§

impl From<String> for TopicId

Source§

fn from(topic_name: String) -> Self

Converts to this type from the input type.
Source§

impl From<Vec<u8>> for TopicId

Treats bytes as a topic name and SHA-512 hashes them. For a pre-computed 32-byte hash, use TopicId::from_hash instead.

Source§

fn from(topic_name: Vec<u8>) -> Self

Converts to this type from the input type.
Source§

impl FromStr for TopicId

Source§

type Err = Error

The associated error which can be returned from parsing.
Source§

fn from_str(topic_name: &str) -> Result<Self, Self::Err>

Parses a string s to return a value of this type. Read more
Source§

impl Hash for TopicId

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for TopicId

Source§

fn eq(&self, other: &TopicId) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Eq for TopicId

Source§

impl StructuralPartialEq for TopicId

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more