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
Stats
1,600
Stars
221
Forks
Active
Maintenance
Kotlin
Language
MIT
License
3h ago
Last commit
3y ago
Created

Repo: vitorpamplona/amethyst

Other skills on amethyst.