Infrastructure

Relay and service reputation

Use this when the subject is a relay, upload service, moderation service, bot, algorithm provider, or other service endpoint rather than a Nostr account.

Use this when the subject is a relay, upload service, moderation service, bot, algorithm provider, or other service endpoint rather than a Nostr account.

Fit

  • Status: supported today.
  • NIP-85 kind: 30385 identifier assertion.
  • Subject examples: relay:wss://relay.example.com, service:blossom:example.com, service:moderation:https://mod.example.com/api.
  • Canonical helpers: canonicalRelaySubject and canonicalServiceSubject.
  • Helpers: contributeIdentifierAssertion, aggregateIdentifierContributions.
  • Proof version: v2 recommended.
  • Useful metrics: rank, comment_cnt, reaction_cnt.

The k value is an application profile namespace. The example below uses 10002 because the profile is scoring relay-selection context; choose a different decimal namespace if your application needs one.

Subject design

  • Use kind 30385 when the subject is an endpoint, service, bot, API, or relay rather than a user pubkey.
  • Canonicalise the identifier before review: scheme, host, port, path, trailing slash, service class, and any punycode or case rules.
  • Use separate subjects for different service classes. A relay uptime score should not automatically apply to an upload service on the same domain.
  • Pick a stable k namespace for the application profile and document it so verifiers know what kind of identifier is being scored.

What to publish

  • A kind 30385 assertion created with aggregateIdentifierContributions.
  • The exact service identifier in d, the profile namespace in k, and metric tags for the service behaviour being scored.
  • Monitoring or audit references outside the assertion when clients need evidence of uptime, censorship behaviour, malware incidents, or support quality.
  • An expiry policy, because service behaviour can change faster than social reputation.

Implementation recipe

  1. Canonicalise the service identifier before signing with canonicalRelaySubject or canonicalServiceSubject: scheme, host, port, trailing slash, and service class.
  2. Define what rank measures for this service class: reliability, censorship behaviour, latency, privacy, malware risk, support quality, or abuse handling.
  3. Use a stable k namespace per application profile and proof v2 for new identifier assertions.
  4. Verify the expected identifier, namespace, circle, threshold, and freshness before using the score for relay or service selection.
  5. Combine nostr-veil with monitoring and incident reports when clients need current operational data.

Worked example

examples/use-cases/relay-service-reputation.ts
import {
  NIP85_KINDS,
  aggregateIdentifierContributions,
  contributeIdentifierAssertion,
  createTrustCircle,
} from 'nostr-veil'
import {
  defaultMembers,
  memberIndex,
  proofVersion,
  verifyUseCaseAssertion,
  withCreatedAt,
} from './_shared.js'

const slug = 'relay-service-reputation'
const relayProfileKind = '10002'
const relayId = 'relay:wss://relay.example.com'
const circle = createTrustCircle(defaultMembers.map(member => member.pub))

const contributions = defaultMembers.map((reviewer, index) =>
  contributeIdentifierAssertion(
    circle,
    relayId,
    relayProfileKind,
    {
      rank: 74 + index * 3,
      reaction_cnt: index + 1,
    },
    reviewer.priv,
    memberIndex(circle, reviewer.pub),
    { proofVersion },
  ),
)

export const assertion = withCreatedAt(aggregateIdentifierContributions(
  circle,
  relayId,
  relayProfileKind,
  contributions,
  { proofVersion },
))

export const result = verifyUseCaseAssertion(slug, assertion, {
  kind: NIP85_KINDS.IDENTIFIER,
  subject: relayId,
  subjectTag: 'k',
  subjectTagValue: relayProfileKind,
  circleId: circle.circleId,
  minDistinctSigners: 3,
  freshAfter: assertion.created_at - 300,
})

What to verify

  • Strict syntax and a valid proof v2.
  • Kind 30385, with d equal to the canonical service id and k equal to the profile namespace the client understands.
  • The service-review ring is trusted for this service class and has enough distinct signers.
  • The score is recent enough for operational use, or paired with live probes for uptime-sensitive decisions.
  • The metric meaning matches the decision: reliability, censorship behaviour, privacy, malware risk, or another declared axis.

What this proves

  • Distinct members of the service-review circle rated the same identifier.
  • The aggregate matches the signed service metrics.
  • Proof v2 binds the assertion to kind 30385 and the chosen k namespace.

What not to claim

  • Do not claim the proof is live uptime monitoring. It is a threshold review signal.
  • Do not claim a high score proves the service is honest or safe. Use probes, audits, canaries, and client-side safety checks.
  • Do not claim one circle's service registry is universal. Clients decide which circles they trust.

Failure handling

  • Reject ambiguous or non-canonical identifiers and require reviewers to sign the corrected subject.
  • Expire stale service scores and prefer live telemetry when the action depends on current uptime.
  • Publish incident or downgrade assertions when a service changes ownership, censors unexpectedly, leaks data, or ships malicious behaviour.
  • Show separate scores for separate service classes instead of merging them by domain.

Operational requirements

Risk to handleRequired control
The proof does not continuously monitor relay uptime.Pair the score with uptime probes, latency checks, incident reports, and expiry.
The proof does not prove a service is honest or non-malicious.Add audits, canary tests, operational transparency, and client-side safety checks.
One circle's service registry is not universal.Publish the circle profile and let clients choose which circles or federations they trust.
Identifier ambiguity can split or merge scores incorrectly.Document canonicalisation and namespace rules before collecting contributions.

Policy choices

  • How is the service identifier canonicalised?
  • Does the score cover uptime, censorship behaviour, performance, malware risk, privacy, or support quality?
  • How quickly should stale service reviews expire?
  • Should different service classes use separate k namespaces?

NIP-85 kind reference

NIP-85 defines the assertion kind by the subject being scored. The kind number is part of the proof v2 context, so deployments should verify both the number and the subject hint tag.

30382 User assertion

Nostr pubkey subjects. Subject hint: p.

30383 Event assertion

Nostr event id subjects. Subject hint: e.

30384 Addressable event assertion

NIP-33 address subjects. Subject hint: a.

30385 NIP-73/external identifier assertion

packages, relays, domains, vendors, and other identifiers. Subject hint: k.

10040 Trusted service provider declaration

provider metadata, not a score assertion. Subject hint: provider tags.

Spec: NIP-85 trusted assertions.

Live relay test

The opt-in relay test signs this canonical example as real Nostr event data, publishes it to wss://relay.trotters.cc, fetches it back by id, and re-runs the application, syntax, Nostr signature, canonical tag, and proof checks.

Passed 2026-05-17T21:54:12.000Z
Events
1/1 fetched from relay
Proof
3/3 threshold from a 3-member ring
Run
tf7bic-3b3a5ae6d6
  • Canonical example passes locally
  • Relay stored and returned every signed event
  • Fetched Nostr event signatures are valid
  • Fetched tags match the canonical example
  • NIP-85 syntax validation passes
  • nostr-veil proof verification passes
  • Deployment profile verifier passes
98a1596347e7...b4d9e75b

Run the same check with npm run test:use-cases:relay -- --write docs/use-case-relay-checks.json.

Safety checks

Each canonical use-case example is also exercised by an adversarial test harness. These are the failure modes a production verifier should reject before acting on the score.

Tampered metric

Published scores must still match the signed contribution aggregate.

Wrong subject

The d tag and subject hint must stay bound to the signed v2 proof.

Wrong kind

The assertion kind must match the profile and the signed v2 context.

Proof downgrade

New deployment profiles require proof v2.

Duplicate signer

Repeated key images must not increase the signer count.

Insufficient threshold

Removing a signature must fail the profile threshold.

Stale assertion

created_at must remain inside the freshness window.

Unknown circle

The circle ID must be accepted by deployment policy.

Unsigned policy

verifyProductionDeployment() should require a signed deployment bundle from a trusted publisher.

Relay mutation

Fetched event content and tags must match the Nostr signature.

Use validateUseCaseProfileDefinition() for custom profiles, then verifyProductionDeployment() with trusted bundle publishers, signed relay events, accepted circle manifests, expected subject, freshness, and threshold policy so these checks are not left to application glue. For application UI and audit logs, use verifyProductionDeploymentReport() or createProductionDecisionReport() so failures include issue codes, remediation text, a recommended action, pass/fail/not-checked status for the controls, and the profile's proofClaims, proofLimitations, requiredControls, and recommendedActions.

Next examples