Use this when a community, relay, marketplace, grant programme, or credential consumer needs to decide which verifiers, issuers, or witness services are acceptable for a credential class. This answers "who verifies the verifier?" without forcing the people reviewing the verifier to become public targets.
Fit
- Status: supported today.
- NIP-85 kind: 30385 identifier assertion.
- Subject: a method-scoped verifier identifier such as
verifier:proof-of-person:in-person:<verifier-pubkey>. - Canonical helper:
canonicalVerifierSubject. - Helpers:
contributeIdentifierAssertionandaggregateIdentifierContributions. - Proof version: v2 recommended.
- Useful metrics:
rankas verifier confidence, plusreaction_cntas a count-like corroboration signal.
Subject design
- Use
verifier:<credential-class>:<method>:<verifier-id>when the community is deciding whether to trust a verifier for one credential class and method. - Use separate subjects for different methods. A verifier trusted for
proof-of-person:in-personis not automatically trusted forproof-of-residency:document-check. - Use service subjects for verifier services only when the method and class are already captured elsewhere in the deployment policy.
- Do not use a generic domain or pubkey score when the decision depends on a specific verification ceremony, credential class, or issuer role.
What to publish
- A kind 30385 identifier assertion whose
dtag is the canonical verifier subject and whosektag is the deployment namespace. - A
rankprofile explaining whether the score means verifier legitimacy, issuer confidence, witness quality, method reliability, or incident status. - Proof v2 tags, accepted reviewer-circle policy, threshold, freshness, and the credential class being considered.
- Companion records for verifier onboarding, audit results, revocation source, issuer metadata, accepted methods, and incident response.
Implementation recipe
- Define the credential class and verification method before reviewers sign.
- Canonicalise the verifier with
canonicalVerifierSubject. - Have an accepted reviewer circle publish proof v2 identifier contributions.
- Verify the exact subject,
knamespace, circle, threshold, freshness, and metric bounds. - Accept attestations from that verifier only when the concrete attestation artefact also passes holder binding, presentation, expiry, and revocation checks.
Worked example
import {
NIP85_KINDS,
aggregateIdentifierContributions,
canonicalVerifierSubject,
contributeIdentifierAssertion,
createTrustCircle,
} from 'nostr-veil'
import {
defaultMembers,
externalProfileKind,
memberIndex,
proofVersion,
verifyUseCaseAssertion,
withCreatedAt,
} from './_shared.js'
const slug = 'verifier-issuer-legitimacy'
const verifierSubject = canonicalVerifierSubject(
'proof-of-person',
'in-person',
'aa'.repeat(32),
)
const circle = createTrustCircle(defaultMembers.map(member => member.pub))
const contributions = defaultMembers.map((reviewer, index) =>
contributeIdentifierAssertion(
circle,
verifierSubject,
externalProfileKind,
{
rank: 82 + index * 3,
reaction_cnt: index + 2,
},
reviewer.priv,
memberIndex(circle, reviewer.pub),
{ proofVersion },
),
)
export const assertion = withCreatedAt(aggregateIdentifierContributions(
circle,
verifierSubject,
externalProfileKind,
contributions,
{ proofVersion },
))
export const result = verifyUseCaseAssertion(slug, assertion, {
kind: NIP85_KINDS.IDENTIFIER,
subject: verifierSubject,
subjectTag: 'k',
subjectTagValue: externalProfileKind,
circleId: circle.circleId,
minDistinctSigners: 3,
freshAfter: assertion.created_at - 300,
})
What to verify
- Strict NIP-85 syntax and a valid proof v2.
- Kind 30385, with
dequal to the canonical verifier subject andkequal to the deployment namespace. - The reviewer circle is accepted for that credential class and method.
proof.distinctSignersmeets the verifier-legitimacy threshold.- The
rankmeaning is documented before it affects verifier admission, warning, rate-limiting, or revocation. - The concrete credential or attestation being consumed still passes its own issuer, holder, presentation, expiry, revocation, and evidence checks.
What this proves
- Distinct members of an accepted reviewer circle scored the same verifier subject.
- The aggregate score is bound to the verifier subject, kind, and subject hint.
- Individual reviewer identities are hidden inside the ring.
What not to claim
- Do not claim a trusted verifier makes every credential true.
- Do not claim nostr-veil verifies the underlying personhood, residency, professional status, or credential claim.
- Do not claim verifier legitimacy replaces revocation, audit, appeal, or incident response.
- Do not let trust for one credential class or method spill into another class without a separate assertion or explicit policy.
Failure handling
- Reject verifier scores from unknown circles, stale manifests, wrong methods, wrong credential classes, or unaccepted namespaces.
- Treat missing holder-binding, presentation, expiry, or revocation checks on the concrete credential as a verifier failure.
- Publish downgrade, incident, or replacement assertions when a verifier is compromised, captured, negligent, or no longer accepted for a method.
- Route borderline scores to manual review rather than making verifier acceptance permanent.
Operational requirements
| Risk to handle | Required control |
|---|---|
| Verifier reputation becomes a single unchecked trust root. | Scope trust by credential class, method, issuer key, accepted circle, expiry, revocation, appeal path, and incident response. |
| A trusted verifier can still issue a bad credential. | Verify each concrete credential artefact, holder binding, presentation challenge, expiry, revocation, and evidence trail. |
| Reviewer pressure can capture verifier admission. | Use anonymous threshold review, conflict rules, rotation, audit logs, and independent circles for high-stakes methods. |
| First-person credentials and verifier-issued credentials start from different trust directions. | Treat first-person claims, social witnessing, verifier ceremonies, and anonymous circle endorsement as separate layers that policy combines explicitly. |
Open questions for deployments
- Does
rankmean verifier admission, ongoing reliability, incident severity, or method confidence? - Which credential classes need method-scoped verifier subjects?
- What revocation source tells clients that a verifier is no longer accepted?
- Which Attestr-style, NIP-VA-style, NIP-58, or custom attestation artefacts can this verifier issue or witness?
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.
Nostr pubkey subjects. Subject hint: p.
Nostr event id subjects. Subject hint: e.
NIP-33 address subjects. Subject hint: a.
packages, relays, domains, vendors, and other identifiers. Subject hint: k.
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.
- Events
- 1/1 fetched from relay
- Proof
- 3/3 threshold from a 3-member ring
- Run
th2og0-c6f633c86f
- 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
aba777849094...35841a4d
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.
Published scores must still match the signed contribution aggregate.
The d tag and subject hint must stay bound to the signed v2 proof.
The assertion kind must match the profile and the signed v2 context.
New deployment profiles require proof v2.
Repeated key images must not increase the signer count.
Removing a signature must fail the profile threshold.
created_at must remain inside the freshness window.
The circle ID must be accepted by deployment policy.
verifyProductionDeployment() should require a signed deployment bundle from a trusted publisher.
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.