pub trait CredentialStore: Send + Sync {
// Required methods
fn get(
&self,
component: &str,
key: &str,
) -> Result<Option<SecretRecord>, StoreError>;
fn put(
&self,
component: &str,
key: &str,
rec: &SecretRecord,
) -> Result<(), StoreError>;
fn erase(&self, component: &str, key: &str) -> Result<(), StoreError>;
fn list(
&self,
component: Option<&str>,
) -> Result<Vec<SecretInfo>, StoreError>;
fn components(&self) -> Result<Vec<String>, StoreError>;
fn update(
&self,
component: &str,
key: &str,
mutate: &mut dyn FnMut(&mut SecretRecord),
) -> Result<Option<SecretRecord>, StoreError>;
}Expand description
Keys are (component, key). component is the resolved reference the
operator used, which is what makes the profile a per-component namespace.
Required Methods§
fn get( &self, component: &str, key: &str, ) -> Result<Option<SecretRecord>, StoreError>
fn put( &self, component: &str, key: &str, rec: &SecretRecord, ) -> Result<(), StoreError>
fn erase(&self, component: &str, key: &str) -> Result<(), StoreError>
Sourcefn list(&self, component: Option<&str>) -> Result<Vec<SecretInfo>, StoreError>
fn list(&self, component: Option<&str>) -> Result<Vec<SecretInfo>, StoreError>
Metadata only, never values. None lists every component.
Sourcefn components(&self) -> Result<Vec<String>, StoreError>
fn components(&self) -> Result<Vec<String>, StoreError>
The components holding at least one credential.
list(None) flattens the profile away, which is the one thing a
listing across components must not lose: profile keys are normalised
(act-cli’s resolve::profile_key), so this is also how an operator
sees which key a set actually landed under. Component names, like
SecretInfo, cannot carry a value.
Sourcefn update(
&self,
component: &str,
key: &str,
mutate: &mut dyn FnMut(&mut SecretRecord),
) -> Result<Option<SecretRecord>, StoreError>
fn update( &self, component: &str, key: &str, mutate: &mut dyn FnMut(&mut SecretRecord), ) -> Result<Option<SecretRecord>, StoreError>
Read, mutate and write one record atomically against other processes.
Ok(None) when the key does not exist; the mutation is not run.
This exists because get then put is not the same thing. Two act
processes sharing the default store — two MCP servers under one client
is the ordinary case — can interleave there, and the loser’s write wins
with data read before the winner’s. For an OAuth refresh that is not a
lost update but a dead credential: with refresh-token rotation the
overwritten token is the only one the server still honours, and the user
is sent back to act login with nothing to explain it.
An implementation MUST hold an exclusive advisory lock across the whole read-modify-write, and the caller MUST re-check inside the closure what it decided outside it: by the time the lock is held, the work may already have been done by whoever held it first.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".