Skip to content
Mobile
Skill

/nip85-trusted-assertions

NIP-85 trusted assertions in Quartz: kind 10040 provider lists, 30382-30385 assertion cards, RankTag/HopsTag/ServiceType, GrapeRank publisher, contact-card nicknames. Use when building, parsing or ranking on these events.

From plugin
amethyst
1.6k12 skills

Claude Code

$ npx -y skills add vitorpamplona/amethyst --skill nip85-trusted-assertions --agent claude-code

SKILL.md

nip85-trusted-assertions.SKILL.md
name: nip85-trusted-assertions
description: NIP-85 trusted assertions in Quartz: kind 10040 provider lists, 30382-30385 assertion cards, RankTag/HopsTag/ServiceType, GrapeRank publisher, contact-card nicknames. Use when building, parsing or ranking on these events.

NIP-85 Trusted Assertions — the Quartz model

Package: `quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip85TrustedAssertions/`. NIP-85 is still an evolving spec; **this package is the operative definition** of what Amethyst-family software writes and reads. This skill states the model (who signs what about whom), the exact kind/d-tag/tag vocabulary, and what consumers may — and may not — assume.

The model in one paragraph

An **assertion is signed by the asserting party** (a trust provider service, or the user themself) **about a subject named in the d-tag**. All assertion kinds are addressable, so "latest card by provider P about subject S" is just the addressable coordinate `(kind, P, S)` and supersession is standard NIP-01 latest-wins. Discovery is the observer's **kind 10040 list**: each entry says *"for metric M on kind K, I trust provider P — fetch their assertions at relay R"*. Quartz enforces none of this cryptographically beyond normal event signatures; the 10040→assertion link is **consumer-side convention** (see "Authorization" below).

Kind map

| Kind | Class | Kind class | d-tag = the subject | Content | |---|---|---|---|---| | 10040 | `list/TrustProviderListEvent` | replaceable | *(none — always `""`)* | NIP-44 private provider entries (optional) | | 30382 | `users/UserAssertionEvent` | addressable | **target user's pubkey** (hex) | NIP-44 private tags (petname/summary/emoji) | | 30383 | `events/EventAssertionEvent` | addressable | **target event id** (hex) | `""` | | 30384 | `addressables/AddressableAssertionEvent` | addressable | **target coordinate** `kind:pubkey:dtag` | `""` | | 30385 | `externalIds/ExternalIdAssertionEvent` | addressable | **external identifier** (e.g. `isbn:978-0-13-468599-1`) | `""` |

Addresses: `UserAssertionEvent.createAddress(owner, target)` → `Address(30382, owner, target)` (owner = signer, target = subject). `TrustProviderListEvent.createAddress(pubKey)` uses `FIXED_D_TAG = ""`. `AssertionEventTest.eventKindsAreCorrect` pins all five numbers.

`UserAssertionEvent` is also a `SearchableEvent` — it indexes only the **public** petname/summary tags plus topics; the encrypted card content is intentionally never indexed.

The 10040 provider entry (`ServiceProviderTag` / `ServiceType`)

There is **no fixed tag name**: `tag[0]` *is* the service string.

["30382:rank", "<provider pubkey, 64 hex>", "wss://nip85.brainstorm.world"]
  • `ServiceType(kind, type)` parses/renders `"<kind>:<type>"` — kind must be an int, the first

`:` splits, colons in the remainder stay in `type`. `ServiceType.isOfKind` is the allocation-free prefix check.

  • `ServiceProviderTag.parse` requires ≥3 elements, non-empty service, 64-char pubkey

(length-only check), and a **normalizable relay URL** (`RelayUrlNormalizer.normalizeOrNull`) — entries failing any check are silently dropped, which is what keeps foreign tags like `["client","nostria"]` out (regression-tested in `ServiceTypeParserTest`).

  • Entries may be **public** (tag array) or **private** (NIP-44 content); `create`/`add` take

`isPrivate`. `remove` always needs decryption and strips from both sides by parsed-value equality.

  • `object ProviderTypes` (`list/tags/ServiceType.kt`) enumerates the *known* service types —

`30382:rank`, `30382:followers`, `30382:first_created_at`, per-metric `30383:*`/`30384:*`/ `30385:*`, etc. It is an **open vocabulary**: real 10040s in the wild (see the fiatjaf → brainstorm fixture in `commonTest/.../experimental/nip85TrustedAssertions/ServiceParser.kt`) carry types Quartz doesn't enumerate (`30382:personalizedGrapeRank_influence`, `30382:hops`, `30382:verifiedFollowersCount`, …). Parse any `kind:type`; special-case only what you rank on.

Authorization — what a consumer may assume

  • **A 30382 (or 30383/…) is meaningful to an observer only if its author is listed in the

observer's 10040 for a matching service type.** Quartz does not enforce this; the consuming code does. The in-repo pattern is `commons/.../model/nip85TrustedAssertions/UserCardsCache.kt`: `rankFlow(trustProviderList)` picks the received card whose **author pubkey equals the provider entry's pubkey** and reads `rank()` from it. Assertions from unlisted signers are simply ignored for trust purposes (they may still be stored; dropping them — as an external store's orphan sweep does — is a legitimate storage policy, not a protocol rule).

  • What an entry authorizes is scoped by its `ServiceType`: `30382:rank` authorizes that

provider's user-rank cards, nothing else. Amethyst models this as one provider slot per metric (`liveUserRankProvider`, `liveUserFollowerCount`: the interface is `commons/.../model/nip85TrustedAssertions/TrustProviderListState.kt`, the account's implementation `commons/.../model/trustedAssertions/TrustProviderListState.kt`).

  • **Multi-provider combination is unprescribed.** When two listed providers assert different

ranks, there is no spec'd merge; Amethyst avoids the question by selecting one provider per metric slot. Consumers choose their own policy — document it.

  • The relay URL in the entry is a **fetch hint, and it is honored**:

`commons/.../relayClient/user/watchers/UserCardsSubAssembler.kt` subscribes for cards at the provider's declared relay via `filterUserAssertionsToTargetKeysFromTrustedAccountsInTheRelay` (`relayClient/assemblers/ContactCardFilters.kt`: user-assertion kinds, `authors=[providers]`, `#d=[targets]`).

The dual use of kind 30382

The same kind serves two roles, distinguished **by author**:

1. **Provider WoT cards** — signed by a trust provider; public metric tags (`rank`, `followers`, `hops`, …); this is what 10040 dis

Read more
@vitorpamplona
Ships withamethyst

Nostr client for Android

Stars on GitHub

Momentum

stars in total
1.6k
Star history of vitorpamplona/amethyst on GitHub, a chart by star-history.com

Other skills on amethyst.

amy-expert

amy-expert

amy CLI in cli/: adding an `amy <verb>` command, Main.kt dispatch, Output.emit/--json contract, exit codes 0/1/2/124, ~/.amy…

android-expert

android-expert

Android shim in `amethyst/`: MainActivity, AndroidManifest/intent filters, :napplet process, play/fdroid flavors, R8 +…

auth-signers

auth-signers

NostrSigner in Amethyst: NostrSignerInternal (local key), NostrSignerRemote (NIP-46 bunker), NostrSignerExternal (NIP-55 Android…

compose-expert

compose-expert

Shared Compose UI in `commonsUI/`: screens, components, the nav shell (`ScreenLayoutSpec`, `AppShellLayout`, Navigation 3 back…

desktop-expert

desktop-expert

Desktop JVM shim in desktopApp/: AmethystDesktop window, DesktopMenuBar, platform ports, OS keyring; :desktopApp:run, packaging.…

More skills from other plugins.

deep-code-review

deep-code-review

In-depth design-focused code review - understands codebase context before evaluating PR changes, posts structured feedback to…

expo4d53k
@expo@expoInstall Skill
modlens

modlens

Plug-in vision for text-only models. Hard rule: when a file path or URL with an image extension (.png, .jpg, .jpeg, .webp, .gif,…

modlens3w4.6k
@liustack@liustackInstall Skill
appllama-app-design-skill

appllama-app-design-sk…

Build native-feeling, benchmark-quality mobile app screens (Expo / React Native). Use when designing or implementing any mobile…

appllama2mo3.9k
@appllama@appllamaInstall Skill

Questions about nip85-trusted-assertions.

Which coding agents can use nip85-trusted-assertions?
nip85-trusted-assertions installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.