Some checks failed
standards workspaces build + test / jouleclaw-rs (push) Has been cancelled
standards workspaces build + test / aac-rs (push) Has been cancelled
standards workspaces build + test / arl-rs (push) Has been cancelled
standards workspaces build + test / eoc-rs (push) Has been cancelled
standards workspaces build + test / jcp-rs (push) Has been cancelled
standards workspaces build + test / joule-code-rs (push) Has been cancelled
standards workspaces build + test / joule-ui-rs (push) Has been cancelled
standards workspaces build + test / joulecontract-rs (push) Has been cancelled
standards workspaces build + test / jouledesk-rs (push) Has been cancelled
standards workspaces build + test / joulehook-rs (push) Has been cancelled
standards workspaces build + test / map-rs (push) Has been cancelled
standards workspaces build + test / mesh-rs (push) Has been cancelled
standards workspaces build + test / openpay (push) Has been cancelled
standards workspaces build + test / proof-rs (push) Has been cancelled
standards workspaces build + test / sandbox-rs (push) Has been cancelled
standards workspaces build + test / sense-rs (push) Has been cancelled
standards workspaces build + test / smart-byte-rs (push) Has been cancelled
standards workspaces build + test / macOS energy backend (jcp-energy) (push) Has been cancelled
no-vanity-metrics / Scan committed Markdown for vanity metrics (push) Has been cancelled
AltSchedule::default() — the surface-only schedule, the common case — has width_m
= +inf. JSON has no infinity: serde_json wrote null and f64 refused it coming back,
so tileset_vectors.json could not be read into the reference types at all
("invalid type: null, expected f64") — the first thing a third party certifying
against the pack would try.
width_m now has one JSON form: +inf <-> null, in human-readable formats only. The
canonical CBOR every hash and signature covers still carries the float, so no
content id or signature moves and the published pack is byte-identical. NaN and
-inf, which mean nothing and have no JSON form, are refused rather than written
as a null that would read back as +inf. SPEC §2 states the rule.
The pack test that pinned the defect now asserts the round trip: the published
manifest reads back, verifies, re-serializes to the pack, equals the rebuilt
manifest, and the revision chains onto it. Falsified: without the adapter the
original error returns; writing null into CBOR too breaks the canonical-form test
and the published signatures.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
130 lines
12 KiB
Markdown
130 lines
12 KiB
Markdown
# Map v0.1 — A Carrier for Place
|
|
|
|
Status: **Draft Standard**. Reference implementation: [`map-rs/`](map-rs/) (Rust workspace, Apache-2.0). Specification text: CC-BY-4.0.
|
|
|
|
The keywords **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, **SHOULD**, **SHOULD NOT**, **MAY**, and **OPTIONAL** are to be interpreted as described in RFC 2119.
|
|
|
|
---
|
|
|
|
## 1. Scope and doctrine
|
|
|
|
Map specifies how a region of the world — at a place, an altitude, an instant, and a fidelity — is identified, addressed, resolved through an energy-tiered fidelity cascade, and reported with its energy cost. It is the **geospatial cargo class** of the Transaction Science substrate. Where [Smart Byte](../smart-byte/) carries value, [Joule Code](../joule-code/) carries coding work, and [EOC](../eoc/) carries compute, Map carries **place**.
|
|
|
|
The **doctrine** is one sentence: *the map is a file, not a service.* Three normative consequences follow.
|
|
|
|
### 1.1 The tile is the cargo (REQUIRED)
|
|
|
|
The unit of place is a **tile**: a content-addressed, signed unit of the world at a single 4D cell, with its energy cost intrinsic. A tile is not an image and not an API response. It is a vesicle carrying exactly five things — identity (the BLAKE3 content address of its canonical serialization), a cell key (where/when/how-detailed it is), provenance (the prior tileset root it was published under, and the author key that signed it), cargo (the payload — terrain, a field, vector features, or splats), and a two-part joule cost (`measured + estimated`, honestly tagged).
|
|
|
|
### 1.2 The cascade (REQUIRED)
|
|
|
|
A conforming resolver **MUST** resolve every render request through an explicit energy-tiered **fidelity cascade**, **MUST** return the highest tier that is at or below the requested fidelity *and* holds data for the cell, and **MUST** fall back toward the base floor — which always closes the request. The tiers are energy-ordered:
|
|
|
|
| Tier | Wire tag | Named tag | Joule class | Payload for a render request |
|
|
|------|----------|------------|-----------------|-------------------------------------------------------------------------|
|
|
| F0 | `F0` | Cache | picojoules | content-addressed tile already resolved in the local store (OPFS) |
|
|
| F1 | `F1` | Base | microjoules | terrain DEM + coastlines + borders — the universal floor, always present |
|
|
| F2 | `F2` | Field | sub-millijoules | raster + scalar/vector field tiles (imagery, weather grids, wind) |
|
|
| F3 | `F3` | Feature | millijoules | vector feature tiles (roads, POIs, labels, airspace) + live entities |
|
|
| F4 | `F4` | Photoreal | joules | 3D Gaussian splats — dense, sparse-coverage; the escape hatch |
|
|
|
|
A resolver **MUST NOT** return a tier above the requested fidelity. A resolver **MUST NOT** fail a render request for a covered region: F1 **SHALL** be defined everywhere on the datum, so graceful fallback is a property of the standard, not of an implementation. A resolver **MUST** record in the receipt the tier that actually closed the request. F0 is the cache short-circuit and **MAY** close any request whose cell key was previously resolved.
|
|
|
|
> The cascade is inverted relative to compute cascades (JouleClaw, Joule Code) only in *intent*: there the cheapest sufficient tier wins; here the highest *available* tier at or below the request wins, with the cheap floor as the guaranteed fallback. The shared invariant is identical — **stop at the first tier that closes the request, and record it.**
|
|
|
|
### 1.3 The receipt (REQUIRED)
|
|
|
|
Every resolve/decode operation **MUST** report a two-part energy cost in integer microjoules — `measured + estimated` — with a `Provenance` tag (`HwShunt | ModelBased | Estimator`) on every reading, sealed as a **JCR-1** envelope (canonical det-CBOR + COSE_Sign1/EdDSA — the family's shared signed-receipt format). A `measured` figure **MUST** originate from a hardware energy interface; the spec never claims an accuracy the platform cannot deliver.
|
|
|
|
---
|
|
|
|
## 2. The cell key
|
|
|
|
A tile is addressed by a 4D **cell key** with four orthogonal axes (`map-schema::CellKey`):
|
|
|
|
```
|
|
CellKey {
|
|
s2: S2CellId, // horizontal position + extent (hierarchical, levels 0..30)
|
|
alt: AltBand, // altitude band index (signed; band 0 = datum surface)
|
|
time: TimeBucket, // half-open [start, start+width) interval; bucket 0 = atemporal
|
|
lod: Lod, // 0..=MAX detail level within the tier
|
|
}
|
|
```
|
|
|
|
- **Horizontal (REQUIRED).** The `s2` axis **MUST** be an S2 cell identifier. S2 is a hierarchical, space-filling (Hilbert-ordered) decomposition of the sphere: adjacent cells are adjacent in the integer key space, which is what makes a tileset range-requestable (§4). An implementation **MUST** treat the S2 level as the horizontal LOD.
|
|
- **Vertical (REQUIRED).** The `alt` axis **MUST** be a signed band index against a declared band schedule (`map-schema::AltSchedule`). Band 0 is the datum surface; negative bands are sub-surface; positive bands are altitude. Atmospheric and orbital payloads occupy positive bands; bathymetry occupies negative bands. A schedule's `width_m` **MAY** be `+∞` — a single unbounded surface band, the 2.5D case; JSON has no infinity, so its JSON form is `null`, which **MUST** be read back as `+∞`, while the canonical CBOR (§3) carries the float itself.
|
|
- **Temporal (REQUIRED).** The `time` axis **MUST** be a `TimeBucket`: a half-open interval anchored to TAI. Bucket 0 is reserved for **atemporal** tiles (a coastline, a border). A weather forecast cycle, an AIRAC navigation cycle, and an ADS-B snapshot are distinct, non-zero buckets. The temporal axis is first-class: a "map of Tuesday at 14:00Z" is a different, equally addressable region from "the same place on Wednesday."
|
|
- **Detail (REQUIRED).** The `lod` axis is the within-tier detail level, independent of the S2 level (which is the horizontal extent). A splat tile at S2 level 20 **MAY** carry LODs 0..N of progressively denser Gaussians.
|
|
|
|
Two cell keys are equal iff all four axes are equal. The canonical serialization of a `CellKey` (§3) is deterministic, so its BLAKE3 hash is a stable join key across implementations.
|
|
|
|
---
|
|
|
|
## 3. Canonical serialization and content addressing
|
|
|
|
Every Map structure that is hashed or signed **MUST** be serialized with the family's deterministic CBOR profile (det-CBOR: definite-length, canonically-ordered keys, no floats where an integer suffices). The **content id** of a tile is `blake3(canonical_serialization)` (`map-schema::ContentId`, 32 bytes). Content ids are the only cross-reference Map uses internally: a tileset directory maps cell keys to content ids; a manifest commits to content ids; a receipt references the content ids it resolved.
|
|
|
|
A tile is **immutable**: changing any byte changes its content id and therefore its identity. There is no in-place edit. Revision happens at the tileset level (§5).
|
|
|
|
---
|
|
|
|
## 4. The tileset (REQUIRED)
|
|
|
|
A **tileset** is the on-disk, range-requestable archive — the "it's just a file" deliverable. A conforming tileset is a single addressable blob on dumb storage (object store, static host, OPFS) with three parts:
|
|
|
|
1. A **header** with a magic tag, the standard version, the datum, the alt schedule, and the byte offset of the directory.
|
|
2. A **directory** mapping `CellKey → (byte_offset, byte_len, content_id)` for every tile in the set, **MUST** be ordered by the S2 (Hilbert) key so that the tiles of one viewport occupy a near-contiguous byte range. A reader **MUST** be able to fetch the directory, compute the cell keys a viewport needs, and issue HTTP range requests for only those byte ranges — no server-side logic, no per-request billing.
|
|
3. The **tile payloads**, content-addressable and independently decodable.
|
|
|
|
A reader **MUST NOT** be required to download the whole tileset to render a viewport. A reader **MUST** verify that each fetched payload hashes to the `content_id` the directory claims.
|
|
|
|
---
|
|
|
|
## 5. The manifest and signed transitions (REQUIRED)
|
|
|
|
A tileset's authenticity rests on a signed **manifest** (`map-schema::Manifest`): a commitment to the directory root (a BLAKE3 Merkle root over the sorted `CellKey → content_id` pairs), the publisher's Ed25519 public key, the standard version, and the cell-key bounds of the set. The manifest **MUST** be signed (Ed25519 over the det-CBOR of the manifest body).
|
|
|
|
A **revision** is a signed transition `prev_root → new_root` (`map-schema::Revision`): a new manifest that names the root it supersedes and is signed by the same publisher key (or a successor key it endorses). Revisions are **append-only and dated** (each carries a TAI timestamp). A new weather forecast hour, a new 28-day AIRAC cycle, and a re-flight of a corridor are revisions — never in-place mutations. A reader **MAY** pin any historical root and **MUST** be able to verify a revision chain back to a trusted root without contacting the publisher.
|
|
|
|
This is the "works forever" guarantee made normative: a newsroom's election map, an archived flight plan, or a forecast cycle is a signed, dated file whose validity does not depend on any service remaining online.
|
|
|
|
---
|
|
|
|
## 6. Energy and the receipt (REQUIRED)
|
|
|
|
The receipt is the family's JCR-1 envelope (`map-receipt`). A receipt **MUST** carry:
|
|
|
|
- the `CellKey` and `content_id` of the tile(s) it covers;
|
|
- the cascade `Tier` that closed the request;
|
|
- an `Energy` record: `measured_uj: Option<u64>`, `estimated_uj: u64`, `provenance: Provenance`;
|
|
- the publisher/resolver identity and an Ed25519 signature (COSE_Sign1/EdDSA over canonical det-CBOR).
|
|
|
|
The provenance coupling is normative: `HwShunt` **REQUIRES** a present `measured_uj`; `Estimator` **MUST** leave it absent; `ModelBased` **MAY** include it. A session's total cost is the sum of its tile receipts. A resolver **MUST NOT** report a `measured` figure it did not measure.
|
|
|
|
A reference (non-normative) estimator orders the tiers by joule class: a cache hit is picojoules; a base/terrain tile is microjoules; a field tile is sub-millijoules; a feature/entity tile is millijoules; a splat tile is joules. The standard is that the figure is reported and honestly tagged — not the constant.
|
|
|
|
---
|
|
|
|
## 7. No capture (REQUIRED)
|
|
|
|
Map carries no protocol-level token. Reading a tileset **MUST** require nothing but the bytes and the publisher's public key. A conforming reader performs its core function — resolve a cell, verify it, render it, meter it — against static storage, with no contact with any single company's servers as a condition of operation. Transaction Science is one steward: it publishes the spec, ships the reference implementation, and operates optional commercial services (capture pipelines, hosted tilesets, a registry) around the substrate — not its proprietor.
|
|
|
|
---
|
|
|
|
## 8. Conformance
|
|
|
|
An implementation is **conformant** if it round-trips the vectors in [`conformance/v1/`](conformance/v1/):
|
|
|
|
- **C1 — cell key.** Canonical serialization and `ContentId` of the cell-key vectors match byte-for-byte.
|
|
- **C2 — tileset directory.** A reader resolves the correct byte range for a viewport over the sample tileset and verifies every payload hash.
|
|
- **C3 — manifest & revision.** Signature verification and revision-chain validation against the sample roots; a tampered directory root **MUST** fail.
|
|
- **C4 — cascade.** For each render-request vector, the resolver closes at the specified tier and falls back to F1 where higher tiers are absent.
|
|
- **C5 — receipt.** JCR-1 receipts seal, verify, and enforce the provenance ↔ measured coupling; vectors are interoperable with the family's existing JCR-1 verifiers.
|
|
|
|
A negative pack (`C-neg`) covers tampered payloads, an out-of-order directory, a revision signed by an unendorsed key, and a receipt claiming `HwShunt` with no `measured_uj`.
|
|
|
|
---
|
|
|
|
## 9. Non-goals
|
|
|
|
Map is a **substrate for read-heavy, published geospatial data**. It is explicitly **not** a live geocoding/routing *service* (the reference ships `map-route` as a local capability over a tileset, not a hosted API), and it is **not** a certified primary navigation source. Air-navigation payloads are for **situational awareness, planning, and simulation only** — never a DO-178C/TSO-certified source of primary flight guidance. A conforming flight-nav tileset **MUST** carry this disclaimer in its manifest metadata.
|