Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions 41.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# 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:** `30300`
- **Type:** Addressable (NIP-01)
- **Parameterized key:** the first `["d", <identifier>]` tag (e.g., a business permit, slug, or another stable ID)

## 3. Required/Recommended Fields

### 3.1. Required fields/tags

- `["d", "<stable-id>"]` — stable place identifier (e.g., `permit:FL:44-54-00003` or `slug:lochloosa-harbor`)
- At least one `["g", "<geohash>"]` tag.
The **recommended pattern** is to include a **ladder** of geohash prefixes from coarse to fine precision (e.g., `d`, `dj`, `dj3`, `dj3p`, …). This enables fast map queries with `#g` matching at various zoom levels.

### 3.2. Recommended tags

- `["name", "<display name>"]`
- `["r", "<canonical url>"]` — official website or listing
- `["image", "<absolute url>"]` — cover image
- `["phone", "<E.164 or human>"]`
- `["addr", "<street>", "<city>", "<region>", "<postal>", "<country>"]`
- `["t", "<type>", ...]` — freeform type tags (e.g., `business`, `rv_park`, `restaurant`, `accepts_btc`) used to reduce results for usecase
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably ok, but hashtags are semantically different from labels. NIP 32 l/L tags might be better here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So my thought here is to be able to filter specific types because this nip is for general physical locations. It seems realistic to only want to see rv parks for example.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, which is more what labels are for. Hashtags are more like #independencedayrvcampout, rather than categorization if you think about how people naturally use them. But it's fine if you prefer t.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a better indexable character for this usecase?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

c for "category" has been used once or twice. The problem with categories is they imply hierarchy, while labeling implies subjectivity and multiplicity. Is there something you don't like about l?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Misunderstood what you said about 'l'. Seems the most appropriate so I'll switch it.

- Provider cross-refs, inventory, and flags as needed—for example:
- `["provider", "<name>", "<id-or-slug>"]`
- `["rv_spaces", "<count>"]`
- `["accepts_btc", "yes"|"no"]`

### 3.3. Optional tags

- Hours, amenities, compliance, etc. (namespaced if domain-specific):
- `["hours", "Mo-Su 08:00-20:00"]` (freeform for now)
- `["amenity", "wifi"]`, `["amenity", "laundry"]`, …
- `["permit", "jurisdiction", "id"]` (if you don’t use it in `d`)

### 3.4. Content

- `content` is a short human description (markdown or plain text). Rich, machine-readable details should live in tags to remain filterable.

---

## 4. Geohash (`g`) Tag Rules

- A `g` tag value is a standard **Base32 geohash** (lowercase), length 1–12.
- Include **multiple** `g` tags forming a **coarse→fine ladder** for the same point, e.g.:

```
["g","d"]
["g","dj"]
["g","dj3"]
["g","dj3p"]
["g","dj3pz"]
["g","dj3pzs"]
["g","dj3pzsu"]
["g","dj3pzsuc"]
```

- Reason: relays and clients can match `#g` **exactly** at any zoom level without needing prefix search. The Place then appears in map queries that request any of those zoom-appropriate buckets.

> Publishers **should** generate the ladder from a single point (lat/lon → geohash) and decide the deepest precision they care to expose (e.g., 7–9 chars for point-of-interest).

---

## 5. Querying

### 5.1. Relay filters

- Fetch all Places within a geohash bucket:

```json
{ "kinds": [30300], "#g": ["dj3pzs"], "#t": ["rv_park"] }
```

- Zoom out: use shorter prefixes already present as `#g` values (e.g., `["dj3p"]`).

- Fetch/refresh a specific place (parameterized key):

```json
{
"kinds": [30300],
"authors": ["<pubkey>"],
"#d": ["permit:FL:44-54-00003"]
}
```

---

## 6. Example (valid per this NIP)

```json
{
"kind": 30300,
"pubkey": "598cece47f7ed9516a30d43f0045b6cfb78454af3829c18a941c4196959345ee",
"created_at": 1758720595,
"tags": [
["d", "permit:FL:44-54-00003"],
["t", "business"],
["t", "rv_park"],
["t", "accepts_btc"],
["name", "CSTP RENTALS, LLC"],
["r", "https://www.mhvillage.com/parks/121442"],
[
"image",
"https://assets.mhvillage.com/assets/image/search-results/home-gray-600.jpg"
],
["phone", "(305) 451-5880"],
["addr", "95350 Overseas Highway", "Key Largo", "FL", "33037", "USA"],
["provider", "campspot", "lochloosa-harbor"],
["rv_spaces", "28"],
["booking_url", "https://www.mhvillage.com/parks/121442"],
["g", "d"],
["g", "dh"],
["g", "dhq"],
["g", "dhqy"],
["g", "dhqy8"],
["g", "dhqy82"],
["g", "dhqy82n"]
],
"content": "CSTP Rentals, LLC mobile home park located in Key Largo, FL. All-Ages community mobile homes for sale. View lots, community details, photos, and more.",
"id": "8121da23881ebfa4be496392a78130699d97e434206c2995465d0df8cbc9fa40",
"sig": "dd00e06be69158902987e1c5d643d47b284e52f367988e62e1d644a6f0d15fb5b0c5e86ccd954c4117a10df435b15825568fd07d533b447c2062450d0b2f0f0f"
}
```

---

## 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.
- Clients should treat unauthenticated data as unverified and display provenance.