@hypercerts-org/lexicon 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/CHANGELOG.md +205 -0
  2. package/README.md +371 -388
  3. package/SCHEMAS.md +533 -75
  4. package/dist/exports.d.ts +900 -10
  5. package/dist/exports.d.ts.map +1 -1
  6. package/dist/generated/exports.d.ts +900 -10
  7. package/dist/generated/exports.d.ts.map +1 -1
  8. package/dist/generated/lexicons.d.ts +2683 -1093
  9. package/dist/generated/lexicons.d.ts.map +1 -1
  10. package/dist/generated/types/app/bsky/richtext/facet.d.ts +44 -7
  11. package/dist/generated/types/app/bsky/richtext/facet.d.ts.map +1 -1
  12. package/dist/generated/types/app/certified/badge/award.d.ts +2 -3
  13. package/dist/generated/types/app/certified/badge/award.d.ts.map +1 -1
  14. package/dist/generated/types/app/certified/badge/definition.d.ts +3 -3
  15. package/dist/generated/types/app/certified/badge/definition.d.ts.map +1 -1
  16. package/dist/generated/types/app/certified/badge/response.d.ts +2 -2
  17. package/dist/generated/types/app/certified/badge/response.d.ts.map +1 -1
  18. package/dist/generated/types/app/certified/link/evm.d.ts +45 -0
  19. package/dist/generated/types/app/certified/link/evm.d.ts.map +1 -0
  20. package/dist/generated/types/org/hypercerts/claim/activity.d.ts +6 -3
  21. package/dist/generated/types/org/hypercerts/claim/activity.d.ts.map +1 -1
  22. package/dist/generated/types/org/hypercerts/collection.d.ts +11 -5
  23. package/dist/generated/types/org/hypercerts/collection.d.ts.map +1 -1
  24. package/dist/generated/types/org/hypercerts/context/attachment.d.ts +5 -3
  25. package/dist/generated/types/org/hypercerts/context/attachment.d.ts.map +1 -1
  26. package/dist/generated/types/org/hypercerts/context/evaluation.d.ts +6 -6
  27. package/dist/generated/types/org/hypercerts/context/evaluation.d.ts.map +1 -1
  28. package/dist/generated/types/org/hypercerts/defs.d.ts +11 -0
  29. package/dist/generated/types/org/hypercerts/defs.d.ts.map +1 -1
  30. package/dist/generated/types/org/hypercerts/funding/receipt.d.ts +17 -5
  31. package/dist/generated/types/org/hypercerts/funding/receipt.d.ts.map +1 -1
  32. package/dist/generated/types/pub/leaflet/blocks/blockquote.d.ts +13 -0
  33. package/dist/generated/types/pub/leaflet/blocks/blockquote.d.ts.map +1 -0
  34. package/dist/generated/types/pub/leaflet/blocks/bskyPost.d.ts +13 -0
  35. package/dist/generated/types/pub/leaflet/blocks/bskyPost.d.ts.map +1 -0
  36. package/dist/generated/types/pub/leaflet/blocks/button.d.ts +12 -0
  37. package/dist/generated/types/pub/leaflet/blocks/button.d.ts.map +1 -0
  38. package/dist/generated/types/pub/leaflet/blocks/code.d.ts +13 -0
  39. package/dist/generated/types/pub/leaflet/blocks/code.d.ts.map +1 -0
  40. package/dist/generated/types/pub/leaflet/blocks/header.d.ts +14 -0
  41. package/dist/generated/types/pub/leaflet/blocks/header.d.ts.map +1 -0
  42. package/dist/generated/types/pub/leaflet/blocks/horizontalRule.d.ts +10 -0
  43. package/dist/generated/types/pub/leaflet/blocks/horizontalRule.d.ts.map +1 -0
  44. package/dist/generated/types/pub/leaflet/blocks/iframe.d.ts +12 -0
  45. package/dist/generated/types/pub/leaflet/blocks/iframe.d.ts.map +1 -0
  46. package/dist/generated/types/pub/leaflet/blocks/image.d.ts +21 -0
  47. package/dist/generated/types/pub/leaflet/blocks/image.d.ts.map +1 -0
  48. package/dist/generated/types/pub/leaflet/blocks/math.d.ts +11 -0
  49. package/dist/generated/types/pub/leaflet/blocks/math.d.ts.map +1 -0
  50. package/dist/generated/types/pub/leaflet/blocks/orderedList.d.ts +31 -0
  51. package/dist/generated/types/pub/leaflet/blocks/orderedList.d.ts.map +1 -0
  52. package/dist/generated/types/pub/leaflet/blocks/page.d.ts +11 -0
  53. package/dist/generated/types/pub/leaflet/blocks/page.d.ts.map +1 -0
  54. package/dist/generated/types/pub/leaflet/blocks/poll.d.ts +12 -0
  55. package/dist/generated/types/pub/leaflet/blocks/poll.d.ts.map +1 -0
  56. package/dist/generated/types/pub/leaflet/blocks/text.d.ts +14 -0
  57. package/dist/generated/types/pub/leaflet/blocks/text.d.ts.map +1 -0
  58. package/dist/generated/types/pub/leaflet/blocks/unorderedList.d.ts +29 -0
  59. package/dist/generated/types/pub/leaflet/blocks/unorderedList.d.ts.map +1 -0
  60. package/dist/generated/types/pub/leaflet/blocks/website.d.ts +14 -0
  61. package/dist/generated/types/pub/leaflet/blocks/website.d.ts.map +1 -0
  62. package/dist/generated/types/pub/leaflet/pages/linearDocument.d.ts +54 -1
  63. package/dist/generated/types/pub/leaflet/pages/linearDocument.d.ts.map +1 -1
  64. package/dist/generated/types/pub/leaflet/richtext/facet.d.ts +97 -0
  65. package/dist/generated/types/pub/leaflet/richtext/facet.d.ts.map +1 -0
  66. package/dist/index.cjs +4486 -1807
  67. package/dist/index.cjs.map +1 -1
  68. package/dist/index.mjs +4407 -1804
  69. package/dist/index.mjs.map +1 -1
  70. package/dist/lexicons.cjs +927 -35
  71. package/dist/lexicons.cjs.map +1 -1
  72. package/dist/lexicons.d.ts +2683 -1093
  73. package/dist/lexicons.d.ts.map +1 -1
  74. package/dist/lexicons.mjs +927 -35
  75. package/dist/lexicons.mjs.map +1 -1
  76. package/dist/tests/validate-external-lexicons.test.d.ts +2 -0
  77. package/dist/tests/validate-external-lexicons.test.d.ts.map +1 -0
  78. package/dist/tests/validate-funding-receipt.test.d.ts +2 -0
  79. package/dist/tests/validate-funding-receipt.test.d.ts.map +1 -0
  80. package/dist/tests/validate-link-evm.test.d.ts +2 -0
  81. package/dist/tests/validate-link-evm.test.d.ts.map +1 -0
  82. package/dist/tests/validate-rights.test.d.ts +2 -0
  83. package/dist/tests/validate-rights.test.d.ts.map +1 -0
  84. package/dist/types/app/bsky/richtext/facet.d.ts +44 -7
  85. package/dist/types/app/bsky/richtext/facet.d.ts.map +1 -1
  86. package/dist/types/app/certified/badge/award.d.ts +2 -3
  87. package/dist/types/app/certified/badge/award.d.ts.map +1 -1
  88. package/dist/types/app/certified/badge/definition.d.ts +3 -3
  89. package/dist/types/app/certified/badge/definition.d.ts.map +1 -1
  90. package/dist/types/app/certified/badge/response.d.ts +2 -2
  91. package/dist/types/app/certified/badge/response.d.ts.map +1 -1
  92. package/dist/types/app/certified/link/evm.d.ts +45 -0
  93. package/dist/types/app/certified/link/evm.d.ts.map +1 -0
  94. package/dist/types/org/hypercerts/claim/activity.d.ts +6 -3
  95. package/dist/types/org/hypercerts/claim/activity.d.ts.map +1 -1
  96. package/dist/types/org/hypercerts/collection.d.ts +11 -5
  97. package/dist/types/org/hypercerts/collection.d.ts.map +1 -1
  98. package/dist/types/org/hypercerts/context/attachment.d.ts +5 -3
  99. package/dist/types/org/hypercerts/context/attachment.d.ts.map +1 -1
  100. package/dist/types/org/hypercerts/context/evaluation.d.ts +6 -6
  101. package/dist/types/org/hypercerts/context/evaluation.d.ts.map +1 -1
  102. package/dist/types/org/hypercerts/defs.d.ts +11 -0
  103. package/dist/types/org/hypercerts/defs.d.ts.map +1 -1
  104. package/dist/types/org/hypercerts/funding/receipt.d.ts +17 -5
  105. package/dist/types/org/hypercerts/funding/receipt.d.ts.map +1 -1
  106. package/dist/types/pub/leaflet/blocks/blockquote.d.ts +13 -0
  107. package/dist/types/pub/leaflet/blocks/blockquote.d.ts.map +1 -0
  108. package/dist/types/pub/leaflet/blocks/bskyPost.d.ts +13 -0
  109. package/dist/types/pub/leaflet/blocks/bskyPost.d.ts.map +1 -0
  110. package/dist/types/pub/leaflet/blocks/button.d.ts +12 -0
  111. package/dist/types/pub/leaflet/blocks/button.d.ts.map +1 -0
  112. package/dist/types/pub/leaflet/blocks/code.d.ts +13 -0
  113. package/dist/types/pub/leaflet/blocks/code.d.ts.map +1 -0
  114. package/dist/types/pub/leaflet/blocks/header.d.ts +14 -0
  115. package/dist/types/pub/leaflet/blocks/header.d.ts.map +1 -0
  116. package/dist/types/pub/leaflet/blocks/horizontalRule.d.ts +10 -0
  117. package/dist/types/pub/leaflet/blocks/horizontalRule.d.ts.map +1 -0
  118. package/dist/types/pub/leaflet/blocks/iframe.d.ts +12 -0
  119. package/dist/types/pub/leaflet/blocks/iframe.d.ts.map +1 -0
  120. package/dist/types/pub/leaflet/blocks/image.d.ts +21 -0
  121. package/dist/types/pub/leaflet/blocks/image.d.ts.map +1 -0
  122. package/dist/types/pub/leaflet/blocks/math.d.ts +11 -0
  123. package/dist/types/pub/leaflet/blocks/math.d.ts.map +1 -0
  124. package/dist/types/pub/leaflet/blocks/orderedList.d.ts +31 -0
  125. package/dist/types/pub/leaflet/blocks/orderedList.d.ts.map +1 -0
  126. package/dist/types/pub/leaflet/blocks/page.d.ts +11 -0
  127. package/dist/types/pub/leaflet/blocks/page.d.ts.map +1 -0
  128. package/dist/types/pub/leaflet/blocks/poll.d.ts +12 -0
  129. package/dist/types/pub/leaflet/blocks/poll.d.ts.map +1 -0
  130. package/dist/types/pub/leaflet/blocks/text.d.ts +14 -0
  131. package/dist/types/pub/leaflet/blocks/text.d.ts.map +1 -0
  132. package/dist/types/pub/leaflet/blocks/unorderedList.d.ts +29 -0
  133. package/dist/types/pub/leaflet/blocks/unorderedList.d.ts.map +1 -0
  134. package/dist/types/pub/leaflet/blocks/website.d.ts +14 -0
  135. package/dist/types/pub/leaflet/blocks/website.d.ts.map +1 -0
  136. package/dist/types/pub/leaflet/pages/linearDocument.d.ts +54 -1
  137. package/dist/types/pub/leaflet/pages/linearDocument.d.ts.map +1 -1
  138. package/dist/types/pub/leaflet/richtext/facet.d.ts +97 -0
  139. package/dist/types/pub/leaflet/richtext/facet.d.ts.map +1 -0
  140. package/lexicons/app/bsky/richtext/facet.json +51 -0
  141. package/lexicons/app/certified/badge/award.json +2 -2
  142. package/lexicons/app/certified/badge/definition.json +10 -2
  143. package/lexicons/app/certified/badge/response.json +2 -2
  144. package/lexicons/app/certified/link/evm.json +88 -0
  145. package/lexicons/org/hypercerts/claim/activity.json +8 -4
  146. package/lexicons/org/hypercerts/collection.json +19 -5
  147. package/lexicons/org/hypercerts/context/attachment.json +16 -5
  148. package/lexicons/org/hypercerts/context/evaluation.json +9 -6
  149. package/lexicons/org/hypercerts/defs.json +21 -0
  150. package/lexicons/org/hypercerts/funding/receipt.json +30 -10
  151. package/lexicons/pub/leaflet/blocks/blockquote.json +22 -0
  152. package/lexicons/pub/leaflet/blocks/bskyPost.json +19 -0
  153. package/lexicons/pub/leaflet/blocks/button.json +19 -0
  154. package/lexicons/pub/leaflet/blocks/code.json +21 -0
  155. package/lexicons/pub/leaflet/blocks/header.json +27 -0
  156. package/lexicons/pub/leaflet/blocks/horizontalRule.json +11 -0
  157. package/lexicons/pub/leaflet/blocks/iframe.json +21 -0
  158. package/lexicons/pub/leaflet/blocks/image.json +37 -0
  159. package/lexicons/pub/leaflet/blocks/math.json +15 -0
  160. package/lexicons/pub/leaflet/blocks/orderedList.json +54 -0
  161. package/lexicons/pub/leaflet/blocks/page.json +15 -0
  162. package/lexicons/pub/leaflet/blocks/poll.json +16 -0
  163. package/lexicons/pub/leaflet/blocks/text.json +26 -0
  164. package/lexicons/pub/leaflet/blocks/unorderedList.json +50 -0
  165. package/lexicons/pub/leaflet/blocks/website.json +27 -0
  166. package/lexicons/pub/leaflet/pages/linearDocument.json +98 -0
  167. package/lexicons/pub/leaflet/richtext/facet.json +149 -0
  168. package/package.json +3 -4
package/README.md CHANGED
@@ -1,39 +1,180 @@
1
- # Hypercerts Lexicon Documentation
2
-
3
- This repository contains ATProto lexicon definitions for the
4
- Hypercerts protocol. Each lexicon defines a record type that can be
5
- stored on the ATProto network.
1
+ # @hypercerts-org/lexicon
2
+
3
+ ATProto lexicon definitions and TypeScript types for the
4
+ [Hypercerts](https://hypercerts.org) protocol a system for tracking,
5
+ evaluating, and funding impact work on the
6
+ [AT Protocol](https://atproto.com) network.
7
+
8
+ ## Lexicon Map
9
+
10
+ ```text
11
+ CLAIMS ─ the core impact record and its parts
12
+ ──────────────────────────────────────────────────────────────────────
13
+ activity ──────────┬──► collection ◄──┐ (recursive nesting)
14
+ (the hypercert) │ │ │
15
+ │ ▼ │
16
+ ├──► contribution (role, timeframe)
17
+ ├──► contributorInformation (identity, avatar)
18
+ ├──► rights (licensing terms)
19
+ └──► workScope
20
+ ├── cel (CEL expression)
21
+ └── tag (reusable scope atom)
22
+
23
+ CONTEXT ─ evidence, data, and social verification
24
+ ──────────────────────────────────────────────────────────────────────
25
+ attachment ─────────────► activity / evaluation / ...
26
+ measurement ────────────► activity / ...
27
+ evaluation ─────────────► activity / attachment
28
+ └──────► measurement
29
+ acknowledgement ────────► activity / collection (bidirectional link)
30
+
31
+ FUNDING ─ payment records
32
+ ──────────────────────────────────────────────────────────────────────
33
+ receipt ────────────────► activity (from funder → to recipient)
34
+
35
+ HYPERBOARDS ─ visual display layer (hyperboards.org)
36
+ ──────────────────────────────────────────────────────────────────────
37
+ board ──────────────────► activity / collection
38
+ └── contributorConfig ► contributorInformation
39
+ displayProfile (per-user visual defaults)
40
+
41
+ CERTIFIED ─ shared lexicons (certified.app)
42
+ ──────────────────────────────────────────────────────────────────────
43
+ location (geo coordinates, GeoJSON, H3, …)
44
+ actor/profile (user profile)
45
+ actor/organization (org metadata)
46
+ badge/definition ──► badge/award ──► badge/response
47
+ ```
6
48
 
7
- ## Entity Relationship Diagram
49
+ Every arrow (`►`) is a `strongRef` or union reference stored on the
50
+ AT Protocol network. Full field-level documentation is in
51
+ [SCHEMAS.md](SCHEMAS.md).
8
52
 
9
- The following diagrams show the relationship between:
53
+ ## Consuming These Lexicons
10
54
 
11
- - data classes represented by ATProto lexicons, which model the data
12
- sets relating to hypercerts
55
+ If you are building a downstream application on top of these lexicons,
56
+ we strongly recommend **NOT** reading from `main` or other development
57
+ branches of the repository, but instead via the following published
58
+ releases:
13
59
 
14
- - contributors to activity records (modelled/identified by ATProto
15
- DIDs rather than lexicons)
60
+ - **For TypeScript / JavaScript code** use [the npm package
61
+ `@hypercerts-org/lexicon`](https://www.npmjs.com/package/@hypercerts-org/lexicon),
62
+ which includes generated types, validation helpers, and schema
63
+ constants.
64
+ - **For other languages** — use the [tagged
65
+ releases](https://github.com/hypercerts-org/hypercerts-lexicon/releases)
66
+ published in this GitHub repository.
16
67
 
17
- - hypercerts protocol tokens which are onchain representations of
18
- activity records in ATProto
68
+ Both npm releases and git tags follow [SemVer](https://semver.org/).
69
+ For npm, you can depend on a version range to receive compatible
70
+ updates automatically. For GitHub releases/tags, pin a specific tag
71
+ or upgrade manually to a newer compatible SemVer release.
19
72
 
20
- Note that contributors and tokens do not require lexicons.
73
+ The raw lexicons published on ATProto can also be used, but they are
74
+ (unavoidably) missing useful context such as full documentation
75
+ (including changelogs), TypeScript type definitions, SemVer
76
+ guarantees, git history, and other tooling provided by the packaged
77
+ releases.
21
78
 
22
- To distinguish these in the diagrams, each class has one of the
23
- following icons:
79
+ ### AI Agent Skill
24
80
 
25
- - "D" means "data class"
26
- - "E" means "entity"
27
- - "P" means "protocol"
81
+ If you use AI coding assistants (e.g. Claude Code, OpenCode), you can
82
+ install a skill that teaches your agent how to build with these
83
+ lexicons:
28
84
 
29
- ![Hypercert ERD](ERD.svg)
30
-
31
- <details>
32
- <summary>View ERD with field details</summary>
85
+ ```bash
86
+ npx skills add hypercerts-org/hypercerts-lexicon
87
+ ```
33
88
 
34
- ![Hypercert ERD with fields](ERD-with-fields.svg)
89
+ This installs the
90
+ [`building-with-hypercerts-lexicons`](.agents/skills/building-with-hypercerts-lexicons/SKILL.md)
91
+ skill, which provides your agent with guidance on package entry points,
92
+ TypeScript types, validation, all lexicon schemas, code examples, and
93
+ AT Protocol conventions.
94
+
95
+ ## Maintenance and publishing releases
96
+
97
+ Clearly stability and predictability for users and developers are
98
+ essential.
99
+
100
+ Unfortunately AT Protocol doesn't support any kind of native
101
+ versioning or migrations which could support lexicon schema changes.
102
+ Instead, the AT Protocol community recommends minimising changes to
103
+ lexicons in general, and to avoid breaking changes wherever possible:
104
+
105
+ - https://atproto.com/guides/lexicon-style-guide
106
+ - https://www.pfrazee.com/blog/lexicon-guidance
107
+
108
+ This project intends to follow that guidance as much as possible
109
+ whilst retaining a pragmatic approach. In practice that means:
110
+
111
+ - Changes to other tooling within this repository which _do not touch
112
+ lexicons_ may be made at any time as long as they follow
113
+ [SemVer](https://semver.org/) to avoid negative impact on
114
+ developers.
115
+
116
+ - Non-breaking changes to lexicons, such as adding an optional
117
+ property or updating a `description`, may be made sparingly. While
118
+ these changes are backwards-compatible at the protocol level, they
119
+ may still require consuming applications and indexers to update
120
+ their schemas for consistent UX.
121
+
122
+ - Breaking changes to lexicons will only be made in exceptional
123
+ circumstances. Specifically, a breaking change will only proceed
124
+ **if and only if**:
125
+ - the broader community — not just the Hypercerts core team —
126
+ agrees that the benefits clearly outweigh the cost of the
127
+ breakage, **and**
128
+ - full consideration is given to all affected parties across the
129
+ community and wider ecosystem, not only those involved in the
130
+ decision, **and**
131
+ - no viable alternative exists, such as releasing a new `.v2`
132
+ version of the lexicon or introducing a `v2` field.
133
+
134
+ To date, breaking changes have only occurred during the early
135
+ stages of launching Hypercerts on AT Protocol, before external
136
+ consumers were building against the lexicons. We intend to keep
137
+ it that way.
138
+
139
+ It is also worth noting that members of the ATProto community have
140
+ been working on tooling to make these problems easier to deal with in
141
+ future, e.g. see https://panproto.dev/
142
+
143
+ ## Use of branches
144
+
145
+ `main` is the only evergreen branch and the default branch on GitHub.
146
+ We aim to minimise deviations between `main` and versions published on
147
+ npm and ATProto. However the publishing processes involve several
148
+ moving parts (including third-party systems), and it is technically
149
+ impossible to update all three at the same time. So **please do not
150
+ assume they will always be perfectly in sync**.
151
+
152
+ See [docs/PUBLISHING.md](docs/PUBLISHING.md) for the full release workflow.
153
+
154
+ > If you see a `develop` branch, it is a stale leftover from a
155
+ > previous workflow and is no longer used; do not open pull requests
156
+ > against it.
157
+
158
+ ## Contributing / development
159
+
160
+ Please see [CONTRIBUTING.md](CONTRIBUTING.md).
161
+
162
+ ### Project Structure
163
+
164
+ ```text
165
+ lexicons/ Source of truth (committed)
166
+ org/hypercerts/ Hypercerts protocol lexicons
167
+ org/hyperboards/ Hyperboards visual layer lexicons
168
+ app/certified/ Shared/certified lexicons
169
+ com/atproto/ ATProto external references
170
+
171
+ generated/ Auto-generated TypeScript (gitignored)
172
+ dist/ Built bundles (gitignored)
173
+ scripts/ Build and codegen scripts
174
+ ```
35
175
 
36
- </details>
176
+ > **Never edit `generated/` or `dist/` directly** — they are
177
+ > regenerated from lexicon JSON files.
37
178
 
38
179
  ## Installation
39
180
 
@@ -41,9 +182,7 @@ following icons:
41
182
  npm install @hypercerts-org/lexicon
42
183
  ```
43
184
 
44
- ## Usage
45
-
46
- ### Basic Import
185
+ ## Quick Start
47
186
 
48
187
  ```typescript
49
188
  import {
@@ -51,8 +190,6 @@ import {
51
190
  ACTIVITY_NSID,
52
191
  validate,
53
192
  } from "@hypercerts-org/lexicon";
54
-
55
- // Use with AT Protocol Agent
56
193
  import { Agent } from "@atproto/api";
57
194
 
58
195
  const agent = new Agent({ service: "https://bsky.social" });
@@ -60,222 +197,152 @@ const agent = new Agent({ service: "https://bsky.social" });
60
197
  // Register lexicons with the agent
61
198
  agent.api.lex.add(...HYPERCERTS_SCHEMAS);
62
199
 
63
- // Create a record
64
- const activityRecord = {
200
+ // Build a record
201
+ const record = {
65
202
  $type: ACTIVITY_NSID,
66
- title: "My Impact Work",
67
- shortDescription: "Description here",
68
- // workScope can be a CEL expression (structured, machine-evaluable):
69
- workScope: {
70
- $type: "org.hypercerts.workscope.cel",
71
- expression:
72
- "scope.hasAll(['mangrove_restoration', 'environmental_education']) && location.country == 'KE'",
73
- usedTags: [
74
- {
75
- uri: "at://did:plc:alice/org.hypercerts.workscope.tag/3k2abc",
76
- cid: "...",
77
- },
78
- {
79
- uri: "at://did:plc:alice/org.hypercerts.workscope.tag/7x9def",
80
- cid: "...",
81
- },
82
- ],
83
- version: "v1",
84
- createdAt: new Date().toISOString(),
85
- },
86
- // OR a strongRef to a single work scope tag:
87
- // workScope: { uri: "at://did:plc:alice/org.hypercerts.workscope.tag/abc123", cid: "..." },
88
- // OR a simple string: workScope: { $type: "org.hypercerts.claim.activity#workScopeString", scope: "Environmental conservation" },
89
- startDate: "2023-01-01T00:00:00Z",
90
- endDate: "2023-12-31T23:59:59Z",
203
+ title: "Reforestation in Amazon Basin 2024",
204
+ shortDescription: "Planted 5,000 native trees across 12 hectares",
91
205
  createdAt: new Date().toISOString(),
92
206
  };
93
207
 
94
- // Validate before creating
95
- const validation = validate(ACTIVITY_NSID, activityRecord);
96
- if (!validation.valid) {
97
- console.error("Validation failed:", validation.errors);
98
- }
208
+ // Validate before writing
209
+ const result = validate(ACTIVITY_NSID, record);
210
+ if (!result.valid) throw new Error(JSON.stringify(result.errors));
99
211
 
212
+ // Write to the network
100
213
  await agent.api.com.atproto.repo.createRecord({
101
214
  repo: agent.session?.did,
102
215
  collection: ACTIVITY_NSID,
103
- record: activityRecord,
216
+ record,
104
217
  });
105
218
  ```
106
219
 
107
- ### Creating Location Records
220
+ ## Lexicon Reference
108
221
 
109
- Location records (`app.certified.location`) specify where work was performed
110
- using geographic coordinates or other location formats. They can be referenced
111
- by activities, collections, attachments, measurements, and evaluations.
222
+ ### Claims (`org.hypercerts.claim.*`)
112
223
 
113
- ```typescript
114
- import { LOCATION_NSID } from "@hypercerts-org/lexicon";
224
+ | Lexicon | NSID | Description |
225
+ | --------------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
226
+ | **Activity** | `org.hypercerts.claim.activity` | The main hypercert record — describes impact work with title, description, contributors, work scope, timeframe, locations, and rights. |
227
+ | **Contribution** | `org.hypercerts.claim.contribution` | Details about a specific contribution: role, description, and timeframe. |
228
+ | **Contributor Information** | `org.hypercerts.claim.contributorInformation` | Identity record for a contributor: identifier (DID or URI), display name, and avatar. |
229
+ | **Rights** | `org.hypercerts.claim.rights` | Licensing and rights terms (e.g. "CC BY-SA 4.0") attached to an activity. |
115
230
 
116
- const locationRecord = {
117
- $type: LOCATION_NSID,
118
- lpVersion: "1.0", // Location Protocol version
119
- srs: "http://www.opengis.net/def/crs/OGC/1.3/CRS84", // Spatial Reference System
120
- locationType: "coordinate-decimal", // or "geojson-point", "geojson", "h3", "geohash", "wkt", "address", etc.
121
- location: {
122
- uri: "https://example.com/location-data.geojson",
123
- },
124
- // Optional fields
125
- name: "Project Site A",
126
- description: "Primary research facility in the Amazon rainforest",
127
- createdAt: new Date().toISOString(),
128
- };
129
- ```
231
+ ### Collections (`org.hypercerts.*`)
130
232
 
131
- - `lpVersion` (required): Version of the Location Protocol specification
132
- - `srs` (required): Spatial Reference System URI defining the coordinate system
133
- - `locationType` (required): Format identifier (e.g., "coordinate-decimal", "geojson-point", "geojson", "h3", "geohash", "wkt", "address", "scaledCoordinates"). See the [Location Protocol spec](https://spec.decentralizedgeo.org/specification/location-types/#location-type-registry) for the full registry.
134
- - `location` (required): Location data as URI, blob, or string
135
- - `name` (optional): Human-readable name for the location
136
- - `description` (optional): Additional context about the location
137
- - `createdAt` (required): Timestamp when the record was created
233
+ | Lexicon | NSID | Description |
234
+ | -------------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
235
+ | **Collection** | `org.hypercerts.collection` | A named, weighted group of activities and/or other collections. Supports recursive nesting. Used for projects, portfolios, favourites, funding rounds, etc. |
138
236
 
139
- **Location data formats:**
237
+ ### Context (`org.hypercerts.context.*`)
140
238
 
141
- The `location` field accepts three formats:
239
+ | Lexicon | NSID | Description |
240
+ | ------------------- | ---------------------------------------- | --------------------------------------------------------------------------------------- |
241
+ | **Attachment** | `org.hypercerts.context.attachment` | Documents, reports, evidence, or other files linked to a record. |
242
+ | **Measurement** | `org.hypercerts.context.measurement` | Quantitative data point (metric + unit + value) linked to one or more records. |
243
+ | **Evaluation** | `org.hypercerts.context.evaluation` | An assessment of a record with evaluators, summary, score, and supporting measurements. |
244
+ | **Acknowledgement** | `org.hypercerts.context.acknowledgement` | Bidirectional link: confirms or rejects inclusion of a record in another context. |
142
245
 
143
- 1. **URI reference**: `{ uri: "https://..." }` - Link to external location data
144
- 2. **Small blob**: Embedded location data (up to 10MB)
145
- 3. **Location string**: Inline string wrapped in an object, containing coordinates or GeoJSON
246
+ ### Work Scope (`org.hypercerts.workscope.*`)
146
247
 
147
- ```typescript
148
- // Example with embedded blob
149
- const locationWithBlob = {
150
- $type: LOCATION_NSID,
151
- lpVersion: "1.0",
152
- srs: "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
153
- locationType: "geojson-point",
154
- location: {
155
- blob: {
156
- $type: "blob",
157
- ref: {
158
- $link: "bafyrei...", // CID of the uploaded blob
159
- },
160
- mimeType: "application/geo+json",
161
- size: 123,
162
- },
163
- },
164
- name: "Amazon Research Station",
165
- createdAt: new Date().toISOString(),
166
- };
248
+ | Lexicon | NSID | Description |
249
+ | ------------------ | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
250
+ | **Tag** | `org.hypercerts.workscope.tag` | Reusable scope atom (topic, domain, method, …) with taxonomy support, aliases, and linked ontologies. |
251
+ | **CEL Expression** | `org.hypercerts.workscope.cel` | Structured work scope using [CEL](https://github.com/google/cel-spec) expressions over tags. Embedded inline in activity records. |
167
252
 
168
- // Example with inline string (coordinates)
169
- const locationWithCoordinates = {
170
- $type: LOCATION_NSID,
171
- lpVersion: "1.0",
172
- srs: "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
173
- locationType: "coordinate-decimal",
174
- location: {
175
- string: "-3.4653, -62.2159", // lat, lon
176
- },
177
- name: "Amazon Research Site",
178
- description: "Field station coordinates",
179
- createdAt: new Date().toISOString(),
180
- };
253
+ ### Funding (`org.hypercerts.funding.*`)
181
254
 
182
- // Example with inline GeoJSON string
183
- const locationWithGeoJSON = {
184
- $type: LOCATION_NSID,
185
- lpVersion: "1.0",
186
- srs: "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
187
- locationType: "geojson-point",
188
- location: {
189
- string: '{"type":"Point","coordinates":[-62.2159,-3.4653]}',
190
- },
191
- name: "Research Station Alpha",
192
- createdAt: new Date().toISOString(),
193
- };
194
- ```
255
+ | Lexicon | NSID | Description |
256
+ | ----------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
257
+ | **Receipt** | `org.hypercerts.funding.receipt` | Records a payment to a recipient, with amount, currency, payment rail, and optional transaction ID. The sender (`from`) is optional to support anonymous funders. |
258
+
259
+ ### Hyperboards (`org.hyperboards.*`)
195
260
 
196
- ### Accessing NSIDs (Lexicon IDs)
261
+ | Lexicon | NSID | Description |
262
+ | ------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
263
+ | **Board** | `org.hyperboards.board` | Visual presentation layer wrapping an activity or collection with background, colors, aspect ratio, and per-contributor styling. |
264
+ | **Display Profile** | `org.hyperboards.displayProfile` | Per-user visual defaults (avatar, hover image, video, click-through URL) reusable across boards. Singleton record (`literal:self`). |
197
265
 
198
- **Recommended**: Use individual NSID constants for cleaner, more readable code:
266
+ ### Certified (`app.certified.*`)
267
+
268
+ | Lexicon | NSID | Description |
269
+ | -------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
270
+ | **Location** | `app.certified.location` | Geographic reference using the [Location Protocol](https://spec.decentralizedgeo.org) (coordinates, GeoJSON, H3, WKT, etc.). |
271
+ | **Profile** | `app.certified.actor.profile` | User account profile with display name, bio, avatar, and banner. |
272
+ | **Organization** | `app.certified.actor.organization` | Organization metadata: legal structure, URLs, location, founding date. |
273
+ | **Badge Definition** | `app.certified.badge.definition` | Defines a badge type with title, icon, and optional issuer allowlist. |
274
+ | **Badge Award** | `app.certified.badge.award` | Awards a badge to a user, project, or activity. |
275
+ | **Badge Response** | `app.certified.badge.response` | Recipient accepts or rejects a badge award. |
276
+ | **EVM Link** | `app.certified.link.evm` | Verifiable ATProto DID ↔ EVM wallet link via EIP-712 signature. Extensible for future proof methods (e.g. ERC-1271, ERC-6492). |
277
+
278
+ > **Full property tables** → [SCHEMAS.md](SCHEMAS.md)
279
+
280
+ ## Entity Relationship Diagram
281
+
282
+ ![Hypercert ERD](ERD.svg)
283
+
284
+ <details>
285
+ <summary>View ERD with field details</summary>
286
+
287
+ ![Hypercert ERD with fields](ERD-with-fields.svg)
288
+
289
+ </details>
290
+
291
+ ## Usage
292
+
293
+ ### Accessing NSIDs
294
+
295
+ Individual constants (recommended):
199
296
 
200
297
  ```typescript
201
298
  import { ACTIVITY_NSID, COLLECTION_NSID } from "@hypercerts-org/lexicon";
202
-
203
- // Clean and explicit
204
- const record = {
205
- $type: ACTIVITY_NSID,
206
- // ...
207
- };
208
299
  ```
209
300
 
210
- **Alternative**: Use the semantic NSID object when you need multiple NSIDs:
301
+ Semantic object:
211
302
 
212
303
  ```typescript
213
304
  import { HYPERCERTS_NSIDS } from "@hypercerts-org/lexicon";
214
305
 
215
- // Access via semantic keys
216
- const activityId = HYPERCERTS_NSIDS.ACTIVITY;
217
- const collectionId = HYPERCERTS_NSIDS.COLLECTION;
218
- const rightsId = HYPERCERTS_NSIDS.RIGHTS;
306
+ const id = HYPERCERTS_NSIDS.ACTIVITY;
219
307
  ```
220
308
 
221
- **Type-based mapping**: If you need to map TypeScript type namespaces to NSIDs:
309
+ Type-based mapping:
222
310
 
223
311
  ```typescript
224
312
  import { HYPERCERTS_NSIDS_BY_TYPE } from "@hypercerts-org/lexicon";
225
313
 
226
- // Access via type namespace names
227
- const activityId = HYPERCERTS_NSIDS_BY_TYPE.OrgHypercertsClaimActivity;
228
- const collectionId = HYPERCERTS_NSIDS_BY_TYPE.OrgHypercertsCollection;
314
+ const id = HYPERCERTS_NSIDS_BY_TYPE.OrgHypercertsClaimActivity;
229
315
  ```
230
316
 
231
- **Lightweight Bundle**: Import from `/lexicons` for runtime validation without TypeScript types (smaller bundle size):
317
+ Lightweight bundle (no TypeScript types, smaller bundle):
232
318
 
233
319
  ```typescript
234
320
  import { schemas, validate, ids } from "@hypercerts-org/lexicon/lexicons";
235
-
236
- // Lighter bundle, type-based namespace access
237
- const result = validate(ids.OrgHypercertsClaimActivity, record);
238
321
  ```
239
322
 
240
- **Note**: Individual constants (e.g., `ACTIVITY_NSID`) are the recommended approach for most use cases as they provide the best developer experience with clear, concise naming.
241
-
242
323
  ### TypeScript Types
243
324
 
244
- All lexicon types are exported as namespaces:
245
-
246
325
  ```typescript
247
326
  import { OrgHypercertsClaimActivity } from "@hypercerts-org/lexicon";
248
327
 
249
- // Use the Main type
250
328
  const activity: OrgHypercertsClaimActivity.Main = {
251
329
  $type: "org.hypercerts.claim.activity",
252
330
  title: "My Impact Work",
253
- // ... other fields
331
+ shortDescription: "...",
332
+ createdAt: new Date().toISOString(),
254
333
  };
255
334
  ```
256
335
 
257
- ### Individual Lexicon Imports
258
-
259
- Each lexicon is available in two forms as individual constants:
336
+ ### Lexicon Documents
260
337
 
261
338
  ```typescript
262
339
  import {
263
- // Raw JSON (untyped) - direct import from JSON files
264
- ACTIVITY_LEXICON_JSON,
265
- RIGHTS_LEXICON_JSON,
266
-
267
- // Typed LexiconDoc - from lexicons.get() at module initialization
268
- ACTIVITY_LEXICON_DOC,
269
- RIGHTS_LEXICON_DOC,
340
+ ACTIVITY_LEXICON_JSON, // raw JSON (untyped)
341
+ ACTIVITY_LEXICON_DOC, // LexiconDoc (typed)
270
342
  } from "@hypercerts-org/lexicon";
271
343
  ```
272
344
 
273
- | Suffix | Type | Source | Use Case |
274
- | ------- | -------------------- | ------------------------- | ------------------------------ |
275
- | `_JSON` | Untyped JSON | Direct JSON import | Raw schema data |
276
- | `_DOC` | `LexiconDoc` (typed) | `lexicons.get()` instance | Type-safe lexicon manipulation |
277
-
278
- Or access all lexicons via semantic mapping objects:
345
+ Or via semantic mapping objects:
279
346
 
280
347
  ```typescript
281
348
  import {
@@ -283,159 +350,111 @@ import {
283
350
  HYPERCERTS_LEXICON_DOC,
284
351
  } from "@hypercerts-org/lexicon";
285
352
 
286
- // Access via semantic keys (same keys as HYPERCERTS_NSIDS)
287
- const activityJSON = HYPERCERTS_LEXICON_JSON.ACTIVITY;
288
- const activityDoc = HYPERCERTS_LEXICON_DOC.ACTIVITY;
289
- const rightsJSON = HYPERCERTS_LEXICON_JSON.RIGHTS;
290
- const rightsDoc = HYPERCERTS_LEXICON_DOC.RIGHTS;
353
+ const doc = HYPERCERTS_LEXICON_DOC.ACTIVITY;
291
354
  ```
292
355
 
293
- ## Schema Documentation
294
-
295
- For complete schema documentation with all lexicon definitions and
296
- property tables, see [SCHEMAS.md](SCHEMAS.md).
297
-
298
356
  ## Examples
299
357
 
300
- ### Collections
301
-
302
- Collections (`org.hypercerts.collection`) are named sets of references to
303
- other records, for any purpose the creator chooses. They live at the
304
- top-level namespace (not under `claim`) because they can contain more than
305
- just claims.
306
-
307
- #### Use Cases
308
-
309
- - Defining which activity claims belong to a project
310
- - Collections of projects
311
- - Favourites lists
312
- - Items associated with a particular funding round or funder
313
- - Portfolios of work by a contributor or organization
314
- - Thematic groupings (by work scope/topic)
315
- - Curated showcases for display (e.g. a hyperboard)
316
- - Milestone groupings (activities in a sprint/cycle)
317
- - Geographic groupings (projects or locations in a region)
318
- - Collections for reporting (e.g. all claims in a grant report)
319
-
320
- **Note**: Hyperboards are a separate concern — they are visualisations
321
- built on top of collections, not collections themselves.
322
-
323
- #### Creating a Collection with Nested Items
358
+ ### Creating Activities with Work Scope
324
359
 
325
360
  ```typescript
326
- import { TID } from "@atproto/common";
361
+ import { ACTIVITY_NSID } from "@hypercerts-org/lexicon";
327
362
 
328
- const collectionRecord = {
329
- $type: "org.hypercerts.collection",
330
- title: "Climate Action Projects",
331
- shortDescription:
332
- "A collection of climate-related activities and sub-collections",
333
- items: [
334
- // Reference to an activity
335
- {
336
- uri: "at://did:plc:alice/org.hypercerts.claim.activity/3k2abc",
337
- cid: "...",
338
- },
339
- // Reference to another activity
340
- {
341
- uri: "at://did:plc:bob/org.hypercerts.claim.activity/7x9def",
342
- cid: "...",
343
- },
344
- // Reference to another collection (recursive!)
345
- {
346
- uri: "at://did:plc:carol/org.hypercerts.collection/4m5ghi",
347
- cid: "...",
348
- },
349
- ],
363
+ const activity = {
364
+ $type: ACTIVITY_NSID,
365
+ title: "Mangrove Restoration in Mombasa",
366
+ shortDescription: "Restored 3 hectares of mangrove forest",
367
+ // Structured work scope via CEL expression:
368
+ workScope: {
369
+ $type: "org.hypercerts.workscope.cel",
370
+ expression:
371
+ "scope.hasAll(['mangrove_restoration']) && location.country == 'KE'",
372
+ usedTags: [
373
+ {
374
+ uri: "at://did:plc:alice/org.hypercerts.workscope.tag/3k2abc",
375
+ cid: "...",
376
+ },
377
+ ],
378
+ version: "v1",
379
+ createdAt: new Date().toISOString(),
380
+ },
381
+ startDate: "2024-01-01T00:00:00Z",
382
+ endDate: "2024-12-31T23:59:59Z",
350
383
  createdAt: new Date().toISOString(),
351
384
  };
352
385
  ```
353
386
 
354
- ### Creating a Project
355
-
356
- Projects are collections with a `type` field set to "project" and can
357
- include rich-text descriptions:
387
+ ### Creating Collections (Projects, Portfolios, etc.)
358
388
 
359
389
  ```typescript
360
- const projectRecord = {
361
- $type: "org.hypercerts.collection",
390
+ import { COLLECTION_NSID } from "@hypercerts-org/lexicon";
391
+
392
+ const project = {
393
+ $type: COLLECTION_NSID,
362
394
  type: "project",
363
395
  title: "Carbon Offset Initiative",
364
- shortDescription: "A project focused on carbon reduction and reforestation",
365
- description: {
366
- uri: "at://did:plc:alice/pub.leaflet.pages.linearDocument/abc123",
367
- cid: "...",
368
- },
396
+ shortDescription: "Activities focused on carbon reduction and reforestation",
369
397
  items: [
370
398
  {
371
- uri: "at://did:plc:alice/org.hypercerts.claim.activity/3k2abc",
372
- cid: "...",
399
+ itemIdentifier: {
400
+ uri: "at://did:plc:alice/org.hypercerts.claim.activity/3k2abc",
401
+ cid: "...",
402
+ },
373
403
  },
374
404
  {
375
- uri: "at://did:plc:bob/org.hypercerts.claim.activity/7x9def",
376
- cid: "...",
405
+ itemIdentifier: {
406
+ uri: "at://did:plc:bob/org.hypercerts.claim.activity/7x9def",
407
+ cid: "...",
408
+ },
409
+ },
410
+ // Collections can contain other collections (recursive nesting):
411
+ {
412
+ itemIdentifier: {
413
+ uri: "at://did:plc:carol/org.hypercerts.collection/4m5ghi",
414
+ cid: "...",
415
+ },
377
416
  },
378
417
  ],
379
418
  createdAt: new Date().toISOString(),
380
419
  };
381
420
  ```
382
421
 
383
- **Note**: The `type` field is optional and can be set to "project",
384
- "favorites", or any other collection type. The `description` field
385
- supports rich-text via Leaflet linear documents.
386
-
387
- ### Adding Visual Representation to Collections
388
-
389
- Collections can include `avatar` and `banner` fields for visual representation:
422
+ ### Creating Location Records
390
423
 
391
424
  ```typescript
392
- import { COLLECTION_NSID } from "@hypercerts-org/lexicon";
425
+ import { LOCATION_NSID } from "@hypercerts-org/lexicon";
393
426
 
394
- const collectionRecord = {
395
- $type: COLLECTION_NSID,
396
- title: "Climate Action Projects",
397
- avatar: {
398
- image: blobRef, // or { uri: "https://..." }
399
- },
400
- banner: {
401
- image: largeBlobRef, // or { uri: "https://..." }
402
- },
403
- items: [
404
- // ... collection items
405
- ],
427
+ // Decimal coordinates
428
+ const location = {
429
+ $type: LOCATION_NSID,
430
+ lpVersion: "1.0",
431
+ srs: "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
432
+ locationType: "coordinate-decimal",
433
+ location: { string: "-3.4653, -62.2159" },
434
+ name: "Amazon Research Station",
406
435
  createdAt: new Date().toISOString(),
407
436
  };
408
- ```
409
437
 
410
- **Note**: Both `avatar` (up to 5MB) and `banner` (up to 10MB) fields
411
- are optional and support either embedded image blobs or URI references to
412
- external images.
438
+ // GeoJSON
439
+ const geoLocation = {
440
+ $type: LOCATION_NSID,
441
+ lpVersion: "1.0",
442
+ srs: "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
443
+ locationType: "geojson-point",
444
+ location: { string: '{"type":"Point","coordinates":[-62.2159,-3.4653]}' },
445
+ name: "Research Station Alpha",
446
+ createdAt: new Date().toISOString(),
447
+ };
448
+ ```
413
449
 
414
450
  ### Acknowledging Inclusion
415
451
 
416
- The `org.hypercerts.context.acknowledgement` record enables bidirectional
417
- linking between records that live in different PDS repositories. When
418
- one user includes another user's record (e.g. adding an activity to a
419
- collection), the owner of the included record can create an
420
- acknowledgement to confirm or reject the inclusion. This forms a
421
- two-way link that an AppView can verify.
422
-
423
- Each acknowledgement uses `com.atproto.repo.strongRef` fields to
424
- reference both the **subject** (the record being included) and the
425
- **context** (the record it's being included in).
426
-
427
- See [SCHEMAS.md](SCHEMAS.md) for the full property reference.
428
-
429
- #### Use Case: Activity Included in a Collection
430
-
431
- A project organizer (Alice) creates a collection and adds Bob's
432
- activity to it via a `strongRef` in the collection's `items[]` array.
433
- Bob then creates an acknowledgement in his own repo to confirm:
452
+ When one user includes another's record (e.g. adding an activity to a
453
+ collection), the owner can confirm or reject with an acknowledgement:
434
454
 
435
455
  ```typescript
436
456
  import { ACKNOWLEDGEMENT_NSID } from "@hypercerts-org/lexicon";
437
457
 
438
- // Bob acknowledges that his activity is included in Alice's collection
439
458
  const ack = {
440
459
  $type: ACKNOWLEDGEMENT_NSID,
441
460
  subject: {
@@ -446,96 +465,26 @@ const ack = {
446
465
  uri: "at://did:plc:alice/org.hypercerts.collection/7x9def",
447
466
  cid: "bafy...",
448
467
  },
449
- acknowledged: true,
450
- createdAt: new Date().toISOString(),
451
- };
452
- ```
453
-
454
- #### Use Case: Contributor Included in an Activity
455
-
456
- Alice creates an activity that lists Bob as a contributor. Bob creates
457
- an acknowledgement in his own repo to confirm his participation:
458
-
459
- ```typescript
460
- const ack = {
461
- $type: ACKNOWLEDGEMENT_NSID,
462
- subject: {
463
- // Bob's contributor information record
464
- uri: "at://did:plc:bob/org.hypercerts.claim.contributorInformation/abc123",
465
- cid: "bafy...",
466
- },
467
- context: {
468
- // Alice's activity that lists Bob as contributor
469
- uri: "at://did:plc:alice/org.hypercerts.claim.activity/3k2abc",
470
- cid: "bafy...",
471
- },
472
- acknowledged: true,
473
- comment: "Confirming my contribution to this reforestation project",
474
- createdAt: new Date().toISOString(),
475
- };
476
- ```
477
-
478
- Setting `acknowledged: false` explicitly rejects inclusion, which an
479
- AppView can use to flag disputed associations.
480
-
481
- ### Adding Locations to Activities
482
-
483
- The `locations` field in activity records is an array of strong references
484
- (`com.atproto.repo.strongRef`) pointing to `app.certified.location` records.
485
- Each strong reference contains two required fields:
486
-
487
- - `uri`: The ATProto URI of the location record (e.g., `at://did:plc:alice/app.certified.location/abc123`)
488
- - `cid`: The content identifier (CID) of the location record, ensuring referential integrity
489
-
490
- **Validation and Expectations**:
491
-
492
- - All location records referenced in the `locations` array must conform to the
493
- `app.certified.location` lexicon schema
494
- - The `uri` field must be a valid ATProto URI pointing to an existing location record
495
- - The `cid` field must match the current CID of the referenced location record
496
- - The `locations` field is optional; activities can be created without location data
497
-
498
- ### Adding Location to Collections
499
-
500
- Collections can include an optional `location` field to specify where the collection's activities were performed:
501
-
502
- ```typescript
503
- const collectionRecord = {
504
- $type: "org.hypercerts.collection",
505
- title: "Climate Action Projects",
506
- shortDescription: "A collection of climate-related activities",
507
- location: {
508
- uri: "at://did:plc:alice/app.certified.location/xyz789",
509
- cid: "...",
510
- },
511
- items: [
512
- // ... collection items
513
- ],
468
+ acknowledged: true, // false to reject
514
469
  createdAt: new Date().toISOString(),
515
470
  };
516
471
  ```
517
472
 
518
- The `location` field is a strong reference to an `app.certified.location` record containing the same `uri` and `cid` fields as described above for activities.
519
-
520
473
  ### Creating Attachments
521
474
 
522
- Attachments provide commentary, context, evidence, or documentary material
523
- related to hypercert records. They can be linked to activities, evaluations,
524
- measurements, or even other attachments:
525
-
526
475
  ```typescript
527
476
  import { ATTACHMENT_NSID } from "@hypercerts-org/lexicon";
528
477
 
529
- const attachmentRecord = {
478
+ const attachment = {
530
479
  $type: ATTACHMENT_NSID,
531
480
  title: "Field Survey Report",
481
+ contentType: "report",
532
482
  subjects: [
533
483
  {
534
484
  uri: "at://did:plc:alice/org.hypercerts.claim.activity/abc123",
535
485
  cid: "...",
536
486
  },
537
487
  ],
538
- contentType: "report",
539
488
  content: [
540
489
  { uri: "https://example.com/reports/survey-2024.pdf" },
541
490
  { uri: "ipfs://Qm..." },
@@ -545,27 +494,61 @@ const attachmentRecord = {
545
494
  };
546
495
  ```
547
496
 
548
- **Key fields:**
497
+ ## Development
498
+
499
+ ### Commands
549
500
 
550
- - `title` (required): String title for the attachment
551
- - `shortDescription`/`description`: Support rich text via facet annotations
552
- - `subjects` (optional): Array of strong references to records this attachment relates to
553
- - `contentType` (optional): Type descriptor (e.g., "report", "audit", "evidence", "testimonial")
554
- - `content` (required): Array of URIs or blobs containing the attachment files
555
- - `location` (optional): Strong reference to an `app.certified.location` record
556
- - `createdAt` (required): Timestamp when the attachment was created
501
+ ```bash
502
+ npm run gen-api # Regenerate TypeScript types from lexicons
503
+ npm run build # Build distributable bundles (ESM, CJS, types)
504
+ npm run check # Validate + typecheck + build (run before committing)
505
+ npm run lint # Check formatting (Prettier + ESLint)
506
+ npm run format # Auto-fix formatting
507
+ npm run gen-schemas-md # Regenerate SCHEMAS.md
508
+ npm run test # Run tests
509
+ ```
557
510
 
558
- **Adding Location to Attachments:**
511
+ ### Linking ATProto Identity to EVM Wallets
512
+
513
+ The `app.certified.link.evm` record enables verifiable linking between
514
+ an ATProto DID and an EVM wallet address. The link is proven via a
515
+ cryptographic signature, allowing any verifier to confirm that the
516
+ wallet owner authorized the binding. Currently supports EOA wallets
517
+ via EIP-712 typed data signatures; the `proof` field is an open union
518
+ to allow future signature methods (e.g. ERC-1271, ERC-6492).
559
519
 
560
520
  ```typescript
561
- const attachmentWithLocation = {
562
- $type: ATTACHMENT_NSID,
563
- title: "Site Inspection Photos",
564
- content: [{ uri: "https://..." }],
565
- location: {
566
- uri: "at://did:plc:alice/app.certified.location/loc123",
567
- cid: "...",
521
+ import { LINK_EVM_NSID } from "@hypercerts-org/lexicon";
522
+
523
+ const evmLinkRecord = {
524
+ $type: LINK_EVM_NSID,
525
+ address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
526
+ proof: {
527
+ $type: "app.certified.link.evm#eip712Proof",
528
+ signature: "0xabc123...", // truncated for readability; real signatures are 130-132 hex chars
529
+ message: {
530
+ $type: "app.certified.link.evm#eip712Message",
531
+ did: "did:plc:alice",
532
+ evmAddress: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
533
+ chainId: "1",
534
+ timestamp: "1709500000",
535
+ nonce: "0",
536
+ },
568
537
  },
569
538
  createdAt: new Date().toISOString(),
570
539
  };
571
540
  ```
541
+
542
+ **Key fields:**
543
+
544
+ - `address` (required): 0x-prefixed EVM wallet address (EIP-55
545
+ checksummed, 42 chars)
546
+ - `proof` (required): Open union containing the cryptographic proof of
547
+ wallet ownership. Each variant bundles its signature with the
548
+ corresponding message format. Currently the only variant is
549
+ `#eip712Proof` for EOA wallets.
550
+ - `createdAt` (required): Timestamp when the record was created
551
+
552
+ ## License
553
+
554
+ MIT