{"id":"5bd5351198308fc6ab131ddb0e039c5ceeff818857217b291ca5336d8629615d","pubkey":"da19f1cd34beca44be74da4b306d9d1dd86b6343cef94ce22c49c6f59816e5bd","created_at":1774659038,"kind":30817,"tags":[["d","nip-signet"],["title","NIP-SIGNET: Progressive Identity Verification"]],"content":"NIP-SIGNET\n==========\n\nProgressive Identity Verification\n-----------------------------------\n\n`draft` `optional`\n\nAuthors: [forgesworn](https://github.com/forgesworn)\n\nTag conventions on kind 31000 ([NIP-VA](https://github.com/nostr-protocol/nips/blob/master/va.md)) for progressive identity verification, plus a community policy format on kind 30078 ([NIP-78](https://github.com/nostr-protocol/nips/blob/master/78.md)). No new event kinds.\n\n> **Design principle:** Identity verification should be progressive (start low, build over time), privacy-preserving (no PII in events), and decentralised (no single authority). Nostr's censorship resistance is meaningless if every feed is 40% spam bots.\n> **Standalone.** This NIP works independently on any Nostr application. It builds on NIP-VA's generic attestation format and NIP-78's app-specific data storage. The reference implementation is [`signet-protocol`](https://github.com/forgesworn/signet) (TypeScript).\n\n## Motivation\n\nNostr has no identity layer. Anyone can create unlimited keypairs and claim to be anyone. The result:\n\n- **Spam and impersonation** dominate feeds. Onboarding feels like walking into a room where everyone is shouting and nobody knows who anyone else is.\n- **Web-of-trust is siloed per client.** Your trust graph on one client does not carry to another. Every client rebuilds trust from scratch.\n- **No middle ground between NIP-05 and nothing.** NIP-05 proves you own a domain, not that you are a real person. There is no standard for \"a professional checked my ID\" or \"three people I know vouched for me.\"\n- **Regulatory pressure is real.** The UK Online Safety Act, US COPPA 2.0, EU eIDAS 2.0, and Australia's under-16 ban all mandate some form of age or identity verification. Centralised solutions (Worldcoin iris scans, government ID uploads) create surveillance infrastructure. A privacy-preserving decentralised alternative is needed.\n\nThis NIP defines a four-tier progressive verification model. Tier 1 costs nothing. Tier 2 requires social connections. Tiers 3-4 require an in-person meeting with a licensed professional. At every level, the credential is a standard Nostr event that any client can parse.\n\n## Relationship to Existing NIPs\n\n### NIP-VA (Kind 31000 -- Verifiable Attestations)\n\nNIP-SIGNET is a set of tag conventions on NIP-VA's generic attestation kind. NIP-VA defines the event structure (`kind: 31000`, `d`, `p`, `type` tags); NIP-SIGNET defines specific `type` values and the additional tags each type requires. A NIP-VA-aware client that does not implement NIP-SIGNET can still parse and display attestations -- it simply will not understand the tier semantics.\n\n### NIP-78 (Kind 30078 -- App-Specific Data)\n\nCommunity verification policies are stored as NIP-78 events with a `signet:policy:*` d-tag prefix. Any client can read these policies; enforcement is opt-in.\n\n### NIP-02 (Contact Lists)\n\nNIP-02 follow lists are binary (follow/unfollow). NIP-SIGNET vouches are structured attestations with method, tier, and context -- they express \"I met this person and I vouch for them\" rather than \"I follow this account.\"\n\n### NIP-05 (DNS Verification)\n\nNIP-05 proves domain ownership. NIP-SIGNET proves identity claims at varying confidence levels. They are complementary: a Tier 3 verified account may also have a NIP-05 identifier.\n\n### Community Attestations (Kind 31871)\n\nThe community Attestations proposal (kind 31871) defines truthfulness claims about Nostr events -- \"this event is accurate\", \"this claim is false.\" NIP-VA (kind 31000) consolidates this functionality into a single generic attestation kind with two modes: direct attestations (about pubkeys) and event attestations (about events). NIP-SIGNET builds on NIP-VA's direct attestation mode for identity verification specifically.\n\n### NIP-91 (Service Attestations)\n\nNIP-91 defines attestations of service completion between identities. It was redirected to NIP-32 labelling during review. NIP-SIGNET credentials are not service completion records -- they are identity verification attestations with tiers, professional accountability, and community policies. The concerns do not overlap.\n\n### TSM (Trust Service Machines)\n\nTSM (kinds 37570-37572) defines infrastructure for trust computation services -- service announcements, output standards, and service requests. NIP-SIGNET provides the raw trust signals (credentials, vouches) that a TSM service could consume to compute aggregate scores. TSM computes; NIP-SIGNET records.\n\n### NIP-VEIL (Anonymous Trust Assertions) -- OPTIONAL Composition\n\nNIP-VEIL provides ring-signature-backed anonymous endorsements on NIP-85 events. NIP-SIGNET's professional credentials MAY optionally use ring signatures for issuer anonymity (proving \"one of N professionals signed this\" without revealing which one). The ring signature format is defined by NIP-VEIL; NIP-SIGNET MAY reference it. Composition with NIP-VEIL is OPTIONAL.\n\n### NIP-32 (Labelling)\n\nNIP-32 defines kind 1985 labels -- lightweight, ephemeral annotations on events or pubkeys. Labels are fire-and-forget: no `d` tag, no addressability, no expiration, no replacement semantics. NIP-91 (agent reputation) was redirected to NIP-32 on this basis.\n\nNIP-SIGNET credentials are fundamentally different:\n\n- **Addressable** (kind 31000 with `d` tag) -- a credential can be queried by subject, replaced by the issuer, and superseded by a newer credential. Labels cannot.\n- **Expirable** -- credentials carry `expiration` tags and clients MUST check them. Labels have no lifecycle.\n- **Structured** -- tiers, verification types, methods, professional metadata. Labels carry a namespace and a label string.\n- **Tiered trust** -- a Tier 3 credential from a registered professional carries different weight than a Tier 1 self-declaration. Labels have no weight hierarchy.\n- **Policy-gated** -- communities can require minimum tiers for participation. There is no equivalent mechanism for labels.\n\nNIP-32 labels are the right tool for \"this content is NSFW\" or \"this post is about #bitcoin.\" NIP-SIGNET credentials are the right tool for \"a solicitor in London verified this person's identity.\"\n\n### NIP-58 (Badges)\n\nNIP-58 defines display-oriented badges awarded by communities (kind 30009 badge definition, kind 8 badge award, kind 30008 profile badges). Badges are achievements: \"attended conference X\", \"top contributor\", \"founding member.\"\n\nNIP-SIGNET credentials are identity attestations, not achievements:\n\n- **Progressive tiers** with clear trust semantics (self-declared through professionally verified). Badges have no tier hierarchy.\n- **Professional accountability** -- Tier 3-4 credentials are issued by licensed professionals whose livelihoods depend on honest attestation. Badge issuers have no such constraint.\n- **Community policies** -- communities can require minimum verification tiers. There is no equivalent policy mechanism for badges.\n- **Verifier network** -- registered verifiers with cross-profession validation. Badges have no issuer qualification system.\n\nNIP-58 and NIP-SIGNET are complementary. A community could award badges AND require verification tiers. They serve different purposes: badges celebrate participation, credentials verify identity.\n\n## Kinds\n\nNo new event kinds are introduced.\n\n| Kind | Source | Usage |\n|------|--------|-------|\n| 31000 | NIP-VA | Credentials, vouches, verifier registrations, challenges |\n| 30078 | NIP-78 | Community verification policies |\n\n## Attestation Patterns\n\nEach attestation type uses the NIP-VA pattern that matches its speech act. Credentials respond to the subject's identity claim (assertion-first). Vouches report the voucher's own experience (direct). Verifier registrations are self-declarations.\n\n| Attestation | Who speaks? | About what? | NIP-VA pattern | d-tag |\n|---|---|---|---|---|\n| Tier 1 self-declaration | Subject about self | \"I exist\" | Self-attestation | `credential:<own-pubkey>` |\n| Vouch | Voucher about their experience | \"I met this person\" | Direct claim | `vouch:<subject-pubkey>` |\n| Tier 2 aggregate credential | Aggregator about a threshold | \"3+ vouches reached\" | Assertion-first | `assertion:<tier-1-event-id>` |\n| Tier 3 credential | Verifier about subject's identity | \"I verified their claim\" | Assertion-first | `assertion:<tier-1-event-id>` |\n| Tier 4 credential | Verifier about subject's identity + child | \"I verified their claim\" | Assertion-first | `assertion:<tier-1-event-id>` |\n| Verifier registration | Verifier about self | \"I am a solicitor\" | Self-attestation | `verifier:<own-pubkey>` |\n| Challenge | Challenger about verifier behaviour | \"This verifier is suspicious\" | Direct claim | `challenge:<verifier-pubkey>` |\n\n**Assertion-first credentials** reference the subject's Tier 1 self-declaration via `e` tag with the `\"assertion\"` marker. The `type: credential` tag is included for relay-side filtering and graceful degradation (hybrid pattern per NIP-VA). If the referenced self-declaration cannot be fetched, Signet-aware clients can still render the credential from its tags.\n\n**Direct claims** (vouches, challenges) originate from the attestor. The voucher reports their own experience of meeting someone. The challenger reports observed behaviour. Neither is a response to a prior assertion from the subject.\n\n## Verification Tiers\n\n| Tier | Name | What it proves | How |\n|------|------|---------------|-----|\n| 1 | Self-declared | \"I claim I am an adult\" | Self-signed credential |\n| 2 | Peer-vouched | \"N people who met me confirm I exist\" | 3+ vouches from Tier 2+ accounts |\n| 3 | Professionally verified | \"A licensed professional checked my ID\" | In-person verification by solicitor, attorney, notary, doctor, pharmacist, chartered accountant, etc. |\n| 4 | Professionally verified (child) | \"A professional confirmed adult + child\" | Tier 3 + child evidence (birth certificate, school record) |\n\nTiers are progressive: each tier subsumes the trust of lower tiers. A Tier 3 account implicitly satisfies Tier 2 and Tier 1 requirements.\n\n---\n\n## Attestation Type: `credential`\n\nA verification credential attests that a subject has been verified at a specific tier.\n\n**Tier 1 (self-declaration)** uses the direct claim pattern -- the subject signs their own credential (`pubkey` equals the `p` tag value):\n\n```json\n{\n  \"kind\": 31000,\n  \"pubkey\": \"<subject-pubkey>\",\n  \"created_at\": 1711540800,\n  \"tags\": [\n    [\"d\", \"credential:<subject-pubkey>\"],\n    [\"p\", \"<subject-pubkey>\"],\n    [\"type\", \"credential\"],\n    [\"tier\", \"1\"],\n    [\"verification-type\", \"self\"],\n    [\"scope\", \"adult\"],\n    [\"method\", \"self-declaration\"],\n    [\"L\", \"signet\"],\n    [\"alt\", \"Signet Tier 1 self-declaration\"]\n  ],\n  \"content\": \"\",\n  \"id\": \"<tier-1-event-id>\",\n  \"sig\": \"<64-byte-hex>\"\n}\n```\n\n**Tier 3 (professional verification)** uses the assertion-first hybrid pattern -- the verifier references the subject's Tier 1 self-declaration and includes `type: credential` for filtering and resilience:\n\n```json\n{\n  \"kind\": 31000,\n  \"pubkey\": \"<verifier-pubkey>\",\n  \"created_at\": 1711540800,\n  \"tags\": [\n    [\"d\", \"assertion:<tier-1-event-id>\"],\n    [\"e\", \"<tier-1-event-id>\", \"wss://relay.example.com\", \"assertion\"],\n    [\"type\", \"credential\"],\n    [\"p\", \"<subject-pubkey>\"],\n    [\"tier\", \"3\"],\n    [\"verification-type\", \"professional\"],\n    [\"scope\", \"adult\"],\n    [\"method\", \"in-person-id\"],\n    [\"profession\", \"solicitor\"],\n    [\"jurisdiction\", \"GB\"],\n    [\"expiration\", \"1743076800\"],\n    [\"L\", \"signet\"],\n    [\"alt\", \"Signet Tier 3 credential for <subject-pubkey>\"]\n  ],\n  \"content\": \"\",\n  \"id\": \"<32-byte-hex>\",\n  \"sig\": \"<64-byte-hex>\"\n}\n```\n\nThe `e` tag with the `\"assertion\"` marker references the subject's Tier 1 self-declaration event. The `type: credential` tag is a hybrid override for relay-side filtering -- clients that cannot fetch the referenced assertion can still parse the credential from its tags.\n\n### Tags\n\n| Tag | Status | Description |\n|-----|--------|-------------|\n| `d` | REQUIRED | Tier 1: `credential:<subject-hex-pubkey>`. Tier 2-4: `assertion:<tier-1-event-id>` |\n| `e` | REQUIRED (Tier 2-4) | `<tier-1-event-id>`, `<relay-hint>`, `\"assertion\"` -- references the subject's Tier 1 self-declaration |\n| `p` | REQUIRED | Subject's hex pubkey |\n| `type` | REQUIRED | `credential` |\n| `L` | RECOMMENDED | `signet` -- namespace label for filtering |\n| `tier` | REQUIRED | `1`, `2`, `3`, or `4` |\n| `verification-type` | REQUIRED | `self`, `peer`, or `professional` |\n| `scope` | REQUIRED | `adult` or `adult+child` |\n| `method` | REQUIRED | `self-declaration`, `in-person`, `online`, or `in-person-id` |\n| `profession` | RECOMMENDED (Tier 3-4) | Verifier's profession (e.g. `solicitor`, `attorney`, `notary`, `doctor`, `pharmacist`, `chartered_accountant`) |\n| `jurisdiction` | RECOMMENDED (Tier 3-4) | ISO 3166-1 alpha-2 country code (e.g. `GB`, `US`, `IE`) |\n| `age-range` | OPTIONAL | Age range string (e.g. `18+`, `8-12`). Tier 4 SHOULD include this. |\n| `expiration` | RECOMMENDED | Unix timestamp. Credentials SHOULD expire. |\n| `nullifier` | OPTIONAL | SHA-256 hash of document fields. Prevents duplicate identity without revealing documents. |\n| `merkle-root` | OPTIONAL | Merkle tree root of verified attributes for selective disclosure. |\n| `entity-type` | OPTIONAL | `natural_person`, `persona`, or `organization` |\n| `guardian` | OPTIONAL | Hex pubkey of guardian (Tier 4, may appear multiple times) |\n| `supersedes` | OPTIONAL | Event ID of a previous credential this one replaces |\n| `alt` | RECOMMENDED | Human-readable summary for non-aware clients |\n\n### Tier-Specific Requirements\n\n**Tier 1 (self-declared):** `verification-type` MUST be `self`. `method` MUST be `self-declaration`. The `pubkey` (author) and `p` tag (subject) MUST be the same keypair -- the subject signs their own credential. Uses the direct claim pattern: `d` tag is `credential:<subject-hex-pubkey>`. No assertion reference.\n\n**Tier 2 (peer-vouched):** `verification-type` MUST be `peer`. The credential is published after the subject accumulates sufficient vouches (see Vouch Threshold). A client or aggregation service publishes the Tier 2 credential referencing the subject's Tier 1 self-declaration via `e` tag with `\"assertion\"` marker. `d` tag is `assertion:<tier-1-event-id>`.\n\n**Tier 3 (professional):** `verification-type` MUST be `professional`. `method` MUST be `in-person-id`. `profession` and `jurisdiction` SHOULD be present. The verifier MUST be a registered professional (see Attestation Type: `verifier`). MUST reference the subject's Tier 1 self-declaration via `e` tag with `\"assertion\"` marker. `d` tag is `assertion:<tier-1-event-id>`.\n\n**Tier 4 (professional + child):** All Tier 3 requirements, plus `scope` MUST be `adult+child` and `age-range` SHOULD be present.\n\n---\n\n## Attestation Type: `vouch`\n\nA vouch is a peer attestation: \"I have met this person and I attest they are who they claim to be.\"\n\n```json\n{\n  \"kind\": 31000,\n  \"pubkey\": \"<voucher-pubkey>\",\n  \"created_at\": 1711540800,\n  \"tags\": [\n    [\"d\", \"vouch:<subject-pubkey>\"],\n    [\"p\", \"<subject-pubkey>\"],\n    [\"type\", \"vouch\"],\n    [\"method\", \"in-person\"],\n    [\"voucher-tier\", \"2\"],\n    [\"voucher-score\", \"85\"],\n    [\"L\", \"signet\"],\n    [\"alt\", \"Signet vouch for <subject-pubkey>\"]\n  ],\n  \"content\": \"Met at London Bitcoin meetup, March 2026\",\n  \"id\": \"<32-byte-hex>\",\n  \"sig\": \"<64-byte-hex>\"\n}\n```\n\n### Tags\n\n| Tag | Status | Description |\n|-----|--------|-------------|\n| `d` | REQUIRED | `vouch:<subject-hex-pubkey>` |\n| `p` | REQUIRED | Subject's hex pubkey |\n| `type` | REQUIRED | `vouch` |\n| `method` | REQUIRED | `in-person` or `online` |\n| `voucher-tier` | RECOMMENDED | Voucher's own current tier (`1`-`4`) |\n| `voucher-score` | OPTIONAL | Voucher's own trust score (0-200, implementation-defined) |\n| `context` | OPTIONAL | Context of the vouch (e.g. `conference`, `workplace`, `family`) |\n| `expiration` | OPTIONAL | Unix timestamp |\n| `alt` | RECOMMENDED | Human-readable summary |\n\n### `content`\n\nFree-text comment about the vouch (e.g. \"Met at conference\"). MAY be empty.\n\n### Vouch Threshold\n\nA subject reaches Tier 2 when they accumulate **3 or more** qualifying vouches from accounts that are themselves Tier 2 or higher. In-person vouches carry more weight than online vouches in trust score computation, but both count towards the threshold. The threshold of 3 and minimum voucher tier of 2 are defaults; community policies (see below) MAY override these values.\n\n---\n\n## Attestation Type: `verifier`\n\nA verifier registration declares that a professional is available to perform identity verification.\n\n```json\n{\n  \"kind\": 31000,\n  \"pubkey\": \"<verifier-pubkey>\",\n  \"created_at\": 1711540800,\n  \"tags\": [\n    [\"d\", \"verifier:<verifier-pubkey>\"],\n    [\"type\", \"verifier\"],\n    [\"profession\", \"solicitor\"],\n    [\"jurisdiction\", \"GB\"],\n    [\"licence\", \"a1b2c3d4e5f6...\"],\n    [\"body\", \"Law Society\"],\n    [\"L\", \"signet\"],\n    [\"alt\", \"Signet verifier: solicitor in GB\"]\n  ],\n  \"content\": \"Available for identity verification appointments in London.\",\n  \"id\": \"<32-byte-hex>\",\n  \"sig\": \"<64-byte-hex>\"\n}\n```\n\n### Tags\n\n| Tag | Status | Description |\n|-----|--------|-------------|\n| `d` | REQUIRED | `verifier:<verifier-hex-pubkey>` |\n| `type` | REQUIRED | `verifier` |\n| `profession` | REQUIRED | Professional title (e.g. `solicitor`, `notary`, `doctor`, `attorney`, `pharmacist`, `chartered_accountant`) |\n| `jurisdiction` | REQUIRED | ISO 3166-1 alpha-2 country code |\n| `licence` | REQUIRED | Hash of licence/registration number (not the raw number) |\n| `body` | REQUIRED | Professional body name (e.g. `Law Society`, `GMC`, `Notary Commission`, `American Bar Association`, `Ordre des Medecins`, `CPA Australia`) |\n| `alt` | RECOMMENDED | Human-readable summary |\n\n### `content`\n\nFree-text statement (availability, location, contact method). MAY be empty.\n\n### Cross-Verification\n\nA verifier SHOULD accumulate 2 or more vouches from other verifiers of **different professions** before their credentials carry full weight. This prevents same-profession collusion. Cross-verification vouches use the standard `vouch` type with `context: cross-verification`.\n\n---\n\n## Attestation Type: `challenge`\n\nA challenge flags a verifier for suspicious behaviour.\n\n```json\n{\n  \"kind\": 31000,\n  \"pubkey\": \"<challenger-pubkey>\",\n  \"created_at\": 1711540800,\n  \"tags\": [\n    [\"d\", \"challenge:<verifier-pubkey>\"],\n    [\"p\", \"<verifier-pubkey>\"],\n    [\"type\", \"challenge\"],\n    [\"reason\", \"anomalous-volume\"],\n    [\"L\", \"signet\"],\n    [\"alt\", \"Signet challenge against <verifier-pubkey>\"]\n  ],\n  \"content\": \"Issued 50 credentials in one hour from a single IP.\",\n  \"id\": \"<32-byte-hex>\",\n  \"sig\": \"<64-byte-hex>\"\n}\n```\n\n### Tags\n\n| Tag | Status | Description |\n|-----|--------|-------------|\n| `d` | REQUIRED | `challenge:<verifier-hex-pubkey>` |\n| `p` | REQUIRED | Challenged verifier's hex pubkey |\n| `type` | REQUIRED | `challenge` |\n| `reason` | REQUIRED | One of: `anomalous-volume`, `registry-mismatch`, `fraudulent-attestation`, `licence-revoked`, `other` |\n| `alt` | RECOMMENDED | Human-readable summary |\n\n### `content`\n\nEvidence supporting the challenge. MAY be empty, but SHOULD contain specific details.\n\n### Challenge Resolution\n\nIf **5 or more** Tier 3+ accounts publish challenges against the same verifier, clients SHOULD consider the verifier's credentials suspect. The threshold of 5 is a default; implementations MAY adjust. Challenge resolution is a social process, not an automated one -- the numbers guide human judgement, not replace it.\n\n---\n\n## Community Policies (Kind 30078)\n\nA community, relay, or group operator publishes a verification policy declaring minimum requirements for participation.\n\n```json\n{\n  \"kind\": 30078,\n  \"pubkey\": \"<operator-pubkey>\",\n  \"created_at\": 1711540800,\n  \"tags\": [\n    [\"d\", \"signet:policy:my-community\"],\n    [\"adult-min-tier\", \"2\"],\n    [\"child-min-tier\", \"4\"],\n    [\"min-score\", \"50\"],\n    [\"enforcement\", \"client\"],\n    [\"alt\", \"Signet verification policy for my-community\"]\n  ],\n  \"content\": \"Family-friendly community. Adults need Tier 2, children need Tier 4.\",\n  \"id\": \"<32-byte-hex>\",\n  \"sig\": \"<64-byte-hex>\"\n}\n```\n\n### Tags\n\n| Tag | Status | Description |\n|-----|--------|-------------|\n| `d` | REQUIRED | `signet:policy:<community-id>` |\n| `adult-min-tier` | OPTIONAL | Minimum tier for adult participants (`1`-`4`). Default: `1`. |\n| `child-min-tier` | OPTIONAL | Minimum tier for child participants (`1`-`4`). Default: same as `adult-min-tier`. |\n| `min-score` | OPTIONAL | Minimum trust score (0-200, implementation-defined). |\n| `enforcement` | OPTIONAL | `client` (display-only), `relay` (AUTH-gated), or `both`. Default: `client`. |\n| `alt` | RECOMMENDED | Human-readable summary |\n\n### `content`\n\nHuman-readable community description. MAY be empty.\n\n### Policy Enforcement\n\n- **`client`**: Clients check credentials before allowing interaction. Non-compliant users see a message explaining the requirement.\n- **`relay`**: Relay requires NIP-42 AUTH and checks the authenticated pubkey's credentials before accepting events. Non-compliant users receive a `CLOSED` message.\n- **`both`**: Both client and relay enforce.\n\nPolicy enforcement is always opt-in. A client that does not implement NIP-SIGNET ignores these events entirely.\n\n---\n\n## Protocol Flow\n\n```mermaid\nsequenceDiagram\n    participant U as User\n    participant C as Client\n    participant R as Relay\n    participant P as Peer\n    participant V as Professional Verifier\n\n    note over U,C: Tier 1: Self-Declaration\n    U->>C: \"I want to verify\"\n    C->>C: Build kind 31000 (type: credential, tier: 1)\n    C->>R: Publish self-declared credential\n\n    note over U,P: Tier 2: Peer Vouching\n    U->>P: Meet in person\n    P->>C: Build kind 31000 (type: vouch, method: in-person)\n    P->>R: Publish vouch\n    note over C: After 3+ qualifying vouches\n    C->>R: Publish Tier 2 credential\n\n    note over U,V: Tier 3: Professional Verification\n    U->>V: Meet in person with government ID\n    V->>V: Verify document, compute nullifier\n    V->>C: Build kind 31000 (type: credential, tier: 3)\n    V->>R: Publish professional credential\n```\n\n## REQ Filters\n\n### Fetch credentials for a pubkey\n\n```json\n{\"kinds\": [31000], \"#p\": [\"<subject-pubkey>\"], \"#type\": [\"credential\"]}\n```\n\n### Fetch vouches for a pubkey\n\n```json\n{\"kinds\": [31000], \"#p\": [\"<subject-pubkey>\"], \"#type\": [\"vouch\"]}\n```\n\n### Fetch all credentials and vouches (badge computation)\n\n```json\n{\"kinds\": [31000], \"#p\": [\"<subject-pubkey>\"], \"#type\": [\"credential\", \"vouch\"]}\n```\n\n### Discover verifiers in a jurisdiction\n\n```json\n{\"kinds\": [31000], \"#type\": [\"verifier\"], \"#jurisdiction\": [\"GB\"]}\n```\n\nNote: `jurisdiction` and `type` are multi-letter tags. Relays that do not support generic tag filtering will return all kind 31000 events; clients MUST post-filter. As a fallback, clients can filter by `d` tag prefix (e.g. `#d: [\"credential:\"]` to fetch credentials) since `d` is a single-letter tag with universal relay support.\n\n### Fetch community policy\n\n```json\n{\"kinds\": [30078], \"#d\": [\"signet:policy:<community-id>\"]}\n```\n\n## Implementation Levels\n\n### Level 1: Display Badges (one weekend)\n\nRead kind 31000 credentials and vouches from relays. Count credentials per tier, count vouches. Display a badge on profiles:\n\n- No credentials: \"Unverified\"\n- Tier 1: \"Self-declared\"\n- Tier 2: \"Vouched\" (+ vouch count)\n- Tier 3+: \"Verified\" (+ profession if present)\n\nNo cryptography beyond standard Nostr event signature verification.\n\n### Level 2: Issue Vouches (a few days)\n\nAdd the ability to publish kind 31000 `type:vouch` events. Requires Schnorr signing (which every Nostr client already does) and relay write access. Users can now build each other's trust by vouching.\n\n### Level 3: Full Protocol (weeks)\n\nProfessional credential issuance, verifier lifecycle management, community policy parsing and enforcement, guardian delegation, credential chains. Reference: [`signet-protocol`](https://github.com/forgesworn/signet) TypeScript library.\n\n## Validation Rules\n\n| Rule | Check | Semantics |\n|------|-------|----------|\n| V-SIG-01 | `type` tag MUST be one of: `credential`, `vouch`, `verifier`, `challenge` | Reject unknown types silently |\n| V-SIG-02 | `tier` MUST be `1`, `2`, `3`, or `4` | Reject credentials with invalid tiers |\n| V-SIG-03 | Tier 1 credential: `pubkey` MUST equal `p` tag value | Self-declaration must be self-signed |\n| V-SIG-04 | Tier 1 credential: `d` tag MUST be `credential:<subject-pubkey>` | Self-declarations use direct claim d-tag |\n| V-SIG-05 | Tier 2-4 credential: `d` tag MUST start with `assertion:` | Higher-tier credentials reference the subject's Tier 1 |\n| V-SIG-06 | Tier 2-4 credential: MUST have an `e` tag with `\"assertion\"` marker | The assertion reference is required, not optional |\n| V-SIG-07 | Tier 3-4 credential: `verification-type` MUST be `professional` | Professional tiers require professional verification |\n| V-SIG-08 | Tier 4 credential: `scope` MUST be `adult+child` | Child safety tier requires child scope |\n| V-SIG-09 | `expiration` tag, if present, MUST be a valid future unix timestamp at time of creation | Reject pre-expired credentials |\n| V-SIG-10 | `nullifier` tag, if present, MUST be a 64-character hex string (SHA-256) | Malformed nullifiers indicate tampering |\n| V-SIG-11 | `method` on vouch MUST be `in-person` or `online` | Reject unknown vouch methods |\n| V-SIG-12 | Vouch: `d` tag MUST be `vouch:<subject-pubkey>` | Vouches use direct claim pattern |\n| V-SIG-13 | Verifier: `profession`, `jurisdiction`, `licence`, `body` all REQUIRED | Incomplete verifier registrations are invalid |\n| V-SIG-14 | Challenge: `reason` MUST be a defined reason code | Reject unknown challenge reasons |\n| V-SIG-15 | Policy `d` tag MUST begin with `signet:policy:` | Policies with other d-tag prefixes are not Signet policies |\n| V-SIG-16 | Policy tier values MUST be `1`-`4` if present | Reject out-of-range tier requirements |\n\n## Security Considerations\n\n### Credential Forgery\n\nAll credentials are standard Nostr events verified by Schnorr signature. A forged credential would require the verifier's private key. Clients MUST verify event signatures before trusting tier claims.\n\n### Tier Spoofing\n\nA malicious actor could publish a self-signed Tier 3 credential. Clients MUST check that Tier 3-4 credentials are signed by a registered verifier (a pubkey with a valid `type:verifier` event). Self-signed credentials at Tier 3+ SHOULD be rejected or flagged.\n\n### Sybil Vouching\n\nAn attacker could create multiple keypairs to self-vouch. The Tier 2 threshold requires vouchers to themselves be Tier 2+, creating a bootstrapping barrier. New accounts cannot self-promote by vouching for each other in a closed loop. The first Tier 2 accounts must be bootstrapped from Tier 3 professional verification or from a pre-existing trust network.\n\n### Verifier Collusion\n\nProfessionals from the same field could collude to issue fraudulent credentials. Cross-verification across different professions mitigates this (a solicitor vouching for another solicitor carries less weight than a solicitor vouching for a doctor). The challenge mechanism provides a decentralised accountability layer.\n\n### PII Protection\n\nNo personally identifiable information appears in any event. Names, dates of birth, addresses, and document numbers are never published. The `nullifier` tag is a one-way SHA-256 hash that prevents duplicate identity without revealing the source document. The `merkle-root` tag enables selective disclosure of verified attributes without exposing the full set.\n\n### Policy Evasion\n\nA user could ignore community policies. Client-side enforcement is advisory; relay-side enforcement (NIP-42 AUTH + credential check) is stronger. The `both` enforcement level is recommended for high-trust communities.\n\n### Expiration\n\nCredentials SHOULD include an `expiration` tag. Clients MUST check expiration before displaying badges. Expired credentials SHOULD be visually distinguished (e.g. greyed out) rather than hidden, to preserve the audit trail.\n\n## Test Vectors\n\n### Minimal Valid Tier 1 Credential\n\n```json\n{\n  \"kind\": 31000,\n  \"tags\": [\n    [\"d\", \"credential:aabbccdd...\"],\n    [\"p\", \"aabbccdd...\"],\n    [\"type\", \"credential\"],\n    [\"tier\", \"1\"],\n    [\"verification-type\", \"self\"],\n    [\"scope\", \"adult\"],\n    [\"method\", \"self-declaration\"]\n  ],\n  \"content\": \"\"\n}\n```\n\nValid when `pubkey` equals the `p` tag value.\n\n### Minimal Valid Vouch\n\n```json\n{\n  \"kind\": 31000,\n  \"tags\": [\n    [\"d\", \"vouch:aabbccdd...\"],\n    [\"p\", \"aabbccdd...\"],\n    [\"type\", \"vouch\"],\n    [\"method\", \"in-person\"]\n  ],\n  \"content\": \"\"\n}\n```\n\nValid when `pubkey` differs from the `p` tag value (you cannot vouch for yourself).\n\n### Minimal Valid Tier 3 Credential (Assertion-First Hybrid)\n\n```json\n{\n  \"kind\": 31000,\n  \"tags\": [\n    [\"d\", \"assertion:ff00ff00...\"],\n    [\"e\", \"ff00ff00...\", \"wss://relay.example.com\", \"assertion\"],\n    [\"type\", \"credential\"],\n    [\"p\", \"aabbccdd...\"],\n    [\"tier\", \"3\"],\n    [\"verification-type\", \"professional\"],\n    [\"scope\", \"adult\"],\n    [\"method\", \"in-person-id\"]\n  ],\n  \"content\": \"\"\n}\n```\n\nValid when `pubkey` differs from the `p` tag value (verifier signs, not the subject). The `e` tag references the subject's Tier 1 self-declaration event (`ff00ff00...`). The `d` tag uses the `assertion:` prefix.\n\n### Invalid: Tier 3 Without Assertion Reference\n\n```json\n{\n  \"kind\": 31000,\n  \"tags\": [\n    [\"d\", \"credential:aabbccdd...\"],\n    [\"p\", \"aabbccdd...\"],\n    [\"type\", \"credential\"],\n    [\"tier\", \"3\"],\n    [\"verification-type\", \"professional\"],\n    [\"scope\", \"adult\"],\n    [\"method\", \"in-person-id\"]\n  ],\n  \"content\": \"\"\n}\n```\n\nINVALID: Tier 3 credentials MUST reference the subject's Tier 1 self-declaration via `e` tag with `\"assertion\"` marker. The `d` tag MUST start with `assertion:`, not `credential:`.\n\n### Invalid: Self-Signed Tier 3\n\n```json\n{\n  \"kind\": 31000,\n  \"tags\": [\n    [\"d\", \"assertion:ff00ff00...\"],\n    [\"e\", \"ff00ff00...\", \"\", \"assertion\"],\n    [\"p\", \"aabbccdd...\"],\n    [\"type\", \"credential\"],\n    [\"tier\", \"3\"],\n    [\"verification-type\", \"professional\"],\n    [\"scope\", \"adult\"],\n    [\"method\", \"in-person-id\"]\n  ],\n  \"content\": \"\"\n}\n```\n\nINVALID when `pubkey` equals the `p` tag value. Tier 3 credentials MUST be signed by a different pubkey (the professional verifier).\n\n### Invalid: Vouch with Unknown Method\n\n```json\n{\n  \"kind\": 31000,\n  \"tags\": [\n    [\"d\", \"vouch:aabbccdd...\"],\n    [\"p\", \"aabbccdd...\"],\n    [\"type\", \"vouch\"],\n    [\"method\", \"telepathy\"]\n  ],\n  \"content\": \"\"\n}\n```\n\nINVALID: `method` must be `in-person` or `online`.\n\n## Dependencies\n\n- NIP-VA (Kind 31000 -- Verifiable Attestations) -- REQUIRED\n- NIP-78 (Kind 30078 -- App-Specific Data) -- REQUIRED for community policies\n- NIP-40 (Expiration Timestamp) -- RECOMMENDED for credential expiration\n- NIP-42 (Authentication of Clients to Relays) -- OPTIONAL for relay-side policy enforcement\n\n## Reference Implementation\n\n- [`signet-protocol`](https://github.com/forgesworn/signet) -- TypeScript protocol library (npm: `signet-protocol`)\n- [`nostr-bray`](https://github.com/forgesworn/bray) -- MCP server with full Signet integration (badge display, vouch issuance, policy checking)","sig":"15b84e40d21ef58e7171dc5e7dd7058dabbedc33d66a15bc9efb8d729ca9e715724e9ef0255f727f68ec7b342c53e9f6ce5a2daa129972ccbdff1184e771fc19"}