pub struct MessagingConfigStore { /* private fields */ }Expand description
In-process config/pairing store for the approval transport, now per-channel.
Reads/writes a single messaging.json under an injectable base dir. Every
mutating method persists synchronously via atomic_write_sync, so the
on-disk state is always the source of truth — a fresh
MessagingConfigStore::with_base_dir over the same dir reloads it.
Method surface is channel-parameterized (is_enabled(channel),
is_allowlisted(channel, handle), …). Back-compat shims without a channel
argument default to ChannelId::IMessage so the #403 callers and the WS
surface (which has no channel field until Unit 6) keep working.
Implementations§
Source§impl MessagingConfigStore
impl MessagingConfigStore
Sourcepub fn from_home() -> MessagingConfigStore
pub fn from_home() -> MessagingConfigStore
Open the store rooted at the CAR state root — $CAR_HOME when set,
otherwise ~/.car resolved from HOME (or USERPROFILE on Windows).
Falls back to a relative .car if none of the three resolve (only
happens in degenerate environments — tests inject a temp dir via
with_base_dir).
Sourcepub fn with_base_dir(base_dir: impl Into<PathBuf>) -> MessagingConfigStore
pub fn with_base_dir(base_dir: impl Into<PathBuf>) -> MessagingConfigStore
Open the store rooted at an explicit base dir (the state-root
equivalent). Tests pass a TempDir path here so they never touch
the developer’s real ~/.car/.
Sourcepub fn load(&self) -> Result<MessagingConfig, String>
pub fn load(&self) -> Result<MessagingConfig, String>
Load the persisted config from disk. A missing or empty file yields the default (no channels configured ⇒ every channel reads fail-closed). A malformed file surfaces as an error rather than silently resetting the trust state (which would be a silent security downgrade).
No migration branch (MC-4): the per-channel format is the first
format ever written to disk, so there is no legacy flat shape to be
tolerant toward. load() is read-only — it never calls save() (a
corrupt file fails closed to Err, it does not silently rewrite).
Sourcepub fn is_enabled_for(&self, channel: ChannelId) -> Result<bool, String>
pub fn is_enabled_for(&self, channel: ChannelId) -> Result<bool, String>
Whether channel is enabled (its master opt-in flag).
Sourcepub fn is_enabled(&self) -> Result<bool, String>
pub fn is_enabled(&self) -> Result<bool, String>
Back-compat: is_enabled() defaults to iMessage.
Sourcepub fn allowlist_for(&self, channel: ChannelId) -> Result<Vec<String>, String>
pub fn allowlist_for(&self, channel: ChannelId) -> Result<Vec<String>, String>
channel’s current allowlist of approver handles.
Sourcepub fn allowlist(&self) -> Result<Vec<String>, String>
pub fn allowlist(&self) -> Result<Vec<String>, String>
Back-compat: allowlist() defaults to iMessage.
Sourcepub fn is_allowlisted_for(
&self,
channel: ChannelId,
handle: &str,
) -> Result<bool, String>
pub fn is_allowlisted_for( &self, channel: ChannelId, handle: &str, ) -> Result<bool, String>
Whether handle is on channel’s allowlist. Compares the
channel-NORMALIZED forms (iMessage: phone punctuation stripped, emails
intact; Slack: member-ID form) so a hand-typed value matches the stored
canonical form. The adapter calls this to drop non-paired senders before
any parse (SC-7).
Sourcepub fn is_allowlisted(&self, handle: &str) -> Result<bool, String>
pub fn is_allowlisted(&self, handle: &str) -> Result<bool, String>
Back-compat: is_allowlisted(handle) defaults to iMessage.
Sourcepub fn set_enabled_for(
&self,
channel: ChannelId,
enabled: bool,
) -> Result<(), String>
pub fn set_enabled_for( &self, channel: ChannelId, enabled: bool, ) -> Result<(), String>
Set channel’s enabled flag. Host-gated path only.
Sourcepub fn set_enabled(&self, enabled: bool) -> Result<(), String>
pub fn set_enabled(&self, enabled: bool) -> Result<(), String>
Back-compat: set_enabled(enabled) defaults to iMessage.
Sourcepub fn set_allowlist_for(
&self,
channel: ChannelId,
handles: Vec<String>,
) -> Result<(), String>
pub fn set_allowlist_for( &self, channel: ChannelId, handles: Vec<String>, ) -> Result<(), String>
Replace channel’s entire allowlist. Host-gated path only. Handles are
channel-normalized before storage.
v1 single-handle cardinality guard (per channel): v1 supports exactly ONE paired/allowlisted user per channel (the orchestrator’s “sole pending approval” logic is correct-by-invariant only under that constraint). A request to set MORE THAN ONE distinct handle is rejected rather than silently widening the trust set.
Sourcepub fn set_allowlist(&self, handles: Vec<String>) -> Result<(), String>
pub fn set_allowlist(&self, handles: Vec<String>) -> Result<(), String>
Back-compat: set_allowlist(handles) defaults to iMessage.
Sourcepub fn add_handle_for(
&self,
channel: ChannelId,
handle: &str,
) -> Result<bool, String>
pub fn add_handle_for( &self, channel: ChannelId, handle: &str, ) -> Result<bool, String>
Add one handle to channel’s allowlist (idempotent). Host-gated path
only. The handle is channel-normalized before storage and comparison.
Returns true if newly added.
v1 single-handle cardinality guard (per channel): if a DIFFERENT
handle is already allowlisted on this channel, this is rejected — v1 binds
exactly one paired user per channel. Re-adding the SAME handle is still
the idempotent Ok(false) no-op.
Sourcepub fn add_handle(&self, handle: &str) -> Result<bool, String>
pub fn add_handle(&self, handle: &str) -> Result<bool, String>
Back-compat: add_handle(handle) defaults to iMessage.
Sourcepub fn remove_handle_for(
&self,
channel: ChannelId,
handle: &str,
) -> Result<bool, String>
pub fn remove_handle_for( &self, channel: ChannelId, handle: &str, ) -> Result<bool, String>
Remove one handle from channel’s allowlist (idempotent). Host-gated
path only. Compares channel-normalized forms. Returns true if removed.
Sourcepub fn remove_handle(&self, handle: &str) -> Result<bool, String>
pub fn remove_handle(&self, handle: &str) -> Result<bool, String>
Back-compat: remove_handle(handle) defaults to iMessage.
Sourcepub fn mint_pairing_code_for(
&self,
channel: ChannelId,
) -> Result<String, String>
pub fn mint_pairing_code_for( &self, channel: ChannelId, ) -> Result<String, String>
Mint a fresh pairing code for channel, persist it as that channel’s
active code, and return it for display in the local UI. Rotates any prior
active code on that channel. Host-gated path only — shown ONLY in local
UI, never produced from any inbound-derived value.
Sourcepub fn mint_pairing_code(&self) -> Result<String, String>
pub fn mint_pairing_code(&self) -> Result<String, String>
Back-compat: mint_pairing_code() defaults to iMessage.
Sourcepub fn active_pairing_code_for(
&self,
channel: ChannelId,
) -> Result<Option<String>, String>
pub fn active_pairing_code_for( &self, channel: ChannelId, ) -> Result<Option<String>, String>
channel’s active pairing code, if a pairing is in flight. Host-gated
read (status surface).
Sourcepub fn active_pairing_code(&self) -> Result<Option<String>, String>
pub fn active_pairing_code(&self) -> Result<Option<String>, String>
Back-compat: active_pairing_code() defaults to iMessage.
Sourcepub fn set_slack_token_ref_for(
&self,
channel: ChannelId,
token_ref: SlackTokenRef,
) -> Result<(), String>
pub fn set_slack_token_ref_for( &self, channel: ChannelId, token_ref: SlackTokenRef, ) -> Result<(), String>
Persist channel’s keychain token REFERENCE (MC-9). Host-gated path
only — the bearer values themselves are written to the OS keychain by
the provisioning write path (slack_adapter::provision_slack_tokens);
THIS stores only the key NAMES (a reference) into messaging.json, and
its presence is the “tokens provisioned” marker. Never accepts a bearer
value, so no xoxb-/xapp- can ever land on disk through this method.
Sourcepub fn slack_token_ref_for(
&self,
channel: ChannelId,
) -> Result<Option<SlackTokenRef>, String>
pub fn slack_token_ref_for( &self, channel: ChannelId, ) -> Result<Option<SlackTokenRef>, String>
channel’s persisted keychain token reference, if its tokens have been
provisioned. Host-gated read (the boot path reads this to construct the
live transport from the persisted refs). None ⇒ not yet provisioned.
Sourcepub fn set_slack_channel_id_for(
&self,
channel: ChannelId,
channel_id: &str,
) -> Result<(), String>
pub fn set_slack_channel_id_for( &self, channel: ChannelId, channel_id: &str, ) -> Result<(), String>
Persist channel’s Slack post-channel id (the conversation/channel id
the outbound prompt posts into, e.g. C0123…). Host-gated path only.
This is CONFIG, not a secret — it lands in messaging.json, never the
keychain. Set on the same host-gated messaging.config.set call as the
tokens.
Sourcepub fn slack_channel_id_for(
&self,
channel: ChannelId,
) -> Result<Option<String>, String>
pub fn slack_channel_id_for( &self, channel: ChannelId, ) -> Result<Option<String>, String>
channel’s persisted Slack post-channel id, if set. The boot path reads
this to construct the adapter with the channel to post into (NOT the
never-written keychain key). None ⇒ no post-channel configured (the
adapter is built but cannot post).
Sourcepub fn validate_and_consume_pairing_code_for(
&self,
channel: ChannelId,
candidate_handle: &str,
code: &str,
) -> Result<PairingOutcome, String>
pub fn validate_and_consume_pairing_code_for( &self, channel: ChannelId, candidate_handle: &str, code: &str, ) -> Result<PairingOutcome, String>
Validate an inbound-echoed pairing code from candidate_handle on
channel, using a CONSTANT-TIME compare against that channel’s active
code. On a match, bind candidate_handle into the channel’s allowlist
and clear/rotate the code; on a miss (or no active code) mutate NOTHING.
This is the ONLY mutation an inbound message can ever cause, and it can only ever add a handle to one channel — never set the enabled flag, never set the allowlist wholesale, never read config. A bare “approve”/“deny” or arbitrary text never reaches here.
Sourcepub fn validate_and_consume_pairing_code(
&self,
candidate_handle: &str,
code: &str,
) -> Result<PairingOutcome, String>
pub fn validate_and_consume_pairing_code( &self, candidate_handle: &str, code: &str, ) -> Result<PairingOutcome, String>
Back-compat: validate_and_consume_pairing_code(handle, code) defaults
to iMessage.
Trait Implementations§
Source§impl Clone for MessagingConfigStore
impl Clone for MessagingConfigStore
Source§fn clone(&self) -> MessagingConfigStore
fn clone(&self) -> MessagingConfigStore
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 Freeze for MessagingConfigStore
impl RefUnwindSafe for MessagingConfigStore
impl Send for MessagingConfigStore
impl Sync for MessagingConfigStore
impl Unpin for MessagingConfigStore
impl UnsafeUnpin for MessagingConfigStore
impl UnwindSafe for MessagingConfigStore
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
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<S, T> Duplex<S> for Twhere
T: FromSample<S> + ToSample<S>,
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
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