Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified ERD-with-fields.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion ERD-with-fields.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified ERD.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions ERD.puml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,36 @@ dataclass location {
!endif
}

' Certified badges (data class + award + response)
dataclass badgeDefinition {
!if (SHOW_FIELDS == "true")
badgeType
title
icon
description?
allowedIssuers[]?
createdAt
!endif
}

dataclass badgeAward {
!if (SHOW_FIELDS == "true")
badge
subject
note?
createdAt
!endif
}

dataclass badgeResponse {
!if (SHOW_FIELDS == "true")
badgeAward
response
weight?
createdAt
!endif
}

' org.hypercerts.claim.rights
dataclass rights {
!if (SHOW_FIELDS == "true")
Expand Down Expand Up @@ -294,4 +324,11 @@ fundingReceipt::to --> contributor

fundingReceipt::for --> activity : funds

badgeAward::badge --> badgeDefinition
badgeResponse::badgeAward --> badgeAward
badgeAward::subject --> contributor
badgeAward::subject --> activity
' This screws up the layout
'badgeAward::subject --[norank]-> project

@enduml
2 changes: 1 addition & 1 deletion ERD.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,53 @@ Certified lexicons are common/shared lexicons that can be used across multiple p
| `description` | `string` | ❌ | Optional description for this location | |
| `createdAt` | `string` | ✅ | Client-declared timestamp when this record was originally created | |

### Badges Lexicon

**Lexicon IDs:** `app.certified.badge.definition`, `app.certified.badge.award`, `app.certified.badge.response`

**Description:** Defines badge metadata, award records, and recipient responses for certified badges that can be used across protocols.

#### Badge Definition

**Lexicon ID:** `app.certified.badge.definition`

**Key:** `tid`

| Property | Type | Required | Description |
| ---------------- | -------- | -------- | ---------------------------------------------------------------------- |
| `badgeType` | `string` | ✅ | Category of the badge (e.g., endorsement, participation, affiliation). |
| `title` | `string` | ✅ | Human-readable title of the badge. |
| `icon` | `blob` | ✅ | Icon representing the badge (accepted `image/*` types, maxSize 1MB). |
| `description` | `string` | ❌ | Optional short statement describing the badge. |
| `allowedIssuers` | `array` | ❌ | Optional allowlist of DIDs allowed to issue this badge. |
| `createdAt` | `string` | ✅ | Client-declared timestamp when this record was originally created. |

#### Badge Award

**Lexicon ID:** `app.certified.badge.award`

**Key:** `tid`

| Property | Type | Required | Description |
| ----------- | -------- | -------- | ------------------------------------------------------------------------------------ |
| `badge` | `ref` | ✅ | Reference to the badge definition for this award (`app.certified.badge.definition`). |
| `subject` | `union` | ✅ | Entity the badge award is for (either a DID or a specific AT Protocol record). |
| `note` | `string` | ❌ | Optional explanation for the award. |
| `createdAt` | `string` | ✅ | Client-declared timestamp when this record was originally created. |

#### Badge Response

**Lexicon ID:** `app.certified.badge.response`

**Key:** `tid`

| Property | Type | Required | Description |
| ------------ | -------- | -------- | ---------------------------------------------------------------------- |
| `badgeAward` | `ref` | ✅ | Reference to the badge award (`app.certified.badge.award`). |
| `response` | `string` | ✅ | Enum: `accepted` or `rejected`. |
| `weight` | `string` | ❌ | Optional relative weight assigned by the recipient (stored as string). |
| `createdAt` | `string` | ✅ | Client-declared timestamp when this record was originally created. |

---

## Hypercerts Lexicons
Expand Down
36 changes: 36 additions & 0 deletions lexicons/app/certified/badge/award.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"lexicon": 1,
"id": "app.certified.badge.award",
"defs": {
"main": {
"type": "record",
"description": "Records a badge award to a user, project, or activity claim.",
"key": "tid",
"record": {
"type": "object",
"required": ["badge", "subject", "createdAt"],
"properties": {
"badge": {
"type": "ref",
"ref": "app.certified.badge.definition",
"description": "Reference to the badge definition for this award."
},
"subject": {
"type": "union",
"description": "Entity the badge award is for (either an account DID or any specific AT Protocol record), e.g. a user, a project, or a specific activity claim.",
"refs": ["app.certified.defs#did", "com.atproto.repo.strongRef"]
},
"note": {
"type": "string",
"description": "Optional statement explaining the reason for this badge award."
},
"createdAt": {
"type": "string",
"format": "datetime",
"description": "Client-declared timestamp when this record was originally created"
}
}
}
}
}
}
53 changes: 53 additions & 0 deletions lexicons/app/certified/badge/definition.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"lexicon": 1,
"id": "app.certified.badge.definition",
"defs": {
"main": {
"type": "record",
"description": "Defines a badge that can be awarded via badge award records to users, projects, or activity claims.",
"key": "tid",
"record": {
"type": "object",
"required": ["title", "badgeType", "icon", "createdAt"],
"properties": {
"badgeType": {
"type": "string",
"description": "Category of the badge (e.g. endorsement, participation, affiliation)."
},
"title": {
"type": "string",
"description": "Human-readable title of the badge."
},
"icon": {
"type": "blob",
"description": "Icon representing the badge, stored as a blob for compact visual display.",
"accept": [
"image/png",
"image/jpeg",
"image/webp",
"image/svg+xml"
],
"maxSize": 1048576
},
"description": {
"type": "string",
"description": "Optional short statement describing what the badge represents."
},
"allowedIssuers": {
"type": "array",
"description": "Optional allowlist of DIDs allowed to issue this badge. If omitted, anyone may issue it.",
"items": {
"type": "ref",
"ref": "app.certified.defs#did"
}
},
Comment on lines +36 to +43
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find and examine the app.certified lexicon files
echo "=== Looking for app.certified lexicon files ==="
find . -path "*app/certified*" -name "*.json" 2>/dev/null | head -20

echo -e "\n=== Examining app.certified.defs.json if it exists ==="
if [ -f "lexicons/app/certified/defs.json" ]; then
  cat lexicons/app/certified/defs.json | grep -A 10 '"did"'
else
  echo "File not found - checking for definition.json"
  find . -path "*app/certified*" -name "*.json" -exec cat {} \; 2>/dev/null | head -50
fi

echo -e "\n=== Running npm run check to validate lexicons ==="
if [ -f "package.json" ]; then
  npm run check 2>&1 | grep -i "certified\|badge\|error" | head -20
else
  echo "package.json not found"
fi

Repository: hypercerts-org/hypercerts-lexicon

Length of output: 579


Use type: "string" with format: "did" instead of type: "ref" for array items.

Per ATProto specifications, refs cannot point to token types, and refs cannot be declared as top-level named types in a schema defs array. The app.certified.defs#did is a top-level named primitive definition (type: "string", format: "did"), which violates this constraint. Standard practice across ATProto lexicons uses inline type: "string" with format: "did" for DID fields.

Change lines 36-43 to:

"allowedIssuers": {
  "type": "array",
  "description": "Optional allowlist of DIDs allowed to issue this badge. If omitted, anyone may issue it.",
  "items": {
    "type": "string",
    "format": "did"
  }
}
🤖 Prompt for AI Agents
In lexicons/app/certified/badge/definition.json around lines 36 to 43, the array
items for "allowedIssuers" currently use "type":"ref" pointing to
app.certified.defs#did which violates ATProto rules for token/primitive types;
replace the items schema with a primitive DID string by changing the items to
use "type":"string" and "format":"did" and update the description if needed so
the array enforces DID strings rather than a ref.

"createdAt": {
"type": "string",
"format": "datetime",
"description": "Client-declared timestamp when this record was originally created"
}
}
}
}
}
}
36 changes: 36 additions & 0 deletions lexicons/app/certified/badge/response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"lexicon": 1,
"id": "app.certified.badge.response",
"defs": {
"main": {
"type": "record",
"description": "Recipient response to a badge award.",
"key": "tid",
"record": {
"type": "object",
"required": ["badgeAward", "response", "createdAt"],
"properties": {
"badgeAward": {
"type": "ref",
"ref": "app.certified.badge.award",
"description": "Reference to the badge award."
},
"response": {
"type": "string",
"enum": ["accepted", "rejected"],
"description": "The recipient’s response for the badge (accepted or rejected)."
},
"weight": {
"type": "string",
"description": "Optional relative weight for accepted badges, assigned by the recipient."
},
"createdAt": {
"type": "string",
"format": "datetime",
"description": "Client-declared timestamp when this record was originally created"
}
}
}
}
}
}
12 changes: 12 additions & 0 deletions lexicons/app/certified/defs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"lexicon": 1,
"id": "app.certified.defs",
"description": "Common type definitions used across certified protocols.",
"defs": {
"did": {
"type": "string",
"format": "did",
"description": "A Decentralized Identifier (DID) string."
}
}
}
4 changes: 2 additions & 2 deletions lexicons/org/hypercerts/claim/evaluation.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@
"type": "array",
"description": "DIDs of the evaluators",
"items": {
"type": "string",
"format": "did"
"type": "ref",
"ref": "app.certified.defs#did"
},
"maxLength": 1000
},
Expand Down
4 changes: 2 additions & 2 deletions lexicons/org/hypercerts/claim/measurement.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
"type": "array",
"description": "DIDs of the entity (or entities) that measured this data",
"items": {
"type": "string",
"format": "did"
"type": "ref",
"ref": "app.certified.defs#did"
},
"maxLength": 100
},
Expand Down
4 changes: 2 additions & 2 deletions lexicons/org/hypercerts/funding/receipt.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"required": ["from", "to", "amount", "currency", "createdAt"],
"properties": {
"from": {
"type": "string",
"format": "did",
"type": "ref",
"ref": "app.certified.defs#did",
"description": "DID of the sender who transferred the funds. Leave empty if sender wants to stay anonymous."
},
"to": {
Expand Down