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
impl VectorCore
Sourcepub fn init(config: CoreConfig) -> Result<Self>
pub fn init(config: CoreConfig) -> Result<Self>
Initialize Vector Core with the given configuration.
Sourcepub async fn login(
&self,
key: &str,
password: Option<&str>,
) -> Result<LoginResult>
pub async fn login( &self, key: &str, password: Option<&str>, ) -> Result<LoginResult>
Login with an nsec key or mnemonic seed phrase.
Sourcepub fn generate_nsec(&self) -> Result<String>
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.
Sourcepub async fn send_dm(&self, to_npub: &str, content: &str) -> Result<SendResult>
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).
Sourcepub async fn send_dm_reply(
&self,
to_npub: &str,
replied_to: &str,
content: &str,
) -> Result<SendResult>
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).
Sourcepub async fn download_attachment(
&self,
attachment: &Attachment,
) -> Result<Vec<u8>>
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.
Sourcepub async fn send_file(
&self,
to_npub: &str,
file_path: &str,
) -> Result<SendResult>
pub async fn send_file( &self, to_npub: &str, file_path: &str, ) -> Result<SendResult>
Send a NIP-17 gift-wrapped file attachment DM.
Sourcepub async fn send_reaction(
&self,
to_npub: &str,
reference_id: &str,
emoji: &str,
emoji_url: Option<&str>,
) -> Result<String>
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.
Sourcepub async fn send_typing(&self, to_npub: &str) -> Result<()>
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.
Sourcepub async fn edit_dm(
&self,
to_npub: &str,
message_id: &str,
new_content: &str,
) -> Result<String>
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.
Sourcepub async fn delete_dm(&self, message_id: &str) -> Result<DeleteOutcome>
pub async fn delete_dm(&self, message_id: &str) -> Result<DeleteOutcome>
Delete a DM you sent (NIP-09 over the retained gift-wrap keys).
Sourcepub async fn get_chats(&self) -> Vec<SerializableChat>
pub async fn get_chats(&self) -> Vec<SerializableChat>
Get chats from the in-memory state.
Sourcepub async fn get_messages(
&self,
chat_id: &str,
limit: usize,
offset: usize,
) -> Vec<Message>
pub async fn get_messages( &self, chat_id: &str, limit: usize, offset: usize, ) -> Vec<Message>
Get messages for a chat (paginated).
Sourcepub async fn get_profile(&self, npub: &str) -> Option<SlimProfile>
pub async fn get_profile(&self, npub: &str) -> Option<SlimProfile>
Get a profile by npub.
Sourcepub async fn load_profile(&self, npub: &str) -> bool
pub async fn load_profile(&self, npub: &str) -> bool
Fetch a profile’s metadata and status from relays.
Sourcepub async fn update_profile(
&self,
name: &str,
avatar: &str,
banner: &str,
about: &str,
) -> bool
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.
Sourcepub async fn update_bot_profile(
&self,
name: &str,
avatar: &str,
banner: &str,
about: &str,
) -> bool
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.
Sourcepub async fn update_status(&self, status: &str) -> bool
pub async fn update_status(&self, status: &str) -> bool
Update the current user’s status and broadcast to relays.
Sourcepub async fn upload_public_image(&self, file_path: &str) -> Result<String>
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.
Sourcepub async fn block_user(&self, npub: &str) -> bool
pub async fn block_user(&self, npub: &str) -> bool
Block a user by npub.
Sourcepub async fn unblock_user(&self, npub: &str) -> bool
pub async fn unblock_user(&self, npub: &str) -> bool
Unblock a user by npub.
Sourcepub async fn set_nickname(&self, npub: &str, nickname: &str) -> bool
pub async fn set_nickname(&self, npub: &str, nickname: &str) -> bool
Set a nickname for a profile.
Sourcepub async fn get_blocked_users(&self) -> Vec<SlimProfile>
pub async fn get_blocked_users(&self) -> Vec<SlimProfile>
Get all blocked profiles.
Sourcepub fn queue_profile_sync(&self, npub: &str, priority: SyncPriority)
pub fn queue_profile_sync(&self, npub: &str, priority: SyncPriority)
Queue a profile for background sync.
Sourcepub async fn list_communities(&self) -> Vec<Value>
pub async fn list_communities(&self) -> Vec<Value>
List every Community held locally (owned or joined), each with its channels.
Sourcepub async fn create_community_v2(&self, name: &str) -> Result<Value>
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.
Sourcepub async fn register_v2_chats(
&self,
community: &CommunityV2,
session: &SessionGuard,
)
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).
Sourcepub async fn join_community(&self, invite_url: &str) -> Result<Value>
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.
Sourcepub fn list_pending_invites(&self) -> Result<Vec<Value>>
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.
Sourcepub async fn accept_pending_invite(&self, community_id: &str) -> Result<Value>
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.
Sourcepub async fn create_community(&self, name: &str) -> Result<Value>
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.
Sourcepub async fn create_public_invite(&self, community_id: &str) -> Result<String>
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.
Sourcepub async fn invite_to_community(
&self,
community_id: &str,
invitee_npub: &str,
) -> Result<Value>
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.
Sourcepub fn list_public_invites(
&self,
community_id: &str,
) -> Result<Vec<PublicInviteRecord>>
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).
Sourcepub async fn revoke_public_invite(
&self,
community_id: &str,
token: &str,
) -> Result<()>
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).
Sourcepub async fn send_community_message(
&self,
channel_id: &str,
content: &str,
replied_to: Option<&str>,
) -> Result<String>
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).
Sourcepub async fn send_community_file(
&self,
channel_id: &str,
file_path: &str,
) -> Result<String>
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.
Sourcepub async fn send_community_typing(&self, channel_id: &str) -> Result<()>
pub async fn send_community_typing(&self, channel_id: &str) -> Result<()>
Send an ephemeral typing indicator to a Community channel.
Sourcepub async fn send_community_reaction(
&self,
channel_id: &str,
message_id: &str,
emoji: &str,
emoji_url: Option<&str>,
) -> Result<()>
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).
Sourcepub async fn edit_community_message(
&self,
channel_id: &str,
message_id: &str,
new_content: &str,
) -> Result<()>
pub async fn edit_community_message( &self, channel_id: &str, message_id: &str, new_content: &str, ) -> Result<()>
Edit one of your own Community messages.
Sourcepub async fn delete_community_message(&self, message_id: &str) -> Result<()>
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.
Sourcepub async fn delete_community_message_in(
&self,
channel_id: &str,
message_id: &str,
) -> Result<()>
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.
Sourcepub async fn sync_community_channel(
&self,
channel_id: &str,
limit: usize,
) -> Result<(usize, Vec<String>)>
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.”
Sourcepub async fn get_chat_commands(&self, chat_id: &str) -> ChatCommandsSnapshot
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.
Sourcepub async fn get_community_members(&self, community_id: &str) -> Vec<Value>
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.
Sourcepub fn community_capabilities(&self, community_id: &str) -> Result<Value>
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.
Sourcepub fn community_roles(&self, community_id: &str) -> Result<Value>
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.
Sourcepub async fn grant_admin(&self, community_id: &str, npub: &str) -> Result<()>
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.
Sourcepub async fn revoke_admin(&self, community_id: &str, npub: &str) -> Result<()>
pub async fn revoke_admin(&self, community_id: &str, npub: &str) -> Result<()>
Revoke a member’s @admin role.
Sourcepub async fn kick_member(&self, community_id: &str, npub: &str) -> Result<()>
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.
Sourcepub async fn set_member_banned(
&self,
community_id: &str,
npub: &str,
banned: bool,
) -> Result<()>
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.
Sourcepub async fn dissolve_community(&self, community_id: &str) -> Result<()>
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.
Sourcepub async fn edit_community_metadata(
&self,
community_id: &str,
name: Option<&str>,
description: Option<&str>,
) -> Result<()>
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.
Sourcepub async fn create_community_channel(
&self,
community_id: &str,
name: &str,
private: bool,
) -> Result<String>
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).
Sourcepub async fn delete_community_channel(
&self,
community_id: &str,
channel_id: &str,
) -> Result<()>
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).
Sourcepub async fn leave_community(&self, community_id: &str) -> Result<()>
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.
Sourcepub async fn sync_dms(
&self,
since_days: Option<u64>,
handler: &dyn InboundEventHandler,
) -> Result<(u32, u32)>
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);Sourcepub async fn subscribe_dms(&self) -> Result<SubscriptionId>
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.
Sourcepub async fn sync_communities(&self) -> Result<()>
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.
Sourcepub async fn listen(&self, handler: Arc<dyn InboundEventHandler>) -> Result<()>
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?;Sourcepub async fn swap_session(&self)
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
impl Clone for VectorCore
Source§fn clone(&self) -> VectorCore
fn clone(&self) -> VectorCore
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreimpl Copy for VectorCore
Auto Trait Implementations§
impl Freeze for VectorCore
impl RefUnwindSafe for VectorCore
impl Send for VectorCore
impl Sync for VectorCore
impl Unpin for VectorCore
impl UnsafeUnpin for VectorCore
impl UnwindSafe for VectorCore
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
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