Skip to main content

ComponentRegistry

Struct ComponentRegistry 

Source
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_dictionary group-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 raw VLBytes and 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

Source

pub fn new() -> Self

Source

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.

Source

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.

Source

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).

Source

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.

Source

pub fn len(&self) -> usize

Returns the number of recognized entries. Unrecognized entries preserved by from_bytes are not counted.

Source

pub fn is_empty(&self) -> bool

Returns true if the registry has no recognized entries. A registry carrying only unrecognized entries still reports empty.

Source

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.

Source

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).

Source

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.

Source

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.

Source

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

Source§

fn clone(&self) -> ComponentRegistry

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ComponentRegistry

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ComponentRegistry

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl PartialEq for ComponentRegistry

Source§

fn eq(&self, other: &ComponentRegistry) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl StructuralPartialEq for ComponentRegistry

Auto Trait Implementations§

Blanket Implementations§

§

impl<T> AggregateExpressionMethods for T

§

fn aggregate_distinct(self) -> Self::Output
where Self: DistinctDsl,

DISTINCT modifier for aggregate functions Read more
§

fn aggregate_all(self) -> Self::Output
where Self: AllDsl,

ALL modifier for aggregate functions Read more
§

fn aggregate_filter<P>(self, f: P) -> Self::Output
where P: AsExpression<Bool>, Self: FilterDsl<<P as AsExpression<Bool>>::Expression>,

Add an aggregate function filter Read more
§

fn aggregate_order<O>(self, o: O) -> Self::Output
where Self: OrderAggregateDsl<O>,

Add an aggregate function order Read more
Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<T> Downcast for T
where T: Any,

§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts 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>

Converts 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)

Converts &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)

Converts &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
where T: Any + Send,

§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

§

fn into_any_sync(self: Box<T>) -> Box<dyn Any + Send + Sync>

Converts Box<Trait> (where Trait: DowncastSync) to Box<dyn Any + Send + Sync>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Send + Sync>

Converts Arc<Trait> (where Trait: DowncastSync) to Arc<Any>, which can then be downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> FromRef<T> for T
where T: Clone,

§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
§

impl<T, W> HasTypeWitness<W> for T
where W: MakeTypeWitness<Arg = T>, T: ?Sized,

§

const WITNESS: W = W::MAKE

A constant of the type witness
§

impl<T> Identity for T
where T: ?Sized,

§

const TYPE_EQ: TypeEq<T, <T as Identity>::Type> = TypeEq::NEW

Proof that Self is the same type as Self::Type, provides methods for casting between Self and Self::Type.
§

type Type = T

The same type as Self, used to emulate type equality bounds (T == U) with associated type equality constraints (T: Identity<Type = U>).
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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

§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
§

impl<T> IntoSql for T

§

fn into_sql<T>(self) -> Self::Expression
where Self: Sized + AsExpression<T>, T: SqlType + TypedExpressionType,

Convert self to an expression for Diesel’s query builder. Read more
§

fn as_sql<'a, T>(&'a self) -> <&'a Self as AsExpression<T>>::Expression
where &'a Self: AsExpression<T>, T: SqlType + TypedExpressionType,

Convert &self to an expression for Diesel’s query builder. Read more
§

impl<L> LayerExt<L> for L

§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where L: Layer<S>,

Applies the layer to a service and wraps it in [Layered].
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<S, T> Upcast<T> for S
where T: UpcastFrom<S> + ?Sized, S: ?Sized,

Source§

fn upcast(&self) -> &T
where Self: ErasableGeneric, T: ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider ref type within the Wasm bindgen generics type system. Read more
Source§

fn upcast_into(self) -> T
where Self: Sized + ErasableGeneric, T: ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider type within the Wasm bindgen generics type system. Read more
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WindowExpressionMethods for T

§

fn over(self) -> Self::Output
where Self: OverDsl,

Turn a function call into a window function call Read more
§

fn window_filter<P>(self, f: P) -> Self::Output
where P: AsExpression<Bool>, Self: FilterDsl<<P as AsExpression<Bool>>::Expression>,

Add a filter to the current window function Read more
§

fn partition_by<E>(self, expr: E) -> Self::Output
where Self: PartitionByDsl<E>,

Add a partition clause to the current window function Read more
§

fn window_order<E>(self, expr: E) -> Self::Output
where Self: OrderWindowDsl<E>,

Add a order clause to the current window function Read more
§

fn frame_by<E>(self, expr: E) -> Self::Output
where Self: FrameDsl<E>,

Add a frame clause to the current window function Read more
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> MaybeSend for T
where T: Send + ?Sized,

Source§

impl<T> MaybeSync for T
where T: Sync + ?Sized,

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,