---
title: Publish with pubsub
description: Enable gossipsub, subscribe and publish signed messages, and consume pubsub events.
---

The `pubsub` feature exposes gossipsub. `EndpointBuilder::gossipsub()` enables it with interoperable defaults and advertises `/meshsub/1.1.0` and `/meshsub/1.0.0`. You need a second peer on the same topic to observe delivery. See [Drive events](/rust/drive-events) for the caller-driven event model.

## Enable gossipsub

```bash
cargo add minip2p-rs --features pubsub
```

Then activate it on the endpoint:

```rust
let mut node = minip2p::Endpoint::builder()
    .gossipsub()
    .bind_quic_dual_stack()?;

node.subscribe("news")?;
```

The Cargo feature alone only compiles the APIs. Calling `subscribe` on an endpoint that was not built with `.gossipsub()` or `.gossipsub_config(...)` returns `GossipsubError::NotEnabled`.

## Publish and keep driving

```rust
node.publish("news", b"hello mesh".to_vec())?;

loop {
    if let Some(event) =
        node.next_gossipsub_event(std::time::Duration::from_secs(5))?
    {
        println!("{event:?}");
        break;
    }
}
```

A successful `publish` means the message passed validation and outbound work was accepted. The endpoint must continue being driven to open streams and send frames.

There is no self-delivery. If the local application needs immediate feedback, handle its submitted value locally rather than waiting to receive its own message.

Later delivery problems appear as `GossipsubEvent::OutboundFailure` or ordinary `Event::Error` runtime events; they are not returned synchronously from an already accepted `publish`.

## Handle messages

```rust
use minip2p::GossipsubEvent;

for event in node.take_gossipsub_events() {
    match event {
        GossipsubEvent::Message {
            from,
            topics,
            data,
            signed,
            ..
        } => {
            println!(
                "from={from} topics={topics:?} signed={signed} data={:?}",
                String::from_utf8_lossy(&data),
            );
        }
        GossipsubEvent::ProtocolViolation { peer, reason } => {
            eprintln!("peer={peer} violated pubsub: {reason}");
        }
        _ => {}
    }
}
```

Use `subscribe` and `unsubscribe` to change local topic membership. Both return `false` when the requested state already held.

## Signing and unsigned compatibility

Published messages are signed with the endpoint identity. By default, gossipsub also rejects unsigned inbound messages. This matches StrictSign behavior without a separate public `StrictSign` type.

Only relax inbound acceptance when interoperating with a peer that intentionally sends unsigned messages:

```rust
use minip2p::{Endpoint, GossipsubConfig};

let config = GossipsubConfig {
    allow_unsigned: true,
    ..GossipsubConfig::default()
};

let node = Endpoint::builder()
    .gossipsub_config(config)
    .bind_quic_dual_stack()?;
```

Messages that include a signature are still verified. `allow_unsigned` does not make local publications unsigned.

For automatic peer address exchange on a signed pubsub topic, continue to [Discover peers](/rust/discover-peers).
