diff --git a/41.md b/41.md new file mode 100644 index 0000000000..708adea4fa --- /dev/null +++ b/41.md @@ -0,0 +1,202 @@ +# NIP-41 + +## Places (geohash ladder addressable locations) + +`draft` `optional` + +Clients need a simple, relay-indexable way to discover physical places (e.g., RV parks, cafés, venues) and to query them efficiently on maps. It should work with relays that don't have "Search Capability" (NIP-50). This NIP defines a parameterized replaceable event for “Places” that are **geohash-addressable** via a ladder of `g` tags—from coarse to fine precision—so clients can query by exact `#g` matches without prefix search. + +## 2. Event Kind + +- **Kind:** `33000` +- **Type:** Addressable (NIP-01) +- **Parameterized key:** the first `["d", ]` tag (e.g., a random ID) + +## 3. Required/Recommended Fields + +### 3.1. Required fields/tags + +- `["d", ""]` — a random ID (e.g., `permit:FL:44-54-00003`, `slug:lochloosa-harbor`, etc.) +- At least one `["g", ""]` tag. + The **recommended pattern** is to include a **ladder** of geohash prefixes from coarse to fine precision (e.g., `d`, `dj`, `dj3`, `dj3p`, …). **Recommended precision is at least 7**. This enables fast map queries with `#g` matching at various zoom levels. + +### 3.2. Recommended tags + +- `["name", ""]` +- `["website", ""]` — official website or listing +- `["image", ""]` — can have multiple -- where first image is the cover +- `["phone", ""]` +- `["email", ""]` +- `["booking_url", ""]` +- `["measurement_system", ""]` +- `["primary_currency", ""]` - currency code like USD +- `["payment_method", "", ""]` - e.g.: ["payment_method", "USD", "cash"] +- `["address", "", "", "", "", ""]` +- `["hours", "", "", "", ...]` - e.g., ["hours","thu","09:00/12:30","13:30/17:00"] +- `["L", "physical-location"]` +- `["l", "", "physical-location"]` - freeform type labels used to reduce results for usecase (e.g., `business`, `rv-park`, `restaurant`, etc.) +- `["t", ""]` - further filter results e.g., `["t", "accepts-bitcoin"], ["t", "washer-dryer"]` + +### 3.3. Optional tags + +- Non-indexed attributes specific to the physical location type: + - `["meta", "", " Publishers **should** generate the ladder from a single point (lat/lon → geohash) and decide the deepest precision they care to expose (e.g., 7–8 chars for point-of-interest). + +--- + +## 5. Querying + +### 5.1. Relay filters + +- Fetch all Places within a geohash bucket: + + ```json + { + "kinds": [33000], + "#g": [ + "dhvj", + "dhvm", + "dhvn", + "dhvp", + "dhvq", + "dhvr", + "dhvt", + "dhvw", + "dhvx", + "djj0", + "djj2", + "djj8" + ], + "#l": ["rv-park"], + "#t": ["accepts-bitcoin"] + } + ``` + +- Zoom out: use shorter prefixes already present as `#g` values (e.g., `["dhv"]`). + +- Fetch/refresh a specific place (parameterized key): + + ```json + { + "kinds": [33000], + "authors": [""], + "#d": [""] + } + ``` + +--- + +## 6. Example (valid per this NIP) + +```json +{ + "kind": 33000, + "pubkey": "0e29efc2a3eb6966403e8c61cb8bea6d78527af5e0565a2baa0f313eff142af2", + "created_at": 1766059634, + "tags": [ + ["L", "physical-location"], + ["l", "business", "physical-location"], + ["l", "rv-park", "physical-location"], + ["name", "Floating RV Park"], + ["d", "39d8fb01-0ab9-47c8-96bc-f350dcbb7b77"], + ["primary_currency", "USD"], + ["measurement_system", "imperial"], + ["website", "https://westernbtc.com"], + [ + "booking_url", + "https://rvparker.westernbtc.com/book/parks/naddr1qvzqqqyqaqpzqr3falp286mfveqrarrpew975mtc2fa0tczktg465re38ml3g2hjqyd8wumn8ghj7un9d3shjtnhv4ehgetjde38gcewvdhk6qg5waehxw309aex2mrp0yhxgctdw4eju6t0qyt8wumn8ghj7un9d3shjtnswf5k6ctv9ehx2aqpp4mhxue69uhkummn9ekx7mqqysenjepcve3rqvfdxpskywfdxsmkxwpd8ymxycedvcen2vryvd3xydmzxumsqvst7f" + ], + ["address", "21000 BTC Ln", "Tampa", "FL", "33602", "USA"], + ["t", "wifi"], + ["t", "washer-dryer"], + ["t", "gym"], + ["t", "full-hookups"], + ["t", "water-front"], + ["t", "pool"], + ["t", "accepts-bitcoin"], + ["hours", "mon", "08:00/17:00"], + ["hours", "tue", "08:00/17:00"], + ["hours", "wed", "08:00/17:00"], + ["hours", "thu", "08:00/17:00"], + ["hours", "fri", "08:00/17:00"], + ["hours", "sat", "08:00/14:00"], + ["hours", "sun"], + ["g", "d"], + ["g", "dh"], + ["g", "dhv"], + ["g", "dhvn"], + ["g", "dhvnz"], + ["g", "dhvnzw"], + ["g", "dhvnzwq"], + [ + "image", + "https://blossom.westernbtc.com/15e5b8c56a347859fa293b80c4e949ae168407da364ca11b36854e2f46906402.webp" + ], + [ + "image", + "https://blossom.westernbtc.com/2d9ec90fc5f1fcdd0ba4119d63b48fddd7f71b003eeb36877a810c8d416ddb2d.webp" + ], + [ + "image", + "https://blossom.westernbtc.com/4acd5cb944f5ef94ea49e1a3d5a73fd5eb47dcfa96ca76530b7bdac1c8ed485e.webp" + ], + ["payment_method", "USD", "credit_card"], + ["payment_method", "USD", "cash"], + ["payment_method", "BTC", "onchain"], + ["payment_method", "BTC", "lightning"] + ], + "content": "The worlds first (and probably last) floating RV park!", + "id": "4efb43978b62b02b228b39fc9dbaa8cbed8ce787144601f362da011c32ae6ec7", + "sig": "ef176c438d72ad905e97fe4392662b5f7cf328f134d697e01d8ddbd1e6c6250926a90ef362c9437cdadf786802c3e18bed9386ac483f501b48beecdb6b6596fe" +} +``` + +--- + +## 7. Client Guidance + +- Clients may choose to `limit` returned events at a more coarse precision. +- For map tiles/zoom levels, choose the geohash precision to query: + - World/continent: 1–2 chars + - Region/state: 3–4 + - Metro/city: 5–6 + - Neighborhood/POI: 7–9 +- Query one or more `#g` values covering the viewport. (For polygons, derive covering geohash buckets and query all of them.) + +--- + +## 8. Examples + +- https://rvparker.westernbtc.com + +## 9. Validation Rules and Privacy + +- Must have at least one `["d", ...]` and one `["g", ...]`. +- `["g", ...]` must be valid Base32 geohash, lowercase, no `a,i,l,o`. +- Publishing precise geohashes reveals location; authors may choose a coarser precision if needed. diff --git a/README.md b/README.md index 0f70482d4f..0f89bd8879 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos - [NIP-38: User Statuses](38.md) - [NIP-39: External Identities in Profiles](39.md) - [NIP-40: Expiration Timestamp](40.md) +- [NIP-41: Physical Locations](41.md) - [NIP-42: Authentication of clients to relays](42.md) - [NIP-43: Relay Access Metadata and Requests](43.md) - [NIP-44: Encrypted Payloads (Versioned)](44.md) @@ -78,6 +79,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos - [NIP-60: Cashu Wallet](60.md) - [NIP-61: Nutzaps](61.md) - [NIP-62: Request to Vanish](62.md) +- [NIP-63: Physical Space Rentals](63.md) - [NIP-64: Chess (PGN)](64.md) - [NIP-65: Relay List Metadata](65.md) - [NIP-66: Relay Discovery and Liveness Monitoring](66.md) @@ -112,6 +114,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos - [NIP-EE: E2EE Messaging using MLS Protocol](EE.md) --- **unrecommended**: superseded by the [Marmot Protocol](https://github.com/marmot-protocol/marmot) ## Event Kinds + | kind | description | NIP | | ------------- | ------------------------------- | -------------------------------------- | | `0` | User Metadata | [01](01.md) | @@ -270,7 +273,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos | `31234` | Draft Event | [37](37.md) | | `31388` | Link Set | [Corny Chat][cornychat-linkset] | | `31890` | Feed | [NUD: Custom Feeds][NUD: Custom Feeds] | -| `31922` | Date-Based Calendar Event | [52](52.md) | +| `31922` | Date-Based Calendar Event | [52](52.md), [63](63.md) | | `31923` | Time-Based Calendar Event | [52](52.md) | | `31924` | Calendar | [52](52.md) | | `31925` | Calendar Event RSVP | [52](52.md) | @@ -278,12 +281,18 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos | `31990` | Handler information | [89](89.md) | | `32267` | Software Application | | | `32388` | User Room Favorites | [Corny Chat][cornychat-roomfavorites] | +| `33000` | Physical Location | [41](41.md) | +| `33001` | Space Class | [63](63.md) | +| `33002` | Space | [63](63.md) | +| `33003` | Availability Snapshot | [63](63.md) | +| `33012` | Pricing Configuration | [63](63.md) | +| `33013` | Rental Policy | [63](63.md) | | `33388` | High Scores | [Corny Chat][cornychat-highscores] | | `34388` | Sound Effects | [Corny Chat][cornychat-soundeffects] | | `34550` | Community Definition | [72](72.md) | +| `37516` | Geocache listing | [geocaching](geocaching) | | `38172` | Cashu Mint Announcement | [87](87.md) | | `38173` | Fedimint Announcement | [87](87.md) | -| `37516` | Geocache listing | [geocaching](geocaching) | | `38383` | Peer-to-peer Order events | [69](69.md) | | `39000-9` | Group metadata events | [29](29.md) | | `39089` | Starter packs | [51](51.md) | @@ -309,7 +318,6 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos [nostr-epoxy]: https://github.com/Origami74/nostr-epoxy-reverse-proxy [marmot]: https://github.com/marmot-protocol/marmot - ## Message types ### Client to Relay @@ -336,79 +344,79 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos ## Common Tags -| name | value | other parameters | NIP | -| ----------------- | ------------------------------------ | ------------------------------- | -------------------------------------------------- | -| `a` | coordinates to an event | relay URL | [01](01.md) | -| `A` | root address | relay URL | [22](22.md) | -| `c` | commit id | | [34](34.md) | -| `d` | identifier | -- | [01](01.md) | -| `e` | event id (hex) | relay URL, marker, pubkey (hex) | [01](01.md), [10](10.md) | -| `E` | root event id | relay URL | [22](22.md) | -| `f` | currency code | -- | [69](69.md) | -| `g` | geohash | -- | [52](52.md) | -| `h` | group id | -- | [29](29.md) | -| `i` | external identity | proof, url hint | [35](35.md), [39](39.md), [73](73.md) | -| `I` | root external identity | -- | [22](22.md) | -| `k` | kind | -- | [18](18.md), [25](25.md), [72](72.md), [73](73.md) | -| `K` | root scope | -- | [22](22.md) | -| `l` | label, label namespace, language name| -- | [32](32.md), [C0](C0.md) | -| `L` | label namespace | -- | [32](32.md) | -| `m` | MIME type | -- | [94](94.md) | -| `p` | pubkey (hex) | relay URL, petname | [01](01.md), [02](02.md), [22](22.md) | -| `P` | pubkey (hex) | -- | [22](22.md), [57](57.md) | -| `q` | event id (hex) | relay URL, pubkey (hex) | [18](18.md) | -| `r` | a reference (URL, etc) | -- | [24](24.md), [25](25.md) | -| `r` | relay url | marker | [65](65.md) | -| `s` | status | -- | [69](69.md) | -| `t` | hashtag | -- | [24](24.md), [34](34.md), [35](35.md) | -| `u` | url | -- | [61](61.md), [98](98.md) | -| `x` | hash | -- | [35](35.md), [56](56.md) | -| `y` | platform | -- | [69](69.md) | -| `z` | order number | -- | [69](69.md) | -| `-` | -- | -- | [70](70.md) | -| `alt` | summary | -- | [31](31.md) | -| `amount` | millisatoshis, stringified | -- | [57](57.md) | -| `bolt11` | `bolt11` invoice | -- | [57](57.md) | -| `branch-name` | branch name suggestion | -- | [34](34.md) | -| `challenge` | challenge string | -- | [42](42.md) | -| `client` | name, address | relay URL | [89](89.md) | -| `clone` | git clone URL | -- | [34](34.md) | -| `content-warning` | reason | -- | [36](36.md) | -| `delegation` | pubkey, conditions, delegation token | -- | [26](26.md) | -| `dep` | Required dependency | -- | [C0](C0.md) | -| `description` | description | -- | [34](34.md), [57](57.md), [58](58.md), [C0](C0.md) | -| `emoji` | shortcode, image URL | -- | [30](30.md) | -| `encrypted` | -- | -- | [90](90.md) | -| `extension` | File extension | -- | [C0](C0.md) | -| `expiration` | unix timestamp (string) | -- | [40](40.md) | -| `file` | full path (string) | -- | [35](35.md) | -| `goal` | event id (hex) | relay URL | [75](75.md) | -| `merge-base` | commit id | | [34](34.md) | -| `HEAD` | `ref: refs/heads/` | | [34](34.md) | -| `image` | image URL | dimensions in pixels | [23](23.md), [52](52.md), [58](58.md) | -| `imeta` | inline metadata | -- | [92](92.md) | -| `license` | License of the shared content | -- | [C0](C0.md) | -| `lnurl` | `bech32` encoded `lnurl` | -- | [57](57.md) | -| `location` | location string | -- | [52](52.md), [99](99.md) | -| `name` | name | -- | [34](34.md), [58](58.md), [72](72.md), [C0](C0.md) | -| `nonce` | random | difficulty | [13](13.md) | -| `preimage` | hash of `bolt11` invoice | -- | [57](57.md) | -| `price` | price | currency, frequency | [99](99.md) | -| `proxy` | external ID | protocol | [48](48.md) | -| `published_at` | unix timestamp (string) | -- | [23](23.md), [B0](B0.md) | -| `relay` | relay url | -- | [42](42.md), [17](17.md) | -| `relays` | relay list | -- | [57](57.md) | -| `repo` | Reference to the origin repository | -- | [C0](C0.md) | -| `runtime` | Runtime or environment specification | -- | [C0](C0.md) | -| `server` | file storage server url | -- | [96](96.md) | -| `sound` | shortcode, sound url, image url | -- | [51](51.md) | -| `subject` | subject | -- | [14](14.md), [17](17.md), [34](34.md) | -| `summary` | summary | -- | [23](23.md), [52](52.md) | -| `thumb` | badge thumbnail | dimensions in pixels | [58](58.md) | -| `title` | title | -- | [23](23.md), [B0](B0.md) | -| `tracker` | torrent tracker URL | -- | [35](35.md) | -| `web` | webpage URL | -- | [34](34.md) | -| `zap` | pubkey (hex), relay URL | weight | [57](57.md) | +| name | value | other parameters | NIP | +| ----------------- | ------------------------------------- | ------------------------------- | -------------------------------------------------- | +| `a` | coordinates to an event | relay URL | [01](01.md) | +| `A` | root address | relay URL | [22](22.md) | +| `c` | commit id | | [34](34.md) | +| `d` | identifier | -- | [01](01.md) | +| `e` | event id (hex) | relay URL, marker, pubkey (hex) | [01](01.md), [10](10.md) | +| `E` | root event id | relay URL | [22](22.md) | +| `f` | currency code | -- | [69](69.md) | +| `g` | geohash | -- | [52](52.md) | +| `h` | group id | -- | [29](29.md) | +| `i` | external identity | proof, url hint | [35](35.md), [39](39.md), [73](73.md) | +| `I` | root external identity | -- | [22](22.md) | +| `k` | kind | -- | [18](18.md), [25](25.md), [72](72.md), [73](73.md) | +| `K` | root scope | -- | [22](22.md) | +| `l` | label, label namespace, language name | -- | [32](32.md), [C0](C0.md) | +| `L` | label namespace | -- | [32](32.md) | +| `m` | MIME type | -- | [94](94.md) | +| `p` | pubkey (hex) | relay URL, petname | [01](01.md), [02](02.md), [22](22.md) | +| `P` | pubkey (hex) | -- | [22](22.md), [57](57.md) | +| `q` | event id (hex) | relay URL, pubkey (hex) | [18](18.md) | +| `r` | a reference (URL, etc) | -- | [24](24.md), [25](25.md) | +| `r` | relay url | marker | [65](65.md) | +| `s` | status | -- | [69](69.md) | +| `t` | hashtag | -- | [24](24.md), [34](34.md), [35](35.md) | +| `u` | url | -- | [61](61.md), [98](98.md) | +| `x` | hash | -- | [35](35.md), [56](56.md) | +| `y` | platform | -- | [69](69.md) | +| `z` | order number | -- | [69](69.md) | +| `-` | -- | -- | [70](70.md) | +| `alt` | summary | -- | [31](31.md) | +| `amount` | millisatoshis, stringified | -- | [57](57.md) | +| `bolt11` | `bolt11` invoice | -- | [57](57.md) | +| `branch-name` | branch name suggestion | -- | [34](34.md) | +| `challenge` | challenge string | -- | [42](42.md) | +| `client` | name, address | relay URL | [89](89.md) | +| `clone` | git clone URL | -- | [34](34.md) | +| `content-warning` | reason | -- | [36](36.md) | +| `delegation` | pubkey, conditions, delegation token | -- | [26](26.md) | +| `dep` | Required dependency | -- | [C0](C0.md) | +| `description` | description | -- | [34](34.md), [57](57.md), [58](58.md), [C0](C0.md) | +| `emoji` | shortcode, image URL | -- | [30](30.md) | +| `encrypted` | -- | -- | [90](90.md) | +| `extension` | File extension | -- | [C0](C0.md) | +| `expiration` | unix timestamp (string) | -- | [40](40.md) | +| `file` | full path (string) | -- | [35](35.md) | +| `goal` | event id (hex) | relay URL | [75](75.md) | +| `merge-base` | commit id | | [34](34.md) | +| `HEAD` | `ref: refs/heads/` | | [34](34.md) | +| `image` | image URL | dimensions in pixels | [23](23.md), [52](52.md), [58](58.md) | +| `imeta` | inline metadata | -- | [92](92.md) | +| `license` | License of the shared content | -- | [C0](C0.md) | +| `lnurl` | `bech32` encoded `lnurl` | -- | [57](57.md) | +| `location` | location string | -- | [52](52.md), [99](99.md) | +| `name` | name | -- | [34](34.md), [58](58.md), [72](72.md), [C0](C0.md) | +| `nonce` | random | difficulty | [13](13.md) | +| `preimage` | hash of `bolt11` invoice | -- | [57](57.md) | +| `price` | price | currency, frequency | [99](99.md) | +| `proxy` | external ID | protocol | [48](48.md) | +| `published_at` | unix timestamp (string) | -- | [23](23.md), [B0](B0.md) | +| `relay` | relay url | -- | [42](42.md), [17](17.md) | +| `relays` | relay list | -- | [57](57.md) | +| `repo` | Reference to the origin repository | -- | [C0](C0.md) | +| `runtime` | Runtime or environment specification | -- | [C0](C0.md) | +| `server` | file storage server url | -- | [96](96.md) | +| `sound` | shortcode, sound url, image url | -- | [51](51.md) | +| `subject` | subject | -- | [14](14.md), [17](17.md), [34](34.md) | +| `summary` | summary | -- | [23](23.md), [52](52.md) | +| `thumb` | badge thumbnail | dimensions in pixels | [58](58.md) | +| `title` | title | -- | [23](23.md), [B0](B0.md) | +| `tracker` | torrent tracker URL | -- | [35](35.md) | +| `web` | webpage URL | -- | [34](34.md) | +| `zap` | pubkey (hex), relay URL | weight | [57](57.md) | Please update these lists when proposing new NIPs.