mirror of
https://github.com/nostr-protocol/nips.git
synced 2025-03-29 03:02:07 +01:00
Update 52.md
Based on comments (thank you!), I rewrote this NIP and focused purely on badges, which was just one of the use cases under "Issued Claims". This greatly simplified the NIP. I also specified the data schemas for two different badge types to be part of the protocol itself, versus leaving it open. A bunch of stuff, like the Refresh Claim Request event or requiring event treated as parameterized replaceable event were removed as not necessary for just the badges use case. There are probably more badge types that would be useful.
This commit is contained in:
parent
9208bdbd66
commit
ce19442bde
187
52.md
187
52.md
@ -2,154 +2,117 @@
|
||||
NIP-52
|
||||
======
|
||||
|
||||
Issued Claims
|
||||
-------------
|
||||
Badges
|
||||
------
|
||||
|
||||
`draft` `optional` `author:neilck`
|
||||
|
||||
This NIP is currently in draft, and review / comments are sought. May overlap with [NIP-32](https://github.com/nostr-protocol/nips/blob/39bfb2db32899dd2eb4a7c9a313f27103a18409b/32.md).
|
||||
*This NIP is currently in draft, and review / comments are sought.*
|
||||
|
||||
A claim is a statement of fact made by an issuer regarding a subject.
|
||||
This NIP enables an app owner (or community manager) to award of badges to their members.
|
||||
|
||||
Example claims include
|
||||
- Alice graduated from the University of Southern California
|
||||
- Bob, the control of pubkey 123EED..., also controls Twitter handle tinydancer
|
||||
- Charlie is over 21 years of age
|
||||
Like Kind 0, badges provide metadata related to a pubkey, but the event is sent (and thus signed) by the app owner, not the subject's pubkey.
|
||||
|
||||
Like Kind 0 metadata, issued claims provide additional data about pubkey, and by extension, the controller of the pukey.
|
||||
## Kind XX: Issue Badge
|
||||
|
||||
Unlike Kind 0 metadata, the data is sent by and vouched for by issuers, which is more trusthworthy than self-reported data, provided we trust the issuer.
|
||||
|
||||
The concept of an issuer comes from [Verifiable Credentials](https://www.w3.org/TR/vc-data-model/).
|
||||
|
||||
It reserves two event kinds (30001, 1001).
|
||||
|
||||
- `30001 - issue claim`
|
||||
- `1001 - refresh claim request`
|
||||
|
||||
An issue claim event records what one user attests is true about another user.
|
||||
|
||||
## Kind 30001: Issue Claim
|
||||
|
||||
Sent by a user acting as an issuer, submitting a claim about another user, the claim's subject.
|
||||
EVENT sent from client to relay to issue a badge.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": <32-bytes lowercase hex-encoded sha256 of the the serialized event data>
|
||||
"pubkey": <32-bytes lowercase hex-encoded public key of the issuer>,
|
||||
"pubkey": <32-bytes lowercase hex-encoded public key of the app owner issuing the badge>,
|
||||
"created_at": <unix timestamp in seconds>,
|
||||
"kind": 30001,
|
||||
"kind": XX,
|
||||
"tags": [
|
||||
["p", <32-bytes hex of the subject's pubkey>, <recommended relay URL>],
|
||||
["d", <32-bytes hex of the subject's pubkey + claim type>],
|
||||
["claim_type", <optional string specifying claim type>],
|
||||
["is_maintained", <optional boolean, yes if refresh claim supported>]
|
||||
["p", <32-bytes hex pubkey of the member receiving the badge>],
|
||||
["badge_type", <integer specifying badge type>]
|
||||
],
|
||||
"content": <claim data>,
|
||||
"content": <stringified json object, matching badge_type schema>,
|
||||
"sig": <64-bytes signature of the sha256 hash of the serialized event data, which is the same as the "id" field>
|
||||
}
|
||||
```
|
||||
|
||||
For example, issuer nostr.directory with hexPubKey 2a1...234 is claiming that nostr user with hexPubKey 3bf...6w6 also controls twitter handle fiatjaf
|
||||
`p tag` MUST contain exactly one entry, the pubkey of the claim's subject.
|
||||
|
||||
`badge_type` identifies badge type. Current accept values are `0 - Achievement Badge` and `1 - Membership Badge`
|
||||
|
||||
Like Kind 0, badges provide metadata about a pubkey, but the event is sent (and thus signed) by the app owner, not the subject's pubkey.
|
||||
|
||||
Two badge types are defined, with the possibility of adding more if required.
|
||||
|
||||
### 0 - Achievement Badge
|
||||
Awarded to a user for an achievement.
|
||||
|
||||
Achievement Badge Schema
|
||||
```json
|
||||
{
|
||||
"id": <32-bytes lowercase hex-encoded sha256 of the the serialized event data>
|
||||
"pubkey": "2a1...234",
|
||||
"created_at": <unix timestamp in seconds>,
|
||||
"kind": 30001,
|
||||
"tags": [
|
||||
["p", "3bf...6w6", <recommended relay URL>],
|
||||
["d", "3bf...6w6:twitteraccount"],
|
||||
["claim_type", "twitteraccount"],
|
||||
["is_maintained", true]
|
||||
],
|
||||
"content": {
|
||||
"twitter_handle": "fiatjaf",
|
||||
"profile_url": "https://twitter.com/fiatjaf"
|
||||
},
|
||||
"sig": <64-bytes signature of the sha256 hash of the serialized event data, which is the same as the "id" field>
|
||||
"issuer_name": <string of app name or community name>,
|
||||
"name": <string of achievement name>,
|
||||
"message": <string of message>,
|
||||
"image": <url of badge image>,
|
||||
"score": <optional integer of score value>
|
||||
}
|
||||
```
|
||||
````
|
||||
|
||||
`"d" tag` is required as the event is treated as a parameterized replaceable event [NIP-33](https://github.com/nostr-protocol/nips/blob/master/33.md). Issuers issuing more than one type of claim will need to append a claim type identifier to the subject's pubkey hex to prevent overwrites.
|
||||
|
||||
The p tag MUST contain exactly one entry, the pubkey of the claim's subject.
|
||||
|
||||
Self-issued claims (issuer is the subject) is specifically allowed.
|
||||
|
||||
|
||||
## Kind 1001: Refresh Claim Request
|
||||
|
||||
A claim may change over time. This event allows a user to request the issuer submit an updated issue claim event for the same subject and claim_type.
|
||||
|
||||
Refresh Claim Request
|
||||
Example as content
|
||||
```json
|
||||
{
|
||||
"id": <32-bytes lowercase hex-encoded sha256 of the the serialized event data>
|
||||
"pubkey": <32-bytes lowercase hex-encoded public key of the sender>,
|
||||
"created_at": 1674607473,
|
||||
"kind": 1001,
|
||||
"tags": [
|
||||
["e", <kind>:<pubkey>:<d-tag>, <relay-url>]
|
||||
],
|
||||
"sig": <64-bytes signature of the sha256 hash of the serialized event data, which is the same as the "id" field>
|
||||
"content": "{\"issuer_name\": \"nostr.directory\", \"name\": \"Donation\", \"message\": \"User has sent a sats donation to noster.directory.\", \"image\": \"https://myapp.com/badges/donation.svg\", \"score\": 1000}",
|
||||
...
|
||||
}
|
||||
```
|
||||
````
|
||||
|
||||
For example, if an issue claim states a user is employed by Microsoft, but is 1 year out of date (1643099996 = Jan 25, 2022), an client may request issuer update the issue claim to check if they still work there.
|
||||
- issuer_name - name of app or community. e.g. "Nosterplebs", "Continuous Delivery Foundation"
|
||||
- name - name of the achievement. e.g. "Donation", "Top Bug Crusher"
|
||||
- message - Text description of the achievement
|
||||
- image - url link to square badge image (1:1 ratio). Ideally .svg image, but if not SHOULD be 32 x 32 pixels.
|
||||
- score - optional integer value. Include when you need to rank achievements. e.g. Display "High Score" list in descending order
|
||||
|
||||
Issue Claim Example
|
||||
Client may include additional key/value pairs in content.
|
||||
|
||||
### 1 - Membership Badge
|
||||
Sent by app owner to confirm that a user is the member of app, community or group.
|
||||
|
||||
Membership Badge Schema
|
||||
```json
|
||||
{
|
||||
"created_at": 1643099996,
|
||||
"kind": 30001,
|
||||
"tags": [
|
||||
["p", "37a...100", <recommended relay URL>],
|
||||
["d", "37a...100:employer"],
|
||||
["claim_type", "employer"],
|
||||
["is_maintained", true]
|
||||
],
|
||||
"content": {
|
||||
"organization_name": "microsoft",
|
||||
"title": "Senior Software Developer"
|
||||
},
|
||||
"sig": <64-bytes signature of the sha256 hash of the serialized event data, which is the same as the "id" field>
|
||||
"issuer_name": <string of app name or community name>,
|
||||
"image": <url of badge image>,
|
||||
"role": <optional string of role of user within the community>,
|
||||
"id": <optional string of unique user identity>,
|
||||
"alias": <optional string ofuser alias>,
|
||||
"member_since": <optional unix timestamp of when member joined>,
|
||||
"expiry_date": <optional unix timestamp of membership expiry date>,
|
||||
"profile": "<optional url to user profile page in app>",
|
||||
"followers": <optional integer count of followers if applicable>,
|
||||
"connections": <optional integer count of connections if applicable>,
|
||||
"posts": <optional integer count of posts if applicable>
|
||||
}
|
||||
```
|
||||
````
|
||||
|
||||
Related Refresh Claim Request Example
|
||||
Example as content
|
||||
```json
|
||||
{
|
||||
"id": <32-bytes lowercase hex-encoded sha256 of the the serialized event data>
|
||||
"pubkey": <32-bytes lowercase hex-encoded public key of the sender>,
|
||||
"created_at": 1674607473,
|
||||
"kind": 1001,
|
||||
"tags": [
|
||||
["e", "30001:37a...100:employer", <relay-url>]
|
||||
],
|
||||
"sig": <64-bytes signature of the sha256 hash of the serialized event data, which is the same as the "id" field>
|
||||
"content": "{\"issuer_name\": \"Nostrplebs\", \"image\": \"https://nostrplebs.com/images/member.svg\", \"role\": \"Premium Member\"}",
|
||||
...
|
||||
}
|
||||
```
|
||||
````
|
||||
|
||||
A client SHOULD check that the original event contains "is_maintained": true before sending event to relay
|
||||
|
||||
A relay MAY choose to ignore this event if original event does not contain "is_maintained"
|
||||
|
||||
Design Notes
|
||||
------------
|
||||
|
||||
### Why treat issue claim events as a parameterized replaceable event (NIP-33)?
|
||||
|
||||
Treating issue claim events as parameterized replaceable events allows any consumer to get the only latest issue claim event for a user by setting a subscription filter specifying the kind, issuer, the subject as the p tag, and a limit of 1.
|
||||
|
||||
### Why is "is_maintained" necessary?
|
||||
|
||||
It's optional, and not technically necessary, but is a practical consideration.
|
||||
|
||||
- If adhered to, prevents clients from sending refresh claim request events that are useless, reducing "garbage" events relays have to handle
|
||||
- Allows users acting as issuers to periodically send new issue claim events, updating claims for individual users "on demand" only when necessary
|
||||
- issuer_name - name of app or community. e.g. "Nostrplebs", "Continuous Delivery Foundation"
|
||||
- image - url link to square badge image (1:1 ratio). Ideally .svg image, but if not SHOULD be 32 x 32 pixels.
|
||||
- role - role of user within the commnity. e.g. "Moderator", "Administrator", "Reviewer"
|
||||
- id - unique user ID, usually system generated e.g. Twitter: user.fields.id
|
||||
- alias - unqiue user ID set by user e.g. Twitter: user.username
|
||||
- member_since - date user first became a member
|
||||
- expiry_date - include if membership has expiry date, usually for paid memberships
|
||||
- profile - url link to user's profile wihin the app
|
||||
- followers - count of followers, if applicable
|
||||
- connections - count of connections e.g. On LinkedIn, a user accepts connections for other users, but anyone can become a follower
|
||||
- posts - total count of tweets, posts, messages, etc.
|
||||
|
||||
Client may include additional key/value pairs in content.
|
||||
|
||||
Motivation
|
||||
----------
|
||||
As a protocol, Nostr is application agnostic, easy to implement, and censorship resistant. While current apps built on Nostr focus on messaging (Twitter, Telegram, etc.) Nostr can be the underlying protocol of other social media apps as well (Instagram, LinkedIn, Tinder, etc.). These apps require more profile information than Nostr current supports. Rather than each app implementing its own profile, it would be nice if we can leverage Nostr for decentralized identity.
|
||||
|
||||
Badges allow a user to be recognized for their contributions within an app / community. With badges stored as events in Nostr against pubkeys, users can bring their reputation with them as they switch between apps, something they can't do with current social media apps.
|
||||
|
Loading…
x
Reference in New Issue
Block a user