pub struct SidecarClient { /* private fields */ }Expand description
A client for the atproto OAuth sidecar’s internal API.
This is the live path for every authed repo operation. Rather than the Rust
server holding PDS tokens, it POSTs {did, action, …} to the sidecar’s
/internal/repo endpoint (gated by the shared X-Internal-Secret); the
sidecar restore(did)s the OAuth session — transparent DPoP + token refresh —
and runs the matching XRPC call via @atproto/api. The did (plus the shared
secret) is what authorizes the call; there is no bearer token on the Rust side.
It also fronts /internal/session/:id, the one-shot handoff the Rust callback
uses to turn a session_id (from the sidecar’s browser redirect) into the
{did, handle} it keys its own signed cookie by.
Cheap to clone (shared reqwest::Client + Arc’d config).
Implementations§
Source§impl SidecarClient
impl SidecarClient
Sourcepub fn new(
http: Client,
public_url: impl Into<String>,
internal_url: impl Into<String>,
internal_secret: impl Into<String>,
) -> Self
pub fn new( http: Client, public_url: impl Into<String>, internal_url: impl Into<String>, internal_secret: impl Into<String>, ) -> Self
Build a sidecar client from the shared reqwest::Client and the
resolved public + internal base URLs + internal secret (from
crate::config::SidecarConfig). public_url anchors the browser
/login redirect; internal_url is the loopback base for the /internal/*
API (they collapse to the same value in single-URL local dev).
Sourcepub fn login_url(&self, handle: &str, return_to: Option<&str>) -> String
pub fn login_url(&self, handle: &str, return_to: Option<&str>) -> String
The sidecar’s public /login URL for a handle, round-tripping an opaque
return value through OAuth state (used to bounce the browser back to a
specific place after login). The browser is redirected here.
Sourcepub async fn resolve_session(
&self,
session_id: &str,
) -> Result<Option<SidecarSession>>
pub async fn resolve_session( &self, session_id: &str, ) -> Result<Option<SidecarSession>>
Resolve a one-shot session_id (from the sidecar’s post-OAuth redirect)
to the {did, handle} that logged in. Ok(None) on 404 SessionNotFound.
Sourcepub async fn revoke_session(&self, did: &str) -> Result<RevokeResult>
pub async fn revoke_session(&self, did: &str) -> Result<RevokeResult>
Revoke a DID’s OAuth session at the sidecar: POST /internal/revoke.
This revokes the refresh + access tokens at the PDS and purges the
sidecar’s stored oauth_session + app_session rows for the DID. It is
idempotent — revoking a DID with no live session returns
had_session: false. Called on /logout (so the cookie clear isn’t the
only thing that ends the session) and on /account/delete.
Sourcepub async fn list_records(
&self,
did: &str,
collection: &str,
limit: Option<u32>,
cursor: Option<&str>,
) -> Result<ListRecordsResponse>
pub async fn list_records( &self, did: &str, collection: &str, limit: Option<u32>, cursor: Option<&str>, ) -> Result<ListRecordsResponse>
list — one page of a collection’s records for did.
Sourcepub async fn list_all_records(
&self,
did: &str,
collection: &str,
) -> Result<Vec<RecordEntry>>
pub async fn list_all_records( &self, did: &str, collection: &str, ) -> Result<Vec<RecordEntry>>
Page through all records in a collection for did.
Sourcepub async fn create_record<T: Serialize>(
&self,
did: &str,
collection: &str,
record: &T,
) -> Result<WriteResult>
pub async fn create_record<T: Serialize>( &self, did: &str, collection: &str, record: &T, ) -> Result<WriteResult>
create — create a record (server-assigned rkey). Returns its strong ref.
Sourcepub async fn put_record<T: Serialize>(
&self,
did: &str,
collection: &str,
rkey: &str,
record: &T,
) -> Result<WriteResult>
pub async fn put_record<T: Serialize>( &self, did: &str, collection: &str, rkey: &str, record: &T, ) -> Result<WriteResult>
put — upsert a record at a known rkey. Returns its strong ref.
Sourcepub async fn delete_record(
&self,
did: &str,
collection: &str,
rkey: &str,
) -> Result<()>
pub async fn delete_record( &self, did: &str, collection: &str, rkey: &str, ) -> Result<()>
delete — delete a record by collection + rkey.
Sourcepub async fn apply_writes(&self, did: &str, writes: &[WriteOp]) -> Result<()>
pub async fn apply_writes(&self, did: &str, writes: &[WriteOp]) -> Result<()>
applyWrites — a batch of create/update/delete ops in one round-trip.
Sourcepub async fn list_subscriptions(
&self,
did: &str,
) -> Result<Vec<(String, Subscription)>>
pub async fn list_subscriptions( &self, did: &str, ) -> Result<Vec<(String, Subscription)>>
List every Subscription record in did’s repo (paged fully).
Sourcepub async fn create_subscription(
&self,
did: &str,
sub: &Subscription,
) -> Result<WriteResult>
pub async fn create_subscription( &self, did: &str, sub: &Subscription, ) -> Result<WriteResult>
Create a Subscription record (subscribe to a feed).
Sourcepub async fn delete_subscription(&self, did: &str, rkey: &str) -> Result<()>
pub async fn delete_subscription(&self, did: &str, rkey: &str) -> Result<()>
Delete a Subscription record by rkey (unsubscribe).
Sourcepub async fn create_subscriptions_batch(
&self,
did: &str,
subs: &[Subscription],
) -> Result<()>
pub async fn create_subscriptions_batch( &self, did: &str, subs: &[Subscription], ) -> Result<()>
Batch-create many Subscription records in one applyWrites — the OPML
import path (one create op per feed, server-assigned rkeys).
Sourcepub async fn list_folders(&self, did: &str) -> Result<Vec<(String, Folder)>>
pub async fn list_folders(&self, did: &str) -> Result<Vec<(String, Folder)>>
List every Folder record in did’s repo.
Sourcepub async fn list_saved(&self, did: &str) -> Result<Vec<(String, Saved)>>
pub async fn list_saved(&self, did: &str) -> Result<Vec<(String, Saved)>>
List every Saved record in did’s repo.
Sourcepub async fn list_read_states(
&self,
did: &str,
) -> Result<Vec<(String, ReadState)>>
pub async fn list_read_states( &self, did: &str, ) -> Result<Vec<(String, ReadState)>>
List every ReadState cursor in did’s repo (the read side a
login-time read-state merge would consume).
Sourcepub async fn put_read_state(
&self,
did: &str,
rkey: &str,
state: &ReadState,
) -> Result<WriteResult>
pub async fn put_read_state( &self, did: &str, rkey: &str, state: &ReadState, ) -> Result<WriteResult>
Upsert a single ReadState cursor at its feed-derived rkey.
Sourcepub async fn flush_read_states(
&self,
did: &str,
cursors: &[(String, ReadState, bool)],
) -> Result<()>
pub async fn flush_read_states( &self, did: &str, cursors: &[(String, ReadState, bool)], ) -> Result<()>
Batch-flush many dirty ReadState cursors in one applyWrites call.
Each (rkey, state, pds_created) becomes a create op at the feed-derived
rkey when the record does NOT yet exist (pds_created == false), and an
update op when it does. This is what makes the FIRST flush of a feed
succeed: applyWrites#update errors on a record that does not pre-exist,
and applyWrites is atomic per-repo, so a single not-yet-created cursor
would otherwise drop the whole DID batch. Both kinds ride the SAME
applyWrites batch so batching is preserved.
Sourcepub async fn add_subscription(
&self,
did: &str,
sub: &Subscription,
) -> Result<String>
pub async fn add_subscription( &self, did: &str, sub: &Subscription, ) -> Result<String>
Add a subscription (subscribe to a feed) — createRecord, server-assigned
tid rkey. Returns the new record’s rkey so the web layer can offer
unsubscribe/rename immediately.
Sourcepub async fn remove_subscription(&self, did: &str, rkey: &str) -> Result<()>
pub async fn remove_subscription(&self, did: &str, rkey: &str) -> Result<()>
Remove a subscription (unsubscribe) by rkey — deleteRecord. Alias of
delete_subscription in the reader vocabulary.
Sourcepub async fn update_subscription(
&self,
did: &str,
rkey: &str,
sub: &Subscription,
) -> Result<WriteResult>
pub async fn update_subscription( &self, did: &str, rkey: &str, sub: &Subscription, ) -> Result<WriteResult>
Update / rename a subscription in place at a known rkey — putRecord.
The whole record is replaced (retitle, move to a folder, change the fetch hint …). Upsert semantics: it also creates the record if the rkey is somehow absent, so it is safe as a general “write this exact record”.
Sourcepub async fn list_subscriptions_sorted(
&self,
did: &str,
) -> Result<Vec<(String, Subscription)>>
pub async fn list_subscriptions_sorted( &self, did: &str, ) -> Result<Vec<(String, Subscription)>>
List every subscription, sorted deterministically — by display title (case-insensitive), then feed URL, then rkey as the final tiebreaker — so the rendered feed list is stable across reads regardless of PDS return order. Untitled feeds sort by their URL.
Sourcepub async fn add_subscriptions_bulk(
&self,
did: &str,
subs: &[Subscription],
) -> Result<Vec<String>>
pub async fn add_subscriptions_bulk( &self, did: &str, subs: &[Subscription], ) -> Result<Vec<String>>
Batch-add many subscriptions in one applyWrites — the OPML-import path.
Each feed becomes one create op. Client-side monotonic tid
rkeys are assigned so the batch is deterministic and the imported feeds
keep OPML order (server-assigned tids would also be monotonic, but pinning
them here makes the whole import reproducible and testable offline).
Returns the assigned rkeys in input order.
Sourcepub async fn add_folder(&self, did: &str, folder: &Folder) -> Result<String>
pub async fn add_folder(&self, did: &str, folder: &Folder) -> Result<String>
Add a folder — createRecord, server-assigned tid rkey. Returns the
new folder’s rkey (subscriptions reference it by its at:// URI).
Sourcepub async fn remove_folder(&self, did: &str, rkey: &str) -> Result<()>
pub async fn remove_folder(&self, did: &str, rkey: &str) -> Result<()>
Remove a folder by rkey — deleteRecord. (Subscriptions referencing it
are left untouched; a dangling folder ref reads as “unfiled”.)
Sourcepub async fn rename_folder(
&self,
did: &str,
rkey: &str,
folder: &Folder,
) -> Result<WriteResult>
pub async fn rename_folder( &self, did: &str, rkey: &str, folder: &Folder, ) -> Result<WriteResult>
Rename / update a folder in place at a known rkey — putRecord
(rename, or change its position sort hint).
Sourcepub async fn list_folders_sorted(
&self,
did: &str,
) -> Result<Vec<(String, Folder)>>
pub async fn list_folders_sorted( &self, did: &str, ) -> Result<Vec<(String, Folder)>>
List every folder, sorted deterministically — by position (the
lexicon’s sort hint; unset sorts last), then name (case-insensitive),
then rkey — so the sidebar order is stable.
Sourcepub async fn add_saved(&self, did: &str, saved: &Saved) -> Result<String>
pub async fn add_saved(&self, did: &str, saved: &Saved) -> Result<String>
Add a saved (starred / save-for-later) entry — createRecord,
server-assigned tid rkey. Returns the new record’s rkey.
Trait Implementations§
Source§impl Clone for SidecarClient
impl Clone for SidecarClient
Source§fn clone(&self) -> SidecarClient
fn clone(&self) -> SidecarClient
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl !RefUnwindSafe for SidecarClient
impl !UnwindSafe for SidecarClient
impl Freeze for SidecarClient
impl Send for SidecarClient
impl Sync for SidecarClient
impl Unpin for SidecarClient
impl UnsafeUnpin for SidecarClient
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,
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
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