Identity Connection (Kind 35521)
A Kind 35521 is a verifiable Nostr event published by an end-user to announce that their Nostr public key is linked to a Legacy Identity Provider (LIDP) account, such as a Discord or X account.
Crucially, because an end-user could lie about owning an account, Kind 35521 alone is insufficient. It must encapsulate cryptographic proof signed by a trusted third party—an Identity Authority (IA).
Event Structure
{
"kind": 35521,
"content": "{\"privacy\": \"public\"}",
"tags": [
["d", "<identifier_hash>"],
["lidp", "<lidp_name>"],
["s", "<embedded_kind_35522_json>", "<ia_pubkey>", "<ia_relay_hint>"]
],
"pubkey": "<user_pubkey>",
"created_at": 1709424015,
"id": "...",
"sig": "..."
}
Required Tags
| Tag | Format | Description |
|---|---|---|
d | ["d", "<sh256_hash>"] | The ConnectionKey. Generated via SHA256(lidp_name + ":" + identifier) (e.g., discord:12345). This allows querying without exposing the raw identifier. |
lidp | ["lidp", "<string>"] | The name of the Legacy Identity Provider (e.g., discord, email, x). |
s | ["s", "<json>", "<pubkey>", "<url>"] | The embedded Kind 35522 evidence event signed by the IA. See below. |
The s Tag and Multi-IA Stacking
The s tag is the heart of Zapf's identity model.
- Index 1: The raw, stringified JSON of the IA's Kind 35522 Attestation.
- Index 2: The pubkey of the IA that signed the attestation.
- Index 3: A relay hint where clients can fetch the original Kind 35522 for an online Deep Check.
Stacking: A user can include multiple s tags in their Kind 35521 event. If a user proves their Discord account to three different IAs (zapf.app, loki.ltd, community.ia), they can embed all three attestations. This means a client only needs to trust one of those three IAs to verify the connection.
Content Privacy Modes
By default, the d tag only broadcasts a hash. If a user wants to be searchable by their raw identifier (e.g., finding @elonmusk on X), they store the raw string in the content field.
The content field is a JSON string with a privacy key:
| Mode | Content Field | Result |
|---|---|---|
| Public | {"privacy": "public", "value": "elonmusk"} | The client can read the value, hash it, and verify it matches the d tag. The user is searchable globally. |
| Private | {"privacy": "private"} | The raw value is entirely omitted. The Connection Key is strictly a hash. Users can only zap this person if they already know their exact email/phone number. |