Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
e57a0b1
docs(design): add permission-sets design for hypercerts & certified data
aspiers Jun 9, 2026
fa1c29a
feat(permissions): implement org.hypercerts and app.certified write p…
aspiers Jun 9, 2026
e9be8af
fix(permissions): remove app.certified.signature.proof from the Certi…
aspiers Jun 9, 2026
e105b3f
fix(permissions): rename sets to authWrite (single-segment NSID under…
aspiers Jun 9, 2026
7cb3ce4
refactor(scripts): clarify generate-exports permission-set exclusion …
aspiers Jun 9, 2026
c327ada
fix(scripts): codegen via `node … | xargs -0 -r` under pipefail, not …
aspiers Jun 9, 2026
1fe9bd3
fix(scripts): run lex codegen via a Node runner, not a shell pipe
aspiers Jun 9, 2026
9ad6b00
docs: fix stale script-name reference (codegen-lexicon-files → run-le…
aspiers Jun 9, 2026
240c059
docs(scripts): correct 'no ARG_MAX' claim — argv is still execve-bounded
aspiers Jun 9, 2026
b0e7ba7
docs(agents): document permission-set sync in the add-a-lexicon check…
aspiers Jun 24, 2026
3dd882a
feat(permissions): add org.hyperboards.authWrite; mark design impleme…
aspiers Jun 24, 2026
bfaea59
feat(permissions): add app.certified.signature.proof to the Certified…
aspiers Jun 24, 2026
1dd0dff
docs(agents): nest changeset step 9 sub-bullets under the list item
aspiers Jul 1, 2026
10cbeff
fix(permissions): drop unsupported description field; document set-gr…
aspiers Jul 2, 2026
194721b
docs(design): frame set-growth as a silent consent widening, confirm …
aspiers Jul 6, 2026
cd08e55
fix(scripts): render permission-set fields in SCHEMAS.md, add parse c…
aspiers Jul 6, 2026
733f555
fix(scripts,docs): address PR #222 review feedback
aspiers Jul 11, 2026
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
37 changes: 37 additions & 0 deletions .agents/skills/building-with-hypercerts-lexicons/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,41 @@ if (result.success) {
}
```

## Permission Sets (OAuth scopes)

To **write** Hypercerts, Hyperboards, or Certified records to a user's repo,
your app needs the user's authorization. AT Protocol grants this via OAuth
scopes: each collection write needs a `repo:<collection>?action=…` scope. Rather
than list every collection by hand, request a **permission set** — a published
bundle — with a single `include:` scope:

| Permission set NSID | Grants write (create/update/delete) on |
| --------------------------- | ------------------------------------------ |
| `org.hypercerts.authWrite` | all `org.hypercerts.*` record collections |
| `org.hyperboards.authWrite` | all `org.hyperboards.*` record collections |
| `app.certified.authWrite` | all `app.certified.*` record collections |

In your OAuth client's authorization request, ask for the set(s) you need:
Comment thread
aspiers marked this conversation as resolved.

```text
scope: include:org.hypercerts.authWrite
```

The user's PDS resolves the published set and expands it into the underlying
`repo:` scopes on the consent screen, where the user sees the set's plain-language
description (e.g. "Manage your Hypercerts data"). Notes:

- **One set per namespace, never combined.** A permission set may only reference
its own namespace authority (atproto permission spec), so `org.hypercerts.*`,
`org.hyperboards.*`, and `app.certified.*` each need their own set. An app
needing more than one requests **each** `include:` scope.
- **Writes only.** These cover create/update/delete. **Reading** records needs no
scope at all — atproto repo records are public.
- **`repo:` scopes carry no `aud`** — request the `include:` without an `?aud=`.
- These same published sets are also consumed by the Certified group service
(CGS) when it expands an API key's `include:` scope — see the CGS integration
guide.

## Lexicon Overview

### Claims — the core impact record
Expand Down Expand Up @@ -673,6 +708,8 @@ avoid typos.
## Further Reading

- [SCHEMAS.md](https://github.com/hypercerts-org/hypercerts-lexicon/blob/main/SCHEMAS.md) — full property-level documentation for every lexicon
- [docs/design/permission-sets.md](https://github.com/hypercerts-org/hypercerts-lexicon/blob/main/docs/design/permission-sets.md) — the permission sets and how they are consumed (OAuth + CGS API keys)
- [ATProto permission spec](https://atproto.com/specs/permission#permission-sets) — OAuth scopes and permission sets
- [CHANGELOG.md](https://github.com/hypercerts-org/hypercerts-lexicon/blob/main/CHANGELOG.md) — version history and migration guides
- [ATProto Lexicon Guide](https://atproto.com/guides/lexicon) — AT Protocol lexicon fundamentals
- [ATProto Lexicon Style Guide](https://atproto.com/guides/lexicon-style-guide) — schema design conventions
Expand Down
15 changes: 15 additions & 0 deletions .changeset/permission-sets-crud.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
"@hypercerts-org/lexicon": minor
---

Add three permission-set lexicons — `org.hypercerts.authWrite`, `org.hyperboards.authWrite`, and `app.certified.authWrite` — each granting create/update/delete over every record collection in its namespace.

A [permission set](https://atproto.com/specs/permission#permission-sets) lets any AT Protocol app request a whole bundle of `repo:` scopes with a single `include:<nsid>` OAuth scope, instead of enumerating each collection by hand. The user's PDS resolves and expands the set during the OAuth grant; the same published set can also be consumed by services (e.g. the Certified group service) when expanding API-key scopes.

There are **three** sets rather than one because the spec requires it: a permission set "is limited to expressing permissions that reference resources under the same NSID namespace as the set itself" and "can not address 'sibling groups' or 'parents'". `org.hypercerts`, `org.hyperboards`, and `app.certified` are separate namespace authorities, so they cannot be combined in a single set — an app needing more than one requests each `include:` scope.

Permission sets are published as-is (they are the source of truth for what gets published to AT Protocol) but have no TypeScript shape — `lex gen-api` cannot generate code for `permission-set` defs. They are therefore excluded from the codegen globs (`gen-api`/`gen-md`/`gen-ts`) and from `generated/exports.ts`, while still shipping as raw lexicon JSON.

Collection lists are enumerated explicitly because the spec forbids wildcards inside a permission set; they must be kept in sync as record types are added. See `docs/design/permission-sets.md`.

The generated `SCHEMAS.md` reference now renders permission-set entries (title, detail, and the resource/collections/actions each set grants) instead of leaving them as empty sections.
33 changes: 27 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,11 +234,32 @@ scripts/ Build and codegen scripts
- Generate types in `generated/` (including vendored external lexicons)
- Auto-generate `generated/exports.ts` with all exports

3. Update `ERD.puml` as appropriate:
3. Update the relevant **permission set** if appropriate:
- The `permission-set` lexicons (`lexicons/org/hypercerts/authWrite.json`,
`lexicons/org/hyperboards/authWrite.json`, and
`lexicons/app/certified/authWrite.json`) each enumerate **every
`type: "record"` collection in their namespace** — the list is both
the grant and its boundary, with no wildcards allowed inside a set.
- When you **add** a new `org.hypercerts.*`, `org.hyperboards.*`, or
`app.certified.*` record lexicon, add its NSID to the matching set's
`permissions[].collection` list, or it will not be grantable via the
published set. Likewise **remove** the NSID when you delete a record
lexicon.
- A set may only reference collections under **its own namespace
authority**, so each namespace's records go in its own set
(`org.hypercerts.*` → `org.hypercerts.authWrite`, `org.hyperboards.*` →
`org.hyperboards.authWrite`, `app.certified.*` →
`app.certified.authWrite`) — never another set.
- This does not apply to non-record defs (e.g. `query`, `object`, or
`permission-set` lexicons themselves), which are not repo collections.
- See [docs/design/permission-sets.md](docs/design/permission-sets.md)
for the full rationale.

4. Update `ERD.puml` as appropriate:
- **Include all fields except facet fields** (they're cosmetic and
don't affect structure)

4. Update `README.md` and `SKILL.md` as appropriate:
5. Update `README.md` and `SKILL.md` as appropriate:
- If `README.md` or
`.agents/skills/building-with-hypercerts-lexicons/SKILL.md`
already references that lexicon, **both files must be updated**
Expand All @@ -263,14 +284,14 @@ scripts/ Build and codegen scripts
valid export names, `$type` strings, and API call signatures.
This will catch many (but not all) documentation errors.

5. Run `npm run gen-schemas-md` to regenerate `SCHEMAS.md`
6. Run `npm run gen-schemas-md` to regenerate `SCHEMAS.md`

6. Run `npm run format` to ensure everything is formatted correctly
7. Run `npm run format` to ensure everything is formatted correctly
via Prettier

7. Run `npm run check` to validate, typecheck, and build
8. Run `npm run check` to validate, typecheck, and build

8. **REQUIRED: Create a changeset file** using the `writing-changesets` skill
9. **REQUIRED: Create a changeset file** using the `writing-changesets` skill
- **This step is MANDATORY for ALL changes that affect users**
- See the `writing-changesets` skill for details on changeset requirements
- Use the skill - do NOT write changeset files manually
Expand Down
42 changes: 42 additions & 0 deletions SCHEMAS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@

Hypercerts-specific lexicons for tracking impact work and claims.

### `org.hypercerts.authWrite`

**Title:** Manage your Hypercerts data

**Detail:** Create, edit, and delete your Hypercerts records (impact claims, evaluations, funding, and related data).

**Resource:** `repo`

**Collections:** `org.hypercerts.claim.activity`, `org.hypercerts.claim.contribution`, `org.hypercerts.claim.contributorInformation`, `org.hypercerts.claim.rights`, `org.hypercerts.collection`, `org.hypercerts.context.acknowledgement`, `org.hypercerts.context.attachment`, `org.hypercerts.context.evaluation`, `org.hypercerts.context.measurement`, `org.hypercerts.funding.receipt`, `org.hypercerts.workscope.tag`

**Actions:** `create`, `update`, `delete`

---

Comment thread
aspiers marked this conversation as resolved.
### `org.hypercerts.claim.activity`

**Description:** A hypercert record tracking impact work.
Expand Down Expand Up @@ -484,6 +498,20 @@ A labeled URL reference.

---

### `app.certified.authWrite`

**Title:** Manage your Certified data

**Detail:** Create, edit, and delete your Certified records (profile, badges, follows, wallet links, and related data).

**Resource:** `repo`

**Collections:** `app.certified.actor.organization`, `app.certified.actor.profile`, `app.certified.badge.award`, `app.certified.badge.definition`, `app.certified.badge.response`, `app.certified.graph.follow`, `app.certified.link.evm`, `app.certified.location`, `app.certified.signature.proof`

**Actions:** `create`, `update`, `delete`

---

### `app.certified.graph.follow`

**Description:** Record declaring a social 'follow' relationship of another account. Duplicate follows will be ignored by the AppView.
Expand Down Expand Up @@ -722,6 +750,20 @@ Specifies the sub-string range a facet feature applies to. Start index is inclus

---

### `org.hyperboards.authWrite`

**Title:** Manage your Hyperboards data

**Detail:** Create, edit, and delete your Hyperboards records (board configurations and display profiles).

**Resource:** `repo`

**Collections:** `org.hyperboards.board`, `org.hyperboards.displayProfile`

**Actions:** `create`, `update`, `delete`

---

### `org.hyperboards.board`

**Description:** Configuration record for a hyperboard, wrapping an underlying activity or collection with visual presentation settings. Stored in the creator's PDS.
Expand Down
Loading