For the complete documentation index, see llms.txt. This page is also available as Markdown.

Vector SDK

Vector SDK is an ergonomic Rust library for building bots on Vector, a private, encrypted messenger built on Nostr. It is a thin, friendly layer over the vector-core engine, the same engine that powers the Vector desktop and mobile apps. A working bot can be built in about a dozen lines of Rust, without ever touching the underlying protocol or encryption.

Version compatibility: This SDK (v0.3.0) is built against Vector v0.4.0 and later. The SDK follows its own semantic versioning independent of the main Vector app, so its version number will not always match the app's release version.

Features

Cover

Vector Bots

Custom Metadata

Cover

Communities

Roles, Modes, Invites

Cover

Send/Receive Files

Private Messages & Files

Cover

Auto-Reconnect

Rides Out Drops

Cover

Embedded Tor

One Builder Call

Cover

Keyless Identity

Zero Setup

Quick Start

[dependencies]
vector_sdk = "0.3"
tokio = { version = "1", features = ["full"] }
use vector_sdk::VectorBot;

#[tokio::main]
async fn main() -> vector_sdk::Result<()> {
    let bot = VectorBot::builder()
        .nsec("nsec1...")          // the bot's key — or omit it and one is created for you
        .build()
        .await?;

    println!("Online as {}", bot.npub());

    // Reply to every message the bot receives.
    bot.on_message(|_bot, msg| async move {
        if msg.is_mine() { return; }
        let _ = msg.reply(&format!("You said: {}", msg.text())).await;
    }).await?;

    Ok(())
}

That bot already handles direct messages and communities, reconnects after a network drop, and catches up on anything it missed while offline.


One API, Everywhere

Your bot sends and receives through a Channel, a direct-message chat or a community channel, handled identically. You never need to branch on "is this a DM or a community?", you just send and reply.


What Your Bot Can Do

Messaging

Available on any chat or channel via bot.channel(id), bot.dm(npub), or msg.channel():

Method
What it does

channel.send(text)

Send a message

channel.reply(msg_id, text)

Reply to a specific message (threaded)

channel.react(msg_id, "👍")

React with an emoji

channel.react_custom(msg_id, ":code:", url)

React with a custom image emoji

channel.typing()

Show a typing indicator

channel.edit(msg_id, text) / channel.delete(msg_id)

Edit / delete a message the bot sent

channel.send_file(path)

Send a file

bot.download_attachment(&att) / bot.save_attachment(&att, path)

Get a received file

On an incoming message, msg.reply(text) answers it, msg.react(emoji) reacts to it, and any received files are available on msg.message.attachments.

Communities

When a message comes from a community, you get the sender as a Member you can act on directly:

A Community also supports edit(name, description) to update its metadata, leave(), and dissolve() (owner-only, permanently ends the community). capabilities() and roles() expose the underlying role-permission data for more advanced moderation logic.

Joining Communities

To be useful in a community, a bot has to accept invites. Choose how:

By default, invites wait for you to handle them manually (bot.pending_invites() / bot.accept_invite(id)). Auto-accept also picks up invites that arrived while the bot was offline, so a restarted bot still joins what it was invited to.

Receiving

bot.on_message(handler) runs your handler for every incoming message, DM or community alike. A slow handler will not hold up the others.

For more than messages, bot.on_event(|bot, event|) gives you the full event stream as a BotEvent, match the parts you care about:

BotEvent covers messages, reactions and edits, deletes, members joining or leaving, typing, invites, and the bot being removed from a community.

Staying Connected

If the bot loses its connection, it reconnects on its own and catches up on what it missed. Your handler fires for messages that arrive while the bot is running; to read older history, use bot.core().get_messages(...).

Profiles

bot.fetch_profile(npub), bot.cached_profile(npub), bot.update_profile(...), bot.set_status(...), bot.block(npub) / bot.unblock(npub), bot.blocked_users(), bot.set_nickname(...), and bot.upload_image(path) for setting an avatar. Bots are automatically tagged as bots when their profile is updated.

Going Deeper

bot.core() exposes the full VectorCore engine for anything not surfaced directly on the SDK, including creating communities, reading message history, and lower-level controls.


Examples

Runnable, self-contained bots live in examples/, each one demonstrating a single concept. Every example needs VECTOR_NSEC (the bot's key); a few take additional environment variables.

Example
What it shows

echo_bot

The minimal hello-world, replies to every message.

slash_command_bot

A /command router: /ping, /echo, /roll, /help.

ai_bot

An LLM chatbot: typing indicator, threaded replies, per-chat history.

moderation_bot

Welcomes new members and auto-bans on a word filter.

whitelist_bot

A private bot that only joins communities it trusts.

file_bot

Sends one file, then exits.

save_files_bot

Saves every received file to disk.


Accounts & Keys

  • No key. build() creates an identity on first run and reuses it on every run after, the simplest way to get a bot online. Running several keyless bots? Give each its own .data_dir(...) so they get distinct identities.

  • .nsec("nsec1...") — use an existing key.

  • .mnemonic("twelve words ...") — use a 12-word seed phrase.

  • .password("...") — only needed for keys that are encrypted at rest.

  • VectorBot::generate_nsec() — mint a fresh key yourself, outside the builder.

A keyless bot's identity is stable across restarts, so it keeps its chats and community memberships. Storage defaults to a per-OS application directory; override it with .data_dir(path).


One Bot per Process

A bot owns the process while it runs, so run one bot per process. To run several bots, run several processes.


Tor (Optional)

To route the bot through Tor, enable the tor feature and call .tor() on the builder. The feature alone only compiles Tor in; .tor() is what actually turns it on:

Tor is bootstrapped during build() before the bot connects, so it never touches the network in the clear. The feature is off by default, keeping the dependency tree light for bots that don't need it.


License

This project is licensed under the MIT License. See the LICENSE file for details.

Last updated