pub struct ComponentRegistry { /* private fields */ }Expand description
A component registry stored as a TlsMap<ComponentId, VLBytes> where
each value is a protobuf-encoded ComponentMetadata describing the
component’s data type and permission policies.
The registry provides deterministic TLS serialization (sorted by ComponentId) and enforces that hardcoded and reserved component IDs cannot be modified through this map (their permissions are enforced in code).
Stored at well-known component ID 0x8000 (ComponentId::COMPONENT_REGISTRY).
§One raw map, a validated view
The map holds every entry exactly as it was read, byte for byte —
including entries this build cannot validate. Validation is applied
lazily on read: get, iter,
contains, and len present only
recognized entries (those that pass
validate_entry). An entry that fails
validation is invisible to all of them, so a write against it falls to
the deny-by-default NoRegistryEntry policy verdict. The raw bytes
still surface through to_bytes (verbatim) and
unrecognized_ids (a diagnostic).
Two invariants motivate carrying bytes we can’t read:
- Never fork by dropping bytes. The registry lives inside the
app_data_dictionarygroup-context extension; every member must compute byte-identical dict bytes or the group splits. A committer reads the dict, changes one component, and re-emits the whole thing, so it must round-trip untouched entries exactly — and prost does not preserve unknown proto fields across a decode/re-encode, which is why we keep rawVLBytesand never re-serialize an entry we didn’t author. - Never brick on state we didn’t write. A single entry left by a newer protocol version, or by a historically buggy writer (a poisoned group), must degrade to “that one component is unwritable here,” never to “no commit on this group validates again.” Rejecting the whole snapshot would do the latter, because the registry is decoded on the validation path of every commit.
Tolerance is a read-side backstop, not an enforcement hole: new entries still cannot enter a group’s registry unvalidated (the steady-state wire path validates per-mutation and the bootstrap validator validates each entry of the initial delta).
Implementations§
Source§impl ComponentRegistry
impl ComponentRegistry
pub fn new() -> Self
Sourcepub fn get(
&self,
id: &ComponentId,
) -> Result<Option<ComponentMetadata>, ComponentRegistryError>
pub fn get( &self, id: &ComponentId, ) -> Result<Option<ComponentMetadata>, ComponentRegistryError>
Get the metadata for a recognized component.
Returns Ok(None) both when no entry exists and when an entry exists
but fails validate_entry — an unreadable
entry is deny-by-default, indistinguishable from absent, so callers
never have to tell the two apart. The Result is retained for API
stability; this method does not currently produce an error.
Sourcepub fn set(
&mut self,
id: ComponentId,
meta: ComponentMetadata,
) -> Result<(), ComponentRegistryError>
pub fn set( &mut self, id: ComponentId, meta: ComponentMetadata, ) -> Result<(), ComponentRegistryError>
Register or update a component’s metadata.
For mutable components in the registry, this silently overwrites any existing entry — there is no audit log here because the audit trail lives in the MLS commit history that produced the change.
A valid write to an id that currently holds an unrecognized entry
repairs it — the raw bytes are overwritten, so to_bytes serializes
the repaired value rather than resurrecting the broken bytes.
Rejects invalid IDs, IDs in the reserved range, hardcoded components (whose permissions are enforced in code, not metadata), immutable components that already hold a recognized entry (write-once semantics), metadata missing required fields, and constrained components with invalid policy values.
Sourcepub fn remove(&mut self, id: &ComponentId) -> Result<(), ComponentRegistryError>
pub fn remove(&mut self, id: &ComponentId) -> Result<(), ComponentRegistryError>
Remove a component from the registry.
Rejects everything set rejects (invalid IDs, reserved,
hardcoded, and overwrite of a write-once-immutable entry). Removing a
modifiable id drops its bytes whether the entry was recognized or an
unrecognized one preserved by from_bytes
(repair-by-delete).
Sourcepub fn contains(&self, id: &ComponentId) -> bool
pub fn contains(&self, id: &ComponentId) -> bool
Returns true if the registry contains a recognized entry for the
given component. An unrecognized (preserved-but-invalid) entry
reports false.
Sourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
Returns the number of recognized entries. Unrecognized entries
preserved by from_bytes are not counted.
Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
Returns true if the registry has no recognized entries. A registry carrying only unrecognized entries still reports empty.
Sourcepub fn iter(
&self,
) -> impl Iterator<Item = Result<(ComponentId, ComponentMetadata), ComponentRegistryError>> + '_
pub fn iter( &self, ) -> impl Iterator<Item = Result<(ComponentId, ComponentMetadata), ComponentRegistryError>> + '_
Iterate over the recognized component IDs and their decoded
metadata, in ComponentId order. Unrecognized entries preserved by
from_bytes are skipped — writes against them
fall to deny-by-default — and are surfaced via
unrecognized_ids instead.
Each item is a Result for API stability; a recognized entry always
decodes, so this only ever yields Ok.
Sourcepub fn to_bytes(&self) -> Result<Vec<u8>, ComponentRegistryError>
pub fn to_bytes(&self) -> Result<Vec<u8>, ComponentRegistryError>
Serialize the registry as the dict-storage format: the raw
TlsMap<ComponentId, VLBytes> snapshot, verbatim. Because the map is
stored exactly as read (recognized and unrecognized entries alike), a
load → store round-trip is byte-identical and never drops data
another (possibly newer) client wrote.
Wire-format encoding (a TlsMapDelta describing changes) is done
piecemeal at the call site — there is no whole-registry wire encoder,
because every steady-state update emits only the few entries it
touches, and the bootstrap encoder builds its TlsMapDelta-from-empty
inline at the synthesis site (see
xmtp_mls::groups::app_data::migration::synthesize_initial_component_values).
Sourcepub fn from_bytes(bytes: &[u8]) -> Result<Self, ComponentRegistryError>
pub fn from_bytes(bytes: &[u8]) -> Result<Self, ComponentRegistryError>
Deserialize a component registry from its dict-storage format: a
raw TlsMap<ComponentId, VLBytes> snapshot.
Fails only when the outer TlsMap doesn’t decode (truncated /
non-canonical bytes). Individual entries are not validated here —
they’re kept raw and validated lazily on read, so an entry from a
newer protocol version (or a historical invalid entry) is preserved
rather than making every future commit on the group unvalidatable.
Callers that care can inspect
unrecognized_ids and log.
New entries cannot enter a group’s registry unvalidated: the
steady-state wire path validates per-mutation
(ComponentRegistryComponent::apply_update_payload /
expand_to_changes) and the bootstrap validator validates each entry
of the initial delta. Tolerance here is the read-side backstop, not
the enforcement point.
Sourcepub fn unrecognized_ids(&self) -> impl Iterator<Item = ComponentId> + '_
pub fn unrecognized_ids(&self) -> impl Iterator<Item = ComponentId> + '_
Component ids of entries from_bytes preserved
but that could not validate. Empty in the overwhelmingly common case;
non-empty means the dict was written by a newer protocol version (or
carries a historical invalid entry) and is worth a log line at the
load site.
Sourcepub fn validate_entry(
id: ComponentId,
raw: &[u8],
) -> Result<(), ComponentRegistryError>
pub fn validate_entry( id: ComponentId, raw: &[u8], ) -> Result<(), ComponentRegistryError>
Validate one (ComponentId, raw bytes) entry against the registry’s
invariants — the single definition of “recognized.” Used by the
wire-decode path in the bootstrap validator (which walks a
TlsMapDelta’s mutations directly so it can surface the Insert-only
check before per-entry validation) and, internally, by every read
method.
Trait Implementations§
Source§impl Clone for ComponentRegistry
impl Clone for ComponentRegistry
Source§fn clone(&self) -> ComponentRegistry
fn clone(&self) -> ComponentRegistry
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for ComponentRegistry
impl Debug for ComponentRegistry
Source§impl Default for ComponentRegistry
impl Default for ComponentRegistry
Source§impl PartialEq for ComponentRegistry
impl PartialEq for ComponentRegistry
Source§fn eq(&self, other: &ComponentRegistry) -> bool
fn eq(&self, other: &ComponentRegistry) -> bool
self and other values to be equal, and is used by ==.impl StructuralPartialEq for ComponentRegistry
Auto Trait Implementations§
impl Freeze for ComponentRegistry
impl RefUnwindSafe for ComponentRegistry
impl Send for ComponentRegistry
impl Sync for ComponentRegistry
impl Unpin for ComponentRegistry
impl UnsafeUnpin for ComponentRegistry
impl UnwindSafe for ComponentRegistry
Blanket Implementations§
§impl<T> AggregateExpressionMethods for T
impl<T> AggregateExpressionMethods for T
§fn aggregate_distinct(self) -> Self::Outputwhere
Self: DistinctDsl,
fn aggregate_distinct(self) -> Self::Outputwhere
Self: DistinctDsl,
DISTINCT modifier for aggregate functions Read more§fn aggregate_all(self) -> Self::Outputwhere
Self: AllDsl,
fn aggregate_all(self) -> Self::Outputwhere
Self: AllDsl,
ALL modifier for aggregate functions Read more§fn aggregate_filter<P>(self, f: P) -> Self::Outputwhere
P: AsExpression<Bool>,
Self: FilterDsl<<P as AsExpression<Bool>>::Expression>,
fn aggregate_filter<P>(self, f: P) -> Self::Outputwhere
P: AsExpression<Bool>,
Self: FilterDsl<<P as AsExpression<Bool>>::Expression>,
§fn aggregate_order<O>(self, o: O) -> Self::Outputwhere
Self: OrderAggregateDsl<O>,
fn aggregate_order<O>(self, o: O) -> Self::Outputwhere
Self: OrderAggregateDsl<O>,
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<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.§impl<T> DowncastSend for T
impl<T> DowncastSend for T
§impl<T> DowncastSync for T
impl<T> DowncastSync for T
§impl<T> Identity for Twhere
T: ?Sized,
impl<T> Identity for Twhere
T: ?Sized,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§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§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request§impl<T> IntoSql for T
impl<T> IntoSql for T
§impl<L> LayerExt<L> for L
impl<L> LayerExt<L> for L
§fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>where
L: Layer<S>,
Layered].