> ## Documentation Index
> Fetch the complete documentation index at: https://docs.astral.global/llms.txt
> Use this file to discover all available pages before exploring further.

# Verifying Location Proofs

> Submit location proofs and understand credibility scores

<Note>**Research Preview** — APIs may change. [GitHub](https://github.com/AstralProtocol)</Note>

# Verifying location proofs

A location proof is a location claim bundled with evidence — stamps from proof-of-location systems. The proof carries everything needed to assess the claim's credibility.

This guide walks you through creating a claim, collecting stamps, bundling them into a proof, and interpreting the verification result.

## What is a location proof?

A location proof has two parts:

* **Claim** — an assertion that a subject was at a location during a time window
* **Stamps** — evidence from one or more proof-of-location systems that support (or contradict) the claim

The verification process evaluates the stamps against the claim and produces a credibility assessment — not a simple yes/no, but a structured evaluation of how strong the evidence is.

## Creating a location claim

A claim follows the [Location Protocol](https://github.com/DecentralizedGeo/location-protocol-spec) format and includes the asserted location, time bounds, and spatial uncertainty:

```typescript theme={null}
const claim = {
  lpVersion: '0.2',
  locationType: 'geojson-point',
  location: { type: 'Point', coordinates: [-122.4194, 37.7749] },
  srs: 'http://www.opengis.net/def/crs/OGC/1.3/CRS84',
  subject: { scheme: 'eth-address', value: '0x1234...abcd' },
  radius: 100,
  time: { start: Date.now() / 1000 - 60, end: Date.now() / 1000 },
  eventType: 'presence'
};
```

Key fields:

| Field       | Description                                                                 |
| ----------- | --------------------------------------------------------------------------- |
| `location`  | GeoJSON geometry — where the subject claims to have been                    |
| `subject`   | Identifier for the entity making the claim (Ethereum address, DID, etc.)    |
| `radius`    | Spatial uncertainty in meters — you cannot claim presence at an exact point |
| `time`      | Temporal bounds as Unix timestamps (start and end)                          |
| `eventType` | What kind of event: `"presence"`, `"transaction"`, `"delivery"`             |

<Info>
  The `radius` field is required. Every location claim involves spatial uncertainty. Claiming a smaller radius requires stronger evidence to achieve the same credibility score.
</Info>

## Collecting stamps

Stamps are evidence from proof-of-location plugins. Each plugin collects signals from its PoL system and produces a stamp:

```typescript theme={null}
import { AstralSDK, MockPlugin } from '@decentralized-geo/astral-sdk';

const astral = new AstralSDK({ chainId: 84532 });

// Register a plugin and collect signals. The SDK ships MockPlugin for
// local development; on a real device, evidence comes from a source like
// the ProofMode app. stamps.collect returns an array.
astral.plugins.register(new MockPlugin({ name: 'mock-1', lat: 37.7749, lon: -122.4194 }));

const signals = await astral.stamps.collect({ plugins: ['mock-1'] });
const unsignedStamp = await astral.stamps.create({ plugin: 'mock-1' }, signals[0]);
const stamp1 = await astral.stamps.sign({ plugin: 'mock-1' }, unsignedStamp, deviceSigner);
```

For stronger verification, collect stamps from multiple **independent** systems — independence is what cross-correlation rewards, so two stamps from the same source add little. Real independence means genuinely different proof-of-location systems (for example the ProofMode app plus a network-based plugin). Those client plugins are still being built; the shape is the same:

```typescript theme={null}
// A second stamp from an independent system (illustrated with a second
// mock instance; in practice this would be a different PoL system).
astral.plugins.register(new MockPlugin({ name: 'mock-2', lat: 37.7750, lon: -122.4193 }));

const witnessSignals = await astral.stamps.collect({ plugins: ['mock-2'] });
const unsignedStamp2 = await astral.stamps.create({ plugin: 'mock-2' }, witnessSignals[0]);
const stamp2 = await astral.stamps.sign({ plugin: 'mock-2' }, unsignedStamp2, nodeSigner);
```

## Bundling into a proof

Combine the claim and stamps into a location proof:

```typescript theme={null}
const proof = astral.proofs.create(claim, [stamp1, stamp2]);
```

A single-stamp proof is valid. Multiple stamps from independent systems enable cross-correlation, which increases confidence.

## Submitting to the verify API

Submit the proof for verification:

```typescript theme={null}
// `mode: 'tee'` routes to the hosted service; the default 'local' mode
// evaluates in-process and returns the credibility vector directly.
const result = await astral.proofs.verify(proof, { mode: 'tee', chainId: 84532 });
```

Or via raw HTTP:

```bash theme={null}
curl -X POST https://staging-api.astral.global/verify/v0/proof \
  -H "Content-Type: application/json" \
  -d '{
    "claim": {
      "lpVersion": "0.2",
      "locationType": "geojson-point",
      "location": { "type": "Point", "coordinates": [-122.4194, 37.7749] },
      "srs": "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
      "subject": { "scheme": "eth-address", "value": "0x1234...abcd" },
      "radius": 100,
      "time": { "start": 1706399940, "end": 1706400000 },
      "eventType": "presence"
    },
    "stamps": [ ... ]
  }'
```

## Understanding credibility scores

The verification result is a structured credibility assessment, not a simple pass/fail:

<Warning>
  The exact structure of the credibility vector is an [open research question](/concepts/location-proof-evaluation) and will change. The fields below are illustrative of the current shape, not a stable contract.
</Warning>

```json theme={null}
{
  "credibility": {
    "dimensions": {
      "spatial": {
        "meanDistanceMeters": 12.5,
        "maxDistanceMeters": 18.3,
        "withinRadiusFraction": 1.0
      },
      "temporal": {
        "meanOverlap": 0.95,
        "minOverlap": 0.90,
        "fullyOverlappingFraction": 0.5
      },
      "validity": {
        "signaturesValidFraction": 1.0,
        "structureValidFraction": 1.0,
        "signalsConsistentFraction": 1.0
      },
      "independence": {
        "uniquePluginRatio": 1.0,
        "spatialAgreement": 0.88,
        "pluginNames": ["mock-1", "mock-2"]
      }
    },
    "stampResults": [ "..." ],
    "meta": { "stampCount": 2, "evaluatedAt": 1706400000, "evaluationMode": "tee" }
  },
  "evaluationMethod": "multifactor-v0",
  "evaluatedAt": 1706400000,
  "attestation": {
    "uid": "0xabc123...",
    "attester": "0x590fdb53..."
  }
}
```

### Credibility dimensions

These dimensions are a **preliminary sketch** — their exact structure and metrics are an [active research area](/concepts/location-proof-evaluation) and will change. The credibility vector is a multidimensional assessment grouped into four dimensions — each an object of metrics, not a single score:

| Dimension        | What it measures                                                                                                              |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **Spatial**      | How closely the stamps' observed locations match the claimed location (mean/max distance, fraction within the claimed radius) |
| **Temporal**     | How well the stamps' time windows overlap the claimed time window                                                             |
| **Validity**     | Fraction of stamps with valid signatures, structure, and consistent signals                                                   |
| **Independence** | How independent and corroborating the sources are (unique-plugin ratio, spatial agreement)                                    |

### No single score

There is **no top-level `confidence` field**. Collapsing the vector into one number requires deciding which dimensions matter most — that judgment belongs to your application, not to Astral. The SDK ships an `exampleWeighting()` helper you can use as a starting point, but you're expected to define your own.

<Warning>
  **A credibility score is not a probability.** Strong metrics mean the evidence is strong — not "an X% chance the claim is true." Calibrating these to true probabilities is future work.
</Warning>

### Cross-correlation

When a proof includes multiple stamps, the verification engine analyzes their relationship:

* **Independence** — are the stamps from truly independent systems? Two stamps from the same underlying data source do not add much.
* **Agreement** — do the stamps agree on location and time? Independent stamps that corroborate each other significantly boost confidence.

## Multi-factor proofs

Multiple stamps from independent systems increase confidence because an attacker would need to compromise multiple unrelated systems simultaneously:

```typescript theme={null}
// Single stamp
const singleResult = await astral.proofs.verify(
  astral.proofs.create(claim, [stamp1]),
  { mode: 'tee', chainId: 84532 }
);

// Multi-stamp from independent systems: the independence dimension reflects
// that the evidence is corroborated rather than redundant
const multiResult = await astral.proofs.verify(
  astral.proofs.create(claim, [stamp1, stamp2]),
  { mode: 'tee', chainId: 84532 }
);
// multiResult.credibility.dimensions.independence.uniquePluginRatio → higher
// multiResult.credibility.dimensions.independence.spatialAgreement   → higher
```

The improvement comes from source independence. Redundant stamps from the same system do not meaningfully strengthen the assessment, but they do not weaken it either.

### Choosing the right level of evidence

The level of evidence you need depends on the value of the transaction the proof underpins:

* **Low-stakes** (check-in rewards, social proof) — a single stamp from a device attestation plugin may be sufficient.
* **Medium-stakes** (delivery verification, access control) — two independent stamps provide meaningful forgery resistance.
* **High-stakes** (insurance payouts, land records) — multiple independent stamps with high forgery resistance, plus onchain submission for an immutable audit trail.

## Using verified proofs as compute inputs

Verified location proofs can serve as trusted inputs to geocomputation operations. This connects the verification pipeline to the spatial reasoning pipeline:

```typescript theme={null}
const verifiedProof = await astral.proofs.verify(proof, { mode: 'tee', chainId: 84532 });

// Use the verified proof as input to a spatial operation. Compute args are
// positional; a verified proof is passed as { verifiedProof } (the whole object).
const result = await astral.compute.contains(
  approvedZonePolygonUID,        // container
  { verifiedProof },             // containee — the verified location proof
  { schema: SCHEMA_UID }
);
```

## Next steps

<CardGroup cols={2}>
  <Card title="Location proofs concept" icon="book" href="/concepts/location-proofs">
    Deeper dive into claims, stamps, and the verification model
  </Card>

  <Card title="Building plugins" icon="puzzle-piece" href="/guides/building-plugins">
    Connect a new proof-of-location system to Astral
  </Card>
</CardGroup>
