Skip to content
Development
Skill

/nip85-trusted-assertions

The NIP-85 trusted-assertions model in Quartz (`nip85TrustedAssertions/`) — kind 10040 trust-provider lists, kind 30382 contact cards / user assertions, 30383 event assertions, 30384 addressable assertions, 30385 external-id assertions. Use when building or parsing these events,

From plugin
amethyst
1.6k30 skills3 commands
Install
$ npx -y skills add vitorpamplona/amethyst --skill nip85-trusted-assertions --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/nip85-trusted-assertions

Context preview

The summary Claude sees to decide when to auto-load this skill.

The NIP-85 trusted-assertions model in Quartz (`nip85TrustedAssertions/`) — kind 10040 trust-provider lists, kind 30382 contact cards / user assertions, 30383 event assertions, 30384 addressable assertions, 30385 external-id assertions. Use when building or parsing these events,

SKILL.md

nip85-trusted-assertions.SKILL.md
name: nip85-trusted-assertions
description: The NIP-85 trusted-assertions model in Quartz (`nip85TrustedAssertions/`) — kind 10040 trust-provider lists, kind 30382 contact cards / user assertions, 30383 event assertions, 30384 addressable assertions, 30385 external-id assertions. Use when building or parsing these events, working with the typed tags (RankTag, HopsTag, FollowerCountTag, ServiceProviderTag/ServiceType, …), wiring a consumer that resolves a 10040 provider entry to the 30382s it signs, ranking on assertion values, or touching the GrapeRank publisher, contact-card nicknames, or the trust projection of an external store.

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/ContactCardEvent` | 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: `ContactCardEvent.createAddress(owner, target)` → `Address(30382, owner, target)` (owner = signer, target = subject). `TrustProviderListEvent.createAddress(pubKey)` uses `FIXED_D_TAG = ""`. `AssertionEventTest.eventKindsAreCorrect` pins all five numbers.

`ContactCardEvent` 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/.../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` in `amethyst/.../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**:

`amethyst/.../UserCardsSubAssembler.kt` subscribes for cards at the provider's declared relay (`kinds=[30382], authors=[provider], #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 pro

Read more
Ships withamethyst

Nostr client for Android

Get the whole plugin

Other skills on amethyst.