> For the complete documentation index, see [llms.txt](https://vector-privacy.gitbook.io/vector-privacy/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://vector-privacy.gitbook.io/vector-privacy/vector-sdk/docs/advanced.md).

# Advanced

Advanced Features & Concepts for Vector SDK

This document covers advanced features and concepts in the Vector SDK.

{% hint style="info" %}
This page assumes you're already comfortable with the basics from [Quickstart](/vector-privacy/vector-sdk/basics/quickstart.md). Everything here builds on `VectorBot`, `Channel`, and the other [Components](/vector-privacy/vector-sdk/basics/components.md).
{% endhint %}

## Table of Contents

* [The Full Event Stream](#the-full-event-stream)
* [Communities & Moderation](#communities-and-moderation)
* [Tor & Embedded Routing](#tor-and-embedded-routing)
* [Identity & Storage](#identity-and-storage)
* [Reaching the Core Engine](#reaching-the-core-engine)

***

### The Full Event Stream

`on_message` is the fast path for replying to messages, but a bot that needs to react to more than incoming text, joins, reactions, edits, invites, should use `on_event` instead. It delivers the full event stream as a `BotEvent`:

```rust
use vector_sdk::{VectorBot, BotEvent};

bot.on_event(|bot, event| async move {
    match event {
        BotEvent::Message(msg) if !msg.is_mine() => {
            msg.reply("hi").await.ok();
        }
        BotEvent::MemberJoin { channel_id, npub } => {
            bot.channel(channel_id).send(&format!("welcome {}!", &npub[..12])).await.ok();
        }
        BotEvent::MessageUpdate { .. } => { /* a reaction or edit landed */ }
        BotEvent::Delete { .. } => { /* a message was deleted */ }
        BotEvent::MemberLeave { .. } => { /* a member left a community */ }
        BotEvent::Typing { .. } => { /* someone is typing */ }
        BotEvent::Invite { .. } => { /* a community invite arrived */ }
        BotEvent::Removed { .. } => { /* the bot itself was kicked or banned */ }
        _ => {}
    }
}).await?;
```

{% hint style="success" %}
&#x20;A slow handler does not block the others, each event is dispatched independently.
{% endhint %}

***

### Communities & Moderation

When an incoming message comes from a community, `msg.member()` returns the sender as a `Member` you can act on directly:

```rust
if let Some(member) = msg.member() {
    if !member.is_admin() {
        member.ban().await?;          // or .kick() / .unban()
    }
}
```

A bot can also manage a community it belongs to directly, rather than only reacting to messages from it:

```rust
let community = bot.community(community_id);

community.invite("npub1...").await?;
let link = community.create_invite().await?;
community.edit(Some("New Name"), None).await?;

for member in community.members().await {
    if member.is_admin() {
        // ...
    }
}
```

`community.capabilities()` and `community.roles()` expose the underlying role-permission data directly, useful for bots that need to check what actions are actually available before attempting them.

{% hint style="danger" %}
**`community.dissolve()` is owner-only and permanently ends the community for everyone.** This action cannot be undone.
{% endhint %}

#### Invite Policy

A bot's invite policy is set once, on the builder, and controls whether it joins communities automatically.

{% tabs %}
{% tab title="Public" %}
Accepts an invite from anyone.

```rust
VectorBot::builder()
    .nsec(key)
    .public()
    .build()
    .await?;
```

{% endtab %}

{% tab title="Whitelist" %}
Automatically accepts invites, but only from explicitly trusted accounts.

```rust
VectorBot::builder()
    .nsec(key)
    .whitelist(["npub1owner…"])
    .build()
    .await?;
```

{% endtab %}

{% tab title="Manual (Default)" %}
Invites wait for you to handle them explicitly.

```rust
let pending = bot.pending_invites()?;
bot.accept_invite(&community_id).await?;
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Auto-accept (under `.public()` or `.whitelist()`) also sweeps up invites that arrived while the bot was offline, so a restarted bot still joins what it was invited to.&#x20;
{% endhint %}

***

### Tor & Embedded Routing

Vector SDK can route a bot's entire connection through an embedded Tor client (Arti), with no system-level proxy required. This is opt-in via the `tor` feature flag:

```toml
vector_sdk = { version = "0.3", features = ["tor"] }
```

```rust
let bot = VectorBot::builder()
    .nsec(key)
    .tor()
    .build()
    .await?;
```

For networks where Tor itself is blocked, bridges can be supplied:

```rust
let bot = VectorBot::builder()
    .nsec(key)
    .tor()
    .tor_bridges(["1.2.3.4:443 <fingerprint>"])
    .build()
    .await?;
```

{% hint style="success" %}
Tor is fully bootstrapped during `build()`, before the bot makes any connection, so it never touches the network in the clear, even during startup.&#x20;
{% endhint %}

***

### Identity & Storage

A bot's identity can be supplied explicitly or generated automatically.

{% tabs %}
{% tab title="Explicit (nsec)" %}

```rust
VectorBot::builder()
    .nsec("nsec1...")
    .build()
    .await?;
```

{% endtab %}

{% tab title="Explicit (mnemonic)" %}

```rust
VectorBot::builder()
    .mnemonic("twelve words ...")
    .build()
    .await?;
```

{% endtab %}

{% tab title="Keyless" %}

```rust
VectorBot::builder()
    .build()
    .await?;
```

{% endtab %}
{% endtabs %}

A keyless bot's identity is written to `identity.nsec` inside its data directory and reused on every subsequent run, so it keeps its chats and community memberships across restarts. It is never regenerated unless that file is removed.

Storage defaults to a per-OS application directory. Override it with `.data_dir(path)`:

```rust
VectorBot::builder()
    .data_dir("./bot-a-data")
    .build()
    .await?;
```

{% hint style="warning" %}
This is required if you intend to run more than one keyless bot, since each needs its own identity file.&#x20;
{% endhint %}

#### One Identity per Process

`vector-core` is built on process-global state. A single process can only hold one active `VectorBot` identity at a time. To run multiple bots, run multiple processes.

***

### Reaching the Core Engine

The SDK intentionally only surfaces the common 90% of what a bot needs. For anything else, including creating communities from scratch, reading older message history beyond what the live event stream delivers, or other lower-level controls, `bot.core()` returns the full `VectorCore` facade that the SDK itself is built on:

```rust
let history = bot.core().get_messages(&chat_id, /* ... */).await?;
```

{% hint style="info" %}
Anything available to the Vector desktop and mobile apps is, at some level, reachable through `VectorCore`.&#x20;
{% endhint %}
