Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XIP-14: Conversation context metadata schema #14

Merged
merged 15 commits into from
May 15, 2024

Conversation

yash-luna
Copy link
Contributor

No description provided.


- Aspect ratio: 1:1
- Minimum resolution: 100x100
- Maximum resolution: 800x800
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If these are externally hosted, why such a low maximum?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on Darick's suggestions. Thoughts on increasing min resolution @darickdang?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@neekolas Since the image would be used in avatar-like settings (or to complement an avatar), I looked at popular services/libraries such as Gravatar (minimum size 1px) and Libravatar (minimum size is 80x80) standards in addition to Apple's Human Interface Guidelines of having a minimum toucharea of 44x44 (and then I doubled that to add @2x support as the minimum) to create the number you see here.

Open to making it bigger though. Not that strong of an opinion from my end here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just wonder if some apps are going to want a clickable/zoomed view of app icons. Or an extra large icon. Maybe that's not a real concern here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it might help to include a mockup of a UI showing how the displayName, profileImage, and primaryColor might be used? While the profileImage might be more obvious, people might be curious about where the displayName and primaryColor might be used? An image might also really drive home the value!

And is the idea now that we don't need to use only the conversationId as conversation label text?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a sample UI is such a good idea @jhaaaa!

Yes, with a conversationId like 'lens.dev/dm/...' there isn't a shared understanding of how to render it? Should the UI display 'Lens', 'lens.dev', or the complete conversationId? The displayName makes it explicit

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great call @jhaaaa. Will look at making an example.

XIPs/xip-10-brandinfo-schema.md Outdated Show resolved Hide resolved

## Security Considerations

The metadata field can be spoofed by malicious apps to display Names and Images that degrade the user experience and are harmful to the brand’s perception. A mechanism for apps to sign messages and related metadata can enable front-ends to verify if the signature is from a credible source in order to prevent such spoofing. An affordance for client verifiability is under consideration.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The invitation is encrypted and authenticated using the sender & recipient keys. So the metadata must have been created by one of the parties if the invitation is successfully unsealed (https://github.com/xmtp/proto/blob/main/proto/message_contents/invitation.proto).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While invitations are authenticated by sender & recipient, setting metadata and conversationId is usually abstracted away from users so front-end clients end up making the decision for what these fields carry.

Spoofing example: A (malicious) client Yash uses to message Martin could set conversationId to a 'lens.dev/...' and set brandInfo that doesn't represent Lens.

Today convoID/metadata<>app/protocol/brand mapping is not verifiable, and therefore relies on good behavior.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, I misread this section. This is however a more general concern that currently apps have complete access to a user's XMTP identity/keys, so a users must necessarily trust the app they are using in all regards not just this particular aspect. It may still be worth mentioning here, but I'm less sure about what the solution should be. Some amount of trust between the user and the app may always be necessary.

Copy link
Contributor

@jhaaaa jhaaaa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So excited about this XRC, @yash-luna - thank you for designing and articulating it so clearly! Added a few comments for your consideration -- please use only what you find helpful!

XIPs/xip-10-brandinfo-schema.md Outdated Show resolved Hide resolved
XIPs/xip-10-brandinfo-schema.md Outdated Show resolved Hide resolved
XIPs/xip-10-brandinfo-schema.md Outdated Show resolved Hide resolved
XIPs/xip-10-brandinfo-schema.md Outdated Show resolved Hide resolved

- Aspect ratio: 1:1
- Minimum resolution: 100x100
- Maximum resolution: 800x800
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it might help to include a mockup of a UI showing how the displayName, profileImage, and primaryColor might be used? While the profileImage might be more obvious, people might be curious about where the displayName and primaryColor might be used? An image might also really drive home the value!

And is the idea now that we don't need to use only the conversationId as conversation label text?

XIPs/xip-10-brandinfo-schema.md Outdated Show resolved Hide resolved
XIPs/xip-10-brandinfo-schema.md Outdated Show resolved Hide resolved
XIPs/xip-10-brandinfo-schema.md Outdated Show resolved Hide resolved
XIPs/xip-10-brandinfo-schema.md Outdated Show resolved Hide resolved
Copy link

@jazzz jazzz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure the Motivation is clear to me.
The examples and text hint towards standardizing Client applications metadata{AppName + Photo} . However the schema naming seems to hint towards conversation metadata {GroupChat Title, GroupChat ProfileImage}

conversationId: "mydomain.xyz/abc/qrs",
metadata: {
"displayInfo.prettyName": "My company",
"displayInfo.profileImage": "mydomain.xyz/assets/myimage.png",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does displayInfo.profilePicture differ from a Contact ProfilePicture or a group conversation image? Is the intention different?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This XIP started with the idea of brandInfo/clientMetadata in mind but we realized that the application is broader - i.e. to add context beyond conversationId that allows a frontend to contextualize conversations

Sample UIs and non-clientMetadata examples are work in progress. Sharing most recent UI mock that highlights how they can be utilized for clientMetadata, NftMetadata, or other metadata that contextualizes the conversation.

image

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example is very helpful.

I might suggest that we scope the properties to something more descriptive than DisplayInfo.DisplayName. In particular we should create specific terms around what to collectively call the OpenSea/Lens tags.

e.g. ConversationSource.DisplayName, ConversationOrigin.DisplayName, AppSource....

My main thought here is that with groupchats coming in the near future, DisplayName will become a loaded term.

conversationId: "galaxy.chat/dm/uniqueIdentifier",
metadata: {
"displayInfo.prettyName": "Galaxy",
"displayInfo.profileImage": "galaxychat.xyz/brandassets/logo.png",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the rationale behind embedding this data into the Conversation Invites?

Why does the invite not contain a pointer to a central registry, which would be easier for updates?
Why is this information not contained in a metamessage within the conversation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rationale is that conversation metadata add nuance & detail to conversationId so this data lives where conversationId lives.

If we consider allowing all metadata to be pointers, and therefore mutable in some sense, should that be considered for conversationId too? I'm curious how the protocol thinks of what data should be mutable vs immutable. Will help make the decision for metadata.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does the invite not contain a pointer to a central registry, which would be easier for updates?

The registry approach is worth considering. The big pro is mutability. But there are some meaningful cons too:

  1. Long term availability. Will the registry still exist 3 years from now? What happens when it disappears?
  2. Consistency. What happens as different registries decide to change the shape of the returned data? Will all client apps have to support all the different registries. How do clients and registries communicate? At least with this, all conversations/apps that support the standard will work in perpetuity. Immutability is also a feature: you can't break existing convos.
  3. Ease of use. Running a registry is real work for someone, and they are unlikely to be paid for it. Who is going to run that registry?

Why is this information not contained in a metamessage within the conversation?

This is information about the conversation as a whole. You may want this to be available as soon as the conversation invite has been read, without having to wade through a potentially lengthy history of meta messages (for example, when showing a conversation list).

Also, this is available today and meta messages are somewhere on our roadmap. Even if we think meta messages are a better solution, do we actually see them becoming first class citizens across all our SDKs in the next few quarters?

Copy link

@jazzz jazzz Feb 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these are valid reasons to embedded into the conversation metadata. I can get behind this.

In my mind a central registry could be ENS with values stored as textfields. It would address Longterm availability (at least in the context of apps), and Ease of use (Passing update costs to the brands/applications). I think consistency will be an issue regardless and a well defined spec is needed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jazzz I really like the ENS approach here. It also would help that there would be a recognizable party that would be accountable for the metadata in the event that it changed, which would be recognized by the associated controller address.

Another reason this might be worthwhile is the many varying indexers for the ENS data.

A concern of mine with including the arbitrary data into the payload vs. a registry is in how it might be manipulated, if even not maliciously.

Copy link
Contributor

@richardhuaaa richardhuaaa Feb 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what's troubling about the current approach is that the relationship between convoID<->metadata is not fixed, clients can put whatever they want in the metadata given the same convoID. A registry approach can make sure that the same convoID renders the same metadata universally, and it's fairly easy to add authentication mechanisms in the future to make sure only the relevant app owners can change the metadata for each convoID.

If we think a registry is hard to maintain, we can have a file in the SDK that has a hardcoded list of convoID->metadata, an API for client apps to query it, and only accept PR's modifying the metadata from the app owners for each convoID.

I understand that convoID is spoofable today, but I still think it's better not to add multiple layers of spoofability/confusion.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@richardhuaaa agree on the registry component for sure.

I do wonder if somehow the metadata itself can be hashed, and the hash compared against the registry, to know what's authentic. And perhaps the conversationId could be used as a checksum against the metadata, like how you might with a barcode.

@yash-luna
Copy link
Contributor Author

yash-luna commented Feb 7, 2023

I'm not sure the Motivation is clear to me.

Motivation: provide a schema to share details that help frontends render conversationId in the UI. Today frontends do it by truncating the conversationId. The proposed schema provides more information, image & color, in addition to a prettyName.

Sharing UI examples in above comments to provide clarity.

Overall, your comment makes me wonder whether we should aim for a generic conversation metadata schema or have different schemas catering to different use-cases (clientMetadata, NftMetadata, groupchatMetadata, etc)?

```json
{
conversationId: "mydomain.xyz/abc/qrs",
metadata: {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to consider adding an explicit version to this schema, so future updates are easier to deploy.

The scheme could be directly tied to conversationVersion, But then schema changes would result in a bump in both.

Perhaps something explicit like:

metadata: {
   "conversationSource_V1": {
       "prettyName": "My company",
       "profileImage": "mydomain.xyz/assets/myimage.png"
   }
}

That would allow the SDK to add multiple Objects to the metadata if older clients need to be supported

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jazzz makes sense. Is there a preference to using a JSON object to an array?

Another route might be:

metadata: {
  "type": "conversation",
  "version": "1.0",
  "content": {
    "title": "My company",
    "description"; "My description that may be used in an app",
    "image": "https://mydomain.xyz/assets/myimage.png",
    "imageURI": "ipfs://zafybeict2kq6gt4ikgulypt7h7nwj4hmfi2kevrqvnx2osibfulyy5x3hu/myimage.png"
  }
}

One possible advantage I see with this is the addition of other metadata types in the future.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@galligan I like the explicit structure of version, type, content.

I might suggest a structure that looks like:

metadata: [
	{
		"type": "conversation_source",
		"version": "1.0" 
		"content": {
		  	"title": "My company",
    		"description"; "My description that may be used in an app",
    		"image": "https://mydomain.xyz/assets/myimage.png",
    		"imageURI": "ipfs://zafybeict2kq6gt4ikgulypt7h7nwj4hmfi2kevrqvnx2osibfulyy5x3hu/myimage.png"
		}
	},
	{
		"type": "group_info",
		"version": "1.0" 
		"content": {
		  	"name": "Frens",
    		"admin"; "....",
    		"image": "ipfs://pkrewapct2kq6gt4ikgulypt7h7nwj4hmfi2kevrqvnx2osibfulypkl39a/myimage.png".
		}
	}
]
  • There should be room for multiple metadata objects
  • Objects names ought to be hyper specific to the data it is representing. Conversation.Image is ambiguous. Codifying clear names for entities will help developers in the future.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth noting that neither an array or nested object are possible with today's metadata. It is strictly a Map<string, string>. Closer to HTTP headers than a free form JavaScript object.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the suggestions above, there's benefits to the protocol and DX to enable metadata to take arrays and/or nested objects. It would enable versioning for metadata schemas and make it friendly for other use cases beyond conversationId context.

@neekolas @jazzz: What are the downsides of such a change? Can it be in scope for xmtp-js v8?

@richardhuaaa
Copy link
Contributor

This is somewhat tangential, but have we ever considered changing the term conversationID? I was confused by this when I first started because it sounds like a globally unique identifier for an individual conversation, rather than an identifier for a set of conversations. Better terms IMO would be something like conversationType or appID

Copy link
Collaborator

@galligan galligan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great start, @yash-luna. Would love to have some of the thoughts in this review to be considered. Happy to have a conversation about some of them and go deeper.

XIPs/xip-10-brandinfo-schema.md Outdated Show resolved Hide resolved
XIPs/xip-10-brandinfo-schema.md Outdated Show resolved Hide resolved

## Motivation

The SDK API currently accepts a unique `conversationId` per address pair and allows `metadata` to be set as well. Today, different apps use these parameters in different ways. Therefore an app may not know how to correctly render in its UI `conversationId` and `metadata` for conversations originating from other apps. We propose a schema to standardize the use of conversation `metadata` for the purpose of app brand expression stemming from the `conversationId`. The `metadata` field may include other non-standard (or future standard) properties.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The SDK API currently accepts a unique `conversationId` per address pair and allows `metadata` to be set as well. Today, different apps use these parameters in different ways. Therefore an app may not know how to correctly render in its UI `conversationId` and `metadata` for conversations originating from other apps. We propose a schema to standardize the use of conversation `metadata` for the purpose of app brand expression stemming from the `conversationId`. The `metadata` field may include other non-standard (or future standard) properties.
The XMTP SDK API currently accepts a unique `conversationId` per address pair and allows `metadata` to be set as well. Today disparate apps may use these parameters in different ways. As a result, an app may not always render the `conversationId` and associated `metadata` as intended, for conversations originating from other apps. This proposal introduces a schema to standardize the use of the conversation `metadata` for the purpose of a defined brand expression as associated with the `conversationId`. It is also worth noting that the `metadata` field may include other non-standard (or future standard) properties.

XIPs/xip-10-brandinfo-schema.md Outdated Show resolved Hide resolved
```json
{
conversationId: "mydomain.xyz/abc/qrs",
metadata: {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jazzz makes sense. Is there a preference to using a JSON object to an array?

Another route might be:

metadata: {
  "type": "conversation",
  "version": "1.0",
  "content": {
    "title": "My company",
    "description"; "My description that may be used in an app",
    "image": "https://mydomain.xyz/assets/myimage.png",
    "imageURI": "ipfs://zafybeict2kq6gt4ikgulypt7h7nwj4hmfi2kevrqvnx2osibfulyy5x3hu/myimage.png"
  }
}

One possible advantage I see with this is the addition of other metadata types in the future.

}
```

The `profileImage` must meet the following criteria:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Must" is doing a lot of work here, and in this case are we sure this can be enforceable? If the image is hosted externally, what prevents the image from being changed after the fact and no longer adhering to the rules?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It originally said 'should' and was changed to 'must' based on @saulmc's comment. I don't have a strong opinion and am ok with changing the verbiage.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Must to me implies that somehow it is enforceable. I'm not sure I see how that would be possible. Should feels more appropriate in this situation but happy to discuss further with @saulmc.

- Maximum resolution: 800x800
- Format: .PNG, .WEBP

The `primaryColor` must be a hex color code.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any concern that primaryColor may conflict with other styles within the same namespace? Perhaps there's something that could be more generic?

Also: what if we were to also include an equivalent of a fall back brand color that would be more generically named. Something like a purple.500? I suggest this because some front-ends may wish to have some more control over what colors are displayed.

Just imagine a #FF0099 being included in a UI somewhere and how garish that might be to see.

A fallback color would allow the front-end to set up their own interpretation of the colors, and perhaps even ignoring some colors altogether. But it would all be in the service of providing a consistent UI.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is a fallback color different from a second hex code? Didn't understand how 'a fallback color would allow the front-end to set up their own interpretation of the colors'

Copy link
Collaborator

@galligan galligan Feb 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yash-luna to use an analogy, a "fallback" color in this case is the description of a color of paint, whereas the hex code would be similar to the paint code instructions on the outside of a can of paint.

In the fallback case the specific color used would be based off of the developer's interpretation. With the hex color there is no interpretation and it is explicit.

The naming scheme for the fallback could be based off of an existing example, such as Material Design or Tailwind CSS's color palette. It usually has a color name and separate value to indicate darkness or saturation.

If given the choice, if I were developing an app I would always pick the fallback color as I'd have a bit more control over how it should be represented. Without that control, I might ignore the color altogether as the results could really clash with my experience.

XIPs/xip-10-brandinfo-schema.md Outdated Show resolved Hide resolved
conversationId: "mydomain.xyz/abc/qrs",
metadata: {
"displayInfo.prettyName": "My company",
"displayInfo.profileImage": "mydomain.xyz/assets/myimage.png",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also include an affordance for an image URI. For web3-native projects they may have an IPFS, IPLD or other resource URI as a default, with a specific image URL as secondary.

This would provide a front-end with the ability to use a URI directly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the URI be set as a value to profileImage?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also include an affordance for an image URI. For web3-native projects they may have an IPFS, IPLD or other resource URI as a default, with a specific image URL as secondary.

This would provide a front-end with the ability to use a URI directly.

If all of these images are externally hosted, isn't that going to add several additional round trip requests for the browser?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the URI be set as a value to profileImage?

@yash-luna it could be, but setting URI as a separate value to URL would have its benefits. For clients that don't support the protocol the URI is pointing to, then it can look at the URL instead.

If all of these images are externally hosted, isn't that going to add several additional round trip requests for the browser?

@petermdenton Possibly. But it can be accounted for. And otherwise I'm not sure where these images will be coming from except through external hosting. Encouraging them to be stored in-protocol may be a slippery slope that we don't want to go down.

With including the URI and using IPFS, a client could easily cache images and maintain integrity without trouble. An image would be fetched via its URI, and could be saved locally with its CID intact such that the image could not be altered without also changing its CID.


## Security considerations

The `metadata` fields can be spoofed by malicious apps to display names and profile images that degrade the user experience and harm brand perception. A mechanism for apps to sign payloads such as conversation metadata and messages can enable frontends to verify the sending client's identity and prevent such spoofing. An affordance for client verifiability is under consideration.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The `metadata` fields can be spoofed by malicious apps to display names and profile images that degrade the user experience and harm brand perception. A mechanism for apps to sign payloads such as conversation metadata and messages can enable frontends to verify the sending client's identity and prevent such spoofing. An affordance for client verifiability is under consideration.
A brand's chosen metadata fields and content may be co-opted by other apps to display the same names and profile images which could impact the user experience and perception of a message's origin or affiliation. A mechanism for apps to sign payloads such as conversation metadata and messages could enable frontends to verify the sending client's metadata authenticity and prevent such spoofing. An affordance for verifying metadata could be considered in a future proposal.

I'm suggesting that we lighten the language on "spoofing" and "malicious" as it's simply an open space, and there's nothing currently preventing a project from co-opting some labels. Not ideal, but we should be careful about using such pointed language as it may diminish its value in other circumstances.

@galligan
Copy link
Collaborator

galligan commented Feb 8, 2023

This is somewhat tangential, but have we ever considered changing the term conversationID? I was confused by this when I first started because it sounds like a globally unique identifier for an individual conversation, rather than an identifier for a set of conversations. Better terms IMO would be something like conversationType or appID

@richardhuaaa It may be useful to include those as additional fields in some fashion, perhaps as the metadata. For example, an app may choose to have multiple conversations between the same parties present. Or perhaps it might attempt to use them to separate "threads". This may not be the intended use but for now that's how it could happen.

@yash-luna yash-luna changed the title brandinfo schema xrc metadata schema xrc Feb 8, 2023
@jhaaaa jhaaaa changed the title metadata schema xrc XIP-14: Conversation context metadata schema May 15, 2024
@jhaaaa
Copy link
Contributor

jhaaaa commented May 15, 2024

No activity for over 6 months. Set status to Stagnant and merging.

@jhaaaa jhaaaa merged commit 804f9c9 into main May 15, 2024
1 check passed
@jhaaaa jhaaaa deleted the brandInfo-metadata-schema-XIP branch May 15, 2024 21:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants