{"id":"93acf6fb01ac13a98d6218b9baa6dff59b55762b0b5334bbf74009a79bf21138","pubkey":"7cc328a08ddb2afdf9f9be77beff4c83489ff979721827d628a542f32a247c0e","created_at":1775756056,"kind":30817,"tags":[["d","nip-grapevine-api"],["title","NIP-GRAPEVINE-API"],["client","nostrhub.io"]],"content":"NIP-GRAPEVINE-API\n======\n\nGrapeVine: Relay-Computed Influence Scores\n-------------------------------------------\n\n`draft` `optional`\n\n## Abstract\n\nThis NIP defines a standard HTTP API for relays to expose trust-weighted influence scores computed using the GrapeVine algorithm. These scores enable clients to filter, rank, and curate content based on social graph topology and trust signals (follows, mutes, reports).\n\n## Motivation\n\nNostr clients need efficient ways to:\n- Filter spam and low-quality content\n- Rank content by trustworthiness from a user's perspective\n- Discover trusted accounts in extended social networks\n- Build reputation systems without centralized authorities\n\nComputing these scores client-side is computationally expensive and requires full graph traversal. Relays can pre-compute and cache these scores, providing instant access to trust metrics.\n\n## Specification\n\n### Authentication\n\nAll GrapeVine API endpoints require **NIP-98 HTTP Authentication**. Clients MUST include a signed Nostr event in the `Authorization` header.\n\n**Authorization Header Format:**\n```\nAuthorization: Nostr <base64-encoded-event>\n```\n\nThe authentication event MUST:\n- Be kind `27235` (HTTP Auth)\n- Have a `u` tag with the full request URL\n- Have a `method` tag with the HTTP method (GET or POST)\n- Be signed by the requesting user's private key\n- Have a `created_at` timestamp within an acceptable time window (typically ±60 seconds)\n\n**Example Authentication Event:**\n```json\n{\n  \"kind\": 27235,\n  \"created_at\": 1680000000,\n  \"tags\": [\n    [\"u\", \"https://relay.example.com/api/grapevine/scores?observer=hexpubkey...\"],\n    [\"method\", \"GET\"]\n  ],\n  \"content\": \"\",\n  \"pubkey\": \"hexpubkey\",\n  \"id\": \"...\",\n  \"sig\": \"...\"\n}\n```\n\n### Access Control\n\n- **Regular users** can only query scores for their own pubkey (the authenticated pubkey)\n- **Owners and admins** can query scores for any observer pubkey\n- Unauthenticated requests return `401 Unauthorized`\n- Requests for other users' scores (non-owner) return `403 Forbidden`\n\n### HTTP Endpoints\n\nRelays MAY expose GrapeVine influence scores via HTTP endpoints under `/api/grapevine/`.\n\n#### 1. Get Full Score Set\n\n```\nGET /api/grapevine/scores?observer=<hex_pubkey>\n```\n\nReturns the complete score set for an observer pubkey.\n\n**Query Parameters:**\n- `observer` (required): Hex-encoded public key (64 characters)\n\n**Response:** JSON object with the following structure:\n\n```json\n{\n  \"observer\": \"abc123...\",\n  \"scores\": [\n    {\n      \"pubkey\": \"def456...\",\n      \"influence\": 0.9763800964692622,\n      \"average\": 1.0,\n      \"certainty\": 0.9763800964692622,\n      \"input\": 2.70192655866738,\n      \"wot_score\": 31,\n      \"depth\": 2\n    }\n  ],\n  \"computed_at\": \"2026-04-09T16:22:15.123Z\",\n  \"compute_ms\": 26431,\n  \"total_pubkeys\": 73542\n}\n```\n\n#### 2. Get Single Score\n\n```\nGET /api/grapevine/score?observer=<hex_pubkey>&target=<hex_pubkey>\n```\n\nReturns the score for a single target pubkey from the observer's perspective.\n\n**Query Parameters:**\n- `observer` (required): Hex-encoded observer public key\n- `target` (required): Hex-encoded target public key\n\n**Response:** JSON object with a single score entry:\n\n```json\n{\n  \"pubkey\": \"def456...\",\n  \"influence\": 0.9763800964692622,\n  \"average\": 1.0,\n  \"certainty\": 0.9763800964692622,\n  \"input\": 2.70192655866738,\n  \"wot_score\": 31,\n  \"depth\": 2\n}\n```\n\n#### 3. Check Computation Status\n\n```\nGET /api/grapevine/status?observer=<hex_pubkey>\n```\n\nReturns the current computation status for an observer.\n\n**Query Parameters:**\n- `observer` (optional): Hex-encoded observer public key (defaults to authenticated user)\n\n**Response:**\n\n```json\n{\n  \"status\": \"completed\",\n  \"observer\": \"abc123...\",\n  \"computed_at\": \"2026-04-09T16:22:15.123Z\",\n  \"total_pubkeys\": 73542\n}\n```\n\n**Status Values:**\n- `not_started`: No scores have been computed yet\n- `computing`: Computation is currently in progress\n- `completed`: Computation finished successfully\n\n**Response Fields:**\n- `status` (string): Current computation status\n- `observer` (string): Observer pubkey\n- `computed_at` (string, optional): ISO 8601 timestamp of last completion (only when `completed`)\n- `total_pubkeys` (integer, optional): Total pubkeys in graph (only when `completed`)\n\n#### 4. Trigger Recalculation (Optional)\n\n```\nPOST /api/grapevine/recalculate\nContent-Type: application/json\n\n{\n  \"observer\": \"abc123...\"\n}\n```\n\nTriggers asynchronous score recalculation for the specified observer.\n\n**Response:**\n\n```json\n{\n  \"status\": \"started\",\n  \"observer\": \"abc123...\"\n}\n```\n\n**Status Values:**\n- `started`: New computation job started\n- `already_computing`: Computation already in progress (no-op)\n\n#### 5. Database Statistics (Optional)\n\n```\nGET /api/stats\n```\n\nReturns database statistics about the relay's social graph.\n\n**Response:**\n\n```json\n{\n  \"kind3_author_count\": 12543,\n  \"kind3_referenced_count\": 73542\n}\n```\n\n**Response Fields:**\n- `kind3_author_count` (integer): Number of unique pubkeys that have published kind-3 (contact list) events\n- `kind3_referenced_count` (integer): Number of unique pubkeys referenced in p-tags of all kind-3 events (total social graph size)\n\n**Note:** This endpoint does not require authentication and provides insight into the size of the relay's social graph index. The referenced count is typically larger than the author count, as it includes all pubkeys that appear in anyone's follow list.\n\n### Response Schema\n\n#### ScoreSet Object\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `observer` | string | Hex pubkey from whose perspective scores are computed |\n| `scores` | array | Array of score entries (see below) |\n| `computed_at` | string | ISO 8601 timestamp of computation |\n| `compute_ms` | integer | Computation time in milliseconds |\n| `total_pubkeys` | integer | Total pubkeys in the social graph |\n\n#### ScoreEntry Object\n\n| Field | Type | Range | Description |\n|-------|------|-------|-------------|\n| `pubkey` | string | 64 hex | Target pubkey being scored |\n| `influence` | number | 0.0 - 1.0 | **Primary score**: trust-weighted reputation |\n| `average` | number | -1.0 - 1.0 | Average rating (1=trusted, -1=distrusted) |\n| `certainty` | number | 0.0 - 1.0 | Confidence in the score (based on signal strength) |\n| `input` | number | 0.0+ | Sum of rating weights (unbounded) |\n| `wot_score` | integer | 0+ | Web of Trust intersection count |\n| `depth` | integer | 0+ | Hop distance from observer in social graph |\n\n### Score Interpretation\n\n#### Influence Score (Primary Metric)\n\nThe `influence` field is the primary trust metric, computed as `average × certainty`:\n\n- **0.8 - 1.0**: Very high trust (close connections, strong positive signals)\n- **0.5 - 0.8**: High trust (extended network, good reputation)\n- **0.2 - 0.5**: Medium trust (distant connections, weak signals)\n- **0.0 - 0.2**: Low trust (very distant or minimal signals)\n- **< 0.0**: Distrust (muted or reported by trusted accounts)\n\n#### WoT Score (Secondary Metric)\n\nThe `wot_score` field counts how many of the observer's direct follows also follow the target pubkey. Higher values indicate stronger social proof.\n\n#### Depth (Distance Metric)\n\nThe `depth` field indicates BFS hop distance in the follow graph:\n\n- **0**: Observer themselves\n- **1**: Direct follow\n- **2**: Friend-of-friend\n- **3+**: Extended network\n\n### Algorithm\n\nThe GrapeVine algorithm is based on iterative trust propagation:\n\n1. **Graph Traversal**: BFS outward from observer up to `max_depth` hops (typically 6)\n2. **Initialization**: Observer starts with influence = 1.0, all others at 0.0\n3. **Convergence Loop**: Iteratively update each pubkey's score based on:\n   - **Follows** (kind 3): Positive trust signal (+1.0 rating)\n   - **Mutes** (kind 10000): Negative trust signal (-1.0 rating)\n   - **Reports** (kind 1984): Negative trust signal (-1.0 rating)\n4. **Attenuation**: Influence decays by distance (typically 0.8 per hop)\n5. **Certainty**: Confidence increases with more rating signals\n6. **Convergence**: Stop when changes fall below threshold or max iterations reached\n\n**Key Parameters:**\n- `max_depth`: BFS hop limit (default: 6)\n- `attenuation_factor`: Weight decay per hop (default: 0.8)\n- `rigor`: Certainty curve steepness (default: 0.25)\n- `follow_confidence`: Base weight for follow edges (default: 0.05)\n\n### Error Responses\n\n**401 Unauthorized**: Missing or invalid NIP-98 authentication\n```json\n{\n  \"error\": \"NIP-98 authentication failed\"\n}\n```\n\n**403 Forbidden**: Authenticated user trying to access another user's scores\n```json\n{\n  \"error\": \"Can only query your own scores\"\n}\n```\n\n**404 Not Found**: Scores not computed for this observer\n```json\n{\n  \"error\": \"Scores not found for observer\"\n}\n```\n\n**400 Bad Request**: Invalid pubkey format\n```json\n{\n  \"error\": \"Invalid pubkey format\"\n}\n```\n\n**503 Service Unavailable**: GrapeVine API not enabled\n```json\n{\n  \"error\": \"GrapeVine API not enabled\"\n}\n```\n\n## Client Usage\n\nClients MUST implement NIP-98 HTTP authentication to access these endpoints. See the Authentication section above for details on creating the required authorization header.\n\n## Relay Implementation\n\n### Computation\n\nRelays SHOULD:\n- Pre-compute scores for configured observer pubkeys\n- Refresh scores periodically (e.g., every 6 hours)\n- Cache results in persistent storage\n- Support on-demand recalculation via POST endpoint\n\n### Performance\n\nTypical computation metrics:\n- **Graph size**: 50k-100k pubkeys\n- **Computation time**: 20-30 seconds\n- **Storage**: ~50 bytes per score entry\n- **Memory**: ~500MB during computation\n\n### Privacy\n\nRelays SHOULD:\n- Only compute scores for explicitly configured observers\n- Rate-limit score requests to prevent enumeration\n- Consider requiring NIP-42 AUTH for score access\n- Not expose scores for arbitrary pubkeys without permission\n\n## Security Considerations\n\n### Sybil Resistance\n\nThe GrapeVine algorithm provides natural Sybil resistance through:\n- Attenuation by distance (fake accounts are distant)\n- Trust propagation (requires connections to trusted accounts)\n- WoT intersection (fake accounts have low social proof)\n\nHowever, sophisticated attacks may still succeed. Clients SHOULD:\n- Combine influence scores with other signals\n- Allow users to adjust trust thresholds\n- Provide manual override mechanisms\n\n### Gaming\n\nMalicious actors may attempt to:\n- Create follow/unfollow cycles to manipulate scores\n- Coordinate fake follow networks\n- Report legitimate accounts\n\nRelays MAY implement additional protections:\n- Temporal analysis (detect rapid follow/unfollow patterns)\n- Account age weighting\n- Cross-relay score comparison\n\n### Privacy\n\nScore computation reveals social graph topology. Relays SHOULD:\n- Limit score access to authenticated users\n- Not expose raw graph data\n- Consider differential privacy techniques for sensitive deployments\n\n## References\n\n- [Brainstorm GrapeRank Algorithm](https://github.com/NosFabrica/brainstorm_graperank_algorithm) - Reference implementation\n- [NIP-02](https://github.com/nostr-protocol/nips/blob/master/02.md) - Follow List (kind 3)\n- [NIP-51](https://github.com/nostr-protocol/nips/blob/master/51.md) - Lists (kind 10000 mute lists)\n- [NIP-56](https://github.com/nostr-protocol/nips/blob/master/56.md) - Reporting (kind 1984)\n- [NIP-85](https://github.com/nostr-protocol/nips/blob/master/85.md) - Trusted Assertions\n- [NIP-98](https://github.com/nostr-protocol/nips/blob/master/98.md) - HTTP Auth\n\n## Changelog\n\n- **2026-04-09**: Initial draft","sig":"1af36d18c5f5a44b3a13a63ccae1f55b77fd9a99d07b49ceb9bde7645859f144d5444c6ab9459de658cc10cf810f2bd0110d891959e826b1fbe9b4eb0c48edba"}