Add place photos NIP, update reviews NIP
This commit is contained in:
105
doc/nostr/nip-place-photos.md
Normal file
105
doc/nostr/nip-place-photos.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# NIP-XX: Place Photos and Media
|
||||||
|
|
||||||
|
`draft` `optional`
|
||||||
|
|
||||||
|
## Abstract
|
||||||
|
|
||||||
|
This NIP defines a standardized event format for sharing photos, videos, and other visual media tied to specific real-world locations (e.g., OpenStreetMap POIs).
|
||||||
|
|
||||||
|
While NIP-68 (Picture-first feeds) caters to general visual feeds, this NIP specifically targets map-based applications, travel logs, and location directories by mandating strict entity identifiers (`i` tags) and spatial indexing (`g` tags).
|
||||||
|
|
||||||
|
## Event Kind
|
||||||
|
|
||||||
|
`kind: 360`
|
||||||
|
|
||||||
|
## Content
|
||||||
|
|
||||||
|
The `.content` of the event SHOULD generally be empty. If a user wishes to provide a detailed description, summary, or caption for a place, clients SHOULD encourage them to create a Place Review event (`kind: 30360`) instead.
|
||||||
|
|
||||||
|
## Tags
|
||||||
|
|
||||||
|
This NIP relies on existing Nostr tag conventions to link media to places and provide inline metadata.
|
||||||
|
|
||||||
|
### Required Tags
|
||||||
|
|
||||||
|
#### 1. `i` — Entity Identifier
|
||||||
|
|
||||||
|
Identifies the exact place the media depicts using an external identifier (as defined in NIP-73). OpenStreetMap data is the default:
|
||||||
|
|
||||||
|
```json
|
||||||
|
["i", "osm:node:123456"]
|
||||||
|
```
|
||||||
|
* For OSM POIs, `<type>` MUST be one of: `node`, `way`, `relation`.
|
||||||
|
|
||||||
|
#### 2. `g` — Geohash
|
||||||
|
|
||||||
|
Used for spatial indexing and discovery. Events MUST include at least one high-precision geohash. To optimize for map-based discovery across different zoom levels, clients SHOULD include geohashes at multiple resolutions:
|
||||||
|
|
||||||
|
```json
|
||||||
|
["g", "thrr"] // coarse (~city)
|
||||||
|
["g", "thrrn5"] // medium (~1km)
|
||||||
|
["g", "thrrn5k"] // fine (~150m)
|
||||||
|
["g", "thrrn5kxyz"] // exact
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. `imeta` — Inline Media Metadata
|
||||||
|
|
||||||
|
Media files MUST be attached using the `imeta` tag as defined in NIP-92. Each `imeta` tag represents one media item. The primary `url` SHOULD also be appended to the event's `.content` for backwards compatibility with clients that do not parse `imeta` tags.
|
||||||
|
|
||||||
|
Clients SHOULD include `alt` (accessibility descriptions), `dim` (dimensions), `m` (MIME type), and `blurhash` where possible.
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
"imeta",
|
||||||
|
"url https://example.com/photo.jpg",
|
||||||
|
"m image/jpeg",
|
||||||
|
"dim 3024x4032",
|
||||||
|
"alt A steaming bowl of ramen on a wooden table at the restaurant.",
|
||||||
|
"blurhash eVF$^OI:${M{o#*0-nNFxakD-?xVM}WEWB%iNKxvR-oetmo#R-aen$"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Optional Tags
|
||||||
|
|
||||||
|
* `t`: Hashtags for categorization (e.g., `["t", "food"]`, `["t", "architecture"]`).
|
||||||
|
* `content-warning`: If the media contains NSFW or sensitive imagery.
|
||||||
|
* `published_at`: Unix timestamp of when the photo was originally taken or published.
|
||||||
|
|
||||||
|
## Example Event
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "<32-bytes hex>",
|
||||||
|
"pubkey": "<32-bytes hex>",
|
||||||
|
"created_at": 1713205000,
|
||||||
|
"kind": 360,
|
||||||
|
"content": "",
|
||||||
|
"tags": [
|
||||||
|
["i", "osm:node:987654321"],
|
||||||
|
["g", "xn0m"],
|
||||||
|
["g", "xn0m7h"],
|
||||||
|
["g", "xn0m7hwq"],
|
||||||
|
|
||||||
|
["imeta",
|
||||||
|
"url https://example.com/ramen.jpg",
|
||||||
|
"m image/jpeg",
|
||||||
|
"dim 1080x1080",
|
||||||
|
"alt A close-up of spicy miso ramen with chashu pork, soft boiled egg, and scallions.",
|
||||||
|
"blurhash UHI=0o~q4T-o~q%MozM{x]t7RjRPt7oKkCWB"
|
||||||
|
],
|
||||||
|
|
||||||
|
["t", "ramen"],
|
||||||
|
["t", "food"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rationale
|
||||||
|
|
||||||
|
### Why not use NIP-68 (Picture-first feeds)?
|
||||||
|
|
||||||
|
NIP-68 is designed for general-purpose social feeds (like Instagram). Place photos require strict guarantees about what entity is being depicted to be useful for map clients, directories, and review aggregators. By mandating the `i` tag for POI linking and the `g` tag for spatial querying, this kind ensures interoperability for geo-spatial applications without cluttering general picture feeds with mundane POI images (like photos of storefronts or menus).
|
||||||
|
|
||||||
|
### Separation from Place Reviews
|
||||||
|
|
||||||
|
Reviews (kind 30360) and media have different lifecycles and data models. A user might upload 10 photos of a park without writing a review, or write a detailed review without attaching photos. Keeping them as separate events allows clients to query `imeta` attachments for a specific `i` tag to quickly build a photo gallery for a place, regardless of whether a review was attached.
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
# NIP-XX: Place Reviews
|
# NIP-XX: Place Reviews
|
||||||
|
|
||||||
|
`draft` `optional`
|
||||||
|
|
||||||
## Abstract
|
## Abstract
|
||||||
|
|
||||||
This NIP defines a standardized event format for decentralized place reviews using Nostr. Reviews are tied to real-world locations (e.g. OpenStreetMap POIs) via tags, and include structured, multi-aspect ratings, a binary recommendation signal, and optional contextual metadata.
|
This NIP defines a standardized event format for decentralized place reviews using Nostr. Reviews are tied to real-world locations (e.g. OpenStreetMap POIs) via tags, and include structured, multi-aspect ratings, a binary recommendation signal, and optional contextual metadata.
|
||||||
@@ -11,13 +13,9 @@ The design prioritizes:
|
|||||||
* Flexibility for different place types
|
* Flexibility for different place types
|
||||||
* Efficient geospatial querying using geohashes
|
* Efficient geospatial querying using geohashes
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Event Kind
|
## Event Kind
|
||||||
|
|
||||||
`kind: 30315` (suggested; subject to coordination)
|
`kind: 30360`
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Tags
|
## Tags
|
||||||
|
|
||||||
@@ -51,8 +49,6 @@ Examples:
|
|||||||
["i", "osm:way:987654"]
|
["i", "osm:way:987654"]
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Geospatial Tags
|
### Geospatial Tags
|
||||||
|
|
||||||
#### `g` — Geohash
|
#### `g` — Geohash
|
||||||
@@ -91,8 +87,6 @@ Geospatial queries are performed using the `g` tag.
|
|||||||
|
|
||||||
Note: Other queries (e.g. fetching reviews for a specific place) are performed using the `i` tag and are outside the scope of geospatial querying.
|
Note: Other queries (e.g. fetching reviews for a specific place) are performed using the `i` tag and are outside the scope of geospatial querying.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Content (JSON)
|
## Content (JSON)
|
||||||
|
|
||||||
The event `content` MUST be valid JSON matching the following schema.
|
The event `content` MUST be valid JSON matching the following schema.
|
||||||
@@ -183,8 +177,6 @@ The event `content` MUST be valid JSON matching the following schema.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
### Restaurant Review Event
|
### Restaurant Review Event
|
||||||
@@ -232,8 +224,6 @@ The event `content` MUST be valid JSON matching the following schema.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Semantics
|
## Semantics
|
||||||
|
|
||||||
### Ratings
|
### Ratings
|
||||||
@@ -248,8 +238,6 @@ The event `content` MUST be valid JSON matching the following schema.
|
|||||||
* Clients MAY define and interpret aspect keys
|
* Clients MAY define and interpret aspect keys
|
||||||
* Clients SHOULD reuse commonly established aspect keys where possible
|
* Clients SHOULD reuse commonly established aspect keys where possible
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Recommendation Signal
|
## Recommendation Signal
|
||||||
|
|
||||||
The `recommend` field represents a binary verdict:
|
The `recommend` field represents a binary verdict:
|
||||||
@@ -259,8 +247,6 @@ The `recommend` field represents a binary verdict:
|
|||||||
|
|
||||||
Clients SHOULD strongly encourage users to provide this value.
|
Clients SHOULD strongly encourage users to provide this value.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Familiarity
|
## Familiarity
|
||||||
|
|
||||||
Represents user familiarity with the place:
|
Represents user familiarity with the place:
|
||||||
@@ -271,8 +257,6 @@ Represents user familiarity with the place:
|
|||||||
|
|
||||||
Clients MAY use this signal for weighting during aggregation.
|
Clients MAY use this signal for weighting during aggregation.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
Optional metadata about the visit.
|
Optional metadata about the visit.
|
||||||
@@ -281,8 +265,6 @@ Optional metadata about the visit.
|
|||||||
* `duration_minutes` represents time spent
|
* `duration_minutes` represents time spent
|
||||||
* `party_size` indicates group size
|
* `party_size` indicates group size
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Interoperability
|
## Interoperability
|
||||||
|
|
||||||
This specification defines a content payload only.
|
This specification defines a content payload only.
|
||||||
@@ -292,8 +274,6 @@ This specification defines a content payload only.
|
|||||||
|
|
||||||
Content payloads SHOULD NOT include place identifiers.
|
Content payloads SHOULD NOT include place identifiers.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Rationale
|
## Rationale
|
||||||
|
|
||||||
### No Place Field in Content
|
### No Place Field in Content
|
||||||
@@ -320,8 +300,6 @@ Multiple resolutions balance:
|
|||||||
* small event size
|
* small event size
|
||||||
* early-stage discoverability
|
* early-stage discoverability
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Future Work
|
## Future Work
|
||||||
|
|
||||||
* Standardized aspect vocabularies
|
* Standardized aspect vocabularies
|
||||||
@@ -329,15 +307,7 @@ Multiple resolutions balance:
|
|||||||
* Indexing/aggregation services
|
* Indexing/aggregation services
|
||||||
* Cross-protocol mappings
|
* Cross-protocol mappings
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Security Considerations
|
## Security Considerations
|
||||||
|
|
||||||
* Clients SHOULD validate all input
|
* Clients SHOULD validate all input
|
||||||
* Malicious or spam reviews may require external moderation or reputation systems
|
* Malicious or spam reviews may require external moderation or reputation systems
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Copyright
|
|
||||||
|
|
||||||
This NIP is public domain.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user