Skip to main content

VectorCore

Struct VectorCore 

Source
pub struct VectorCore;
Expand description

The main entry point for Vector Core.

Provides a high-level API for all Vector operations. Internally uses global state (same pattern as the Tauri backend) for compatibility.

use vector_core::{VectorCore, CoreConfig};
use std::path::PathBuf;

let core = VectorCore::init(CoreConfig {
    data_dir: PathBuf::from("/tmp/vector-data"),
    event_emitter: None,
})?;

// Login with nsec
let result = core.login("nsec1...", None).await?;
println!("Logged in as {}", result.npub);

Implementations§

Source§

impl VectorCore

Source

pub fn init(config: CoreConfig) -> Result<Self>

Initialize Vector Core with the given configuration.

Source

pub fn accounts(&self) -> Result<Vec<String>>

Get all available accounts.

Source

pub async fn login( &self, key: &str, password: Option<&str>, ) -> Result<LoginResult>

Login with an nsec key or mnemonic seed phrase.

Source

pub fn generate_nsec(&self) -> Result<String>

Generate a fresh random account secret key (bech32 nsec). Lets a headless client spin up a brand-new identity (add_account with no key) without depending on nostr-sdk directly.

Source

pub async fn send_dm(&self, to_npub: &str, content: &str) -> Result<SendResult>

Send a NIP-17 gift-wrapped text DM using the full pipeline. Retries a transient publish miss (headless preset, 3 attempts) so an SDK/CLI bot rides out a relay blip instead of silently dropping the message on the first miss; self_send: false keeps it a plain send (no inbox self-copy).

Source

pub async fn send_dm_reply( &self, to_npub: &str, replied_to: &str, content: &str, ) -> Result<SendResult>

Send a DM as a threaded reply to replied_to (an existing message’s event id).

Source

pub async fn download_attachment( &self, attachment: &Attachment, ) -> Result<Vec<u8>>

Download a received attachment and decrypt it to plaintext bytes. Fetches the encrypted blob from its Blossom URL (SSRF/Tor-aware client, size-capped) and AES-decrypts with the attachment’s embedded key + nonce.

Source

pub async fn send_file( &self, to_npub: &str, file_path: &str, ) -> Result<SendResult>

Send a NIP-17 gift-wrapped file attachment DM.

Source

pub async fn send_reaction( &self, to_npub: &str, reference_id: &str, emoji: &str, emoji_url: Option<&str>, ) -> Result<String>

Send a NIP-25 reaction to a DM message. emoji_url carries the NIP-30 image URL when reacting with a custom-pack emoji (content stays :shortcode:). Returns the reaction’s rumor id. Local echo + persistence are best-effort — the gift-wrap send is the source of truth.

Source

pub async fn send_typing(&self, to_npub: &str) -> Result<()>

Send an ephemeral typing indicator to a DM recipient. Fire-and-forget with a 30-second NIP-40 expiry so relays purge it quickly.

Source

pub async fn edit_dm( &self, to_npub: &str, message_id: &str, new_content: &str, ) -> Result<String>

Edit a DM you previously sent (kind-16 edit) with an optimistic local echo. Returns the edit event id. Persistence is best-effort and only happens when the chat already exists locally.

Source

pub async fn delete_dm(&self, message_id: &str) -> Result<DeleteOutcome>

Delete a DM you sent (NIP-09 over the retained gift-wrap keys).

Source

pub async fn get_chats(&self) -> Vec<SerializableChat>

Get chats from the in-memory state.

Source

pub async fn get_messages( &self, chat_id: &str, limit: usize, offset: usize, ) -> Vec<Message>

Get messages for a chat (paginated).

Source

pub async fn get_profile(&self, npub: &str) -> Option<SlimProfile>

Get a profile by npub.

Source

pub async fn load_profile(&self, npub: &str) -> bool

Fetch a profile’s metadata and status from relays.

Source

pub async fn update_profile( &self, name: &str, avatar: &str, banner: &str, about: &str, ) -> bool

Update the current user’s profile metadata and broadcast to relays.

Source

pub async fn update_bot_profile( &self, name: &str, avatar: &str, banner: &str, about: &str, ) -> bool

Like update_profile but marks the profile as a bot (bot: true in the metadata). The SDK uses this for every bot; build human clients on update_profile.

Source

pub async fn update_status(&self, status: &str) -> bool

Update the current user’s status and broadcast to relays.

Source

pub async fn upload_public_image(&self, file_path: &str) -> Result<String>

Upload an image file to Blossom unencrypted and return its public URL — for avatars, banners, and other images other clients must fetch directly. (The opposite of send_file’s encrypted attachments.) Pass the URL to update_profile.

Source

pub async fn block_user(&self, npub: &str) -> bool

Block a user by npub.

Source

pub async fn unblock_user(&self, npub: &str) -> bool

Unblock a user by npub.

Source

pub async fn set_nickname(&self, npub: &str, nickname: &str) -> bool

Set a nickname for a profile.

Source

pub async fn get_blocked_users(&self) -> Vec<SlimProfile>

Get all blocked profiles.

Source

pub fn queue_profile_sync(&self, npub: &str, priority: SyncPriority)

Queue a profile for background sync.

Source

pub fn my_npub(&self) -> Option<String>

Get the current user’s npub.

Source

pub async fn list_communities(&self) -> Vec<Value>

List every Community held locally (owned or joined), each with its channels.

Source

pub async fn create_community_v2(&self, name: &str) -> Result<Value>

Create a fresh Concord v2 community owned by the local identity (the SDK’s default; the GUI’s create_community stays v1 during the migration window). Mints the self-certifying id + genesis, persists, publishes, and registers each channel as a chat. Returns a version: 2 JSON summary.

Source

pub async fn register_v2_chats( &self, community: &CommunityV2, session: &SessionGuard, )

Register each of a v2 community’s channels as a chat row (so it surfaces in the chat list / communities()), mirroring the v1 create path. session is captured by the caller BEFORE its network I/O, so this STATE write is skipped if the account swapped mid-flight (else we’d write A’s community into B’s in-memory chats).

Source

pub async fn join_community(&self, invite_url: &str) -> Result<Value>

Join a Community from a public invite URL (vectorapp.io/invite#...). Fetches the token-encrypted bundle, persists the member-view Community, and registers its channels as chats. Returns a JSON summary.

Source

pub fn list_pending_invites(&self) -> Result<Vec<Value>>

List the parked private invites (giftwrapped) awaiting acceptance. Each entry is the community id, its name (from the stored bundle), and the inviter’s npub.

Source

pub async fn accept_pending_invite(&self, community_id: &str) -> Result<Value>

Accept a PARKED private invite by community id: rebuild the member-view Community from the stored bundle, finalize the join exactly like a public link, then drop the pending row. Mirrors the desktop’s consent-then-join for an invite delivered over a gift wrap.

Source

pub async fn create_community(&self, name: &str) -> Result<Value>

Create a Community (single “general” channel) on the default trusted relays. Signs the owner attestation with this identity (so the creator is the proven owner), registers the channel as a chat, and returns a JSON summary.

Source

pub async fn create_public_invite(&self, community_id: &str) -> Result<String>

Mint a public invite link for a Community this identity owns. Returns the shareable URL.

Source

pub async fn invite_to_community( &self, community_id: &str, invitee_npub: &str, ) -> Result<Value>

Send a PRIVATE invite: gift-wrap this Community’s invite bundle directly to an npub over a NIP-17 DM (the same transport as a regular DM). The invitee parks it pending consent (accept_pending_invite). Requires CREATE_INVITE; a banned npub can’t be re-invited. Returns the wrap’s event id + relays.

Source

pub fn list_public_invites( &self, community_id: &str, ) -> Result<Vec<PublicInviteRecord>>

The public invite links this account minted for a Community (to list + revoke). Each carries the hex token (the link secret) needed by Self::revoke_public_invite. A local read for both protocols — links minted on this device (a v2 mint also syncs the cross-device 13303 record; v2 join_count is not yet tracked and is always 0).

Source

pub async fn revoke_public_invite( &self, community_id: &str, token: &str, ) -> Result<()>

Revoke a public invite link by its hex token. Retiring the LAST active link flips the Community to Private, which re-founds (rotates the base key + every channel key) to cut link-joined lurkers. Idempotent: a token this account doesn’t hold is a no-op. Needs a local key when the revoke triggers the privatize rekey (a bunker account can’t rotate).

Source

pub async fn send_community_message( &self, channel_id: &str, content: &str, replied_to: Option<&str>, ) -> Result<String>

Post a text message to a Community channel. Returns the message id (the inner id).

Source

pub async fn send_community_file( &self, channel_id: &str, file_path: &str, ) -> Result<String>

Send a file to a Community channel as an encrypted attachment. Returns the message id. Mirrors the DM file pipeline (encrypt → Blossom upload → NIP-92 imeta) but publishes over the community transport.

Source

pub async fn send_community_typing(&self, channel_id: &str) -> Result<()>

Send an ephemeral typing indicator to a Community channel.

Source

pub async fn send_community_reaction( &self, channel_id: &str, message_id: &str, emoji: &str, emoji_url: Option<&str>, ) -> Result<()>

React to a Community message. emoji_url carries the NIP-30 image URL for a custom :shortcode: reaction (parity with DMs).

Source

pub async fn edit_community_message( &self, channel_id: &str, message_id: &str, new_content: &str, ) -> Result<()>

Edit one of your own Community messages.

Source

pub async fn delete_community_message(&self, message_id: &str) -> Result<()>

Delete one of your own Community messages, resolving its channel from local state (the GUI path). A headless v2 consumer holds no local history — use Self::delete_community_message_in with the channel id instead.

Source

pub async fn delete_community_message_in( &self, channel_id: &str, message_id: &str, ) -> Result<()>

Delete one of your own Community messages in channel_id: a NIP-09 relay nuke when the per-message key is held (v1) or the in-plane kind-5 (v2), plus a cooperative tombstone so peers hide it, plus best-effort attachment cleanup.

Source

pub async fn sync_community_channel( &self, channel_id: &str, limit: usize, ) -> Result<(usize, Vec<String>)>

Catch a Community channel up from relays. v1: fetch + ingest the latest page of messages, reactions, edits, and deletes, returning how many were brand-new. v2: consensus catch-up only (rekeys + control refold) — chat history delivers over the live handler bridge, so the count is always 0. Returns (new_message_count, warnings); warnings are NON-FATAL errors hit during the sync (catch-up, control fold, read-cut resume) — surfaced rather than swallowed so a headless caller is never blind to “the sync ran but a re-founding couldn’t be resumed.”

Source

pub async fn get_chat_commands(&self, chat_id: &str) -> ChatCommandsSnapshot

The composer’s / picker snapshot for chat_id, answered INSTANTLY from local state: the chat’s bot-flagged members (kind-0 bot: true — the SDK sets it on every bot it builds) and their last-known manifests from the persistent store. When the last refresh is older than a minute (or the bot set changed), ONE background REQ re-fetches every bot’s manifest together (5s unification window), persists newer editions, and emits chat_commands_updated — the UI swaps the list in when it lands. Works for BOTH community protocols (an invocation is plain content; only the optional routing tag is v2-only) and DMs. The manifest REQ always includes the discovery indexers beside the chat’s own relays, so an unreachable or stranger-dropping community relay can’t blind the picker.

Source

pub async fn get_community_members(&self, community_id: &str) -> Vec<Value>

Observed members of a Community (best-effort: those who’ve posted or announced a join, minus anyone who’s left or is banned). v1 entries are {npub, last_active}; a v2 entry is {npub} (the Complete Memberlist carries no activity time). Best-effort throughout: a transport failure yields an empty list, never an error.

Source

pub fn community_capabilities(&self, community_id: &str) -> Result<Value>

My effective management capabilities in a community (role engine — owner is just position 0). Use to confirm a promotion/demotion landed. A local read: the roster is folded + persisted by the passive sync (v1) / control follow (v2), never fetched here.

Source

pub fn community_roles(&self, community_id: &str) -> Result<Value>

The community’s owner npub + the admin npubs (role overview). A local read, like Self::community_capabilities.

Source

pub async fn grant_admin(&self, community_id: &str, npub: &str) -> Result<()>

Grant a member the @admin role. Requires MANAGE_ROLES + outranking the role’s position.

Source

pub async fn revoke_admin(&self, community_id: &str, npub: &str) -> Result<()>

Revoke a member’s @admin role.

Source

pub async fn kick_member(&self, community_id: &str, npub: &str) -> Result<()>

Cooperatively kick a member — they self-remove but can rejoin. Requires KICK + outrank.

Source

pub async fn set_member_banned( &self, community_id: &str, npub: &str, banned: bool, ) -> Result<()>

Ban (true) or unban (false) a member. Ban is terminal (no rejoin); in a private community it also fires the read-cut rekey (needs a local key). Requires BAN + outrank.

Source

pub async fn dissolve_community(&self, community_id: &str) -> Result<()>

Owner dissolution / “Delete Community”: publish the terminal GroupDissolved tombstone (and retire the owner’s own invite links, no rekey), sealing the community permanently. Owner-only (re-verified cryptographically in service::dissolve_community); irreversible.

Source

pub async fn edit_community_metadata( &self, community_id: &str, name: Option<&str>, description: Option<&str>, ) -> Result<()>

Edit community metadata (name / description) as an authorized member (MANAGE_METADATA). None leaves a field unchanged; an empty description clears it.

Source

pub async fn create_community_channel( &self, community_id: &str, name: &str, private: bool, ) -> Result<String>

Create a new channel in a v2 community. A PUBLIC channel derives from the community_root, so peers fold it in with nothing to distribute; a PRIVATE one mints an independent key at channel-epoch 1 and delivers it to every current member over the rekey plane (CORD-03 §2 / CORD-06). Requires MANAGE_CHANNELS. Returns the new channel id (hex).

Source

pub async fn delete_community_channel( &self, community_id: &str, channel_id: &str, ) -> Result<()>

Delete (tombstone) a v2 community channel. Requires MANAGE_CHANNELS (reader-gated).

Source

pub async fn leave_community(&self, community_id: &str) -> Result<()>

Leave a Community: announce a best-effort “left” presence (before dropping keys), then drop the held keys + local channel chats. You need a fresh invite to rejoin.

Source

pub async fn sync_dms( &self, since_days: Option<u64>, handler: &dyn InboundEventHandler, ) -> Result<(u32, u32)>

Sync DM history from relays using NIP-77 negentropy set reconciliation.

Reconciles local wrapper history with relay state, fetches missing events, and processes them through the standard prepare → commit pipeline.

Returns (total_events, new_messages).

let core = vector_core::VectorCore;
// Sync last 7 days of DMs
let (events, new) = core.sync_dms(Some(7), &vector_core::NoOpEventHandler).await?;
println!("Processed {} events, {} new messages", events, new);
Source

pub async fn subscribe_dms(&self) -> Result<SubscriptionId>

Subscribe to incoming DM events (NIP-17 GiftWraps).

Returns the subscription ID for use in a custom notification loop. For a complete listen-and-process loop, use listen() instead.

Source

pub async fn sync_communities(&self) -> Result<()>

Catch up every locally-held Community: fold control / re-foundings / rekeys / banlist and fetch recent messages into local state for each channel. State-only (does not replay to an InboundEventHandler). Called at listen() start and periodically for outage resilience; also safe to call manually after a known disconnect.

Catch up every locally-held Community. v1 channels are synced inline; a v2 community is ENQUEUED for the follow worker (control/rekey re-fold + adopt), non-blocking. State-only (no handler replay of history). Called at listen() start and on reconnect; safe to call manually — the v2 enqueue is a no-op if no listen() worker is running.

Source

pub async fn listen(&self, handler: Arc<dyn InboundEventHandler>) -> Result<()>

Start listening for incoming DMs.

Blocks until the client disconnects. Processes GiftWraps (DMs, files) → prepare_event → commit_prepared_event.

use vector_core::*;
use std::sync::Arc;

struct MyBot;
impl InboundEventHandler for MyBot {
    fn on_dm_received(&self, chat_id: &str, msg: &Message, _is_new: bool) {
        if msg.mine { return; }
        let to = chat_id.to_string();
        let reply = format!("Echo: {}", msg.content);
        tokio::spawn(async move {
            let _ = VectorCore.send_dm(&to, &reply).await;
        });
    }
}

let core = VectorCore::init(CoreConfig {
    data_dir: "/tmp/bot-data".into(),
    event_emitter: None,
})?;
core.login("nsec1...", None).await?;
core.listen(Arc::new(MyBot)).await?;
Source

pub async fn logout(&self)

Disconnect and clean up.

Source

pub async fn swap_session(&self)

Tear down the current session for an in-process account swap — the account-agnostic core of the app’s reset_session(). Advances the session generation FIRST so any background task holding a SessionGuard short-circuits before it can touch the next account’s storage; shuts the client down (which ends any listen() notification loop bound to it, so the old account’s events can’t land in the new account’s DB); closes the DB pool; and clears the key vaults plus all in-memory per-account state. Follow with login() to bind the next account, then re-attach listen(). (The app’s reset_session() additionally clears Tauri-only caches it owns.)

Trait Implementations§

Source§

impl Clone for VectorCore

Source§

fn clone(&self) -> VectorCore

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

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

Performs copy-assignment from source. Read more
Source§

impl Copy for VectorCore

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> AsOut<T> for T
where T: Copy,

Source§

fn as_out(&mut self) -> Out<'_, T>

Returns an out reference to self.
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> 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> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + 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: Sized + 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