Developers  /  GeoProof Standard v1

GeoProof Reference Standard — v1

Published exactly as attested in Oxyon's signed provenance record. Verify the fingerprint · raw file

Proof of Presence

Status: Reference Standard v1.1 · Date: 2026-07 · Verified against: @xyo-network 7.0.11

This is the first GeoProof Reference Standard: the artifact future products are measured against. The code in this package is one conformant expression of the standard — not the standard itself. GeoWallet, GeoJustice, GeoPresence, GeoAgora, GeoHackers, and GeoForge implement against this, rather than copying example code.

Oxyon is the company. GeoProof is the protocol (see ../geoproof/SPEC.md). This is the protocol's reference implementation.

1. Purpose

To establish a single, runnable, verified baseline for producing and verifying a GeoProof event — so that every product in the ecosystem can be checked for conformance against the same reference, instead of each re-deriving (and quietly diverging from) how a proof is made.

2. The problem it solves

Without a reference, "supports GeoProof" means whatever each team decided it meant. Schemas drift, signing differs, and two products that both claim to emit proofs can produce events the other cannot verify. A reference standard fixes the meaning of "conformant" to something executable.

3. Architectural overview

A GeoProof proof is produced in three steps, mapping to the philosophy:

Observe + Locate     createPresenceProof()   → a GeoProof event (XYO payload)
Verify (authorship)  signProof()             → a signed XYO bound witness
Verify (cryptographic) verifyProof()         → validation result ([] = valid)
  • The event is an XYO payload under oxyon.geoproof.presence.v1 (schema + fields), structurally validated by @geo/geoproof.
  • The bound witness (network.xyo.boundwitness) carries addresses, payload_hashes, payload_schemas, previous_hashes, and $signatures — providing authorship and tamper-evidence.
  • Validation is delegated to @xyo-network/boundwitness-validator. The standard does not reimplement XYO cryptography.

4. The reference implementation

src/presence.ts — three functions:

  • createPresenceProof(params) → builds and structurally validates a PresenceProof.
  • signProof(proof, signer) → binds it into a signed bound witness via BoundWitnessBuilder().payload(proof).signer(account).build().
  • verifyProof(boundWitness) → returns problems; [] means valid.

5. Runnable example

src/presence.example.ts:

npm install          # pulls @xyo-network 7.0.x
npm run example
npm test

6. Expected inputs and outputs

Input — an observation with location and a signing account:

createPresenceProof({
  producer: 'geopresence',
  subject: account.address,
  location: { lat: 39.95, lon: -75.16, accuracyM: 8 },
  method: 'gps',
  observedAt: 1_750_000_000_000,
});

Output — a signed, valid bound witness (addresses/hash will differ per run and key; structure and validity will not):

{
  "signer": "ecc49fd644b41a8847a71a1c90780d500aa30814",
  "boundWitness": {
    "schema": "network.xyo.boundwitness",
    "addresses": ["ecc49fd644b41a8847a71a1c90780d500aa30814"],
    "payload_schemas": ["oxyon.geoproof.presence.v1"],
    "payload_hashes": ["e1710b74857e1cef006ba4c3071baa0a653db969d27b55f86b17b397d539f717"],
    "signatures": 1
  },
  "valid": true,
  "errors": []
}

7. Verification steps

A producer is conformant for Proof of Presence if, for a generated proof:

  1. payload_schemas[0] === 'oxyon.geoproof.presence.v1' (canonical; legacy winlew.geo.presence.* accepted via canonicalizeSchema).
  2. addresses contains the signer's address.
  3. $signatures.length >= 1.
  4. BoundWitnessValidator(bw).validate() returns [].
  5. The event passes validateEventShape (valid location, producer, subject, time).

8. Tests

src/presence.test.ts — runs under node --test:

  • a presence proof binds, signs, and cryptographically validates ([]);
  • a malformed event (out-of-range location) is rejected before signing.

Verified in-repo on Node 24 with @xyo-network 7.0.11: 9/9 lifecycle tests passing (v1.1).

9. Phase 1 Protocol Completions (v1.1)

The following lifecycle functions were added in v1.1 to complete the protocol for production use:

9.1 Supersession (supersede)

const { supersession, boundWitness } = await supersede(oldReceiptBw, issuerAccount, {
  reason: 'New sensor calibration invalidated the original reading.',
  replacementBw: newReceiptBw,  // optional
});

Adds a new node to the DAG saying "given what we now know, the conclusion has changed." The superseded record is NOT deleted — it still validates; you can still replay it. The supersession is the chain's way of saying "don't stop there." reason is required — silent supersession is a protocol violation.

9.2 Revocation (revoke)

const { revocation, boundWitness } = await revoke(statementBw, issuerAccount, {
  reason: 'GPS data fabricated — fraud confirmed.',
});

Full withdrawal with no replacement. History stays intact; this is a terminus node, not an erasure. reason is required.

9.3 Key Rotation (rotateKey)

const { rotation, boundWitness } = await rotateKey(oldKey, newKey, {
  reason: 'Scheduled quarterly rotation.',
});
// verify: both addresses in bw.addresses; BW validates; payload in payload_hashes
const problems = await verifyRotation(boundWitness, rotation);

The dual-signer bound witness IS the endorsement. Both old and new keys must be present as signers. A verifier confirms both: (a) both signatures valid, (b) both addresses in bw.addresses, (c) rotation payload in bw.payload_hashes, (d) not superseded or revoked. To complete rotation: update GEOPROOF_MCP_SEED_FILE to the new seed and archive the old one.

9.4 Chained Attestations (previous_hashes)

When the same account builds consecutive bound witnesses, the XYO SDK auto-populates previous_hashes[0] with PayloadBuilder.dataHash(priorBw). This creates a per-identity transparency log where any gap or reordering is detectable. Applications issuing multiple receipts through the same verifier account get chaining for free — no application code required.

Confirmed via probe: previous_hashes[0] == PayloadBuilder.dataHash(bw1), NOT PayloadBuilder.hash(bw1). Use dataHash for conformance vectors, not hash.

9.5 Conformance Vectors

packages/geoproof/vectors/v1.json contains 7 deterministic GeoProof payloads and their canonical XYO dataHash values. Run:

node packages/geoproof/src/vectors.test.ts

All 7 pass deterministically. These are the test vectors for cross-language implementations.

10. Known limitations

  • Single signer. This reference covers one-party authorship. Multi-party co-signed proofs (escrow/atomic-exchange shapes) are a future standard.
  • No on-chain anchoring here. Binding + verification only; XL1 anchoring (oxyon.geoproof.anchor.batch) and indexing are separate references.
  • Version-pinned. Verified against @xyo-network 7.0.11; re-verify when bumping — the XYO API has changed across major lines.
  • Schema import. The reference imports the canonical schema from the sibling @geo/geoproof source by relative path so it runs without workspace wiring; a published product uses import … from '@geo/geoproof'.

10. Extension points for future products

  • Other event types — attestation, receipt, anchor.batch follow the same three-step shape with their own schema and fields.
  • GeoJustice — consumes verifyProof output and adds semantic/trust scoring, emitting an oxyon.geoproof.receipt linked to the proof via $sources.
  • GeoWallet — owns the signing account; signProof is its core operation.
  • Multi-signer — BoundWitnessBuilder().signers([...]) for co-authored proofs.
  • Anchoring/indexing — batch payload_hashes into oxyon.geoproof.anchor.batch and replay with the indexer (floor block + atomic checkpoints).