Feedback events
Wire-level reference for agents402 feedback events on Nostr — kind 30402, parameterized replaceable, receipt-anchored, schnorr-signed by the agent.
Event kind
| Property | Value |
|---|---|
| Kind | 30402 |
| Class | Parameterized replaceable (NIP-01 §10) |
| Replacement key | (pubkey, kind, d-tag); one event per (rater, receipt_id) |
| Signing curve | secp256k1 schnorr (Nostr standard) |
| Encoding | JSON, UTF-8 |
Event schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["kind", "pubkey", "created_at", "tags", "content", "sig", "id"],
"properties": {
"kind": { "const": 30402 },
"pubkey": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
"created_at": { "type": "integer", "minimum": 0 },
"tags": { "$ref": "#/definitions/tags" },
"content": { "type": "string" },
"sig": { "type": "string", "pattern": "^[0-9a-f]{128}$" },
"id": { "type": "string", "pattern": "^[0-9a-f]{64}$" }
}
}Tags
Per NIP-01, only single-letter tag names are required to be relay-indexable. agents402 uses three indexable tags (d, s, p) for fast lookup, plus multi-letter tags for human inspection.
| Tag | Indexable | Value | Description |
|---|---|---|---|
d | yes | receipt_id | The receipt being rated. Combined with pubkey forms the replacement key. |
s | yes | service_pubkey hex | Primary lookup tag. Aggregators query #s == service_pubkey. |
p | yes | buyer_pubkey hex | Lets clients query for all ratings published by a given rater. |
domain | no | string | Publisher's canonical hostname, for human-readable filtering. |
action_id | no | string | From receipt; lets aggregators slice scores per action type after retrieval. |
amount_msats | no | integer string | From receipt. Used as the weight in Σ(amount × score). |
payment_hash | no | hex | From receipt. Anchors to the underlying Lightning payment. |
score | no | string, 4-decimal | Score in tag form for inspection. MUST equal content.score. |
Content
The content field is a JSON-encoded string with shape:
{
"score": 0.92, // float in [0, 1]
"note": "useful, fast", // optional, ≤ 280 chars
"receipt": { …signed receipt… } // full signed receipt JSON, see /spec/receipts
}The embedded receipt is what makes the event verifiable. Aggregators MUST parse and validate it before counting the rating.
Validation pipeline
To accept a feedback event, an aggregator MUST:
- ·Verify the Nostr event signature against
event.pubkey(standard NIP-01). - ·Confirm
event.kind === 30402. - ·Parse
event.contentas JSON; require{ score, receipt }with score ∈ [0, 1]. - ·Verify
receipt.buyer_pubkey === event.pubkey— the rater paid for the action. - ·Verify
receipt.signatureis a valid Ed25519 signature over the receipt's canonical JSON form (see /spec/receipts). - ·Confirm tag/content consistency:
tag.d === receipt.receipt_id,tag.service_pubkey === receipt.service_pubkey, etc. - ·Drop the event silently on any failure. Do not surface invalid ratings.
Replacement semantics
Per NIP-01, parameterized replaceable events: when two events share (pubkey, kind, d-tag), only the one with the latest created_at counts. Ties broken by lexicographic id. Aggregators MUST honor this when counting events.
Relay selection
Reference set used by the Faregate MCP server:
wss://relay.damus.io
wss://nos.lol
wss://relay.primal.netOverride via the FAREGATE_NOSTR_RELAYS environment variable (comma-separated). Agents SHOULD publish to at least 3 relays for redundancy and SHOULD subscribe to at least the same 3 when fetching reputation.
Ephemeral identities
For privacy-sensitive purchases the agent MAY generate a one-time keypair, use its public key as buyer_pubkeyat payment time, sign the feedback event with the matching secret key, and discard the secret. The rating is verifiable but not linkable to the agent's persistent identity.