Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
51 changes: 41 additions & 10 deletions .agents/skills/building-with-hypercerts-lexicons/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,16 +273,17 @@ description (e.g. "Manage your Hypercerts data"). Notes:

### Certified — shared lexicons

| Lexicon | NSID | Purpose |
| -------------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| **Location** | `app.certified.location` | Geographic reference via [Location Protocol](https://spec.decentralizedgeo.org) |
| **Profile** | `app.certified.actor.profile` | User profile: display name, bio, avatar, banner |
| **Organization** | `app.certified.actor.organization` | Organization metadata: legal structure, URLs, location, founding date, optional long description, discoverability visibility |
| **Badge Definition** | `app.certified.badge.definition` | Defines a badge with type, title, icon, optional issuer allowlist |
| **Badge Award** | `app.certified.badge.award` | Awards a badge to a user, project, or activity |
| **Badge Response** | `app.certified.badge.response` | Recipient accepts or rejects a badge award |
| **EVM Link** | `app.certified.link.evm` | Verifiable ATProto DID to EVM wallet link via EIP-712 signature. Can additionally carry `signatures[]` for record provenance |
| **Follow** | `app.certified.graph.follow` | Social-graph follow: declares the author follows another account by DID. Schema-compatible with `app.bsky.graph.follow` |
| Lexicon | NSID | Purpose |
| -------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Location** | `app.certified.location` | Geographic reference via [Location Protocol](https://spec.decentralizedgeo.org) |
| **Profile** | `app.certified.actor.profile` | User profile: display name, bio, avatar, banner |
| **Organization** | `app.certified.actor.organization` | Organization metadata: legal structure, URLs, location, founding date, optional long description, discoverability visibility |
| **Badge Definition** | `app.certified.badge.definition` | Defines a badge with type, title, icon, optional issuer allowlist |
| **Badge Award** | `app.certified.badge.award` | Awards a badge to a user, project, or activity |
| **Badge Response** | `app.certified.badge.response` | Recipient accepts or rejects a badge award |
| **EVM Link** | `app.certified.link.evm` | Verifiable ATProto DID to EVM wallet link via EIP-712 signature. Can additionally carry `signatures[]` for record provenance |
| **Follow** | `app.certified.graph.follow` | Social-graph follow: declares the author follows another account by DID. Schema-compatible with `app.bsky.graph.follow` |
| **Entity Follow** | `app.certified.graph.entityFollow` | Social-graph follow for non-account entities (e.g. a record, by AT-URI without a CID). Account (DID) follows stay in `app.certified.graph.follow`; `subject` is an open union for future non-DID entity kinds |

### Signatures — cryptographic attestation

Expand Down Expand Up @@ -329,6 +330,7 @@ CERTIFIED
actor/organization (org metadata)
badge/response ──> badge/award ──> badge/definition
graph/follow ───────────> account DID (social follow)
graph/entityFollow ─────> record (by AT-URI) (non-account follow)
signature/defs (shared #list and #inline defs)
signature/proof (remote attestation proof record)

Expand Down Expand Up @@ -463,6 +465,35 @@ key strategy and fields including the optional `via` strongRef), so
feed-builders and view services can index it with the same logic
they already use for Bluesky follows.

### Following a Non-Account Entity

```typescript
import { GRAPH_ENTITY_FOLLOW_NSID } from "@hypercerts-org/lexicon";

const entityFollow = {
$type: GRAPH_ENTITY_FOLLOW_NSID,
// subject is a union — use $type to specify the variant
subject: {
$type: "app.certified.graph.entityFollow#recordSubject",
uri: "at://did:plc:alice/org.hypercerts.claim.activity/3k2abc",
},
createdAt: new Date().toISOString(),
// Optional `via` strongRef — same semantics as on `app.certified.graph.follow`.
// via: {
// uri: "at://did:plc:alice/app.certified.graph.starterpack/3k2abc",
// cid: "bafyreigh2akiscaildcqabsyg3dfr6chu3fgpregiymsck7e7aqa4s52zy",
// },
};
```

`app.certified.graph.entityFollow` is the sibling of
`app.certified.graph.follow` for following anything that is not an
account — accounts (DIDs) stay in `app.certified.graph.follow`. The
only subject variant today is `#recordSubject`, a record referenced by
AT-URI (DID form, no CID) so the follow survives later updates to the
record; `subject` is an open union so future non-DID entity kinds can
be added non-breakingly.

### Linking an EVM Wallet

```typescript
Expand Down
5 changes: 5 additions & 0 deletions .changeset/add-graph-entity-follow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hypercerts-org/lexicon": minor
---

Add `app.certified.graph.entityFollow`, a follow record for non-account entities. `subject` is an open union currently offering only `#recordSubject` (a record referenced by AT-URI without a CID, so the follow survives updates to the referenced record); account follows remain in `app.certified.graph.follow`. The new collection is also added to the `app.certified.authWrite` permission set so it is grantable alongside the other Certified records.
24 changes: 24 additions & 0 deletions ERD.puml
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,26 @@ dataclass follow {
!endif
}

' app.certified.graph.entityFollow
' subject is an open union (currently just #recordSubject); DIDs are
' deliberately out of scope here and belong to app.certified.graph.follow.
dataclass entityFollow {
!if (SHOW_FIELDS == "true")
subject
via?
createdAt
!endif
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

' Followed subjects that are records are referenced generically by
' AT-URI (DID form, no CID) rather than modelled as a specific lexicon,
' so the follow survives updates to the referenced record.
entity "record (any lexicon, by AT-URI)" as entityFollowSubject #FFD4A3 {
!if (SHOW_FIELDS == "true")
AT-URI (DID form, no CID)
!endif
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

' org.hypercerts.workscope.cel
dataclass celExpression {
!if (SHOW_FIELDS == "true")
Expand Down Expand Up @@ -422,6 +442,10 @@ badgeAward::subject --> activity

' app.certified.graph.follow points at an account DID (the entity being followed)
follow::subject --> contributorEntity : follows

' app.certified.graph.entityFollow points at a non-account record (open
' union; account DIDs are out of scope here, see app.certified.graph.follow)
entityFollow::subject --> entityFollowSubject : follows
' This screws up the layout
'badgeAward::subject --[norank]-> collection

Expand Down
52 changes: 42 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ CERTIFIED ─ shared lexicons (certified.app)
actor/organization (org metadata)
badge/response ──► badge/award ──► badge/definition
graph/follow ────────────► account DID (social follow)
graph/entityFollow ──────► record (by AT-URI) (non-account follow)
signature/defs (shared #list and #inline defs)
signature/proof (remote attestation proof record)
```
Expand Down Expand Up @@ -269,16 +270,17 @@ await agent.api.com.atproto.repo.createRecord({

### Certified (`app.certified.*`)

| Lexicon | NSID | Description |
| -------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| **Location** | `app.certified.location` | Geographic reference using the [Location Protocol](https://spec.decentralizedgeo.org) (coordinates, GeoJSON, H3, WKT, etc.). |
| **Profile** | `app.certified.actor.profile` | User account profile with display name, bio, avatar, and banner. |
| **Organization** | `app.certified.actor.organization` | Organization metadata: legal structure, URLs, location, founding date, optional long description, and discoverability visibility. |
| **Badge Definition** | `app.certified.badge.definition` | Defines a badge type with title, icon, and optional issuer allowlist. |
| **Badge Award** | `app.certified.badge.award` | Awards a badge to a user, project, or activity. |
| **Badge Response** | `app.certified.badge.response` | Recipient accepts or rejects a badge award. |
| **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). |
| **Follow** | `app.certified.graph.follow` | Social-graph follow relationship — declares that the author follows another account by DID. Schema-compatible with `app.bsky.graph.follow`. |
| Lexicon | NSID | Description |
| -------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Location** | `app.certified.location` | Geographic reference using the [Location Protocol](https://spec.decentralizedgeo.org) (coordinates, GeoJSON, H3, WKT, etc.). |
| **Profile** | `app.certified.actor.profile` | User account profile with display name, bio, avatar, and banner. |
| **Organization** | `app.certified.actor.organization` | Organization metadata: legal structure, URLs, location, founding date, optional long description, and discoverability visibility. |
| **Badge Definition** | `app.certified.badge.definition` | Defines a badge type with title, icon, and optional issuer allowlist. |
| **Badge Award** | `app.certified.badge.award` | Awards a badge to a user, project, or activity. |
| **Badge Response** | `app.certified.badge.response` | Recipient accepts or rejects a badge award. |
| **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). |
| **Follow** | `app.certified.graph.follow` | Social-graph follow relationship — declares that the author follows another account by DID. Schema-compatible with `app.bsky.graph.follow`. |
| **Entity Follow** | `app.certified.graph.entityFollow` | Social-graph follow relationship for non-account entities (e.g. a record, referenced by AT-URI without a CID so the follow survives updates). Account (DID) follows remain in `app.certified.graph.follow`; `subject` is an open union so future non-DID entity kinds can be added non-breakingly. |

### Signatures (`app.certified.signature.*`)

Expand Down Expand Up @@ -759,6 +761,36 @@ The optional `via` field is a `com.atproto.repo.strongRef` to any
record that mediated the follow (e.g. a starter pack or other curated
list), mirroring the equivalent field on `app.bsky.graph.follow`.

### Following a non-account entity

`app.certified.graph.entityFollow` is the sibling of
`app.certified.graph.follow` for following anything that is **not** an
account. Account (DID) follows are explicitly out of scope here — use
`app.certified.graph.follow` for those. Today the only subject variant
is `#recordSubject`, a record referenced by AT-URI (DID form, no CID),
so the reference survives later updates to the record. `subject` is an
open union so additional non-DID entity kinds can be added in the
future without a breaking change.

```typescript
import { GRAPH_ENTITY_FOLLOW_NSID } from "@hypercerts-org/lexicon";

const entityFollow = {
$type: GRAPH_ENTITY_FOLLOW_NSID,
// subject is a union — use $type to specify the variant
subject: {
$type: "app.certified.graph.entityFollow#recordSubject",
uri: "at://did:plc:alice/org.hypercerts.claim.activity/3k2abc",
},
createdAt: new Date().toISOString(),
// Optional `via` strongRef — same semantics as on `app.certified.graph.follow`.
// via: {
// uri: "at://did:plc:alice/app.certified.graph.starterpack/3k2abc",
// cid: "bafyreigh2akiscaildcqabsyg3dfr6chu3fgpregiymsck7e7aqa4s52zy",
// },
};
```

### Linking ATProto Identity to EVM Wallets

The `app.certified.link.evm` record enables verifiable linking between
Expand Down
Loading