# GeoProof Protocol — Specification

**Status:** Draft v1.0 · **Date:** 2026-07 · **Layer:** application protocol over XYO + XL1
**Package:** `@geo/geoproof`

> GeoProof is the canonical proof schema/protocol of the Oxyon ecosystem.
> **Oxyon is the company; GeoProof is the protocol.** Every Oxyon product emits
> and consumes GeoProof events so that observations from any product can be
> verified and connected by any other.

## 1. Design goals
1. **One proof model across all products** — a presence proof from GeoPresence and
   a signing proof from GeoWallet are the same shape, verifiable by GeoJustice.
2. **Built on XYO, not reinventing it** — GeoProof events ARE XYO payloads,
   carried in XYO bound witnesses. GeoProof does **not** define new crypto.
3. **Anchored on XL1** — proofs are batch-anchored for durable, ordered finality.
4. **Brand-correct namespace** — schemas live under `oxyon.geoproof.*`, never the
   sponsor token's name. (Alpha products currently emit `winlew.geo.*`; §7.)
5. **Forward-compatible** — versioned schemas; additive evolution.

## 2. Foundation: XYO + XL1 (normative references)
A GeoProof **event** is an XYO **payload** — a JSON object with a `schema` string
and arbitrary fields; its identity is its deterministic XYO hash. Events gain trust
only when bound into an XYO **bound witness** (`network.xyo.boundwitness`), which
carries:

| BW field | Role |
|---|---|
| `addresses` | the account(s) that authored/witnessed the payloads |
| `payload_hashes` / `payload_schemas` | the bound payloads (tamper-evident) |
| `previous_hashes` | per-address chain linkage |
| `$signatures` | signatures over the BW hash by each address |
| `$sources` | links this BW to prior BWs/payloads (cross-phase linkage) |

> Construction/validation is performed by `@xyo-network/boundwitness-builder` and
> `@xyo-network/boundwitness-validator`. GeoProof MUST NOT reimplement hashing or
> signing. *(Exact package APIs drift between XYO releases — pin and verify against
> the version each product uses.)*

## 3. The four-phase model (Observe · Locate · Verify · Connect)
GeoProof maps Oxyon's mission to a lifecycle:

```
Observe   an app records an observation        → GeoProofEvent (payload)
Locate    attach XY/context                     → event.location (GeoPoint)
   ↓      sign + bind                            → XYO bound witness (authorship)
Verify    GeoJustice judges the bound witness   → VerificationReceipt
Connect   batch-anchor to XL1; index; relate    → AnchorBatch + $sources graph
```

| Phase | Role | Product | Emits |
|---|---|---|---|
| Observe/Locate | producer | GeoWallet, GeoPresence, any app | `oxyon.geoproof.{event,presence,attestation}` |
| Verify | Oracle | **GeoJustice** | `oxyon.geoproof.receipt` |
| Connect | anchor + index | anchor service / `services/geo-indexer` | `oxyon.geoproof.anchor.batch` |

## 4. Schemas
Canonical names (versioned in the schema string, XYO-style):

| Schema | Meaning |
|---|---|
| `oxyon.geoproof.event.v1` | base envelope (producer, subject, observedAt, location?, claims?) |
| `oxyon.geoproof.presence.v1` | Proof of Presence (location required; method) |
| `oxyon.geoproof.attestation.v1` | generic signed statement about a subject |
| `oxyon.geoproof.receipt.v1` | verification verdict + trust score (GeoJustice) |
| `oxyon.geoproof.anchor.batch.v1` | set of proof hashes committed to XL1 |
| `oxyon.geoproof.supersession.v1` | truth changed — new conclusion replaces old (history intact) |
| `oxyon.geoproof.revocation.v1` | statement withdrawn — no replacement (history intact) |
| `oxyon.geoproof.rotation.v1` | old key endorses new key — dual-signer BW IS the endorsement |

TypeScript definitions: [`src/schemas.ts`](src/schemas.ts). Field-level contracts
are the source of truth; this table is a summary.

### 4.1 Envelope (all events)
`producer` (string), `subject` (string — address / device id / content hash),
`observedAt` (epoch ms UTC), `location?` (`GeoPoint`: lat, lon, accuracyM?, altM?),
`claims?` (object). Presence narrows `location` to required.

### 4.2 Supersession
A supersession records that a prior receipt or attestation has been overtaken by new
evidence. **It does not delete history.** The superseded record remains in the DAG
exactly as it was — replay still works — but the supersession says: "given what we
know now, the current conclusion is different." Required fields: `supersededHash`
(BW hash of what is being superseded), `reason` (non-empty string — silent supersession
is a protocol violation), `effectiveAt` (epoch ms), `issuer` (address). Optional:
`replacementHash` (BW hash of the new statement). If no replacement exists, use
`Revocation` instead.

### 4.3 Revocation
Full withdrawal: the statement is no longer valid and there is no replacement.
Required: `revokedHash`, `reason` (non-empty), `effectiveAt`, `issuer`.
History stays intact; the revocation is a terminus node, not an erasure.

### 4.4 Key Rotation
The retiring key (`oldAddress`) and the successor key (`newAddress`) must BOTH sign
the carrying bound witness — the dual-signer BW IS the endorsement. A verifier must
confirm: both signatures present and valid; both addresses present in `bw.addresses`;
the rotation payload in `bw.payload_hashes`; the rotation has not been superseded or
revoked. Reference: `rotateKey(oldKey, newKey)` in `@geo/geoproof-reference`.

### 4.5 Chained Attestations
XYO bound witnesses carry `previous_hashes`, which the SDK auto-populates with
`PayloadBuilder.dataHash(priorBw)` when the same account builds consecutive BWs.
This creates a per-identity transparency log: any gap or reordering is detectable.
Applications that issue multiple receipts through the same verifier account get
chaining for free. Reference: confirmed via probe in 2026-06.

## 5. Verification
Two layers, kept distinct:
1. **Cryptographic** (who/whether-tampered) — delegated to
   `@xyo-network/boundwitness-validator` over the carrying BW: signatures valid,
   hashes match, chain consistent.
2. **Semantic / trust** — GeoJustice applies policy: schema validity
   (`validateEventShape`), plausibility (e.g. location accuracy, velocity between
   presence proofs), authority allow-lists, fraud signals → a `verdict` and a
   `0..1` `score`, emitted as a `receipt` linked via `$sources` to the proof.

`validateEventShape()` in this package is **structural only** and never a
substitute for (1).

## 6. Anchoring on XL1
Producers and verifiers write proofs/receipts; an anchor process periodically
commits their bound-witness hashes to XL1 as `oxyon.geoproof.anchor.batch` (the
pattern the alpha products call `winlew.geo.anchor.batch.v2`). `network` MUST come
from env (never hardcoded). `services/geo-indexer` replays anchored batches with a
floor block + atomic checkpoints to build queryable proof state.

## 7. Migration from `winlew.geo.*`
Alpha products (GeoPresence) emit `winlew.geo.presence.v2`,
`winlew.geo.presence.receipt.v1`, `winlew.geo.anchor.batch.v2`. These predate the
Oxyon brand and put the **sponsor token's name on the protocol** — incorrect under
"Oxyon builds, WinLEW rewards." Path:
1. **Read both** — `canonicalizeSchema()` maps legacy → canonical; consumers accept
   either immediately.
2. **Emit canonical** — new emissions use `oxyon.geoproof.*`.
3. **Optionally dual-emit** during transition for older indexers.
No on-chain rewrite of historical proofs is needed — they remain valid XYO BWs;
only the indexer's schema mapping changes.

## 8. Conformance
A product is GeoProof-conformant if it:
(a) emits events whose `schema` is canonical (or maps via §7),
(b) binds them in valid XYO bound witnesses,
(c) passes `validateEventShape` for base events and the type-specific validators
    (`validateSupersession`, `validateRevocation`, `validateKeyRotation`) for Phase 1 types,
(d) for verifiers, emits receipts whose verdict is EARNED via cryptographic validation
    (never caller-chosen),
(e) for key rotation, produces a dual-signer BW with both old and new keys.

### 8.1 Conformance Test Vectors
`packages/geoproof/vectors/v1.json` contains deterministic GeoProof payloads and
their canonical XYO `dataHash` values. Any implementation in any language uses these
to verify its hashing matches the reference. Payload hashes ARE deterministic (XYO
sorts and canonicalizes fields). BW hashes and signatures are NOT deterministic
(random nonce). The vectors cover payload-level hashing only.

To regenerate (only when intentionally changing the hashing algorithm):
```
node packages/geoproof/src/vectors.gen.ts
# then bump version in vectors/v1.json and create a migration map
```

## 9. Open questions (verify before hardening)
- Exact `@xyo-network` package + API versions per product (XYO moved fast during
  the alpha build — **confirm, don't assume**).
- Whether `subject` should be a typed union (address | did | contentHash) vs string.
- Receipt score model + who the authoritative verifiers are (authority allow-list).
- XL1 anchor cadence + finalized-vs-latest read semantics for the indexer.
