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) carriesaddresses,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 aPresenceProof.signProof(proof, signer)→ binds it into a signed bound witness viaBoundWitnessBuilder().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:
payload_schemas[0] === 'oxyon.geoproof.presence.v1'(canonical; legacywinlew.geo.presence.*accepted viacanonicalizeSchema).addressescontains the signer's address.$signatures.length >= 1.BoundWitnessValidator(bw).validate()returns[].- 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-network7.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/geoproofsource by relative path so it runs without workspace wiring; a published product usesimport … from '@geo/geoproof'.
10. Extension points for future products
- Other event types —
attestation,receipt,anchor.batchfollow the same three-step shape with their own schema and fields. - GeoJustice — consumes
verifyProofoutput and adds semantic/trust scoring, emitting anoxyon.geoproof.receiptlinked to the proof via$sources. - GeoWallet — owns the signing account;
signProofis its core operation. - Multi-signer —
BoundWitnessBuilder().signers([...])for co-authored proofs. - Anchoring/indexing — batch
payload_hashesintooxyon.geoproof.anchor.batchand replay with the indexer (floor block + atomic checkpoints).