feat(metadata): add AppMetadata value class (pluggable-encryption provider metadata)#408
Merged
Conversation
…vider metadata)
Mirrors the canonical at_commons AppMetadata (at_key.dart): an SDK-owned
providerId routing field plus opaque `additional` entries, exposed on Metadata
as the appMetadata field.
- New AppMetadata value class: providerId + additional Map, with toJson()
(flat {providerId, ...additional}), fromJson(Map), encode() (base64(JSON)),
and decode() (accepts a base64 String, a flat Map, or an AppMetadata) —
mirroring canonical encodeAppMetadata / decodeAppMetadata.
- Metadata.appMetadata typed as AppMetadata; toString() appends
:appMetadata:<base64(JSON)> LAST after immutable, matching the canonical
metadata fragment order. Static Metadata.encodeAppMetadata /
decodeAppMetadata delegate for API parity, and Jackson
(@jsonvalue / @JsonCreator) round-trips the flat-object form used in
metadata maps.
Groundwork for the Java client's fast-follow adoption of the pluggable
encrypt/decrypt provider model (the post-quantum-safety work in flight on the
Dart SDK), which routes on appMetadata.providerId. It also lets a Java
atServer parse, store, and re-emit appMetadata through llookup:all / sync /
notify so the receiver can select the right decryptor after sync or cached
delivery.
MetadataTest +8 (72 total).
22f1644 to
06088b9
Compare
Xlin123
approved these changes
Jul 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds an
AppMetadatavalue class toat_clientand exposes it asMetadata.appMetadata, mirroring the canonical Dartat_commonsAppMetadata(at_key.dart).AppMetadatavalue class — an SDK-ownedproviderIdrouting field plusan opaque
additionalmap the SDK preserves but does not interpret. Provides:toJson()→ the flat object{ "providerId": …, …additional }(additionalentries serialised flat, not nested — matching canonical).
fromJson(Map)→ routes every key other thanproviderIdintoadditional;throws on a missing/blank
providerId.encode()→base64(JSON(toJson())), the wire form.decode(Object)→ accepts a base64(JSON)String(wire), a flatMap(metadata-map form), or an already-parsed
AppMetadata;null/"null"→ null.Metadata.appMetadatatyped asAppMetadata.toString()appends:appMetadata:<base64(JSON)>last, afterimmutable, matching thecanonical fragment order. Static
Metadata.encodeAppMetadata/Metadata.decodeAppMetadatadelegate to the value class for API parity withthe Dart client, and Jackson (
@JsonValue/@JsonCreator) round-trips theflat-object form used in metadata maps.
Why
appMetadatais the provider-owned crypto metadata the SDK stamps on encryptedrecords. Its
providerIdselects the crypto provider able to decrypt the value— the routing key of the pluggable encrypt/decrypt provider model. The Java
client is a fast follower to implement that model as part of adopting the
post-quantum-safety enhancements currently in flight on the Dart client SDK, and
the provider model depends on
AppMetadata; this change lands the value class sothat work has it to build on.
It also unblocks a Java atServer implementation, which needs
AppMetadataonMetadatato parse, store, and re-emitappMetadatathroughllookup:all/sync / notify — the receiver relies on
providerIdto pick the right decryptorafter sync or cached delivery. Without it the field is dropped and pluggable-
encryption round-trips fail.
Canonical parity
at_commonsAppMetadata)at_client)providerId+additionalvalue objectproviderId+additionalvalue classtoJson{providerId, …additional}{providerId, …additional}fromJsonproviderIdkeys →additional; validatesproviderIdkeys →additional; validatesbase64(JSON)viaencodeAppMetadatabase64(JSON)viaencode()/encodeAppMetadatadecodeAppMetadatadecode()/decodeAppMetadataimmutableimmutableTests
MetadataTest+8 (72 total):toJson/fromJsonflattening + validation,encode/decoderound-trip (incl. the staticMetadatahelpers),decodeof Map/AppMetadata/null, the base64
toString()fragment, and Jacksondeserialisation of a nested
appMetadataobject.