From 280db6d85851b32c3d63ee0d3066e55d7c4c31fb Mon Sep 17 00:00:00 2001 From: Luke Rohenaz Date: Wed, 26 Aug 2026 18:58:12 +0000 Subject: [PATCH 01/13] feat: add BRC-229 Game World System Define a MAP key convention for placing objects in Earth and seeded game worlds using Web Mercator quadkeys, join tags, spend-lineage updates, and prefix/viewport queries. --- README.md | 1 + SUMMARY.md | 1 + apps/0229.md | 270 +++++++++++++++++++++++++++++++++++++++++++++++++ apps/README.md | 1 + 4 files changed, 273 insertions(+) create mode 100644 apps/0229.md diff --git a/README.md b/README.md index 7e56cce5..e7fc6af0 100644 --- a/README.md +++ b/README.md @@ -235,6 +235,7 @@ BRC | Standard 226 | [Miner-Enforced Resale-Royalty Covenant Tokens (OP_PUSH_TX)](./tokens/0226.md) 227 | [Frictionless On-Chain Onboarding via Pre-Funded Claimable Tokens](./apps/0227.md) 228 | [Unlinkable Payments under the Identity Paradigm](./payments/0228.md) +229 | [Game World System (GWS)](./apps/0229.md) ## License diff --git a/SUMMARY.md b/SUMMARY.md index 4cd46a51..a8b13658 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -20,6 +20,7 @@ * [NotaryHash — Privacy-Preserving Signed-Hash Notarization with SPV-Verifiable Certificates](./apps/0220.md) * [Block Media Format (BMF) — Composable On-Chain Audio/Video](./apps/0224.md) * [Frictionless On-Chain Onboarding via Pre-Funded Claimable Tokens](./apps/0227.md) +* [Game World System (GWS)](./apps/0229.md) ## Wallet diff --git a/apps/0229.md b/apps/0229.md new file mode 100644 index 00000000..e36529c7 --- /dev/null +++ b/apps/0229.md @@ -0,0 +1,270 @@ +# BRC-229: Game World System (GWS) + +Luke Rohenaz (rohenaz@gmail.com) + +## Abstract + +This BRC defines the **Game World System (GWS)**: a key convention for placing objects in — and across — worlds, using on-chain key/value tags. A location is a **Web Mercator quadtree cell**. Worlds are partitioned by a **world id** (a seed). Earth is the default world. One transaction MAY attach several identifiers to the same output so clients can join them (a URL on a place, a UPC on a shelf, a foreign txid on a pin). A later spend of a tagged output moves or unplaces the object. Indexers expose **prefix** and **viewport** queries; this document does not name a product. + +GWS does not invent a new `OP_RETURN` framing. It rides on the Magic Attribute Protocol (MAP) `SET` / `DELETE` command already used to attach arbitrary keys to a transaction. This BRC specifies only the GWS keys, the cell encoding, update lineage, and query predicates. + +## Motivation + +Maps, AR clients, and games all need the same primitive: *what is here?* "Here" may be a street corner on Earth or a plot in a seeded world that reuses Earth's coordinate system. Without a shared cell id, world id, and update rule, every app invents a private tag and no indexer can answer a region query for all of them. + +A cell id that subdivides a latitude/longitude rectangle spends equal bits on lon and lat. East–west metres per degree shrink as `cos(latitude)`, so those cells stay about the same height and become tall and skinny toward the poles. The same defect appears in a naive lat/lng quadtree. The Web Mercator tile pyramid avoids it: the ellipsoid is mapped to a square, then that square is quartered. Cells stay square in the projection (consistent aspect on the ground). Resolution gets finer toward the projection cutoff; the poles themselves are outside the square (about ±85.05°). + +GWS therefore uses **one** cell encoding — a Bing-style **quadkey** over that pyramid — so a prefix is a parent cell, and a region query is a prefix (plus ancestor) lookup. + +3D models and other payloads are out of scope. Content MAY be attached to the same output by other protocols. + +## Specification + +The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted as described in RFC 2119[1](#footnote-1). + +### 1. MAP carriage + +GWS keys are MAP keys. Readers MUST scan an output's script for a MAP command: a data push of the ASCII Bitcom protocol id + +``` +1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5 +``` + +followed by a command push `SET` or `DELETE`, then zero or more key/value push pairs (`DELETE` MAY omit values). MAP MAY share the output with other protocols. When protocols are concatenated, the separator is a push of the single byte `0x7c` (`|`). Inscriptions or other locking payload MAY precede `OP_RETURN`. + +Illustrative shape (one output): + +``` + +OP_RETURN + 1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5 + SET + gws.cell 032010110301120232 + gws.world earth + url https://example.invalid/place +``` + +Unknown keys MUST be ignored by readers that do not implement them. If a key is pushed more than once in one command, the **last** pair wins. A `SET` that includes `gws.cell` **places** the output. A `DELETE` of `gws.cell` with no later `SET` of `gws.cell` on that output **unplaces** it. + +The `gws.` prefix is reserved by this BRC. Writers MUST NOT emit `gws.*` keys other than those defined here. + +### 2. Keys + +| Key | Value | Rule | +|-----|--------|------| +| `gws.cell` | Quadkey (section 4) | MUST on a placed output. ASCII digits `0`–`3`, length 1..30. | +| `gws.world` | World id (section 3) | MAY. Omitted or empty means Earth. | + +Presence of a valid `gws.cell` is the GWS signal. Writers MAY also set MAP `app` or `type`; indexers MUST NOT require them. + +Any other MAP keys on the same command are **join tags**. A client that has one identifier can look up the others on that output. Common join keys (not owned by GWS; listed so writers pick the same spelling): + +| Key | Value | +|-----|--------| +| `url` | URI | +| `upc` | UPC / EAN / GTIN | +| `tx` | Hex txid (this chain or as labeled by the writer) | +| `btctx` | Bitcoin txid | +| `ethtx` | Ethereum txid | +| `name` | Display name | + +One output MAY carry several join keys together with `gws.cell` (location + URL, location + UPC, location + foreign txid, and so on). + +### 3. Worlds + +A **world** is a partition of the cell space. The same cell in two worlds is not the same place. + +- **Earth** is the default world. It is the world of a record whose `gws.world` is omitted, empty, or the ASCII string `earth`. +- Any other world id is the exact UTF-8 bytes of the `gws.world` push (case-sensitive). Length MUST be 1..128 bytes. Writers SHOULD use NFC. +- A world id is a seed, not a registry. Apps that need uniqueness SHOULD namespace (`myapp:`) or use a hex hash. + +Indexers MUST treat omitted, empty, and `earth` as one world, and MUST NOT mix that world with any other id. + +### 4. Cell encoding (Web Mercator quadkey) + +The cell space is the standard Web Mercator / slippy-map tile pyramid (EPSG:3857)[2](#footnote-2) over WGS 84[3](#footnote-3). The on-chain id is a **quadkey**[4](#footnote-4): the path from the root square to a tile, one digit per zoom level. + +#### 4.1 Projection limits + +``` +MAX_LAT = atan(sinh(π)) × 180/π + ≈ 85.0511287798 degrees +``` + +Longitude is the half-open interval `[-180, 180)`. Latitude outside `[-MAX_LAT, MAX_LAT]` is outside the projection. Writers MUST clip latitude to that range before encoding. Writers MUST reduce longitude with + +``` +λ ← ((λ + 180) mod 360) - 180 +``` + +so that `+180` becomes `-180`. + +#### 4.2 WGS 84 point → tile `(z, x, y)` + +`z` is the zoom (cell precision), an integer `1..30`. Let `n = 2^z`. + +``` +x = floor( (λ + 180) / 360 × n ) +y = floor( (1 − ln((1 + sin φ) / (1 − sin φ)) / (2π)) / 2 × n ) +``` + +`φ` is clipped latitude in radians; `λ` is reduced longitude in degrees. Then clamp: if `x = n` then `x = 0`; `y` MUST be clamped to `[0, n-1]`. + +`floor` assigns a point on an east or south tile edge to the eastern / southern neighbour. The north and west edges belong to the tile. + +`(z, x, y)` is the computational form. It is not written on chain. + +#### 4.3 Tile → quadkey + +``` +cell = "" +for i from z-1 down to 0: + digit = 0 + mask = 1 << i + if (x & mask) ≠ 0: digit += 1 + if (y & mask) ≠ 0: digit += 2 + append ASCII('0' + digit) to cell +``` + +Digits on the map (y grows south, as in web tiles): + +``` ++---+---+ +| 0 | 1 | ++---+---+ +| 2 | 3 | ++---+---+ +``` + +`len(cell) = z`. `gws.cell` MUST match `^[0-3]{1,30}$`. Indexers MUST ignore an invalid `gws.cell` as a placement. + +#### 4.4 Quadkey → tile + +``` +z = len(cell) +x = 0 +y = 0 +for i, ch in enumerate(cell): + bit = z - 1 - i + d = ch - '0' + if d & 1: x |= (1 << bit) + if d & 2: y |= (1 << bit) +``` + +#### 4.5 Tile → WGS 84 bbox + +``` +west = x / n × 360 - 180 +east = (x + 1) / n × 360 - 180 +north = atan(sinh(π × (1 - 2y / n))) × 180/π +south = atan(sinh(π × (1 - 2(y+1) / n))) × 180/π +``` + +A point lies in the unique tile produced by section 4.2. These bbox formulas are the inverse of that assignment. + +#### 4.6 Parents, children, prefixes + +| Relation | Rule | +|----------|------| +| Parent | Drop the last digit. The root (whole projected square) has no digit and is not written as a tag. | +| Children | Append `0`, `1`, `2`, or `3`. | +| Ancestor | `A` is an ancestor of `C` iff `A` is a prefix of `C`. | +| Prefix match | `C` matches prefix `P` iff `C = P` or `C` starts with `P`. | + +A prefix query for `P` returns tags at `P` and all finer cells inside it. + +#### 4.7 Choosing `z` + +`z` is the object's size, not a map zoom the user happens to be looking at. Writers SHOULD pick a cell whose ground size is about the object: + +| `z` | Approx. equator edge | +|-----|----------------------| +| 10 | 39 km | +| 14 | 2.4 km | +| 16 | 611 m | +| 18 | 153 m | +| 20 | 38 m | +| 22 | 9.6 m | +| 24 | 2.4 m | + +Ground edge length scales as `cos(latitude)` times the equator figure (the cells stay square). Equator edge ≈ `40075016.69 / 2^z` metres. + +#### 4.8 Test vectors + +| lon | lat | z | x | y | `gws.cell` | +|-----|-----|---|-----|-----|------------| +| -74.0445 | 40.6892 | 18 | 77154 | 98583 | `032010110301120232` | +| -74.0445 | 40.6892 | 16 | 19288 | 24645 | `0320101103011202` | +| 0 | 0 | 1 | 1 | 1 | `3` | + +The first row's bbox is approximately west −74.045105, south 40.688969, east −74.043732, north 40.690010. Parent of the z=18 cell is the z=16 cell shown. + +### 5. Objects and updates + +A **GWS output** is an output that contains a MAP `SET` of a valid `gws.cell`. + +**Origin.** The first GWS output in a spend chain that does not spend a GWS output. The origin outpoint identifies the object. + +**Move.** A later transaction that spends a GWS output and creates a GWS output is a move of that object. The new output is the successor. The spend **is** the authorization: whoever can spend the current output can move it. An extra identity signature over the MAP payload MAY be attached by any identity protocol on the output; indexers that understand that protocol SHOULD record the signer. Indexers MUST NOT require a particular identity protocol. + +**Unplace.** A spend of a GWS output that does not create a successor (no valid `gws.cell` on any paired output) removes the object from the spatial index. History MUST be retained. + +**Pairing** when a transaction has several GWS inputs or outputs, in script order: + +| GWS inputs | GWS outputs | Meaning | +|------------|-------------|---------| +| 1 | 1 | Move that object | +| N | N | Move: pair by index | +| N | 0 | Unplace each input object | +| 0 | M | M new origins | +| N | M, N ≠ M | Pair `min(N,M)` by index; leftover inputs are unplaced; leftover outputs are new origins | + +**Tip.** The current record is the unspent GWS output reachable from the origin by following paired successors. If the last spend unplaced the object, there is no current cell. + +**Order.** Confirmed before unconfirmed. Then block height, transaction index in the block, output index. A reorg rewinds tips. Indexers MAY show an unconfirmed successor as the provisional tip. + +**History.** An indexer MUST be able to emit the ordered list of `(outpoint, cell, world, join keys)` from origin to tip (and the unplace, if any). + +Tags are claims, not proof of presence. Anyone MAY place a cell; only the current signer can move that object. + +### 6. Query + +This section is the predicate. It is not an HTTP API and not a particular indexer. + +Let `world(q)` be Earth when the query omits a world id or passes `earth`; otherwise the exact id. + +A stored record **matches world** iff its world equals `world(q)` under section 3. + +**Prefix query.** Given `world` and prefix `P` (a quadkey, or empty `P` for the whole projection): every current tip that matches the world and whose `gws.cell` prefix-matches `P` (section 4.6). + +**Viewport query.** Given `world` and a WGS 84 box `(west, south, east, north)`, with optional clip of `south`/`north` to `±MAX_LAT`: + +1. If the box crosses the antimeridian (`west > east`), split into `(west, south, 180, north)` and `(-180, south, east, north)` and union the results. +2. A record matches iff it matches the world **and** its cell bbox (section 4.5) **intersects** the (clipped, split) box. + +Intersect includes descendants (finer tags inside the view), the covering cells, **and ancestors** (a coarse city-sized tag still appears when the view is a street). Empty `P` / a view of the whole projection returns every current tip in that world. + +A sufficient algorithm for intersect using only prefix lookups: take the tiles at some zoom `z` that overlap the box; for each tile `T`, query prefix(`T`) and also the exact cells of every ancestor of `T`. Implementations MAY use any spatial index that returns the same set. + +Indexers MUST implement prefix and viewport as defined here. They MAY add filters (join key, time, origin). They MUST return at least: origin outpoint, tip outpoint, `gws.cell`, world, remaining MAP keys on the tip, txid, and confirmation height when mined. + +### 7. Out of scope + +- Content bytes (images, inscriptions, 3D models). Attach them with other protocols on the same output; GWS does not read them. +- A geographic-rectangle cell id. Writers that want GWS interop MUST use section 4. +- Overlay wire formats, payment, or wallet baskets. + +## Implementations + +A compatible implementation tags outputs as in sections 1–5, answers section 6, and works for **Earth maps and seeded worlds** with the same cell math. + +**World of Alchema** ([alchema.world](https://alchema.world)) is an existing project in this family — a game world that places things in space. This BRC does not specify Alchema's internals. It exists so Earth-map clients and seeded-world clients can share indexers and join tags. + +Any client that already renders a Web Mercator map or globe can decode `gws.cell` to `(z, x, y)` or a bbox and draw the record without a second projection. + +## References + +- 1: Bradner, S. (1997). *Key words for use in RFCs to Indicate Requirement Levels*. RFC 2119. https://www.rfc-editor.org/rfc/rfc2119 +- 2: IOGP. *WGS 84 / Pseudo-Mercator* — EPSG:3857. The well-known Web Mercator tile pyramid (slippy-map `z/x/y`). +- 3: NIMA. (2000). *Department of Defense World Geodetic System 1984*. TR8350.2. Horizontal positions in this BRC are WGS 84 (EPSG:4326) degrees. +- 4: Microsoft. *Bing Maps Tile System* — quadkey encoding of the Web Mercator pyramid. https://learn.microsoft.com/en-us/bingmaps/articles/bing-maps-tile-system diff --git a/apps/README.md b/apps/README.md index fe5d588b..875ae919 100644 --- a/apps/README.md +++ b/apps/README.md @@ -14,3 +14,4 @@ BRC | Standard 220 | [NotaryHash — Privacy-Preserving Signed-Hash Notarization with SPV-Verifiable Certificates](./0220.md) 224 | [Block Media Format (BMF) — Composable On-Chain Audio/Video](./0224.md) 227 | [Frictionless On-Chain Onboarding via Pre-Funded Claimable Tokens](./0227.md) +229 | [Game World System (GWS)](./0229.md) From 0cc73402116dac97c65a91624c3d4345c300295d Mon Sep 17 00:00:00 2001 From: Luke Rohenaz Date: Wed, 26 Aug 2026 20:51:28 +0000 Subject: [PATCH 02/13] docs(BRC-229): add optional lat/lng join tags Recommend WGS 84 lat and lng on the same MAP SET as gws.cell so key-presence queries can find a point. They do not place the output and are not a substitute cell id. --- apps/0229.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/0229.md b/apps/0229.md index e36529c7..c82bee1c 100644 --- a/apps/0229.md +++ b/apps/0229.md @@ -40,6 +40,8 @@ OP_RETURN 1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5 SET gws.cell 032010110301120232 + lat 40.6892 + lng -74.0445 gws.world earth url https://example.invalid/place ``` @@ -61,6 +63,8 @@ Any other MAP keys on the same command are **join tags**. A client that has one | Key | Value | |-----|--------| +| `lat` | WGS 84 latitude, decimal degrees, ASCII | +| `lng` | WGS 84 longitude, decimal degrees, ASCII | | `url` | URI | | `upc` | UPC / EAN / GTIN | | `tx` | Hex txid (this chain or as labeled by the writer) | @@ -68,6 +72,8 @@ Any other MAP keys on the same command are **join tags**. A client that has one | `ethtx` | Ethereum txid | | `name` | Display name | +`lat` and `lng` are ordinary MAP keys, not `gws.*`. Writers that emit them MUST emit both or neither. They do not place the output; `gws.cell` does. A writer MAY include them so a key-presence query can find the point without decoding the quadkey. A point (`lat` / `lng`) and a cell (`gws.cell`) are different jobs and MAY appear together; `lat` / `lng` MUST NOT be treated as a substitute cell id. + One output MAY carry several join keys together with `gws.cell` (location + URL, location + UPC, location + foreign txid, and so on). ### 3. Worlds From ec4ff2b8dbb018fd112308a5f02b7c1504cc38ae Mon Sep 17 00:00:00 2001 From: Luke Rohenaz Date: Wed, 26 Aug 2026 20:51:42 +0000 Subject: [PATCH 03/13] docs(BRC-229): split point-vs-cell sentence Stand the lat/lng vs gws.cell jobs sentence on its own. --- apps/0229.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/0229.md b/apps/0229.md index c82bee1c..f10f7e6c 100644 --- a/apps/0229.md +++ b/apps/0229.md @@ -72,7 +72,9 @@ Any other MAP keys on the same command are **join tags**. A client that has one | `ethtx` | Ethereum txid | | `name` | Display name | -`lat` and `lng` are ordinary MAP keys, not `gws.*`. Writers that emit them MUST emit both or neither. They do not place the output; `gws.cell` does. A writer MAY include them so a key-presence query can find the point without decoding the quadkey. A point (`lat` / `lng`) and a cell (`gws.cell`) are different jobs and MAY appear together; `lat` / `lng` MUST NOT be treated as a substitute cell id. +`lat` and `lng` are ordinary MAP keys, not `gws.*`. Writers that emit them MUST emit both or neither. They do not place the output; `gws.cell` does. A writer MAY include them so a key-presence query can find the point without decoding the quadkey. + +A point (`lat` / `lng`) and a cell (`gws.cell`) are different jobs and MAY appear together. `lat` / `lng` MUST NOT be treated as a substitute cell id. One output MAY carry several join keys together with `gws.cell` (location + URL, location + UPC, location + foreign txid, and so on). From 795c00b9923b1cb48b1da923eb46a0b194af99ef Mon Sep 17 00:00:00 2001 From: Luke Rohenaz Date: Wed, 26 Aug 2026 21:10:43 +0000 Subject: [PATCH 04/13] refactor(BRC-229): drop GWS branding, plain-word keys Rename gws.cell to cell and gws.world to world; delete the reserved-prefix rule (value grammar is the discriminator). Retitle as MAP Location Keys, generic for bitcoinschema. Note cell is a quadkey, never geohash; other cell encodings stay out of scope. Update all three indices. --- README.md | 2 +- SUMMARY.md | 2 +- apps/0229.md | 82 +++++++++++++++++++++++++------------------------- apps/README.md | 2 +- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index e7fc6af0..760ca754 100644 --- a/README.md +++ b/README.md @@ -235,7 +235,7 @@ BRC | Standard 226 | [Miner-Enforced Resale-Royalty Covenant Tokens (OP_PUSH_TX)](./tokens/0226.md) 227 | [Frictionless On-Chain Onboarding via Pre-Funded Claimable Tokens](./apps/0227.md) 228 | [Unlinkable Payments under the Identity Paradigm](./payments/0228.md) -229 | [Game World System (GWS)](./apps/0229.md) +229 | [MAP Location Keys (cell, world, lat, lng)](./apps/0229.md) ## License diff --git a/SUMMARY.md b/SUMMARY.md index a8b13658..92a74d45 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -20,7 +20,7 @@ * [NotaryHash — Privacy-Preserving Signed-Hash Notarization with SPV-Verifiable Certificates](./apps/0220.md) * [Block Media Format (BMF) — Composable On-Chain Audio/Video](./apps/0224.md) * [Frictionless On-Chain Onboarding via Pre-Funded Claimable Tokens](./apps/0227.md) -* [Game World System (GWS)](./apps/0229.md) +* [MAP Location Keys (cell, world, lat, lng)](./apps/0229.md) ## Wallet diff --git a/apps/0229.md b/apps/0229.md index f10f7e6c..d6d44e8d 100644 --- a/apps/0229.md +++ b/apps/0229.md @@ -1,12 +1,12 @@ -# BRC-229: Game World System (GWS) +# BRC-229: MAP Location Keys (`cell`, `world`, `lat`, `lng`) Luke Rohenaz (rohenaz@gmail.com) ## Abstract -This BRC defines the **Game World System (GWS)**: a key convention for placing objects in — and across — worlds, using on-chain key/value tags. A location is a **Web Mercator quadtree cell**. Worlds are partitioned by a **world id** (a seed). Earth is the default world. One transaction MAY attach several identifiers to the same output so clients can join them (a URL on a place, a UPC on a shelf, a foreign txid on a pin). A later spend of a tagged output moves or unplaces the object. Indexers expose **prefix** and **viewport** queries; this document does not name a product. +This BRC defines plain-word MAP keys for placing things in — and across — worlds, so indexers can answer a region query and clients can render a map or globe. A location is a **Web Mercator quadtree cell** carried in the `cell` key. Parallel worlds (game seeds, simulations) are partitioned by the `world` key; Earth is the default. One transaction MAY attach several identifiers to the same output so clients can join them (a URL on a place, a UPC on a shelf, a foreign txid on a pin). A later spend of a placed output moves or unplaces the thing. Indexers expose **prefix** and **viewport** queries; this document does not name a product. -GWS does not invent a new `OP_RETURN` framing. It rides on the Magic Attribute Protocol (MAP) `SET` / `DELETE` command already used to attach arbitrary keys to a transaction. This BRC specifies only the GWS keys, the cell encoding, update lineage, and query predicates. +This BRC invents no `OP_RETURN` framing. It rides on the Magic Attribute Protocol (MAP) `SET` / `DELETE` command already used to attach arbitrary keys to a transaction, and follows the existing convention of ordinary words as field names. This document specifies only the keys, the cell encoding, update lineage, and query predicates. ## Motivation @@ -14,7 +14,7 @@ Maps, AR clients, and games all need the same primitive: *what is here?* "Here" A cell id that subdivides a latitude/longitude rectangle spends equal bits on lon and lat. East–west metres per degree shrink as `cos(latitude)`, so those cells stay about the same height and become tall and skinny toward the poles. The same defect appears in a naive lat/lng quadtree. The Web Mercator tile pyramid avoids it: the ellipsoid is mapped to a square, then that square is quartered. Cells stay square in the projection (consistent aspect on the ground). Resolution gets finer toward the projection cutoff; the poles themselves are outside the square (about ±85.05°). -GWS therefore uses **one** cell encoding — a Bing-style **quadkey** over that pyramid — so a prefix is a parent cell, and a region query is a prefix (plus ancestor) lookup. +This BRC therefore uses **one** cell encoding — a Bing-style **quadkey** over that pyramid — so a prefix is a parent cell, and a region query is a prefix (plus ancestor) lookup. 3D models and other payloads are out of scope. Content MAY be attached to the same output by other protocols. @@ -24,13 +24,13 @@ The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted ### 1. MAP carriage -GWS keys are MAP keys. Readers MUST scan an output's script for a MAP command: a data push of the ASCII Bitcom protocol id +The location keys are MAP keys. Readers MUST scan an output's script for a MAP command: a data push of the ASCII Bitcom protocol id ``` 1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5 ``` -followed by a command push `SET` or `DELETE`, then zero or more key/value push pairs (`DELETE` MAY omit values). MAP MAY share the output with other protocols. When protocols are concatenated, the separator is a push of the single byte `0x7c` (`|`). Inscriptions or other locking payload MAY precede `OP_RETURN`. +followed by a command push `SET` or `DELETE`, then zero or more key/value push pairs (`DELETE` MAY omit values). MAP MAY share the output with other protocols. When protocols are concatenated, the separator is a push of the single byte `0x7c` (`|`). Inscriptions or other locking payload MAY precede `OP_RETURN` — one output can be, for example, a 1Sat lock plus this MAP `SET`; spending that output is what moves it (section 5). Illustrative shape (one output): @@ -39,32 +39,36 @@ Illustrative shape (one output): OP_RETURN 1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5 SET - gws.cell 032010110301120232 - lat 40.6892 - lng -74.0445 - gws.world earth - url https://example.invalid/place + cell 032010110301120232 + lat 40.6892 + lng -74.0445 + world earth + url https://example.invalid/place ``` -Unknown keys MUST be ignored by readers that do not implement them. If a key is pushed more than once in one command, the **last** pair wins. A `SET` that includes `gws.cell` **places** the output. A `DELETE` of `gws.cell` with no later `SET` of `gws.cell` on that output **unplaces** it. +Unknown keys MUST be ignored by readers that do not implement them. If a key is pushed more than once in one command, the **last** pair wins. A `SET` that includes a valid `cell` **places** the output. A `DELETE` of `cell` with no later `SET` of `cell` on that output **unplaces** it. -The `gws.` prefix is reserved by this BRC. Writers MUST NOT emit `gws.*` keys other than those defined here. +The keys are plain words in the shared MAP key space; no prefix is reserved. The discriminator for `cell` is its value grammar (section 4.3): indexers MUST ignore, as a placement, any `cell` value that does not match it. ### 2. Keys | Key | Value | Rule | |-----|--------|------| -| `gws.cell` | Quadkey (section 4) | MUST on a placed output. ASCII digits `0`–`3`, length 1..30. | -| `gws.world` | World id (section 3) | MAY. Omitted or empty means Earth. | +| `cell` | Quadkey (section 4) | MUST on a placed output. ASCII digits `0`–`3`, length 1..30. | +| `world` | World id (section 3) | MAY. Omitted or empty means Earth. | +| `lat` | WGS 84 latitude, decimal degrees, ASCII | MAY. Both `lat` and `lng` or neither. | +| `lng` | WGS 84 longitude, decimal degrees, ASCII | MAY. Both `lat` and `lng` or neither. | -Presence of a valid `gws.cell` is the GWS signal. Writers MAY also set MAP `app` or `type`; indexers MUST NOT require them. +Presence of a valid `cell` is the placement signal. Writers MAY also set MAP `app` or `type`; indexers MUST NOT require them. -Any other MAP keys on the same command are **join tags**. A client that has one identifier can look up the others on that output. Common join keys (not owned by GWS; listed so writers pick the same spelling): +`lat` and `lng` do not place the output; `cell` does. A writer MAY include them so a key-presence query can find the point without decoding the quadkey. A point (`lat` / `lng`) and a cell (`cell`) are different jobs and MAY appear together. `lat` / `lng` MUST NOT be treated as a substitute cell id. + +`cell` is a quadkey, not a geohash. Geohash is a different encoding with different cell geometry; writers MUST NOT put a quadkey in a `geohash` key or a geohash in `cell`. + +Any other MAP keys on the same command are **join tags**. A client that has one identifier can look up the others on that output. Common join keys (not owned by this BRC; listed so writers pick the same spelling): | Key | Value | |-----|--------| -| `lat` | WGS 84 latitude, decimal degrees, ASCII | -| `lng` | WGS 84 longitude, decimal degrees, ASCII | | `url` | URI | | `upc` | UPC / EAN / GTIN | | `tx` | Hex txid (this chain or as labeled by the writer) | @@ -72,18 +76,14 @@ Any other MAP keys on the same command are **join tags**. A client that has one | `ethtx` | Ethereum txid | | `name` | Display name | -`lat` and `lng` are ordinary MAP keys, not `gws.*`. Writers that emit them MUST emit both or neither. They do not place the output; `gws.cell` does. A writer MAY include them so a key-presence query can find the point without decoding the quadkey. - -A point (`lat` / `lng`) and a cell (`gws.cell`) are different jobs and MAY appear together. `lat` / `lng` MUST NOT be treated as a substitute cell id. - -One output MAY carry several join keys together with `gws.cell` (location + URL, location + UPC, location + foreign txid, and so on). +One output MAY carry several join keys together with `cell` (location + URL, location + UPC, location + foreign txid, and so on). ### 3. Worlds A **world** is a partition of the cell space. The same cell in two worlds is not the same place. -- **Earth** is the default world. It is the world of a record whose `gws.world` is omitted, empty, or the ASCII string `earth`. -- Any other world id is the exact UTF-8 bytes of the `gws.world` push (case-sensitive). Length MUST be 1..128 bytes. Writers SHOULD use NFC. +- **Earth** is the default world. It is the world of a record whose `world` is omitted, empty, or the ASCII string `earth`. +- Any other world id is the exact UTF-8 bytes of the `world` push (case-sensitive). Length MUST be 1..128 bytes. Writers SHOULD use NFC. - A world id is a seed, not a registry. Apps that need uniqueness SHOULD namespace (`myapp:`) or use a hex hash. Indexers MUST treat omitted, empty, and `earth` as one world, and MUST NOT mix that world with any other id. @@ -144,7 +144,7 @@ Digits on the map (y grows south, as in web tiles): +---+---+ ``` -`len(cell) = z`. `gws.cell` MUST match `^[0-3]{1,30}$`. Indexers MUST ignore an invalid `gws.cell` as a placement. +`len(cell) = z`. A `cell` value MUST match `^[0-3]{1,30}$`. Indexers MUST ignore an invalid `cell` as a placement. #### 4.4 Quadkey → tile @@ -199,7 +199,7 @@ Ground edge length scales as `cos(latitude)` times the equator figure (the cells #### 4.8 Test vectors -| lon | lat | z | x | y | `gws.cell` | +| lon | lat | z | x | y | `cell` | |-----|-----|---|-----|-----|------------| | -74.0445 | 40.6892 | 18 | 77154 | 98583 | `032010110301120232` | | -74.0445 | 40.6892 | 16 | 19288 | 24645 | `0320101103011202` | @@ -209,25 +209,25 @@ The first row's bbox is approximately west −74.045105, south 40.688969, east ### 5. Objects and updates -A **GWS output** is an output that contains a MAP `SET` of a valid `gws.cell`. +A **placed output** is an output that contains a MAP `SET` of a valid `cell`. -**Origin.** The first GWS output in a spend chain that does not spend a GWS output. The origin outpoint identifies the object. +**Origin.** The first placed output in a spend chain that does not spend a placed output. The origin outpoint identifies the object. -**Move.** A later transaction that spends a GWS output and creates a GWS output is a move of that object. The new output is the successor. The spend **is** the authorization: whoever can spend the current output can move it. An extra identity signature over the MAP payload MAY be attached by any identity protocol on the output; indexers that understand that protocol SHOULD record the signer. Indexers MUST NOT require a particular identity protocol. +**Move.** A later transaction that spends a placed output and creates a placed output is a move of that object. The new output is the successor. The spend **is** the authorization: whoever can spend the current output can move it. An extra identity signature over the MAP payload MAY be attached by any identity protocol on the output; indexers that understand that protocol SHOULD record the signer. Indexers MUST NOT require a particular identity protocol. -**Unplace.** A spend of a GWS output that does not create a successor (no valid `gws.cell` on any paired output) removes the object from the spatial index. History MUST be retained. +**Unplace.** A spend of a placed output that does not create a successor (no valid `cell` on any paired output) removes the object from the spatial index. History MUST be retained. -**Pairing** when a transaction has several GWS inputs or outputs, in script order: +**Pairing** when a transaction has several placed inputs or outputs, in script order: -| GWS inputs | GWS outputs | Meaning | -|------------|-------------|---------| +| Placed inputs | Placed outputs | Meaning | +|---------------|----------------|---------| | 1 | 1 | Move that object | | N | N | Move: pair by index | | N | 0 | Unplace each input object | | 0 | M | M new origins | | N | M, N ≠ M | Pair `min(N,M)` by index; leftover inputs are unplaced; leftover outputs are new origins | -**Tip.** The current record is the unspent GWS output reachable from the origin by following paired successors. If the last spend unplaced the object, there is no current cell. +**Tip.** The current record is the unspent placed output reachable from the origin by following paired successors. If the last spend unplaced the object, there is no current cell. **Order.** Confirmed before unconfirmed. Then block height, transaction index in the block, output index. A reorg rewinds tips. Indexers MAY show an unconfirmed successor as the provisional tip. @@ -243,7 +243,7 @@ Let `world(q)` be Earth when the query omits a world id or passes `earth`; other A stored record **matches world** iff its world equals `world(q)` under section 3. -**Prefix query.** Given `world` and prefix `P` (a quadkey, or empty `P` for the whole projection): every current tip that matches the world and whose `gws.cell` prefix-matches `P` (section 4.6). +**Prefix query.** Given `world` and prefix `P` (a quadkey, or empty `P` for the whole projection): every current tip that matches the world and whose `cell` prefix-matches `P` (section 4.6). **Viewport query.** Given `world` and a WGS 84 box `(west, south, east, north)`, with optional clip of `south`/`north` to `±MAX_LAT`: @@ -254,12 +254,12 @@ Intersect includes descendants (finer tags inside the view), the covering cells, A sufficient algorithm for intersect using only prefix lookups: take the tiles at some zoom `z` that overlap the box; for each tile `T`, query prefix(`T`) and also the exact cells of every ancestor of `T`. Implementations MAY use any spatial index that returns the same set. -Indexers MUST implement prefix and viewport as defined here. They MAY add filters (join key, time, origin). They MUST return at least: origin outpoint, tip outpoint, `gws.cell`, world, remaining MAP keys on the tip, txid, and confirmation height when mined. +Indexers MUST implement prefix and viewport as defined here. They MAY add filters (join key, time, origin). They MUST return at least: origin outpoint, tip outpoint, `cell`, world, remaining MAP keys on the tip, txid, and confirmation height when mined. ### 7. Out of scope -- Content bytes (images, inscriptions, 3D models). Attach them with other protocols on the same output; GWS does not read them. -- A geographic-rectangle cell id. Writers that want GWS interop MUST use section 4. +- Content bytes (images, inscriptions, 3D models). Attach them with other protocols on the same output; this BRC does not read them. +- Alternate cell encodings (geohash, H3, geographic-rectangle grids). They are lossy against this quadkey and not interchangeable, especially near the poles. Interoperable placements use section 4. - Overlay wire formats, payment, or wallet baskets. ## Implementations @@ -268,7 +268,7 @@ A compatible implementation tags outputs as in sections 1–5, answers section 6 **World of Alchema** ([alchema.world](https://alchema.world)) is an existing project in this family — a game world that places things in space. This BRC does not specify Alchema's internals. It exists so Earth-map clients and seeded-world clients can share indexers and join tags. -Any client that already renders a Web Mercator map or globe can decode `gws.cell` to `(z, x, y)` or a bbox and draw the record without a second projection. +Any client that already renders a Web Mercator map or globe can decode `cell` to `(z, x, y)` or a bbox and draw the record without a second projection. ## References diff --git a/apps/README.md b/apps/README.md index 875ae919..8537a9d4 100644 --- a/apps/README.md +++ b/apps/README.md @@ -14,4 +14,4 @@ BRC | Standard 220 | [NotaryHash — Privacy-Preserving Signed-Hash Notarization with SPV-Verifiable Certificates](./0220.md) 224 | [Block Media Format (BMF) — Composable On-Chain Audio/Video](./0224.md) 227 | [Frictionless On-Chain Onboarding via Pre-Funded Claimable Tokens](./0227.md) -229 | [Game World System (GWS)](./0229.md) +229 | [MAP Location Keys (cell, world, lat, lng)](./0229.md) From c7fabff9cb460fb5ec1756a29175f00f189371c7 Mon Sep 17 00:00:00 2001 From: Luke Rohenaz Date: Wed, 26 Aug 2026 21:17:33 +0000 Subject: [PATCH 05/13] refactor(BRC-229): rename cell key to quadkey Name the key for its encoding, matching the geohash precedent, so the value is decodable on sight and cannot be mislabeled. Drop the plain-word meta-commentary from the abstract. --- README.md | 2 +- SUMMARY.md | 2 +- apps/0229.md | 54 +++++++++++++++++++++++++------------------------- apps/README.md | 2 +- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 760ca754..deb3c5dd 100644 --- a/README.md +++ b/README.md @@ -235,7 +235,7 @@ BRC | Standard 226 | [Miner-Enforced Resale-Royalty Covenant Tokens (OP_PUSH_TX)](./tokens/0226.md) 227 | [Frictionless On-Chain Onboarding via Pre-Funded Claimable Tokens](./apps/0227.md) 228 | [Unlinkable Payments under the Identity Paradigm](./payments/0228.md) -229 | [MAP Location Keys (cell, world, lat, lng)](./apps/0229.md) +229 | [MAP Location Keys (quadkey, world, lat, lng)](./apps/0229.md) ## License diff --git a/SUMMARY.md b/SUMMARY.md index 92a74d45..fe779121 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -20,7 +20,7 @@ * [NotaryHash — Privacy-Preserving Signed-Hash Notarization with SPV-Verifiable Certificates](./apps/0220.md) * [Block Media Format (BMF) — Composable On-Chain Audio/Video](./apps/0224.md) * [Frictionless On-Chain Onboarding via Pre-Funded Claimable Tokens](./apps/0227.md) -* [MAP Location Keys (cell, world, lat, lng)](./apps/0229.md) +* [MAP Location Keys (quadkey, world, lat, lng)](./apps/0229.md) ## Wallet diff --git a/apps/0229.md b/apps/0229.md index d6d44e8d..c3d9d11a 100644 --- a/apps/0229.md +++ b/apps/0229.md @@ -1,12 +1,12 @@ -# BRC-229: MAP Location Keys (`cell`, `world`, `lat`, `lng`) +# BRC-229: MAP Location Keys (`quadkey`, `world`, `lat`, `lng`) Luke Rohenaz (rohenaz@gmail.com) ## Abstract -This BRC defines plain-word MAP keys for placing things in — and across — worlds, so indexers can answer a region query and clients can render a map or globe. A location is a **Web Mercator quadtree cell** carried in the `cell` key. Parallel worlds (game seeds, simulations) are partitioned by the `world` key; Earth is the default. One transaction MAY attach several identifiers to the same output so clients can join them (a URL on a place, a UPC on a shelf, a foreign txid on a pin). A later spend of a placed output moves or unplaces the thing. Indexers expose **prefix** and **viewport** queries; this document does not name a product. +This BRC defines MAP keys for placing things in — and across — worlds, so indexers can answer a region query and clients can render a map or globe. A location is a **Web Mercator quadtree cell** carried in the `quadkey` key. Parallel worlds (game seeds, simulations) are partitioned by the `world` key; Earth is the default. One transaction MAY attach several identifiers to the same output so clients can join them (a URL on a place, a UPC on a shelf, a foreign txid on a pin). A later spend of a placed output moves or unplaces the thing. Indexers expose **prefix** and **viewport** queries; this document does not name a product. -This BRC invents no `OP_RETURN` framing. It rides on the Magic Attribute Protocol (MAP) `SET` / `DELETE` command already used to attach arbitrary keys to a transaction, and follows the existing convention of ordinary words as field names. This document specifies only the keys, the cell encoding, update lineage, and query predicates. +This BRC invents no `OP_RETURN` framing. It rides on the Magic Attribute Protocol (MAP) `SET` / `DELETE` command already used to attach arbitrary keys to a transaction. This document specifies only the keys, the cell encoding, update lineage, and query predicates. ## Motivation @@ -39,31 +39,31 @@ Illustrative shape (one output): OP_RETURN 1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5 SET - cell 032010110301120232 - lat 40.6892 - lng -74.0445 - world earth - url https://example.invalid/place + quadkey 032010110301120232 + lat 40.6892 + lng -74.0445 + world earth + url https://example.invalid/place ``` -Unknown keys MUST be ignored by readers that do not implement them. If a key is pushed more than once in one command, the **last** pair wins. A `SET` that includes a valid `cell` **places** the output. A `DELETE` of `cell` with no later `SET` of `cell` on that output **unplaces** it. +Unknown keys MUST be ignored by readers that do not implement them. If a key is pushed more than once in one command, the **last** pair wins. A `SET` that includes a valid `quadkey` **places** the output. A `DELETE` of `quadkey` with no later `SET` of `quadkey` on that output **unplaces** it. -The keys are plain words in the shared MAP key space; no prefix is reserved. The discriminator for `cell` is its value grammar (section 4.3): indexers MUST ignore, as a placement, any `cell` value that does not match it. +No key prefix is reserved. The discriminator for `quadkey` is its value grammar (section 4.3): indexers MUST ignore, as a placement, any `quadkey` value that does not match it. ### 2. Keys | Key | Value | Rule | |-----|--------|------| -| `cell` | Quadkey (section 4) | MUST on a placed output. ASCII digits `0`–`3`, length 1..30. | +| `quadkey` | Web Mercator quadkey (section 4) | MUST on a placed output. ASCII digits `0`–`3`, length 1..30. | | `world` | World id (section 3) | MAY. Omitted or empty means Earth. | | `lat` | WGS 84 latitude, decimal degrees, ASCII | MAY. Both `lat` and `lng` or neither. | | `lng` | WGS 84 longitude, decimal degrees, ASCII | MAY. Both `lat` and `lng` or neither. | -Presence of a valid `cell` is the placement signal. Writers MAY also set MAP `app` or `type`; indexers MUST NOT require them. +Presence of a valid `quadkey` is the placement signal. Writers MAY also set MAP `app` or `type`; indexers MUST NOT require them. -`lat` and `lng` do not place the output; `cell` does. A writer MAY include them so a key-presence query can find the point without decoding the quadkey. A point (`lat` / `lng`) and a cell (`cell`) are different jobs and MAY appear together. `lat` / `lng` MUST NOT be treated as a substitute cell id. +`lat` and `lng` do not place the output; `quadkey` does. A writer MAY include them so a key-presence query can find the point without decoding the quadkey. A point (`lat` / `lng`) and a cell (`quadkey`) are different jobs and MAY appear together. `lat` / `lng` MUST NOT be treated as a substitute cell id. -`cell` is a quadkey, not a geohash. Geohash is a different encoding with different cell geometry; writers MUST NOT put a quadkey in a `geohash` key or a geohash in `cell`. +The key is named for its encoding, as `geohash` is elsewhere. Geohash is a different encoding with different cell geometry; writers MUST NOT put a quadkey in a `geohash` key or a geohash (or H3, or any other id) in `quadkey`. Any other MAP keys on the same command are **join tags**. A client that has one identifier can look up the others on that output. Common join keys (not owned by this BRC; listed so writers pick the same spelling): @@ -76,7 +76,7 @@ Any other MAP keys on the same command are **join tags**. A client that has one | `ethtx` | Ethereum txid | | `name` | Display name | -One output MAY carry several join keys together with `cell` (location + URL, location + UPC, location + foreign txid, and so on). +One output MAY carry several join keys together with `quadkey` (location + URL, location + UPC, location + foreign txid, and so on). ### 3. Worlds @@ -125,13 +125,13 @@ y = floor( (1 − ln((1 + sin φ) / (1 − sin φ)) / (2π)) / 2 × n ) #### 4.3 Tile → quadkey ``` -cell = "" +quadkey = "" for i from z-1 down to 0: digit = 0 mask = 1 << i if (x & mask) ≠ 0: digit += 1 if (y & mask) ≠ 0: digit += 2 - append ASCII('0' + digit) to cell + append ASCII('0' + digit) to quadkey ``` Digits on the map (y grows south, as in web tiles): @@ -144,15 +144,15 @@ Digits on the map (y grows south, as in web tiles): +---+---+ ``` -`len(cell) = z`. A `cell` value MUST match `^[0-3]{1,30}$`. Indexers MUST ignore an invalid `cell` as a placement. +`len(quadkey) = z`. A `quadkey` value MUST match `^[0-3]{1,30}$`. Indexers MUST ignore an invalid `quadkey` as a placement. #### 4.4 Quadkey → tile ``` -z = len(cell) +z = len(quadkey) x = 0 y = 0 -for i, ch in enumerate(cell): +for i, ch in enumerate(quadkey): bit = z - 1 - i d = ch - '0' if d & 1: x |= (1 << bit) @@ -199,7 +199,7 @@ Ground edge length scales as `cos(latitude)` times the equator figure (the cells #### 4.8 Test vectors -| lon | lat | z | x | y | `cell` | +| lon | lat | z | x | y | `quadkey` | |-----|-----|---|-----|-----|------------| | -74.0445 | 40.6892 | 18 | 77154 | 98583 | `032010110301120232` | | -74.0445 | 40.6892 | 16 | 19288 | 24645 | `0320101103011202` | @@ -209,13 +209,13 @@ The first row's bbox is approximately west −74.045105, south 40.688969, east ### 5. Objects and updates -A **placed output** is an output that contains a MAP `SET` of a valid `cell`. +A **placed output** is an output that contains a MAP `SET` of a valid `quadkey`. **Origin.** The first placed output in a spend chain that does not spend a placed output. The origin outpoint identifies the object. **Move.** A later transaction that spends a placed output and creates a placed output is a move of that object. The new output is the successor. The spend **is** the authorization: whoever can spend the current output can move it. An extra identity signature over the MAP payload MAY be attached by any identity protocol on the output; indexers that understand that protocol SHOULD record the signer. Indexers MUST NOT require a particular identity protocol. -**Unplace.** A spend of a placed output that does not create a successor (no valid `cell` on any paired output) removes the object from the spatial index. History MUST be retained. +**Unplace.** A spend of a placed output that does not create a successor (no valid `quadkey` on any paired output) removes the object from the spatial index. History MUST be retained. **Pairing** when a transaction has several placed inputs or outputs, in script order: @@ -231,7 +231,7 @@ A **placed output** is an output that contains a MAP `SET` of a valid `cell`. **Order.** Confirmed before unconfirmed. Then block height, transaction index in the block, output index. A reorg rewinds tips. Indexers MAY show an unconfirmed successor as the provisional tip. -**History.** An indexer MUST be able to emit the ordered list of `(outpoint, cell, world, join keys)` from origin to tip (and the unplace, if any). +**History.** An indexer MUST be able to emit the ordered list of `(outpoint, quadkey, world, join keys)` from origin to tip (and the unplace, if any). Tags are claims, not proof of presence. Anyone MAY place a cell; only the current signer can move that object. @@ -243,7 +243,7 @@ Let `world(q)` be Earth when the query omits a world id or passes `earth`; other A stored record **matches world** iff its world equals `world(q)` under section 3. -**Prefix query.** Given `world` and prefix `P` (a quadkey, or empty `P` for the whole projection): every current tip that matches the world and whose `cell` prefix-matches `P` (section 4.6). +**Prefix query.** Given `world` and prefix `P` (a quadkey, or empty `P` for the whole projection): every current tip that matches the world and whose `quadkey` prefix-matches `P` (section 4.6). **Viewport query.** Given `world` and a WGS 84 box `(west, south, east, north)`, with optional clip of `south`/`north` to `±MAX_LAT`: @@ -254,7 +254,7 @@ Intersect includes descendants (finer tags inside the view), the covering cells, A sufficient algorithm for intersect using only prefix lookups: take the tiles at some zoom `z` that overlap the box; for each tile `T`, query prefix(`T`) and also the exact cells of every ancestor of `T`. Implementations MAY use any spatial index that returns the same set. -Indexers MUST implement prefix and viewport as defined here. They MAY add filters (join key, time, origin). They MUST return at least: origin outpoint, tip outpoint, `cell`, world, remaining MAP keys on the tip, txid, and confirmation height when mined. +Indexers MUST implement prefix and viewport as defined here. They MAY add filters (join key, time, origin). They MUST return at least: origin outpoint, tip outpoint, `quadkey`, world, remaining MAP keys on the tip, txid, and confirmation height when mined. ### 7. Out of scope @@ -268,7 +268,7 @@ A compatible implementation tags outputs as in sections 1–5, answers section 6 **World of Alchema** ([alchema.world](https://alchema.world)) is an existing project in this family — a game world that places things in space. This BRC does not specify Alchema's internals. It exists so Earth-map clients and seeded-world clients can share indexers and join tags. -Any client that already renders a Web Mercator map or globe can decode `cell` to `(z, x, y)` or a bbox and draw the record without a second projection. +Any client that already renders a Web Mercator map or globe can decode `quadkey` to `(z, x, y)` or a bbox and draw the record without a second projection. ## References diff --git a/apps/README.md b/apps/README.md index 8537a9d4..22173154 100644 --- a/apps/README.md +++ b/apps/README.md @@ -14,4 +14,4 @@ BRC | Standard 220 | [NotaryHash — Privacy-Preserving Signed-Hash Notarization with SPV-Verifiable Certificates](./0220.md) 224 | [Block Media Format (BMF) — Composable On-Chain Audio/Video](./0224.md) 227 | [Frictionless On-Chain Onboarding via Pre-Funded Claimable Tokens](./0227.md) -229 | [MAP Location Keys (cell, world, lat, lng)](./0229.md) +229 | [MAP Location Keys (quadkey, world, lat, lng)](./0229.md) From 9b02b775787c26508c7189e2cc1d8b1181cb67de Mon Sep 17 00:00:00 2001 From: Luke Rohenaz Date: Wed, 26 Aug 2026 21:32:42 +0000 Subject: [PATCH 06/13] feat(BRC-229): earth-by-omission, translation matrix, geohash/h3 read keys Earth default is now expressed by omitting world (indexers stay tolerant of empty/earth). Add section 4.9: system comparison matrix, translation rules (point->cell exact, cell->cell lossy), granularity equivalence table, and the record-the-point guidance. Allow geohash and h3 as non-placing read keys derived from the same point; forbid cross-labeled encodings incl. H3-in-geohash. --- apps/0229.md | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/apps/0229.md b/apps/0229.md index c3d9d11a..60f9912c 100644 --- a/apps/0229.md +++ b/apps/0229.md @@ -4,7 +4,7 @@ Luke Rohenaz (rohenaz@gmail.com) ## Abstract -This BRC defines MAP keys for placing things in — and across — worlds, so indexers can answer a region query and clients can render a map or globe. A location is a **Web Mercator quadtree cell** carried in the `quadkey` key. Parallel worlds (game seeds, simulations) are partitioned by the `world` key; Earth is the default. One transaction MAY attach several identifiers to the same output so clients can join them (a URL on a place, a UPC on a shelf, a foreign txid on a pin). A later spend of a placed output moves or unplaces the thing. Indexers expose **prefix** and **viewport** queries; this document does not name a product. +This BRC defines MAP keys for placing things in — and across — worlds, so indexers can answer a region query and clients can render a map or globe. A location is a **Web Mercator quadtree cell** carried in the `quadkey` key. Parallel worlds (game seeds, simulations) are partitioned by the `world` key; Earth is the default and is expressed by omitting `world` entirely. One transaction MAY attach several identifiers to the same output so clients can join them (a URL on a place, a UPC on a shelf, a foreign txid on a pin). A later spend of a placed output moves or unplaces the thing. Indexers expose **prefix** and **viewport** queries; this document does not name a product. This BRC invents no `OP_RETURN` framing. It rides on the Magic Attribute Protocol (MAP) `SET` / `DELETE` command already used to attach arbitrary keys to a transaction. This document specifies only the keys, the cell encoding, update lineage, and query predicates. @@ -42,7 +42,6 @@ OP_RETURN quadkey 032010110301120232 lat 40.6892 lng -74.0445 - world earth url https://example.invalid/place ``` @@ -55,15 +54,19 @@ No key prefix is reserved. The discriminator for `quadkey` is its value grammar | Key | Value | Rule | |-----|--------|------| | `quadkey` | Web Mercator quadkey (section 4) | MUST on a placed output. ASCII digits `0`–`3`, length 1..30. | -| `world` | World id (section 3) | MAY. Omitted or empty means Earth. | +| `world` | World id (section 3) | MAY. Omit for Earth. | | `lat` | WGS 84 latitude, decimal degrees, ASCII | MAY. Both `lat` and `lng` or neither. | | `lng` | WGS 84 longitude, decimal degrees, ASCII | MAY. Both `lat` and `lng` or neither. | +| `geohash` | Geohash (base-32 string) of the same point | MAY. Never places. | +| `h3` | H3 cell id (15-char lowercase hex) of the same point | MAY. Never places. | Presence of a valid `quadkey` is the placement signal. Writers MAY also set MAP `app` or `type`; indexers MUST NOT require them. `lat` and `lng` do not place the output; `quadkey` does. A writer MAY include them so a key-presence query can find the point without decoding the quadkey. A point (`lat` / `lng`) and a cell (`quadkey`) are different jobs and MAY appear together. `lat` / `lng` MUST NOT be treated as a substitute cell id. -The key is named for its encoding, as `geohash` is elsewhere. Geohash is a different encoding with different cell geometry; writers MUST NOT put a quadkey in a `geohash` key or a geohash (or H3, or any other id) in `quadkey`. +`geohash` and `h3` follow the same pattern: redundant read keys for consumers already indexing those systems. When present they MUST be derived from the same point as `quadkey` (and `lat` / `lng`, if given). They never place the output and MUST NOT be treated as substitute cell ids. Section 4.9 explains why the systems do not substitute for one another. + +Every one of these keys is named for its encoding. Writers MUST NOT put one encoding's value under another's key: no quadkey in `geohash`, no geohash (or H3, or any other id) in `quadkey`, and **no H3 cell id in `geohash`** — an H3 id is not a geohash, and mislabeling it poisons every consumer that trusts the key name. Any other MAP keys on the same command are **join tags**. A client that has one identifier can look up the others on that output. Common join keys (not owned by this BRC; listed so writers pick the same spelling): @@ -82,11 +85,11 @@ One output MAY carry several join keys together with `quadkey` (location + URL, A **world** is a partition of the cell space. The same cell in two worlds is not the same place. -- **Earth** is the default world. It is the world of a record whose `world` is omitted, empty, or the ASCII string `earth`. +- **Earth is the default and is expressed by omission.** A writer placing on Earth SHOULD NOT emit `world` at all. `world` appears only when the record is *not* on this planet's default space. - Any other world id is the exact UTF-8 bytes of the `world` push (case-sensitive). Length MUST be 1..128 bytes. Writers SHOULD use NFC. - A world id is a seed, not a registry. Apps that need uniqueness SHOULD namespace (`myapp:`) or use a hex hash. -Indexers MUST treat omitted, empty, and `earth` as one world, and MUST NOT mix that world with any other id. +For tolerance, indexers MUST treat an omitted `world`, an empty `world`, and the ASCII string `earth` as the same (default) world, and MUST NOT mix that world with any other id. ### 4. Cell encoding (Web Mercator quadkey) @@ -207,6 +210,35 @@ Ground edge length scales as `cos(latitude)` times the equator figure (the cells The first row's bbox is approximately west −74.045105, south 40.688969, east −74.043732, north 40.690010. Parent of the z=18 cell is the z=16 cell shown. +#### 4.9 Other geospatial systems: granularity and translation + +Other cell systems exist and are indexed by other consumers. They are different tessellations, not different spellings of the same thing: + +| System | Id form | Cell shape | Hierarchy | High latitudes | +|--------|---------|-----------|-----------|----------------| +| Web Mercator quadkey (this BRC) | base-4 string | square in projection, square-ish on ground | exact — parent is a string prefix | consistent aspect; clipped past ±85.05° | +| Geohash | base-32 string | lat/lng rectangle | exact — parent is a string prefix | tall-skinny cells; aspect also alternates ~1:2 by length parity | +| H3 | 64-bit id (15-char hex) | hexagon (12 pentagons) on an icosahedron | approximate — a child hex is not exactly contained by its parent; ids are not plain string prefixes | uniform aspect; poles covered | +| Point (`lat` / `lng`) | decimal degrees | — | — | poles representable | + +Translation rules: + +1. **Point → any cell is exact.** Every system defines point-to-cell at every resolution. A record that carries `lat` / `lng` can be indexed under any system, at any granularity, now or later. +2. **Cell → cell across systems is lossy.** The grids tessellate differently, so a cell in one system is covered by several partially-overlapping cells of another, and the covering varies with latitude. A cell id translated across systems MUST be treated as approximate. +3. **Cell → point recovers only the cell's center or bbox** — error up to the cell size (section 4.7). + +Therefore: **a writer that expects consumers on other geospatial systems SHOULD include `lat` / `lng`**, with enough decimal places for the intended precision (5 decimals ≈ 1.1 m of latitude; 6 ≈ 0.11 m). The quadkey carries the record's *extent*; the point carries its translatable *position*. A record published as a cell id alone — in any system — has permanently discarded everything finer than that cell. + +Approximate granularity equivalence at the equator (area-matched; geohash ground size is latitude-dependent and its even lengths are ~1:2 rectangles): + +| Ground scale | quadkey `z` | geohash length | H3 resolution | +|--------------|-------------|----------------|---------------| +| ~5 km | 13 | 5 | 6–7 | +| ~1 km | 15–16 | 6 | 8 | +| ~150 m | 18 | 7 | 9–10 | +| ~40 m | 20 | 8 | 11 | +| ~5 m | 23 | 9 | 13 | + ### 5. Objects and updates A **placed output** is an output that contains a MAP `SET` of a valid `quadkey`. @@ -259,7 +291,7 @@ Indexers MUST implement prefix and viewport as defined here. They MAY add filter ### 7. Out of scope - Content bytes (images, inscriptions, 3D models). Attach them with other protocols on the same output; this BRC does not read them. -- Alternate cell encodings (geohash, H3, geographic-rectangle grids). They are lossy against this quadkey and not interchangeable, especially near the poles. Interoperable placements use section 4. +- Alternate *placement* encodings. `geohash` and `h3` may ride along as non-placing read keys (sections 2, 4.9), but cross-system cell translation is lossy and placement is defined over `quadkey` only. - Overlay wire formats, payment, or wallet baskets. ## Implementations From 5df72e7f22bab1c0b443bcf00772f4781151b33e Mon Sep 17 00:00:00 2001 From: Luke Rohenaz Date: Wed, 26 Aug 2026 21:58:24 +0000 Subject: [PATCH 07/13] feat(BRC-229): polar cap semantics and decision grid Clipping makes polar placement deterministic, not absent: north cap collapses to the y=0 row (quadkey digits 0/1), south to y=n-1 (digits 2/3), longitude still resolving. Add the property grid (nesting, aspect, renderer-native decode, poles) to 4.9 and state the trade this BRC takes. --- apps/0229.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apps/0229.md b/apps/0229.md index 60f9912c..33bc36ff 100644 --- a/apps/0229.md +++ b/apps/0229.md @@ -110,6 +110,8 @@ Longitude is the half-open interval `[-180, 180)`. Latitude outside `[-MAX_LAT, so that `+180` becomes `-180`. +**Polar caps.** The clip makes polar placement deterministic and coarse, not absent. Every point with `φ ≥ MAX_LAT` encodes to the northernmost tile row (`y = 0`; its quadkeys consist only of digits `0` and `1`); every point with `φ ≤ -MAX_LAT` encodes to the southernmost row (`y = n-1`; digits only `2` and `3`). Longitude still resolves within the cap, but all latitude detail poleward of the clip is flattened into that row. A writer placing polar content SHOULD therefore include `lat` / `lng` (section 4.9), which represents the poles exactly. + #### 4.2 WGS 84 point → tile `(z, x, y)` `z` is the zoom (cell precision), an integer `1..30`. Let `n = 2^z`. @@ -221,6 +223,17 @@ Other cell systems exist and are indexed by other consumers. They are different | H3 | 64-bit id (15-char hex) | hexagon (12 pentagons) on an icosahedron | approximate — a child hex is not exactly contained by its parent; ids are not plain string prefixes | uniform aspect; poles covered | | Point (`lat` / `lng`) | decimal degrees | — | — | poles representable | +The decisive properties, and what each system trades: + +| Property | Quadkey | Geohash | H3 | +|----------|---------|---------|-----| +| Exact nesting — parent is a string prefix, child fully contained, so a region query is a prefix match on a plain key-value index | yes | yes | no | +| Consistent cell aspect at high latitudes | yes | no | yes | +| Decodes directly to the web-map tile pyramid (`z/x/y`) every renderer already draws | yes | no | no | +| Poles addressable | caps collapse to the edge rows (section 4.1) — deterministic, coarse in latitude | yes, but cells degrade to ground slivers and the pole point is degenerate across longitudes | yes, with normal-sized cells | + +No candidate wins every row. This BRC takes exact nesting and renderer-native decoding, and pays for them at the polar caps; a record that must stay precise there carries the point. + Translation rules: 1. **Point → any cell is exact.** Every system defines point-to-cell at every resolution. A record that carries `lat` / `lng` can be indexed under any system, at any granularity, now or later. From 552a5a5dc3ea382d3ebef3ae509c92fe37c60146 Mon Sep 17 00:00:00 2001 From: Luke Rohenaz Date: Thu, 27 Aug 2026 07:06:31 -0400 Subject: [PATCH 08/13] BRC-315: renumber from 229, defer custody to BRC-159, adopt MAP conventions Renumber 229 -> 315 so the BRC sits above every dependency (Bitcom 300, MAP 303, 1Sat 306, envelopes 307, and the contested 314). Section 5 no longer restates 1Sat lineage. Custody -- origin, transfer by sat ordering, tip, burn -- is cited to BRC-159. Position is this BRC's: an object occupies the cells in effect at the tip, a transaction that re-declares quadkey relocates it, a transfer on its own does not. Removes the origin/tip/pairing/order machinery, whose index-based pairing rule contradicted BRC-159's sat ordering. Authorization is inherited from spend authority rather than restated. Adopt MAP's command set (BRC-303) instead of inventing one: SET places, ADD adds a cell, DELETE drops a cell, REMOVE unplaces. quadkey becomes list-valued so one object can occupy several cells. Cites the ::: instruction-set separator. Replace bare lat/lng with MAP's established coordinates.lat/.lng/.alt convention; add altitude. Coordinates stay single-valued -- quadkey carries extent, the point carries position. World ids must self-uniquify: genesis outpoint preferred, hex hash for off-chain worlds, myapp: a fallback; bare seeds forbidden. world may change across a transfer, specified as a portal. Empty-world write/read asymmetry stated as one clause. Query returns a multi-cell record once if any cell matches. Adds three conformance cases. Section 4 math is unchanged. --- README.md | 2 +- SUMMARY.md | 2 +- apps/0229.md | 323 --------------------------------------------- apps/0315.md | 348 +++++++++++++++++++++++++++++++++++++++++++++++++ apps/README.md | 2 +- 5 files changed, 351 insertions(+), 326 deletions(-) delete mode 100644 apps/0229.md create mode 100644 apps/0315.md diff --git a/README.md b/README.md index deb3c5dd..9a3a02ee 100644 --- a/README.md +++ b/README.md @@ -235,7 +235,7 @@ BRC | Standard 226 | [Miner-Enforced Resale-Royalty Covenant Tokens (OP_PUSH_TX)](./tokens/0226.md) 227 | [Frictionless On-Chain Onboarding via Pre-Funded Claimable Tokens](./apps/0227.md) 228 | [Unlinkable Payments under the Identity Paradigm](./payments/0228.md) -229 | [MAP Location Keys (quadkey, world, lat, lng)](./apps/0229.md) +315 | [MAP Location Keys (quadkey, world, coordinates.*)](./apps/0315.md) ## License diff --git a/SUMMARY.md b/SUMMARY.md index fe779121..f871325c 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -20,7 +20,7 @@ * [NotaryHash — Privacy-Preserving Signed-Hash Notarization with SPV-Verifiable Certificates](./apps/0220.md) * [Block Media Format (BMF) — Composable On-Chain Audio/Video](./apps/0224.md) * [Frictionless On-Chain Onboarding via Pre-Funded Claimable Tokens](./apps/0227.md) -* [MAP Location Keys (quadkey, world, lat, lng)](./apps/0229.md) +* [MAP Location Keys (quadkey, world, coordinates.*)](./apps/0315.md) ## Wallet diff --git a/apps/0229.md b/apps/0229.md deleted file mode 100644 index 33bc36ff..00000000 --- a/apps/0229.md +++ /dev/null @@ -1,323 +0,0 @@ -# BRC-229: MAP Location Keys (`quadkey`, `world`, `lat`, `lng`) - -Luke Rohenaz (rohenaz@gmail.com) - -## Abstract - -This BRC defines MAP keys for placing things in — and across — worlds, so indexers can answer a region query and clients can render a map or globe. A location is a **Web Mercator quadtree cell** carried in the `quadkey` key. Parallel worlds (game seeds, simulations) are partitioned by the `world` key; Earth is the default and is expressed by omitting `world` entirely. One transaction MAY attach several identifiers to the same output so clients can join them (a URL on a place, a UPC on a shelf, a foreign txid on a pin). A later spend of a placed output moves or unplaces the thing. Indexers expose **prefix** and **viewport** queries; this document does not name a product. - -This BRC invents no `OP_RETURN` framing. It rides on the Magic Attribute Protocol (MAP) `SET` / `DELETE` command already used to attach arbitrary keys to a transaction. This document specifies only the keys, the cell encoding, update lineage, and query predicates. - -## Motivation - -Maps, AR clients, and games all need the same primitive: *what is here?* "Here" may be a street corner on Earth or a plot in a seeded world that reuses Earth's coordinate system. Without a shared cell id, world id, and update rule, every app invents a private tag and no indexer can answer a region query for all of them. - -A cell id that subdivides a latitude/longitude rectangle spends equal bits on lon and lat. East–west metres per degree shrink as `cos(latitude)`, so those cells stay about the same height and become tall and skinny toward the poles. The same defect appears in a naive lat/lng quadtree. The Web Mercator tile pyramid avoids it: the ellipsoid is mapped to a square, then that square is quartered. Cells stay square in the projection (consistent aspect on the ground). Resolution gets finer toward the projection cutoff; the poles themselves are outside the square (about ±85.05°). - -This BRC therefore uses **one** cell encoding — a Bing-style **quadkey** over that pyramid — so a prefix is a parent cell, and a region query is a prefix (plus ancestor) lookup. - -3D models and other payloads are out of scope. Content MAY be attached to the same output by other protocols. - -## Specification - -The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted as described in RFC 2119[1](#footnote-1). - -### 1. MAP carriage - -The location keys are MAP keys. Readers MUST scan an output's script for a MAP command: a data push of the ASCII Bitcom protocol id - -``` -1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5 -``` - -followed by a command push `SET` or `DELETE`, then zero or more key/value push pairs (`DELETE` MAY omit values). MAP MAY share the output with other protocols. When protocols are concatenated, the separator is a push of the single byte `0x7c` (`|`). Inscriptions or other locking payload MAY precede `OP_RETURN` — one output can be, for example, a 1Sat lock plus this MAP `SET`; spending that output is what moves it (section 5). - -Illustrative shape (one output): - -``` - -OP_RETURN - 1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5 - SET - quadkey 032010110301120232 - lat 40.6892 - lng -74.0445 - url https://example.invalid/place -``` - -Unknown keys MUST be ignored by readers that do not implement them. If a key is pushed more than once in one command, the **last** pair wins. A `SET` that includes a valid `quadkey` **places** the output. A `DELETE` of `quadkey` with no later `SET` of `quadkey` on that output **unplaces** it. - -No key prefix is reserved. The discriminator for `quadkey` is its value grammar (section 4.3): indexers MUST ignore, as a placement, any `quadkey` value that does not match it. - -### 2. Keys - -| Key | Value | Rule | -|-----|--------|------| -| `quadkey` | Web Mercator quadkey (section 4) | MUST on a placed output. ASCII digits `0`–`3`, length 1..30. | -| `world` | World id (section 3) | MAY. Omit for Earth. | -| `lat` | WGS 84 latitude, decimal degrees, ASCII | MAY. Both `lat` and `lng` or neither. | -| `lng` | WGS 84 longitude, decimal degrees, ASCII | MAY. Both `lat` and `lng` or neither. | -| `geohash` | Geohash (base-32 string) of the same point | MAY. Never places. | -| `h3` | H3 cell id (15-char lowercase hex) of the same point | MAY. Never places. | - -Presence of a valid `quadkey` is the placement signal. Writers MAY also set MAP `app` or `type`; indexers MUST NOT require them. - -`lat` and `lng` do not place the output; `quadkey` does. A writer MAY include them so a key-presence query can find the point without decoding the quadkey. A point (`lat` / `lng`) and a cell (`quadkey`) are different jobs and MAY appear together. `lat` / `lng` MUST NOT be treated as a substitute cell id. - -`geohash` and `h3` follow the same pattern: redundant read keys for consumers already indexing those systems. When present they MUST be derived from the same point as `quadkey` (and `lat` / `lng`, if given). They never place the output and MUST NOT be treated as substitute cell ids. Section 4.9 explains why the systems do not substitute for one another. - -Every one of these keys is named for its encoding. Writers MUST NOT put one encoding's value under another's key: no quadkey in `geohash`, no geohash (or H3, or any other id) in `quadkey`, and **no H3 cell id in `geohash`** — an H3 id is not a geohash, and mislabeling it poisons every consumer that trusts the key name. - -Any other MAP keys on the same command are **join tags**. A client that has one identifier can look up the others on that output. Common join keys (not owned by this BRC; listed so writers pick the same spelling): - -| Key | Value | -|-----|--------| -| `url` | URI | -| `upc` | UPC / EAN / GTIN | -| `tx` | Hex txid (this chain or as labeled by the writer) | -| `btctx` | Bitcoin txid | -| `ethtx` | Ethereum txid | -| `name` | Display name | - -One output MAY carry several join keys together with `quadkey` (location + URL, location + UPC, location + foreign txid, and so on). - -### 3. Worlds - -A **world** is a partition of the cell space. The same cell in two worlds is not the same place. - -- **Earth is the default and is expressed by omission.** A writer placing on Earth SHOULD NOT emit `world` at all. `world` appears only when the record is *not* on this planet's default space. -- Any other world id is the exact UTF-8 bytes of the `world` push (case-sensitive). Length MUST be 1..128 bytes. Writers SHOULD use NFC. -- A world id is a seed, not a registry. Apps that need uniqueness SHOULD namespace (`myapp:`) or use a hex hash. - -For tolerance, indexers MUST treat an omitted `world`, an empty `world`, and the ASCII string `earth` as the same (default) world, and MUST NOT mix that world with any other id. - -### 4. Cell encoding (Web Mercator quadkey) - -The cell space is the standard Web Mercator / slippy-map tile pyramid (EPSG:3857)[2](#footnote-2) over WGS 84[3](#footnote-3). The on-chain id is a **quadkey**[4](#footnote-4): the path from the root square to a tile, one digit per zoom level. - -#### 4.1 Projection limits - -``` -MAX_LAT = atan(sinh(π)) × 180/π - ≈ 85.0511287798 degrees -``` - -Longitude is the half-open interval `[-180, 180)`. Latitude outside `[-MAX_LAT, MAX_LAT]` is outside the projection. Writers MUST clip latitude to that range before encoding. Writers MUST reduce longitude with - -``` -λ ← ((λ + 180) mod 360) - 180 -``` - -so that `+180` becomes `-180`. - -**Polar caps.** The clip makes polar placement deterministic and coarse, not absent. Every point with `φ ≥ MAX_LAT` encodes to the northernmost tile row (`y = 0`; its quadkeys consist only of digits `0` and `1`); every point with `φ ≤ -MAX_LAT` encodes to the southernmost row (`y = n-1`; digits only `2` and `3`). Longitude still resolves within the cap, but all latitude detail poleward of the clip is flattened into that row. A writer placing polar content SHOULD therefore include `lat` / `lng` (section 4.9), which represents the poles exactly. - -#### 4.2 WGS 84 point → tile `(z, x, y)` - -`z` is the zoom (cell precision), an integer `1..30`. Let `n = 2^z`. - -``` -x = floor( (λ + 180) / 360 × n ) -y = floor( (1 − ln((1 + sin φ) / (1 − sin φ)) / (2π)) / 2 × n ) -``` - -`φ` is clipped latitude in radians; `λ` is reduced longitude in degrees. Then clamp: if `x = n` then `x = 0`; `y` MUST be clamped to `[0, n-1]`. - -`floor` assigns a point on an east or south tile edge to the eastern / southern neighbour. The north and west edges belong to the tile. - -`(z, x, y)` is the computational form. It is not written on chain. - -#### 4.3 Tile → quadkey - -``` -quadkey = "" -for i from z-1 down to 0: - digit = 0 - mask = 1 << i - if (x & mask) ≠ 0: digit += 1 - if (y & mask) ≠ 0: digit += 2 - append ASCII('0' + digit) to quadkey -``` - -Digits on the map (y grows south, as in web tiles): - -``` -+---+---+ -| 0 | 1 | -+---+---+ -| 2 | 3 | -+---+---+ -``` - -`len(quadkey) = z`. A `quadkey` value MUST match `^[0-3]{1,30}$`. Indexers MUST ignore an invalid `quadkey` as a placement. - -#### 4.4 Quadkey → tile - -``` -z = len(quadkey) -x = 0 -y = 0 -for i, ch in enumerate(quadkey): - bit = z - 1 - i - d = ch - '0' - if d & 1: x |= (1 << bit) - if d & 2: y |= (1 << bit) -``` - -#### 4.5 Tile → WGS 84 bbox - -``` -west = x / n × 360 - 180 -east = (x + 1) / n × 360 - 180 -north = atan(sinh(π × (1 - 2y / n))) × 180/π -south = atan(sinh(π × (1 - 2(y+1) / n))) × 180/π -``` - -A point lies in the unique tile produced by section 4.2. These bbox formulas are the inverse of that assignment. - -#### 4.6 Parents, children, prefixes - -| Relation | Rule | -|----------|------| -| Parent | Drop the last digit. The root (whole projected square) has no digit and is not written as a tag. | -| Children | Append `0`, `1`, `2`, or `3`. | -| Ancestor | `A` is an ancestor of `C` iff `A` is a prefix of `C`. | -| Prefix match | `C` matches prefix `P` iff `C = P` or `C` starts with `P`. | - -A prefix query for `P` returns tags at `P` and all finer cells inside it. - -#### 4.7 Choosing `z` - -`z` is the object's size, not a map zoom the user happens to be looking at. Writers SHOULD pick a cell whose ground size is about the object: - -| `z` | Approx. equator edge | -|-----|----------------------| -| 10 | 39 km | -| 14 | 2.4 km | -| 16 | 611 m | -| 18 | 153 m | -| 20 | 38 m | -| 22 | 9.6 m | -| 24 | 2.4 m | - -Ground edge length scales as `cos(latitude)` times the equator figure (the cells stay square). Equator edge ≈ `40075016.69 / 2^z` metres. - -#### 4.8 Test vectors - -| lon | lat | z | x | y | `quadkey` | -|-----|-----|---|-----|-----|------------| -| -74.0445 | 40.6892 | 18 | 77154 | 98583 | `032010110301120232` | -| -74.0445 | 40.6892 | 16 | 19288 | 24645 | `0320101103011202` | -| 0 | 0 | 1 | 1 | 1 | `3` | - -The first row's bbox is approximately west −74.045105, south 40.688969, east −74.043732, north 40.690010. Parent of the z=18 cell is the z=16 cell shown. - -#### 4.9 Other geospatial systems: granularity and translation - -Other cell systems exist and are indexed by other consumers. They are different tessellations, not different spellings of the same thing: - -| System | Id form | Cell shape | Hierarchy | High latitudes | -|--------|---------|-----------|-----------|----------------| -| Web Mercator quadkey (this BRC) | base-4 string | square in projection, square-ish on ground | exact — parent is a string prefix | consistent aspect; clipped past ±85.05° | -| Geohash | base-32 string | lat/lng rectangle | exact — parent is a string prefix | tall-skinny cells; aspect also alternates ~1:2 by length parity | -| H3 | 64-bit id (15-char hex) | hexagon (12 pentagons) on an icosahedron | approximate — a child hex is not exactly contained by its parent; ids are not plain string prefixes | uniform aspect; poles covered | -| Point (`lat` / `lng`) | decimal degrees | — | — | poles representable | - -The decisive properties, and what each system trades: - -| Property | Quadkey | Geohash | H3 | -|----------|---------|---------|-----| -| Exact nesting — parent is a string prefix, child fully contained, so a region query is a prefix match on a plain key-value index | yes | yes | no | -| Consistent cell aspect at high latitudes | yes | no | yes | -| Decodes directly to the web-map tile pyramid (`z/x/y`) every renderer already draws | yes | no | no | -| Poles addressable | caps collapse to the edge rows (section 4.1) — deterministic, coarse in latitude | yes, but cells degrade to ground slivers and the pole point is degenerate across longitudes | yes, with normal-sized cells | - -No candidate wins every row. This BRC takes exact nesting and renderer-native decoding, and pays for them at the polar caps; a record that must stay precise there carries the point. - -Translation rules: - -1. **Point → any cell is exact.** Every system defines point-to-cell at every resolution. A record that carries `lat` / `lng` can be indexed under any system, at any granularity, now or later. -2. **Cell → cell across systems is lossy.** The grids tessellate differently, so a cell in one system is covered by several partially-overlapping cells of another, and the covering varies with latitude. A cell id translated across systems MUST be treated as approximate. -3. **Cell → point recovers only the cell's center or bbox** — error up to the cell size (section 4.7). - -Therefore: **a writer that expects consumers on other geospatial systems SHOULD include `lat` / `lng`**, with enough decimal places for the intended precision (5 decimals ≈ 1.1 m of latitude; 6 ≈ 0.11 m). The quadkey carries the record's *extent*; the point carries its translatable *position*. A record published as a cell id alone — in any system — has permanently discarded everything finer than that cell. - -Approximate granularity equivalence at the equator (area-matched; geohash ground size is latitude-dependent and its even lengths are ~1:2 rectangles): - -| Ground scale | quadkey `z` | geohash length | H3 resolution | -|--------------|-------------|----------------|---------------| -| ~5 km | 13 | 5 | 6–7 | -| ~1 km | 15–16 | 6 | 8 | -| ~150 m | 18 | 7 | 9–10 | -| ~40 m | 20 | 8 | 11 | -| ~5 m | 23 | 9 | 13 | - -### 5. Objects and updates - -A **placed output** is an output that contains a MAP `SET` of a valid `quadkey`. - -**Origin.** The first placed output in a spend chain that does not spend a placed output. The origin outpoint identifies the object. - -**Move.** A later transaction that spends a placed output and creates a placed output is a move of that object. The new output is the successor. The spend **is** the authorization: whoever can spend the current output can move it. An extra identity signature over the MAP payload MAY be attached by any identity protocol on the output; indexers that understand that protocol SHOULD record the signer. Indexers MUST NOT require a particular identity protocol. - -**Unplace.** A spend of a placed output that does not create a successor (no valid `quadkey` on any paired output) removes the object from the spatial index. History MUST be retained. - -**Pairing** when a transaction has several placed inputs or outputs, in script order: - -| Placed inputs | Placed outputs | Meaning | -|---------------|----------------|---------| -| 1 | 1 | Move that object | -| N | N | Move: pair by index | -| N | 0 | Unplace each input object | -| 0 | M | M new origins | -| N | M, N ≠ M | Pair `min(N,M)` by index; leftover inputs are unplaced; leftover outputs are new origins | - -**Tip.** The current record is the unspent placed output reachable from the origin by following paired successors. If the last spend unplaced the object, there is no current cell. - -**Order.** Confirmed before unconfirmed. Then block height, transaction index in the block, output index. A reorg rewinds tips. Indexers MAY show an unconfirmed successor as the provisional tip. - -**History.** An indexer MUST be able to emit the ordered list of `(outpoint, quadkey, world, join keys)` from origin to tip (and the unplace, if any). - -Tags are claims, not proof of presence. Anyone MAY place a cell; only the current signer can move that object. - -### 6. Query - -This section is the predicate. It is not an HTTP API and not a particular indexer. - -Let `world(q)` be Earth when the query omits a world id or passes `earth`; otherwise the exact id. - -A stored record **matches world** iff its world equals `world(q)` under section 3. - -**Prefix query.** Given `world` and prefix `P` (a quadkey, or empty `P` for the whole projection): every current tip that matches the world and whose `quadkey` prefix-matches `P` (section 4.6). - -**Viewport query.** Given `world` and a WGS 84 box `(west, south, east, north)`, with optional clip of `south`/`north` to `±MAX_LAT`: - -1. If the box crosses the antimeridian (`west > east`), split into `(west, south, 180, north)` and `(-180, south, east, north)` and union the results. -2. A record matches iff it matches the world **and** its cell bbox (section 4.5) **intersects** the (clipped, split) box. - -Intersect includes descendants (finer tags inside the view), the covering cells, **and ancestors** (a coarse city-sized tag still appears when the view is a street). Empty `P` / a view of the whole projection returns every current tip in that world. - -A sufficient algorithm for intersect using only prefix lookups: take the tiles at some zoom `z` that overlap the box; for each tile `T`, query prefix(`T`) and also the exact cells of every ancestor of `T`. Implementations MAY use any spatial index that returns the same set. - -Indexers MUST implement prefix and viewport as defined here. They MAY add filters (join key, time, origin). They MUST return at least: origin outpoint, tip outpoint, `quadkey`, world, remaining MAP keys on the tip, txid, and confirmation height when mined. - -### 7. Out of scope - -- Content bytes (images, inscriptions, 3D models). Attach them with other protocols on the same output; this BRC does not read them. -- Alternate *placement* encodings. `geohash` and `h3` may ride along as non-placing read keys (sections 2, 4.9), but cross-system cell translation is lossy and placement is defined over `quadkey` only. -- Overlay wire formats, payment, or wallet baskets. - -## Implementations - -A compatible implementation tags outputs as in sections 1–5, answers section 6, and works for **Earth maps and seeded worlds** with the same cell math. - -**World of Alchema** ([alchema.world](https://alchema.world)) is an existing project in this family — a game world that places things in space. This BRC does not specify Alchema's internals. It exists so Earth-map clients and seeded-world clients can share indexers and join tags. - -Any client that already renders a Web Mercator map or globe can decode `quadkey` to `(z, x, y)` or a bbox and draw the record without a second projection. - -## References - -- 1: Bradner, S. (1997). *Key words for use in RFCs to Indicate Requirement Levels*. RFC 2119. https://www.rfc-editor.org/rfc/rfc2119 -- 2: IOGP. *WGS 84 / Pseudo-Mercator* — EPSG:3857. The well-known Web Mercator tile pyramid (slippy-map `z/x/y`). -- 3: NIMA. (2000). *Department of Defense World Geodetic System 1984*. TR8350.2. Horizontal positions in this BRC are WGS 84 (EPSG:4326) degrees. -- 4: Microsoft. *Bing Maps Tile System* — quadkey encoding of the Web Mercator pyramid. https://learn.microsoft.com/en-us/bingmaps/articles/bing-maps-tile-system diff --git a/apps/0315.md b/apps/0315.md new file mode 100644 index 00000000..beb6601b --- /dev/null +++ b/apps/0315.md @@ -0,0 +1,348 @@ +# BRC-315: MAP Location Keys (`quadkey`, `world`, `coordinates.*`) + +Luke Rohenaz (rohenaz@gmail.com) + +## Abstract + +This BRC defines MAP[1](#footnote-1) keys for placing things in — and across — worlds, so indexers can answer a region query and clients can render a map or globe. A location is one or more **Web Mercator quadtree cells** carried in the `quadkey` key. Parallel worlds (game seeds, simulations) are partitioned by the `world` key; Earth is the default and is expressed by omitting `world` entirely. One transaction MAY attach several identifiers to the same output so clients can join them (a URL on a place, a UPC on a shelf, a foreign txid on a pin). Indexers expose **prefix** and **viewport** queries; this document does not name a product. + +This BRC invents no `OP_RETURN` framing, no command set, and no ownership model. It rides the Magic Attribute Protocol (MAP) for attributes and 1Sat Ordinals[2](#footnote-2) for identity, custody and authorization. This document specifies only the keys, the cell encoding, the resulting spatial state, and the query predicates. + +## Motivation + +Maps, AR clients, and games all need the same primitive: *what is here?* "Here" may be a street corner on Earth or a plot in a seeded world that reuses Earth's coordinate system. Without a shared cell id, world id, and update rule, every app invents a private tag and no indexer can answer a region query for all of them. + +A cell id that subdivides a latitude/longitude rectangle spends equal bits on lon and lat. East–west metres per degree shrink as `cos(latitude)`, so those cells stay about the same height and become tall and skinny toward the poles. The same defect appears in a naive lat/lng quadtree. The Web Mercator tile pyramid avoids it: the ellipsoid is mapped to a square, then that square is quartered. Cells stay square in the projection (consistent aspect on the ground). Resolution gets finer toward the projection cutoff; the poles themselves are outside the square (about ±85.05°). + +This BRC therefore uses **one** cell encoding — a Bing-style **quadkey** over that pyramid — so a prefix is a parent cell, and a region query is a prefix (plus ancestor) lookup. + +3D models and other payloads are out of scope. Content MAY be attached to the same output by other protocols. + +## Specification + +The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted as described in RFC 2119[3](#footnote-3). + +### 1. MAP carriage + +The location keys are MAP keys. This BRC defines no commands of its own; it uses MAP's command set as specified in BRC-303[1](#footnote-1). + +Readers MUST scan an output's script for a MAP command: a data push of the ASCII Bitcom protocol id + +``` +1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5 +``` + +followed by a MAP command and its arguments. MAP MAY share the output with other protocols; when protocols are concatenated the separator is a push of the single byte `0x7c` (`|`), and multiple MAP commands in one output are joined by the `:::` instruction-set separator (BRC-303 §3) and evaluated in order. + +Inscriptions or other locking payload MAY precede `OP_RETURN` — one output can be, for example, a 1Sat lock plus this MAP data; spending that output is what carries the object forward (section 5). Note that a placed output MUST remain spendable, so the `OP_FALSE OP_RETURN` data-carrier template MUST NOT be used when the lock has to stay redeemable[7](#footnote-7). + +Illustrative shape (one output): + +``` + +OP_RETURN + 1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5 + SET + quadkey 032010110301120232 + coordinates.lat 40.6892 + coordinates.lng -74.0445 + url https://example.invalid/place +``` + +Unknown keys MUST be ignored by readers that do not implement them. Where MAP resolves a key's value at a point in an object's history, that resolution governs; this BRC adds no precedence rule of its own. + +No key prefix is reserved. The discriminator for `quadkey` is its value grammar (section 4.3): indexers MUST ignore, as a placement, any `quadkey` value that does not match it. + +### 2. Keys + +| Key | Value | Cardinality | Rule | +|-----|--------|-------------|------| +| `quadkey` | Web Mercator quadkey (section 4) | list | MUST on a placed output. Each member is ASCII digits `0`–`3`, length 1..30. | +| `world` | World id (section 3) | single | MAY. Omit for Earth. | +| `coordinates.lat` | WGS 84 latitude, decimal degrees, ASCII | single | MAY. Both `coordinates.lat` and `coordinates.lng` or neither. | +| `coordinates.lng` | WGS 84 longitude, decimal degrees, ASCII | single | MAY. Both `coordinates.lat` and `coordinates.lng` or neither. | +| `coordinates.alt` | Altitude in metres above the WGS 84 ellipsoid, ASCII | single | MAY. Only with `coordinates.lat` and `coordinates.lng`. Never places. | +| `geohash` | Geohash (base-32 string) of the same point | single | MAY. Never places. | +| `h3` | H3 **cell** index (15-char lowercase hex) of the same point | single | MAY. Never places. | + +The `coordinates.*` keys are MAP's established compound-attribute convention for geolocation and are not introduced here. Writers MUST NOT use bare `lat` or `lng` for these values. + +Presence of at least one valid `quadkey` value is the placement signal. Writers MAY also set MAP `app` or `type`; indexers MUST NOT require them. + +**Extent and position are different jobs.** `quadkey` carries the record's *extent* — the cells it occupies — and is therefore list-valued. `coordinates.*` carries its translatable *position* — a single anchor point — and never places the output. A record with many cells still has one point. `coordinates.*` MUST NOT be treated as a substitute cell id. + +`geohash` and `h3` follow the same pattern: redundant read keys for consumers already indexing those systems. When present they MUST be derived from the same point as `coordinates.*`. They never place the output and MUST NOT be treated as substitute cell ids. Section 4.9 explains why the systems do not substitute for one another. + +Every one of these keys is named for its encoding. Writers MUST NOT put one encoding's value under another's key: no quadkey in `geohash`, no geohash (or H3, or any other id) in `quadkey`, and **no H3 cell index in `geohash`** — an H3 index is not a geohash, and mislabeling it poisons every consumer that trusts the key name. + +#### 2.1 Placing, moving and unplacing + +These are MAP commands (BRC-303), not new operations: + +| Intent | Command | +|--------|---------| +| Place at one cell, replacing any cells in effect | `SET quadkey ` | +| Occupy an additional cell | `ADD quadkey ` | +| Give up one cell, keeping the rest | `SELECT DELETE quadkey ` | +| Unplace entirely (clears every cell at once) | `SELECT REMOVE quadkey` | + +Writers SHOULD keep the cell list small, choosing a coarser `z` (section 4.7) in preference to enumerating many fine cells. Indexers MAY cap the number of cells they will index for one object and MUST document that cap. + +Any other MAP keys on the same command are **join tags**. A client that has one identifier can look up the others on that output. Key spellings for global identifiers (`url`, `tx`, `btctx`, `ethtx`, `topic`, `upc`, `name`, and others) are MAP's shared keyspace, defined in BRC-303 §5; this BRC does not own them and does not restate the list. + +### 3. Worlds + +A **world** is a partition of the cell space. The same cell in two worlds is not the same place. + +- **Earth is the default and is expressed by omission.** A writer placing on Earth SHOULD NOT emit `world` at all. `world` appears only when the record is *not* on this planet's default space. +- Any other world id is the exact UTF-8 bytes of the `world` push (case-sensitive), 1..128 bytes. + +World equality is those exact bytes. It never consults MAP's `app` key, so **a world id MUST carry its own uniqueness**. In order of preference: + +| Form | Use | +|------|-----| +| **Genesis outpoint** — the outpoint of an output that creates the world, in the `txid_vout` form and normalization of BRC-159 | Preferred. Unique with no coordination, and gives world metadata and world ownership somewhere to live without a registry. | +| **Hex hash** of the world's genesis parameters | For procedurally generated worlds with no on-chain presence. | +| `myapp:` | Fallback for hand-authored development worlds. Uniqueness depends on app authors choosing distinct prefixes. | + +A bare seed such as `seed-7` MUST NOT be used: with world equality defined on `world` alone, every unrelated application shipping the same string would silently share one world. + +Writers MUST NOT emit an empty `world`; for tolerance, indexers MUST accept an absent `world`, an empty `world`, and the ASCII string `earth` as the same (default) world, and MUST NOT mix that world with any other id. + +If `app` and `world` disagree, `world` governs; `app` has no role in world identity. + +### 4. Cell encoding (Web Mercator quadkey) + +The cell space is the standard Web Mercator / slippy-map tile pyramid (EPSG:3857)[6](#footnote-6) over WGS 84[5](#footnote-5). The on-chain id is a **quadkey**[4](#footnote-4): the path from the root square to a tile, one digit per zoom level. + +#### 4.1 Projection limits + +``` +MAX_LAT = atan(sinh(π)) × 180/π + ≈ 85.0511287798 degrees +``` + +Longitude is the half-open interval `[-180, 180)`. Latitude outside `[-MAX_LAT, MAX_LAT]` is outside the projection. Writers MUST clip latitude to that range before encoding. Writers MUST reduce longitude with + +``` +λ ← ((λ + 180) mod 360) - 180 +``` + +so that `+180` becomes `-180`. + +**Polar caps.** The clip makes polar placement deterministic and coarse, not absent. Every point with `φ ≥ MAX_LAT` encodes to the northernmost tile row (`y = 0`; its quadkeys consist only of digits `0` and `1`); every point with `φ ≤ -MAX_LAT` encodes to the southernmost row (`y = n-1`; digits only `2` and `3`). Longitude still resolves within the cap, but all latitude detail poleward of the clip is flattened into that row. A writer placing polar content SHOULD therefore include `coordinates.lat` / `coordinates.lng` (section 4.9), which represents the poles exactly. + +#### 4.2 WGS 84 point → tile `(z, x, y)` + +`z` is the zoom (cell precision), an integer `1..30`. Let `n = 2^z`. + +``` +x = floor( (λ + 180) / 360 × n ) +y = floor( (1 − ln((1 + sin φ) / (1 − sin φ)) / (2π)) / 2 × n ) +``` + +`φ` is clipped latitude in radians; `λ` is reduced longitude in degrees. Then clamp: if `x = n` then `x = 0`; `y` MUST be clamped to `[0, n-1]`. + +`floor` assigns a point on an east or south tile edge to the eastern / southern neighbour. The north and west edges belong to the tile. + +`(z, x, y)` is the computational form. It is not written on chain. + +#### 4.3 Tile → quadkey + +``` +quadkey = "" +for i from z-1 down to 0: + digit = 0 + mask = 1 << i + if (x & mask) ≠ 0: digit += 1 + if (y & mask) ≠ 0: digit += 2 + append ASCII('0' + digit) to quadkey +``` + +Digits on the map (y grows south, as in web tiles): + +``` ++---+---+ +| 0 | 1 | ++---+---+ +| 2 | 3 | ++---+---+ +``` + +`len(quadkey) = z`. A `quadkey` value MUST match `^[0-3]{1,30}$`. Indexers MUST ignore an invalid `quadkey` as a placement. + +#### 4.4 Quadkey → tile + +``` +z = len(quadkey) +x = 0 +y = 0 +for i, ch in enumerate(quadkey): + bit = z - 1 - i + d = ch - '0' + if d & 1: x |= (1 << bit) + if d & 2: y |= (1 << bit) +``` + +#### 4.5 Tile → WGS 84 bbox + +``` +west = x / n × 360 - 180 +east = (x + 1) / n × 360 - 180 +north = atan(sinh(π × (1 - 2y / n))) × 180/π +south = atan(sinh(π × (1 - 2(y+1) / n))) × 180/π +``` + +A point lies in the unique tile produced by section 4.2. These bbox formulas are the inverse of that assignment. + +#### 4.6 Parents, children, prefixes + +| Relation | Rule | +|----------|------| +| Parent | Drop the last digit. The root (whole projected square) has no digit and is not written as a tag. | +| Children | Append `0`, `1`, `2`, or `3`. | +| Ancestor | `A` is an ancestor of `C` iff `A` is a prefix of `C`. | +| Prefix match | `C` matches prefix `P` iff `C = P` or `C` starts with `P`. | + +A prefix query for `P` returns tags at `P` and all finer cells inside it. + +#### 4.7 Choosing `z` + +`z` is a precision, not a map zoom the user happens to be looking at. Writers SHOULD pick a precision suited to the object and then name as many cells as it occupies (section 2.1): + +| `z` | Approx. equator edge | +|-----|----------------------| +| 10 | 39 km | +| 14 | 2.4 km | +| 16 | 611 m | +| 18 | 153 m | +| 20 | 38 m | +| 22 | 9.6 m | +| 24 | 2.4 m | + +Ground edge length scales as `cos(latitude)` times the equator figure (the cells stay square). Equator edge ≈ `40075016.69 / 2^z` metres. + +#### 4.8 Test vectors + +| lon | lat | z | x | y | `quadkey` | +|-----|-----|---|-----|-----|------------| +| -74.0445 | 40.6892 | 18 | 77154 | 98583 | `032010110301120232` | +| -74.0445 | 40.6892 | 16 | 19288 | 24645 | `0320101103011202` | +| 0 | 0 | 1 | 1 | 1 | `3` | + +The first row's bbox is approximately west −74.045105, south 40.688969, east −74.043732, north 40.690010. Parent of the z=18 cell is the z=16 cell shown. + +#### 4.9 Other geospatial systems: granularity and translation + +Other cell systems exist and are indexed by other consumers. They are different tessellations, not different spellings of the same thing: + +| System | Id form | Cell shape | Hierarchy | High latitudes | +|--------|---------|-----------|-----------|----------------| +| Web Mercator quadkey (this BRC) | base-4 string | square in projection, square-ish on ground | exact — parent is a string prefix | consistent aspect; clipped past ±85.05° | +| Geohash | base-32 string | lat/lng rectangle | exact — parent is a string prefix | tall-skinny cells; aspect alternates by length parity — even lengths are about twice as wide as tall | +| H3 cell index | 64-bit id (15-char hex) | hexagon (12 pentagons) on an icosahedron | approximate — a child hex is not exactly contained by its parent; ids are not plain string prefixes | uniform aspect; poles covered | +| Point (`coordinates.lat` / `coordinates.lng`) | decimal degrees | — | — | poles representable | + +The decisive properties, and what each system trades: + +| Property | Quadkey | Geohash | H3 | +|----------|---------|---------|-----| +| Exact nesting — parent is a string prefix, child fully contained, so a region query is a prefix match on a plain key-value index | yes | yes | no | +| Consistent cell aspect at high latitudes | yes | no | yes | +| Decodes directly to the web-map tile pyramid (`z/x/y`) every renderer already draws | yes | no | no | +| Poles addressable | caps collapse to the edge rows (section 4.1) — deterministic, coarse in latitude | yes, but cells degrade to ground slivers and the pole point is degenerate across longitudes | yes, with normal-sized cells | + +No candidate wins every row. This BRC takes exact nesting and renderer-native decoding, and pays for them at the polar caps; a record that must stay precise there carries the point. + +Translation rules: + +1. **Point → any cell is exact.** Every system defines point-to-cell at every resolution. A record that carries `coordinates.lat` / `coordinates.lng` can be indexed under any system, at any granularity, now or later. +2. **Cell → cell across systems is lossy.** The grids tessellate differently, so a cell in one system is covered by several partially-overlapping cells of another, and the covering varies with latitude. A cell id translated across systems MUST be treated as approximate. +3. **Cell → point recovers only the cell's center or bbox** — error up to the cell size (section 4.7). + +Therefore: **a writer that expects consumers on other geospatial systems SHOULD include `coordinates.lat` / `coordinates.lng`**, with enough decimal places for the intended precision (5 decimals ≈ 1.1 m of latitude; 6 ≈ 0.11 m). The quadkey carries the record's *extent*; the point carries its translatable *position*. A record published as a cell id alone — in any system — has permanently discarded everything finer than that cell. + +Approximate granularity equivalence at the equator (area-matched; geohash ground size is latitude-dependent and its even lengths are about twice as wide as tall): + +| Ground scale | quadkey `z` | geohash length | H3 resolution | +|--------------|-------------|----------------|---------------| +| ~5 km | 13 | 5 | 6–7 | +| ~1 km | 15–16 | 6 | 8 | +| ~150 m | 18 | 7 | 9–10 | +| ~40 m | 20 | 8 | 11 | +| ~5 m | 23 | 9 | 13 | + + +### 5. Objects, custody and position + +A **placed object** is a 1Sat token[2](#footnote-2) whose resolved MAP state carries at least one valid `quadkey`. + +**Custody follows BRC-159.** The object's identity is its BRC-159 origin outpoint. It passes from holder to holder by BRC-159 transfer — a 1-satoshi input landing in a 1-satoshi output under sat ordering. Burn, new-origin detection and tip resolution are BRC-159's, unchanged. This BRC adds nothing to that model and defines no authorization of its own. + +**Position is this document's.** An object occupies the cells named by the `quadkey` values in effect at the tip of its custody chain. A transaction that re-declares `quadkey` (section 2.1) **relocates** it. A transfer that does not leaves it at the same coordinates under new ownership. Removing `quadkey` unplaces it, and the object leaves the spatial index. + +The two layers advance at different rates along the same chain: every relocation rides on a transfer, because writing MAP data means spending the output, but most transfers relocate nothing. + +**Authorization is inherited, not restated.** Only the party who can spend the tip can write new MAP data onto the chain, so BRC-159's spend authority already gates every relocation. That is why this BRC needs no signature scheme. An identity signature over the MAP payload MAY be attached by any identity protocol on the output; indexers that understand that protocol SHOULD record the signer. Indexers MUST NOT require a particular identity protocol. + +**Portals.** `world` MAY change across a transfer. Because a transfer that writes only `world` leaves `quadkey` in effect, the object arrives at the same cell in a different world — a **portal**. A portal MAY also relocate the object by writing `quadkey` in the same command. The BRC-159 origin is unchanged throughout, so identity survives a portal. To return to the default world, clear the key with `SELECT REMOVE world`; `SET world earth` is honoured under section 3 but SHOULD NOT be written in preference to removal. + +An object occupies many cells but exactly one world at a time. + +A destination world receives whatever anyone able to spend a tip sends it; arrival is not an authorization. A world that restricts what it displays MUST filter on its own terms. + +**History.** An indexer MUST be able to emit the ordered list of `(outpoint, quadkey, world, join keys)` from origin to tip, and the unplace if any. This is a projection over the BRC-159 chain, not a separate structure. + +Tags are claims, not proof of presence. Anyone MAY place a cell; only the current holder can relocate that object. + +> **Dependency note.** Resolving a MAP key's value at a point along a 1Sat chain — the rule that makes "in effect at the tip" well defined — is not specified by MAP, which has no ownership model, nor by BRC-160. Implementations resolve it by merging each MAP revision along the chain in sequence order, last write wins. This BRC depends on that rule and should cite it once it is written down. + +### 6. Query + +This section is the predicate. It is not an HTTP API and not a particular indexer. + +Let `world(q)` be Earth when the query omits a world id or passes `earth`; otherwise the exact id. + +A stored record **matches world** iff its world equals `world(q)` under section 3. + +A record with several cells matches a query iff **any** of its cells matches. Indexers MUST return such a record once, not once per matching cell. + +**Prefix query.** Given `world` and prefix `P` (a quadkey, or empty `P` for the whole projection): every current tip that matches the world and any of whose `quadkey` values prefix-matches `P` (section 4.6). + +**Viewport query.** Given `world` and a WGS 84 box `(west, south, east, north)`, with optional clip of `south`/`north` to `±MAX_LAT`: + +1. If the box crosses the antimeridian (`west > east`), split into `(west, south, 180, north)` and `(-180, south, east, north)` and union the results. +2. A record matches iff it matches the world **and** the bbox of at least one of its cells (section 4.5) **intersects** the (clipped, split) box. + +Intersect includes descendants (finer tags inside the view), the covering cells, **and ancestors** (a coarse city-sized tag still appears when the view is a street). Empty `P` / a view of the whole projection returns every current tip in that world. + +A sufficient algorithm for intersect using only prefix lookups: take the tiles at some zoom `z` that overlap the box; for each tile `T`, query prefix(`T`) and also the exact cells of every ancestor of `T`. Implementations MAY use any spatial index that returns the same set. + +Indexers MUST implement prefix and viewport as defined here. They MAY add filters (join key, time, origin). They MUST return at least: origin outpoint, tip outpoint, every `quadkey` value in effect, world, remaining MAP keys on the tip, txid, and confirmation height when mined. + +Three cases separate a conforming implementation from a plausible one, and SHOULD appear in any test suite: an ancestor tag coarser than the viewport (matches); a box crossing the antimeridian (splits and unions); and a point exactly on a tile's east or south edge (lands in the neighbour, per section 4.2). + +### 7. Out of scope + +- Content bytes (images, inscriptions, 3D models). Attach them with other protocols on the same output; this BRC does not read them. +- Geometry. `quadkey` names cells an object occupies; polylines, polygons and meshes are not expressible and are not intended to be. +- Alternate *placement* encodings. `geohash` and `h3` may ride along as non-placing read keys (sections 2, 4.9), but cross-system cell translation is lossy and placement is defined over `quadkey` only. +- Overlay wire formats, payment, or wallet baskets. + +## Implementations + +A compatible implementation tags outputs as in sections 1–5, answers section 6, and works for **Earth maps and seeded worlds** with the same cell math. + +**World of Alchema** ([alchema.world](https://alchema.world)) is an existing project in this family — a game world that places things in space. This BRC does not specify Alchema's internals. It exists so Earth-map clients and seeded-world clients can share indexers and join tags. + +Any client that already renders a Web Mercator map or globe can decode `quadkey` to `(z, x, y)` or a bbox and draw the record without a second projection. + +## References + +- 1: BRC-303 — *MAP — Magic Attribute Protocol*. Commands, the `:::` instruction-set separator, and the shared keyspace including the `coordinates.*` compound-attribute convention. +- 2: [BRC-159](../tokens/0159.md) — *1Sat Ordinals — Single-Satoshi Tokens and Origin Tracking*. Origin, sat ordering, transfer, tip and burn. +- 3: Bradner, S. (1997). *Key words for use in RFCs to Indicate Requirement Levels*. RFC 2119. https://www.rfc-editor.org/rfc/rfc2119 +- 4: IOGP. *WGS 84 / Pseudo-Mercator* — EPSG:3857. The well-known Web Mercator tile pyramid (slippy-map `z/x/y`). +- 5: NIMA. (2000). *Department of Defense World Geodetic System 1984*. TR8350.2. Horizontal positions in this BRC are WGS 84 (EPSG:4326) degrees. +- 6: Microsoft. *Bing Maps Tile System* — quadkey encoding of the Web Mercator pyramid. https://learn.microsoft.com/en-us/bingmaps/articles/bing-maps-tile-system +- 7: [BRC-160](../tokens/0160.md) — *1Sat Ordinals — Inscription Envelopes*. diff --git a/apps/README.md b/apps/README.md index 22173154..c640aa3b 100644 --- a/apps/README.md +++ b/apps/README.md @@ -14,4 +14,4 @@ BRC | Standard 220 | [NotaryHash — Privacy-Preserving Signed-Hash Notarization with SPV-Verifiable Certificates](./0220.md) 224 | [Block Media Format (BMF) — Composable On-Chain Audio/Video](./0224.md) 227 | [Frictionless On-Chain Onboarding via Pre-Funded Claimable Tokens](./0227.md) -229 | [MAP Location Keys (quadkey, world, lat, lng)](./0229.md) +315 | [MAP Location Keys (quadkey, world, coordinates.*)](./0315.md) From 550f9648b658dce87934c1811c3dbcc637459518 Mon Sep 17 00:00:00 2001 From: Luke Rohenaz Date: Thu, 27 Aug 2026 07:14:39 -0400 Subject: [PATCH 09/13] renumber BRC-315 to BRC-515; cite BRC-503 for MAP Upstream bsv-blockchain/BRCs has BRC-300 and BRC-369 claimed by open PRs, so the corpus moved from 300-313 to 500-513. This BRC follows into the same block and updates its MAP citation from BRC-303 to BRC-503. Also notes: BRC-229 was itself already claimed upstream by an open PR (Wallet-Native Elliptic Curve Point Multiplication), so the original number was a live collision independent of the dependency-ordering problem. --- README.md | 2 +- SUMMARY.md | 2 +- apps/{0315.md => 0515.md} | 12 ++++++------ apps/README.md | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) rename apps/{0315.md => 0515.md} (98%) diff --git a/README.md b/README.md index 9a3a02ee..1683a2fc 100644 --- a/README.md +++ b/README.md @@ -235,7 +235,7 @@ BRC | Standard 226 | [Miner-Enforced Resale-Royalty Covenant Tokens (OP_PUSH_TX)](./tokens/0226.md) 227 | [Frictionless On-Chain Onboarding via Pre-Funded Claimable Tokens](./apps/0227.md) 228 | [Unlinkable Payments under the Identity Paradigm](./payments/0228.md) -315 | [MAP Location Keys (quadkey, world, coordinates.*)](./apps/0315.md) +515 | [MAP Location Keys (quadkey, world, coordinates.*)](./apps/0515.md) ## License diff --git a/SUMMARY.md b/SUMMARY.md index f871325c..95290343 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -20,7 +20,7 @@ * [NotaryHash — Privacy-Preserving Signed-Hash Notarization with SPV-Verifiable Certificates](./apps/0220.md) * [Block Media Format (BMF) — Composable On-Chain Audio/Video](./apps/0224.md) * [Frictionless On-Chain Onboarding via Pre-Funded Claimable Tokens](./apps/0227.md) -* [MAP Location Keys (quadkey, world, coordinates.*)](./apps/0315.md) +* [MAP Location Keys (quadkey, world, coordinates.*)](./apps/0515.md) ## Wallet diff --git a/apps/0315.md b/apps/0515.md similarity index 98% rename from apps/0315.md rename to apps/0515.md index beb6601b..6407a109 100644 --- a/apps/0315.md +++ b/apps/0515.md @@ -1,4 +1,4 @@ -# BRC-315: MAP Location Keys (`quadkey`, `world`, `coordinates.*`) +# BRC-515: MAP Location Keys (`quadkey`, `world`, `coordinates.*`) Luke Rohenaz (rohenaz@gmail.com) @@ -24,7 +24,7 @@ The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted ### 1. MAP carriage -The location keys are MAP keys. This BRC defines no commands of its own; it uses MAP's command set as specified in BRC-303[1](#footnote-1). +The location keys are MAP keys. This BRC defines no commands of its own; it uses MAP's command set as specified in BRC-503[1](#footnote-1). Readers MUST scan an output's script for a MAP command: a data push of the ASCII Bitcom protocol id @@ -32,7 +32,7 @@ Readers MUST scan an output's script for a MAP command: a data push of the ASCII 1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5 ``` -followed by a MAP command and its arguments. MAP MAY share the output with other protocols; when protocols are concatenated the separator is a push of the single byte `0x7c` (`|`), and multiple MAP commands in one output are joined by the `:::` instruction-set separator (BRC-303 §3) and evaluated in order. +followed by a MAP command and its arguments. MAP MAY share the output with other protocols; when protocols are concatenated the separator is a push of the single byte `0x7c` (`|`), and multiple MAP commands in one output are joined by the `:::` instruction-set separator (BRC-503 §3) and evaluated in order. Inscriptions or other locking payload MAY precede `OP_RETURN` — one output can be, for example, a 1Sat lock plus this MAP data; spending that output is what carries the object forward (section 5). Note that a placed output MUST remain spendable, so the `OP_FALSE OP_RETURN` data-carrier template MUST NOT be used when the lock has to stay redeemable[7](#footnote-7). @@ -77,7 +77,7 @@ Every one of these keys is named for its encoding. Writers MUST NOT put one enco #### 2.1 Placing, moving and unplacing -These are MAP commands (BRC-303), not new operations: +These are MAP commands (BRC-503), not new operations: | Intent | Command | |--------|---------| @@ -88,7 +88,7 @@ These are MAP commands (BRC-303), not new operations: Writers SHOULD keep the cell list small, choosing a coarser `z` (section 4.7) in preference to enumerating many fine cells. Indexers MAY cap the number of cells they will index for one object and MUST document that cap. -Any other MAP keys on the same command are **join tags**. A client that has one identifier can look up the others on that output. Key spellings for global identifiers (`url`, `tx`, `btctx`, `ethtx`, `topic`, `upc`, `name`, and others) are MAP's shared keyspace, defined in BRC-303 §5; this BRC does not own them and does not restate the list. +Any other MAP keys on the same command are **join tags**. A client that has one identifier can look up the others on that output. Key spellings for global identifiers (`url`, `tx`, `btctx`, `ethtx`, `topic`, `upc`, `name`, and others) are MAP's shared keyspace, defined in BRC-503 §5; this BRC does not own them and does not restate the list. ### 3. Worlds @@ -339,7 +339,7 @@ Any client that already renders a Web Mercator map or globe can decode `quadkey` ## References -- 1: BRC-303 — *MAP — Magic Attribute Protocol*. Commands, the `:::` instruction-set separator, and the shared keyspace including the `coordinates.*` compound-attribute convention. +- 1: BRC-503 — *MAP — Magic Attribute Protocol*. Commands, the `:::` instruction-set separator, and the shared keyspace including the `coordinates.*` compound-attribute convention. - 2: [BRC-159](../tokens/0159.md) — *1Sat Ordinals — Single-Satoshi Tokens and Origin Tracking*. Origin, sat ordering, transfer, tip and burn. - 3: Bradner, S. (1997). *Key words for use in RFCs to Indicate Requirement Levels*. RFC 2119. https://www.rfc-editor.org/rfc/rfc2119 - 4: IOGP. *WGS 84 / Pseudo-Mercator* — EPSG:3857. The well-known Web Mercator tile pyramid (slippy-map `z/x/y`). diff --git a/apps/README.md b/apps/README.md index c640aa3b..e2132ef9 100644 --- a/apps/README.md +++ b/apps/README.md @@ -14,4 +14,4 @@ BRC | Standard 220 | [NotaryHash — Privacy-Preserving Signed-Hash Notarization with SPV-Verifiable Certificates](./0220.md) 224 | [Block Media Format (BMF) — Composable On-Chain Audio/Video](./0224.md) 227 | [Frictionless On-Chain Onboarding via Pre-Funded Claimable Tokens](./0227.md) -315 | [MAP Location Keys (quadkey, world, coordinates.*)](./0315.md) +515 | [MAP Location Keys (quadkey, world, coordinates.*)](./0515.md) From d89ac91906ba8015e7541d4e06746d2fdc97391a Mon Sep 17 00:00:00 2001 From: Luke Rohenaz Date: Thu, 27 Aug 2026 07:52:21 -0400 Subject: [PATCH 10/13] BRC-515: close the authorization hole and make state resolution normative Advisory review found that the spec's authorization story was asserted in prose but never enforced, and that the command grammar it borrowed made the gap exploitable. MAP's SELECT exists so an unrelated transaction can target a previously written record, and MAP has no ownership model. Section 2.1 adopted that grammar for unplace and drop-cell while leaving unbound, and nothing required an indexer to reject a command arriving outside the object's custody chain. A conforming vanilla-MAP indexer would therefore let anyone unplace anyone else's object with one unrelated transaction, while an indexer written by the authors would not. Two changes close it: is bound to the spent tip's txid, and indexers MUST apply a MAP command to a placed object only when the carrying transaction is a link in that object's BRC-159 chain. 'The quadkey in effect at the tip' is the spec's central noun and its definition lived in a non-normative dependency note, which nothing can conform to. Promoted to an interim normative rule that also pins the two orderings the note omitted: multiple MAP outputs in one transaction, and multiple commands under :::. Marked superseded when a general resolution BRC lands. Also settles CLEAR, which BRC-503 defines and this BRC ignored. Other fixes, each verified before applying: - footnotes 4 and 6 were swapped at section 4 - EPSG:3857 pointed at the Bing quadkey reference and quadkey pointed at IOGP - section 6 returned 'MAP keys on the tip', contradicting resolved-state semantics; a join tag set at origin and not repeated would be returned by one indexer and omitted by another - world-id genesis outpoints cited BRC-159 normalization, which makes txid.0 equal txid_0, while world equality is exact bytes; underscore only - geohash and h3 now carry value grammars, with the reason honest naming has to be normative: 8928308280fffff is a valid H3 index that also satisfies the geohash grammar, since base32 drops a/i/l/o - BRC-503 section 5 does not list btctx, ethtx or name; citation corrected - indexer cell cap MUST be at least 16, so a cap of 1 is not conforming - closed intersection at shared edges; west = east is degenerate, not a wrap - geohash and h3 MAY appear only alongside coordinates.* - carriage deviation from BRC-503 section 1 stated normatively --- apps/0515.md | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/apps/0515.md b/apps/0515.md index 6407a109..621d9c5d 100644 --- a/apps/0515.md +++ b/apps/0515.md @@ -34,7 +34,7 @@ Readers MUST scan an output's script for a MAP command: a data push of the ASCII followed by a MAP command and its arguments. MAP MAY share the output with other protocols; when protocols are concatenated the separator is a push of the single byte `0x7c` (`|`), and multiple MAP commands in one output are joined by the `:::` instruction-set separator (BRC-503 §3) and evaluated in order. -Inscriptions or other locking payload MAY precede `OP_RETURN` — one output can be, for example, a 1Sat lock plus this MAP data; spending that output is what carries the object forward (section 5). Note that a placed output MUST remain spendable, so the `OP_FALSE OP_RETURN` data-carrier template MUST NOT be used when the lock has to stay redeemable[7](#footnote-7). +Inscriptions or other locking payload MAY precede `OP_RETURN` — one output can be, for example, a 1Sat lock plus this MAP data; spending that output is what carries the object forward (section 5). This deviates deliberately from BRC-503 §1, which frames MAP data in the `OP_FALSE OP_RETURN` data-carrier template of BRC-18: a placed output MUST remain spendable, because spending it is what carries the object forward (section 5), and `OP_FALSE OP_RETURN` renders the output unspendable in the lock-plus-envelope patterns this BRC relies on[7](#footnote-7). Indexers implementing BRC-503 MUST therefore accept MAP data introduced by a bare `OP_RETURN` following a lock. Illustrative shape (one output): @@ -57,13 +57,13 @@ No key prefix is reserved. The discriminator for `quadkey` is its value grammar | Key | Value | Cardinality | Rule | |-----|--------|-------------|------| -| `quadkey` | Web Mercator quadkey (section 4) | list | MUST on a placed output. Each member is ASCII digits `0`–`3`, length 1..30. | +| `quadkey` | Web Mercator quadkey (section 4) | list | Each member is ASCII digits `0`–`3`, length 1..30. Presence of at least one valid value places the output. | | `world` | World id (section 3) | single | MAY. Omit for Earth. | | `coordinates.lat` | WGS 84 latitude, decimal degrees, ASCII | single | MAY. Both `coordinates.lat` and `coordinates.lng` or neither. | | `coordinates.lng` | WGS 84 longitude, decimal degrees, ASCII | single | MAY. Both `coordinates.lat` and `coordinates.lng` or neither. | | `coordinates.alt` | Altitude in metres above the WGS 84 ellipsoid, ASCII | single | MAY. Only with `coordinates.lat` and `coordinates.lng`. Never places. | -| `geohash` | Geohash (base-32 string) of the same point | single | MAY. Never places. | -| `h3` | H3 **cell** index (15-char lowercase hex) of the same point | single | MAY. Never places. | +| `geohash` | Geohash of the same point, `^[0-9b-hjkmnp-z]{1,12}$` | single | MAY. Never places. | +| `h3` | H3 **cell** index of the same point, `^[0-9a-f]{15}$` | single | MAY. Never places. | The `coordinates.*` keys are MAP's established compound-attribute convention for geolocation and are not introduced here. Writers MUST NOT use bare `lat` or `lng` for these values. @@ -71,10 +71,12 @@ Presence of at least one valid `quadkey` value is the placement signal. Writers **Extent and position are different jobs.** `quadkey` carries the record's *extent* — the cells it occupies — and is therefore list-valued. `coordinates.*` carries its translatable *position* — a single anchor point — and never places the output. A record with many cells still has one point. `coordinates.*` MUST NOT be treated as a substitute cell id. -`geohash` and `h3` follow the same pattern: redundant read keys for consumers already indexing those systems. When present they MUST be derived from the same point as `coordinates.*`. They never place the output and MUST NOT be treated as substitute cell ids. Section 4.9 explains why the systems do not substitute for one another. +`geohash` and `h3` follow the same pattern: redundant read keys for consumers already indexing those systems. They MAY appear only when `coordinates.*` is present, and MUST be derived from that same point. They never place the output and MUST NOT be treated as substitute cell ids. Section 4.9 explains why the systems do not substitute for one another. Every one of these keys is named for its encoding. Writers MUST NOT put one encoding's value under another's key: no quadkey in `geohash`, no geohash (or H3, or any other id) in `quadkey`, and **no H3 cell index in `geohash`** — an H3 index is not a geohash, and mislabeling it poisons every consumer that trusts the key name. +Validation cannot catch this for you. The geohash alphabet is base-32 with `a`, `i`, `l` and `o` removed, and many H3 indexes are composed entirely of surviving characters — `8928308280fffff` satisfies the `geohash` grammar above while being an H3 index. Honest naming is therefore a normative rule rather than something a parser can enforce. + #### 2.1 Placing, moving and unplacing These are MAP commands (BRC-503), not new operations: @@ -86,9 +88,13 @@ These are MAP commands (BRC-503), not new operations: | Give up one cell, keeping the rest | `SELECT DELETE quadkey ` | | Unplace entirely (clears every cell at once) | `SELECT REMOVE quadkey` | -Writers SHOULD keep the cell list small, choosing a coarser `z` (section 4.7) in preference to enumerating many fine cells. Indexers MAY cap the number of cells they will index for one object and MUST document that cap. +In every command above, `` is the txid of the transaction whose tip outpoint this transaction spends (section 5). Writers MUST use that txid and no other. A `SELECT` naming any other transaction is not a command of this protocol and MUST be ignored by indexers. + +`CLEAR `, subject to the same rules, erases the object's entire resolved MAP state: it unplaces the object and drops its join tags. Writers SHOULD prefer `SELECT REMOVE quadkey` when only unplacement is intended. + +Writers SHOULD keep the cell list small, choosing a coarser `z` (section 4.7) in preference to enumerating many fine cells. Indexers MAY cap the number of cells they will index for one object and MUST document that cap. The cap MUST be at least 16. -Any other MAP keys on the same command are **join tags**. A client that has one identifier can look up the others on that output. Key spellings for global identifiers (`url`, `tx`, `btctx`, `ethtx`, `topic`, `upc`, `name`, and others) are MAP's shared keyspace, defined in BRC-503 §5; this BRC does not own them and does not restate the list. +Any other MAP keys on the same command are **join tags**. A client that has one identifier can look up the others on that output. Key spellings for global identifiers are MAP's shared keyspace — BRC-503 §5 establishes `url`, `tx`, `topic`, `upc`, `isbn` and hash names such as `md5`; `name` follows BRC-160's MAP table. This BRC owns none of them and does not restate the list. ### 3. Worlds @@ -101,19 +107,19 @@ World equality is those exact bytes. It never consults MAP's `app` key, so **a w | Form | Use | |------|-----| -| **Genesis outpoint** — the outpoint of an output that creates the world, in the `txid_vout` form and normalization of BRC-159 | Preferred. Unique with no coordination, and gives world metadata and world ownership somewhere to live without a registry. | +| **Genesis outpoint** — the outpoint of an output that creates the world, in the `txid_vout` underscore form. The dot form is not a valid world id and indexers MUST NOT normalize it; world equality remains exact bytes. | Preferred. Unique with no coordination, and gives world metadata and world ownership somewhere to live without a registry. | | **Hex hash** of the world's genesis parameters | For procedurally generated worlds with no on-chain presence. | | `myapp:` | Fallback for hand-authored development worlds. Uniqueness depends on app authors choosing distinct prefixes. | A bare seed such as `seed-7` MUST NOT be used: with world equality defined on `world` alone, every unrelated application shipping the same string would silently share one world. -Writers MUST NOT emit an empty `world`; for tolerance, indexers MUST accept an absent `world`, an empty `world`, and the ASCII string `earth` as the same (default) world, and MUST NOT mix that world with any other id. +Writers MUST NOT emit an empty `world`; for tolerance, indexers MUST accept an absent `world`, an empty `world`, and the lowercase ASCII string `earth` as the same (default) world (any other casing is an ordinary world id), and MUST NOT mix that world with any other id. If `app` and `world` disagree, `world` governs; `app` has no role in world identity. ### 4. Cell encoding (Web Mercator quadkey) -The cell space is the standard Web Mercator / slippy-map tile pyramid (EPSG:3857)[6](#footnote-6) over WGS 84[5](#footnote-5). The on-chain id is a **quadkey**[4](#footnote-4): the path from the root square to a tile, one digit per zoom level. +The cell space is the standard Web Mercator / slippy-map tile pyramid (EPSG:3857)[4](#footnote-4) over WGS 84[5](#footnote-5). The on-chain id is a **quadkey**[6](#footnote-6): the path from the root square to a tile, one digit per zoom level. #### 4.1 Projection limits @@ -283,7 +289,7 @@ A **placed object** is a 1Sat token[2](#footnote-2) whose resolved MA The two layers advance at different rates along the same chain: every relocation rides on a transfer, because writing MAP data means spending the output, but most transfers relocate nothing. -**Authorization is inherited, not restated.** Only the party who can spend the tip can write new MAP data onto the chain, so BRC-159's spend authority already gates every relocation. That is why this BRC needs no signature scheme. An identity signature over the MAP payload MAY be attached by any identity protocol on the output; indexers that understand that protocol SHOULD record the signer. Indexers MUST NOT require a particular identity protocol. +**Authorization is inherited, not restated.** Only the party who can spend the tip can write new MAP data onto the chain, so BRC-159's spend authority already gates every relocation. That is why this BRC needs no signature scheme. Indexers MUST apply a MAP command to a placed object's state only when the transaction carrying it is a link in that object's BRC-159 chain — that is, it spends the object's current tip. A MAP command in any other transaction, including a `SELECT` naming a txid in the chain, MUST NOT alter the object's cells, world, or join tags. An identity signature over the MAP payload MAY be attached by any identity protocol on the output; indexers that understand that protocol SHOULD record the signer. Indexers MUST NOT require a particular identity protocol. **Portals.** `world` MAY change across a transfer. Because a transfer that writes only `world` leaves `quadkey` in effect, the object arrives at the same cell in a different world — a **portal**. A portal MAY also relocate the object by writing `quadkey` in the same command. The BRC-159 origin is unchanged throughout, so identity survives a portal. To return to the default world, clear the key with `SELECT REMOVE world`; `SET world earth` is honoured under section 3 but SHOULD NOT be written in preference to removal. @@ -295,7 +301,7 @@ A destination world receives whatever anyone able to spend a tip sends it; arriv Tags are claims, not proof of presence. Anyone MAY place a cell; only the current holder can relocate that object. -> **Dependency note.** Resolving a MAP key's value at a point along a 1Sat chain — the rule that makes "in effect at the tip" well defined — is not specified by MAP, which has no ownership model, nor by BRC-160. Implementations resolve it by merging each MAP revision along the chain in sequence order, last write wins. This BRC depends on that rule and should cite it once it is written down. +**State resolution.** Until a dedicated BRC specifies MAP state over a 1Sat chain, the following rule is normative for the keys of this document: an indexer resolves an object's MAP state by applying each in-chain MAP instruction set in chain order — origin to tip; within one transaction, output order; within one output, `:::` order — with later writes overriding earlier ones per command semantics (last write wins). "In effect at the tip" means the result of that fold. When a general resolution BRC exists, it supersedes this paragraph and this BRC will cite it. ### 6. Query @@ -312,13 +318,13 @@ A record with several cells matches a query iff **any** of its cells matches. In **Viewport query.** Given `world` and a WGS 84 box `(west, south, east, north)`, with optional clip of `south`/`north` to `±MAX_LAT`: 1. If the box crosses the antimeridian (`west > east`), split into `(west, south, 180, north)` and `(-180, south, east, north)` and union the results. -2. A record matches iff it matches the world **and** the bbox of at least one of its cells (section 4.5) **intersects** the (clipped, split) box. +2. A record matches iff it matches the world **and** the bbox of at least one of its cells (section 4.5) **intersects** the (clipped, split) box. Intersection is closed: a shared edge counts as intersecting. `west = east` is a degenerate zero-width box, not a full wrap; the split in step 1 triggers only on `west > east`. Intersect includes descendants (finer tags inside the view), the covering cells, **and ancestors** (a coarse city-sized tag still appears when the view is a street). Empty `P` / a view of the whole projection returns every current tip in that world. A sufficient algorithm for intersect using only prefix lookups: take the tiles at some zoom `z` that overlap the box; for each tile `T`, query prefix(`T`) and also the exact cells of every ancestor of `T`. Implementations MAY use any spatial index that returns the same set. -Indexers MUST implement prefix and viewport as defined here. They MAY add filters (join key, time, origin). They MUST return at least: origin outpoint, tip outpoint, every `quadkey` value in effect, world, remaining MAP keys on the tip, txid, and confirmation height when mined. +Indexers MUST implement prefix and viewport as defined here. They MAY add filters (join key, time, origin). They MUST return at least: origin outpoint, tip outpoint, every `quadkey` value in effect, world, remaining MAP keys in effect (the resolved state, section 5), txid, and confirmation height when mined. Three cases separate a conforming implementation from a plausible one, and SHOULD appear in any test suite: an ancestor tag coarser than the viewport (matches); a box crossing the antimeridian (splits and unions); and a point exactly on a tile's east or south edge (lands in the neighbour, per section 4.2). From b90d6239429cf974a91a32ed8477d2fe581bd4ad Mon Sep 17 00:00:00 2001 From: Luke Rohenaz Date: Thu, 27 Aug 2026 08:01:52 -0400 Subject: [PATCH 11/13] BRC-515: authorization is required but not defined here Corrects an over-narrow fix from the previous commit, which wrote BRC-159 spend authority into the spec as the only way a position command can be authorized. That is one binding, not the model. The layering is: ownership and permissioning belong to whatever system owns the asset; placement semantics belong to MAP and to this document. A system may not use ordinals at all - it might identify users by AIP or Sigma signature and authorize a SELECT by checking that the signer is entitled to edit the target record. Section 5 now requires that an authorization model be in force, requires the indexer to state which one it enforces, and requires commands it cannot authorize to be ignored rather than applied - without mandating any particular model. Spend authority and identity signature are described as the two already available; anything else is free to define its own. The security requirement is unchanged and is the reason the section exists: MAP's SELECT lets any transaction name any earlier one, so an indexer that applies position commands without an authorization check will let anyone move anyone else's object. Generalizes the language that assumed a 1Sat chain: 'tip' becomes 'current record' except where spend authority is the subject, carriage explains why a spend-authorized output needs a bare OP_RETURN rather than asserting it of every placement, and portals and history are stated in terms of authorized writes. --- apps/0515.md | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/apps/0515.md b/apps/0515.md index 621d9c5d..f272cfb7 100644 --- a/apps/0515.md +++ b/apps/0515.md @@ -34,7 +34,9 @@ Readers MUST scan an output's script for a MAP command: a data push of the ASCII followed by a MAP command and its arguments. MAP MAY share the output with other protocols; when protocols are concatenated the separator is a push of the single byte `0x7c` (`|`), and multiple MAP commands in one output are joined by the `:::` instruction-set separator (BRC-503 §3) and evaluated in order. -Inscriptions or other locking payload MAY precede `OP_RETURN` — one output can be, for example, a 1Sat lock plus this MAP data; spending that output is what carries the object forward (section 5). This deviates deliberately from BRC-503 §1, which frames MAP data in the `OP_FALSE OP_RETURN` data-carrier template of BRC-18: a placed output MUST remain spendable, because spending it is what carries the object forward (section 5), and `OP_FALSE OP_RETURN` renders the output unspendable in the lock-plus-envelope patterns this BRC relies on[7](#footnote-7). Indexers implementing BRC-503 MUST therefore accept MAP data introduced by a bare `OP_RETURN` following a lock. +Inscriptions or other locking payload MAY precede `OP_RETURN` — one output can be, for example, a 1Sat lock plus this MAP data[7](#footnote-7). + +BRC-503 §1 frames MAP data in the `OP_FALSE OP_RETURN` data-carrier template of BRC-18, which makes the output unspendable. An object whose authorization model is spend authority (section 5) needs its output to stay spendable, because the spend is what carries it forward, so such an output MUST use a bare `OP_RETURN` after the lock instead. Indexers implementing BRC-503 MUST accept both framings. An object under a model that does not rely on spending — an identity signature, say — MAY use either. Illustrative shape (one output): @@ -88,7 +90,7 @@ These are MAP commands (BRC-503), not new operations: | Give up one cell, keeping the rest | `SELECT DELETE quadkey ` | | Unplace entirely (clears every cell at once) | `SELECT REMOVE quadkey` | -In every command above, `` is the txid of the transaction whose tip outpoint this transaction spends (section 5). Writers MUST use that txid and no other. A `SELECT` naming any other transaction is not a command of this protocol and MUST be ignored by indexers. +In every command above, `` is the transaction carrying the record being edited. Whether a given transaction is allowed to edit that record is an **authorization** question, and this BRC does not answer it — section 5 does not define an ownership model, it requires that one exist. An indexer MUST NOT apply any of these commands unless the carrying transaction is authorized under the object's model. `CLEAR `, subject to the same rules, erases the object's entire resolved MAP state: it unplaces the object and drops its join tags. Writers SHOULD prefer `SELECT REMOVE quadkey` when only unplacement is intended. @@ -279,25 +281,39 @@ Approximate granularity equivalence at the equator (area-matched; geohash ground | ~5 m | 23 | 9 | 13 | -### 5. Objects, custody and position +### 5. Objects, authorization and position + +A **placed object** is an output whose resolved MAP state carries at least one valid `quadkey`. + +Two separate questions follow, and this BRC answers only the second: + +| Question | Answered by | +|----------|-------------| +| Who may move this thing? | The object's **authorization model** — not this BRC | +| Where is it, and what does a query return? | **This BRC** | + +**Authorization is required, and is not defined here.** An indexer MUST NOT apply a position command unless the transaction carrying it is authorized to act on the target record. This BRC defines no ownership model and mandates none. It requires that one be in force, that the indexer state which one it enforces, and that commands it cannot authorize be ignored rather than applied. + +Two models are already available, and an application MAY use either: -A **placed object** is a 1Sat token[2](#footnote-2) whose resolved MAP state carries at least one valid `quadkey`. +- **Spend authority (1Sat).** The object is a 1Sat token[2](#footnote-2). Its identity is the BRC-159 origin outpoint; it passes from holder to holder by BRC-159 transfer — a 1-satoshi input landing in a 1-satoshi output under sat ordering. Authority is simply the ability to spend the current tip, so a command carried by a transaction in the object's own chain is authorized and one carried by any other transaction is not. Burn, new-origin detection and tip resolution are BRC-159's, unchanged. +- **Identity signature.** The object need not be a token at all. A system that identifies users by signature — AIP[8](#footnote-8) or Sigma[9](#footnote-9), for instance — authorizes a command by verifying that the signature on the carrying transaction belongs to the party entitled to edit the target record. -**Custody follows BRC-159.** The object's identity is its BRC-159 origin outpoint. It passes from holder to holder by BRC-159 transfer — a 1-satoshi input landing in a 1-satoshi output under sat ordering. Burn, new-origin detection and tip resolution are BRC-159's, unchanged. This BRC adds nothing to that model and defines no authorization of its own. +The point of the split is that a system using neither is still free to define its own. What it may not do is skip the question: MAP's `SELECT ` lets any transaction name any earlier one, so an indexer that applies position commands without an authorization check will let anyone move anyone else's object. -**Position is this document's.** An object occupies the cells named by the `quadkey` values in effect at the tip of its custody chain. A transaction that re-declares `quadkey` (section 2.1) **relocates** it. A transfer that does not leaves it at the same coordinates under new ownership. Removing `quadkey` unplaces it, and the object leaves the spatial index. +**Position is this document's.** An object occupies the cells named by the `quadkey` values in effect at its **current record** — the most recent authorized write, which under spend authority is the tip of its 1Sat chain. A transaction that re-declares `quadkey` (section 2.1) **relocates** it. One that does not leaves it where it was, whoever now holds it. Removing `quadkey` unplaces it, and the object leaves the spatial index. -The two layers advance at different rates along the same chain: every relocation rides on a transfer, because writing MAP data means spending the output, but most transfers relocate nothing. +Ownership and position advance independently. Under spend authority they share a chain but move at different rates: every relocation rides on a transfer, because writing MAP data means spending the output, yet most transfers relocate nothing. An indexer that treats a change of holder as a change of place has conflated the two layers. -**Authorization is inherited, not restated.** Only the party who can spend the tip can write new MAP data onto the chain, so BRC-159's spend authority already gates every relocation. That is why this BRC needs no signature scheme. Indexers MUST apply a MAP command to a placed object's state only when the transaction carrying it is a link in that object's BRC-159 chain — that is, it spends the object's current tip. A MAP command in any other transaction, including a `SELECT` naming a txid in the chain, MUST NOT alter the object's cells, world, or join tags. An identity signature over the MAP payload MAY be attached by any identity protocol on the output; indexers that understand that protocol SHOULD record the signer. Indexers MUST NOT require a particular identity protocol. +**Why this BRC needs no signature scheme of its own.** Both models above already bind a command to a party — one through the spend, the other through a signature — so adding a third would only compete with them. Indexers MUST NOT require a particular identity protocol. -**Portals.** `world` MAY change across a transfer. Because a transfer that writes only `world` leaves `quadkey` in effect, the object arrives at the same cell in a different world — a **portal**. A portal MAY also relocate the object by writing `quadkey` in the same command. The BRC-159 origin is unchanged throughout, so identity survives a portal. To return to the default world, clear the key with `SELECT REMOVE world`; `SET world earth` is honoured under section 3 but SHOULD NOT be written in preference to removal. +**Portals.** `world` MAY change on any authorized write. Because a write that sets only `world` leaves `quadkey` in effect, the object arrives at the same cell in a different world — a **portal**. A portal MAY also relocate the object by writing `quadkey` in the same command. The object's identity is unchanged throughout — under spend authority, the BRC-159 origin — so identity survives a portal. To return to the default world, clear the key with `SELECT REMOVE world`; `SET world earth` is honoured under section 3 but SHOULD NOT be written in preference to removal. An object occupies many cells but exactly one world at a time. A destination world receives whatever anyone able to spend a tip sends it; arrival is not an authorization. A world that restricts what it displays MUST filter on its own terms. -**History.** An indexer MUST be able to emit the ordered list of `(outpoint, quadkey, world, join keys)` from origin to tip, and the unplace if any. This is a projection over the BRC-159 chain, not a separate structure. +**History.** An indexer MUST be able to emit the ordered list of `(outpoint, quadkey, world, join keys)` from origin to current record, and the unplace if any. This is a projection over the object's authorized writes, not a separate structure. Tags are claims, not proof of presence. Anyone MAY place a cell; only the current holder can relocate that object. @@ -313,18 +329,18 @@ A stored record **matches world** iff its world equals `world(q)` under section A record with several cells matches a query iff **any** of its cells matches. Indexers MUST return such a record once, not once per matching cell. -**Prefix query.** Given `world` and prefix `P` (a quadkey, or empty `P` for the whole projection): every current tip that matches the world and any of whose `quadkey` values prefix-matches `P` (section 4.6). +**Prefix query.** Given `world` and prefix `P` (a quadkey, or empty `P` for the whole projection): every current record that matches the world and any of whose `quadkey` values prefix-matches `P` (section 4.6). **Viewport query.** Given `world` and a WGS 84 box `(west, south, east, north)`, with optional clip of `south`/`north` to `±MAX_LAT`: 1. If the box crosses the antimeridian (`west > east`), split into `(west, south, 180, north)` and `(-180, south, east, north)` and union the results. 2. A record matches iff it matches the world **and** the bbox of at least one of its cells (section 4.5) **intersects** the (clipped, split) box. Intersection is closed: a shared edge counts as intersecting. `west = east` is a degenerate zero-width box, not a full wrap; the split in step 1 triggers only on `west > east`. -Intersect includes descendants (finer tags inside the view), the covering cells, **and ancestors** (a coarse city-sized tag still appears when the view is a street). Empty `P` / a view of the whole projection returns every current tip in that world. +Intersect includes descendants (finer tags inside the view), the covering cells, **and ancestors** (a coarse city-sized tag still appears when the view is a street). Empty `P` / a view of the whole projection returns every current record in that world. A sufficient algorithm for intersect using only prefix lookups: take the tiles at some zoom `z` that overlap the box; for each tile `T`, query prefix(`T`) and also the exact cells of every ancestor of `T`. Implementations MAY use any spatial index that returns the same set. -Indexers MUST implement prefix and viewport as defined here. They MAY add filters (join key, time, origin). They MUST return at least: origin outpoint, tip outpoint, every `quadkey` value in effect, world, remaining MAP keys in effect (the resolved state, section 5), txid, and confirmation height when mined. +Indexers MUST implement prefix and viewport as defined here. They MAY add filters (join key, time, origin). They MUST return at least: origin outpoint, current-record outpoint, every `quadkey` value in effect, world, remaining MAP keys in effect (the resolved state, section 5), txid, and confirmation height when mined. Three cases separate a conforming implementation from a plausible one, and SHOULD appear in any test suite: an ancestor tag coarser than the viewport (matches); a box crossing the antimeridian (splits and unions); and a point exactly on a tile's east or south edge (lands in the neighbour, per section 4.2). @@ -352,3 +368,5 @@ Any client that already renders a Web Mercator map or globe can decode `quadkey` - 5: NIMA. (2000). *Department of Defense World Geodetic System 1984*. TR8350.2. Horizontal positions in this BRC are WGS 84 (EPSG:4326) degrees. - 6: Microsoft. *Bing Maps Tile System* — quadkey encoding of the Web Mercator pyramid. https://learn.microsoft.com/en-us/bingmaps/articles/bing-maps-tile-system - 7: [BRC-160](../tokens/0160.md) — *1Sat Ordinals — Inscription Envelopes*. +- 8: BRC-502 — *AIP — Author Identity Protocol*. One available authorization model; not required by this BRC. +- 9: BRC-504 — *Sigma — Transaction-Bound Script Signatures*. One available authorization model; not required by this BRC. From 2df617b8960188e48de3be2e39877e008e4a1705 Mon Sep 17 00:00:00 2001 From: Luke Rohenaz Date: Thu, 27 Aug 2026 08:24:26 -0400 Subject: [PATCH 12/13] BRC-515: cite BRC-514 for state resolution, correct the CLEAR gloss CLEAR was described as erasing the object's entire resolved MAP state. MAP defines it as voiding the values a named transaction wrote, so CLEAR against one revision leaves every other revision standing. An indexer built on the old sentence would unplace objects that are still placed. Replaces the interim state-resolution paragraph with a citation. The paragraph carried its own supersession clause and BRC-514 now exists to take it. --- apps/0515.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/0515.md b/apps/0515.md index f272cfb7..bdfbd3ed 100644 --- a/apps/0515.md +++ b/apps/0515.md @@ -92,7 +92,7 @@ These are MAP commands (BRC-503), not new operations: In every command above, `` is the transaction carrying the record being edited. Whether a given transaction is allowed to edit that record is an **authorization** question, and this BRC does not answer it — section 5 does not define an ownership model, it requires that one exist. An indexer MUST NOT apply any of these commands unless the carrying transaction is authorized under the object's model. -`CLEAR `, subject to the same rules, erases the object's entire resolved MAP state: it unplaces the object and drops its join tags. Writers SHOULD prefer `SELECT REMOVE quadkey` when only unplacement is intended. +`CLEAR ` folds per BRC-514[10](#footnote-10) §5.6: it voids the writes of the named revision, not the whole record. Writers wanting to unplace SHOULD use `SELECT REMOVE quadkey`; erasing an object's entire MAP state requires naming every revision that wrote state. Writers SHOULD keep the cell list small, choosing a coarser `z` (section 4.7) in preference to enumerating many fine cells. Indexers MAY cap the number of cells they will index for one object and MUST document that cap. The cap MUST be at least 16. @@ -317,7 +317,7 @@ A destination world receives whatever anyone able to spend a tip sends it; arriv Tags are claims, not proof of presence. Anyone MAY place a cell; only the current holder can relocate that object. -**State resolution.** Until a dedicated BRC specifies MAP state over a 1Sat chain, the following rule is normative for the keys of this document: an indexer resolves an object's MAP state by applying each in-chain MAP instruction set in chain order — origin to tip; within one transaction, output order; within one output, `:::` order — with later writes overriding earlier ones per command semantics (last write wins). "In effect at the tip" means the result of that fold. When a general resolution BRC exists, it supersedes this paragraph and this BRC will cite it. +**State resolution.** "In effect" means the result of folding the object's authorized writes as specified in BRC-514[10](#footnote-10). That document defines the revision order, the state model, and how each MAP command applies; this BRC adds nothing to it and defines no resolution rules of its own. ### 6. Query @@ -370,3 +370,4 @@ Any client that already renders a Web Mercator map or globe can decode `quadkey` - 7: [BRC-160](../tokens/0160.md) — *1Sat Ordinals — Inscription Envelopes*. - 8: BRC-502 — *AIP — Author Identity Protocol*. One available authorization model; not required by this BRC. - 9: BRC-504 — *Sigma — Transaction-Bound Script Signatures*. One available authorization model; not required by this BRC. +- 10: BRC-514 — *MAP State Resolution over a 1Sat Chain*. Defines how a MAP key's value is resolved across a record's revisions. From 263307ea32d269a7a0dd9c79ee2d07b21832856b Mon Sep 17 00:00:00 2001 From: Luke Rohenaz Date: Fri, 28 Aug 2026 10:29:13 -0400 Subject: [PATCH 13/13] BRC-515: drop the project citation from Implementations A specification does not cite the applications built on it. Projects cite the spec, not the other way around. External references are limited to the BRCs this document depends on. Removes the World of Alchema paragraph. The Implementations section now states what a compatible implementation does and nothing else. --- apps/0515.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/0515.md b/apps/0515.md index bdfbd3ed..06924b0e 100644 --- a/apps/0515.md +++ b/apps/0515.md @@ -355,8 +355,6 @@ Three cases separate a conforming implementation from a plausible one, and SHOUL A compatible implementation tags outputs as in sections 1–5, answers section 6, and works for **Earth maps and seeded worlds** with the same cell math. -**World of Alchema** ([alchema.world](https://alchema.world)) is an existing project in this family — a game world that places things in space. This BRC does not specify Alchema's internals. It exists so Earth-map clients and seeded-world clients can share indexers and join tags. - Any client that already renders a Web Mercator map or globe can decode `quadkey` to `(z, x, y)` or a bbox and draw the record without a second projection. ## References