Skip to main content

Module muxbus

Module muxbus 

Source

Structs§

CrossProcessGuard 🔒
A held OS advisory lock; released when dropped (the handle closes).
MuxBusBlob 🔒
On-disk JSON shape for the single-blob format (macOS/Linux’s preferred layout, write_single_blob/blob_key). Wraps MuxBusTokens with a generation stamp — the SAME new_generation() nanosecond-timestamp scheme the chunked layout already uses for its own cross-field torn-write check — so a blob and split entries found coexisting (muxbus_load_tokens’s reconciliation) can be compared for actual freshness instead of assuming a fixed direction is always right.
MuxBusCredentials
MuxBusTokens 🔒
The actual secret material. Every field is written to its own keychain entry (field_key); this struct only groups them for callers and for deserializing the legacy combined-blob format during migration. Everything else on MuxBusCredentials is non-secret metadata and stays in SQLite, matching how SecretRef::Keychain accounts already split pointer-metadata (DB) from plaintext (keychain) elsewhere in this codebase.

Enums§

FieldIncomplete 🔒
Read field_key’s value + generation stamp back from its chunk entries. Ok(None) means no :count entry exists yet (this field was never written under the chunked layout — caller falls through to the legacy-blob / legacy-plaintext sources). Err means a read genuinely failed, or the chunk state is internally inconsistent (a chunk went missing between the count write and now) — not just “no entry”. A missing :gen entry on an otherwise-complete field is treated as a read failure too, not defaulted to some sentinel — see read_split_tokens, which needs a REAL generation to compare across fields, not a value that would spuriously “match” another field’s own missing generation. Why read_chunked_field has no value: the field is mid-write or was interrupted (Yes, a torn read to retry), or the keychain failed (No).
PriorKeychainState 🔒
What a keychain entry held immediately before a write to it, so a later failure (the SQL write in muxbus_save, or a sibling field’s write in write_split_tokens) can be rolled back to that exact prior state. Three-way, not a bool: reagent P1 on #2260 caught a first version of this that collapsed Ok(None) (“genuinely no prior entry”) and Err (“the read itself failed, prior state UNKNOWN”) into the same None, so an unrelated transient read failure would make the rollback branch delete a real, valid, previously-stored credential it simply couldn’t read — turning a transient glitch into a forced full re-login. Unknown gets neither restore nor delete: we truly don’t know what was there, so the only safe move is to leave whatever the write just wrote and log loudly.
SplitRead 🔒
What the three split entries held when read.

Constants§

FIELD_ACCESS 🔒
FIELD_ID 🔒
FIELD_REFRESH 🔒
GLOBAL_KEYCHAIN_NS 🔒
The host-wide keychain namespace: the stable channel’s (and any non-isolated channel’s) MuxBus tokens. Same string as the broker’s credential id, crate::muxbus::CREDENTIAL_ID.
MAX_CHUNK_LEN 🔒
A first fix attempt (splitting the combined blob into one keychain entry per field) turned out NOT to be sufficient: live-tested against a real Cognito login, the exact same “Attribute ‘password’ is longer than platform limit of 2560 chars” error still fired — a single token field can itself exceed the cap depending on the app client’s token content (custom claims, refresh token length), not just the three combined. So each field is further chunked into as many entries as it takes, tracked by an explicit <field>:count entry (write_chunked_field / read_chunked_field below) — this holds regardless of any individual token’s real-world size, instead of relying on an assumption about it.
MAX_PLAUSIBLE_CHUNKS 🔒
A previous, longer value at a field could in principle need many chunks; no real Cognito token comes remotely close (MAX_PLAUSIBLE_CHUNKS * MAX_CHUNK_LEN = 32,000 chars). Used only as muxbus_clear’s fallback deletion bound when the real count can’t be read (see its call site) — secret_store::delete on a non-existent entry is a no-op success, so scanning past the real count there is harmless, just wasted calls.
REFRESH_LOCK_WAIT 🔒
How long a refresh waits for another process’s refresh of the same sign-in: long enough to cover a slow token request.
XPROC_LOCK_WAIT 🔒
How long a save, load or clear waits for another process. Past it the operation fails as transient (a load is retried, a sign-in reports an error) instead of running unlocked, which is what tore sign-ins.

Functions§

agent_credential_prefix_for 🔒
Prefix for this channel’s rows in db_agent_credentials, which is keyed by agent id alone: empty for stable and for the channel’s own store. An agent id is [A-Za-z0-9_-], so the / can’t collide with one.
blob_key 🔒
Single-entry key holding all three tokens as one JSON blob. Two different roles depending on platform (see muxbus_load_tokens / muxbus_save):
chunk_key 🔒
chunk_value 🔒
Pure chunking split — no keychain I/O — so the boundary math is testable without touching the real OS keychain. A token’s bytes are always ASCII in practice (JWTs / opaque base64url strings), so splitting on byte boundaries never lands mid-character; unwrap_or("") is a defensive fallback only, not an expected path.
count_key 🔒
current_agent_credential_prefix 🔒
current_row_id 🔒
delete_split_tokens 🔒
Delete every entry the chunked per-field layout could have written (FIELD_ACCESS/FIELD_REFRESH/FIELD_ID’s chunks + :count + :gen). Shared by muxbus_clear (unconditional logout cleanup, any platform) and the macOS/Linux migration path in muxbus_load_tokens (collapsing a pre-fix install’s chunked entries into the single-blob format — see blob_key’s doc comment). Best-effort: secret_store::delete on a non-existent entry is a no-op success, and a real delete failure here just leaves an orphaned, unreadable-without-the-others chunk behind rather than losing anything live.
field_key 🔒
generation_as_number 🔒
Parse a new_generation()-style nanosecond-timestamp string into a comparable number. An empty or corrupted value reads as 0 — the oldest possible timestamp — so it always loses a freshness comparison rather than erroring a read that would otherwise succeed.
generation_key 🔒
keychain_namespace 🔒
The keychain namespace this process’s MuxBus tokens live under.
lock_file 🔒
Take the lock at path, polling until wait has passed. None when it couldn’t be taken: another process held it the whole time, or the file couldn’t be opened.
lock_in 🔒
The lock for keychain namespace ns, under root’s muxbus-locks. With no shared root (some test setups) there is nowhere to put it: Ok(None).
lock_namespace 🔒
namespace_for 🔒
new_generation 🔒
A per-write identifier stamped onto all three fields by a single write_split_tokens call, so read_split_tokens can detect a torn write ACROSS a process crash — not the concurrent-read-in-one-process case muxbus_load_impl’s lock already covers, but a kill signal landing mid-write, between one field’s write_chunked_field call completing and the next one starting (reagent P2: each field is individually self-consistent, per write_chunked_field’s own chunk+rollback atomicity, but with no lock surviving the process there’s nothing to stop the OLD stale value for the not-yet-reached field from still being present when the other two ARE the fresh ones — silently reconstructing a token set that pairs pieces from two different login sessions). High-resolution wall-clock nanos is unique enough for this purpose in practice; a formal UUID would work too but adds a dependency for no real benefit here.
read_any_layout 🔒
The tokens stored under ns in either layout (chunked split entries or the single blob), without migrating or deleting anything. None when neither layout holds a complete, consistent set or a read fails. Used only to adopt the host-wide session into a channel namespace.
read_chunk_count 🔒
Ok(0) means the :count entry genuinely doesn’t exist (field never written under the chunked layout). Err means the read itself failed — reagent P1: a prior version of this collapsed both into the same 0, which muxbus_clear used to bound its per-field deletion loop (for i in 0..count) — a transient read failure made that loop delete NOTHING, while the field’s :count key was still deleted unconditionally right after, so logout appeared to succeed while the real token chunks were silently orphaned in the OS keychain. Callers must not treat Err as “0 chunks.”
read_chunked_field 🔒
read_split_settled 🔒
Read the split entries, retrying a few times while they look torn: a save by an older build (no cross-process lock) may be mid-write, and a moment later the same read is whole. A tear that persists is genuine.
read_split_state 🔒
read_split_tokens 🔒
Read the three split-entry (chunked) tokens. Ok(None) means none of the three fields exist yet (not migrated to this layout — caller falls through to the legacy-blob / legacy-plaintext sources). Err means a read itself failed (locked keychain, no Secret Service daemon, permission denied — not just “no entry”), which the caller may still recover from via a legacy fallback. Returns the tokens plus the generation stamp shared by all three fields (validated equal below) — callers that need to compare this layout’s freshness against a coexisting blob use the second element; callers that don’t (the common case) just ignore it.
retry_while_torn 🔒
row_id_for 🔒
The db_muxbus_credentials row id: global for stable and for a store that is already the channel’s own, channel:<ch> for a channel’s row in the store channels share.
same_account 🔒
Whether tokens were issued to the account user_sub names. A token that isn’t a JWT, or a row with no user_sub, matches nothing.
sign_in_channel 🔒
The channel whose own MuxBus sign-in this process uses. A channel is the unit of sign-in, not the host: every channel but stable (the installed releases, which have always used the host-wide set) signs in on its own, so two channels on one machine can be signed in at once, to the same account or to different ones, and one’s refresh, sign-in or sign-out never touches the other’s (SPEC_MUXBUS_SIGN_IN_PER_CHANNEL_2026_10_08.md).
store_is_channel_local 🔒
Whether the store this process reads is already this channel’s own file (isolated auth with an instance dir: registry::paths::resolve_shared_store_path), where the channel needs no per-row scoping.
write_chunked_field 🔒
Write value to field_key as one or more chunk entries (each under MAX_CHUNK_LEN) plus a :count entry, clearing any stale trailing chunks left over from a previous, longer value at this key, and stamps generation (same value across all three fields for one write_split_tokens call — see new_generation’s doc comment) on this field’s :gen entry. Rolls back everything this call touched if any single write/delete fails partway through.
write_single_blob 🔒
Write all three tokens as one combined JSON blob under a single keychain entry (blob_key — the CURRENT preferred format on macOS/Linux, see that constant’s doc comment). One SecItemAdd/ SecItemUpdate call is inherently atomic at the OS level, so this needs none of write_split_tokens’s cross-field generation-stamp/rollback machinery — there’s only one field. Returns the pre-call state wrapped in the same Vec<(String, PriorKeychainState)> shape write_split_tokens returns, so muxbus_save’s SQL-failure rollback branch works unchanged regardless of which one ran.
write_split_tokens 🔒
Write all three token fields, each independently chunked (write_chunked_field). On a later field’s failure, rolls back every entry every earlier field in this call already wrote — otherwise a partial failure would leave the three fields holding a mix of old and new tokens, which is worse than either the old or the new set alone.