diff --git a/RESTRUCTURE.md b/RESTRUCTURE.md new file mode 100644 index 0000000..2d6538b --- /dev/null +++ b/RESTRUCTURE.md @@ -0,0 +1,158 @@ +# Documentation restructure plan + +_Context: The v0 product works. The docs need to present it to a wider audience than Ethereum developers — anyone who needs verifiable spatial computation, including agent developers, logistics platforms, insurance, compliance. The blockchain integration is one path, not the identity._ + +_Source: Claude Code session `b7e7e54d-1e4a-4825-b342-abf678f74fe8` in `~/tmp-astral`._ + +_Canonical terminology: see `concept-map.md` in the docs-plan repo (`~/Code/astral/docs-plan/concept-map.md`). All docs pages should use the definitions established there. Key discipline: never say "attestation" when you mean "stamp" or "proof" — reserve "attestation" for EAS-specific contexts._ + +--- + +## Principles + +1. **Lead with the capability, not the delivery mechanism.** "Verifiable geospatial computation" is the headline. Blockchain is an integration path introduced after the reader understands what the system does. + +2. **Separate the concept from the quickstart.** The landing page sells the idea. The quickstart is for people who've already decided to try it. Don't mix them. + +3. **Task-oriented guides, not feature-oriented.** "How do I verify a delivery?" not "The contains endpoint." Each guide answers a question and is self-contained. + +4. **Trust is the product — make the trust model prominent.** If you're selling verifiability, be transparent about what's verified and what's assumed. Elevating this signals confidence; burying it signals doubt. + +5. **Blockchain and agent integration are peer paths.** Neither is privileged. A dapp developer goes to the blockchain guide. An agent developer goes to the agent guide. Both are first-class. + +6. **Examples are complete stories, not snippets.** Each example is a scenario: who you are, what you need, what you call, what you get back, what you do with it. + +## Audiences + +- **Any developer** who needs a spatial answer they can trust (the widest aperture) +- **Agent/AI developers** building autonomous systems with spatial reasoning needs +- **Blockchain developers** who want verifiable spatial results onchain +- **Enterprise evaluators** (insurance, logistics, compliance) assessing the product +- **Researchers** who want to understand the trust model and contribute + +## Proposed structure + +See `mint.proposed.json` for the Mintlify navigation config. + +### Getting Started +- **Introduction** — What is Astral. The problem (untrusted location data), the solution (verifiable spatial computation), who it's for. No jargon. No chain IDs. +- **Quickstart** — Zero to first API call. curl examples against the hosted service. Under 5 minutes. Include the "aha" moment: you sent two points, you got back a signed distance with a cryptographic receipt. +- **How it works** — Architecture in 60 seconds. Geometry in → PostGIS computes in TEE → signed result out. Diagram. Then: "the result can go anywhere — an agent, a smart contract, a database, a compliance report." + +### Core Concepts +Reference material for when people need to understand the underlying model. Terminology follows the concept map (`docs-plan/concept-map.md`) exactly — one canonical definition per term. + +- **Geographic features** — What the system operates on. GeoJSON geometries (points, polygons, lines), verified locations from location proofs, or references to previously computed results. The provenance of a feature matters: a raw coordinate and a verified location are both valid inputs to geocomputation, but they carry different trust properties. This is the most fundamental concept — every API call starts with geographic features. +- **Geocomputation** — The six spatial operations (distance, area, length, contains, within, intersects), what they compute, when to use which. Results are signed with proof of correct execution. Geocomputation can take verified locations as input, bridging the two halves of the system. +- **Verifiable computation** — What "verifiable" means: TEE execution, signed results, input hashing. What the signature covers. What you're trusting. Applies to both geocomputation and the evaluation function. +- **Location proofs** — The full lifecycle from the concept map: location claims → PoL systems → signals → location stamps → location evidence → location proofs → evaluation function → credibility scores → weighting schemes → decisions. This is the deeper conceptual contribution. Each term has a precise definition; the page should walk through the dependency graph. +- **Result format** — How signed results are structured, what the signature covers, how to decode and verify independently. This is the universal format — not EAS-specific. Any integrator (agent, backend, smart contract) needs to understand how to consume and verify a result. +- **Privacy model** — Encrypted inputs, what the TEE operator can and can't see, current guarantees. The TEE accepts encrypted inputs, decrypts inside the enclave, computes, and returns only the result — the operator never sees raw location data. + +### Guides +Task-oriented walkthroughs. +- **Local development setup** — Clone, Docker, env config, health check, first local API call. Thorough. Covers port conflicts, env file loading, platform notes. +- **Calling the API** — Request format (from/to/chainId), geographic feature inputs (raw GeoJSON, verified locations, UID references), authentication, interpreting responses, error handling. The "I want to integrate this into my backend" guide. +- **Verifying location proofs** — Submitting location proofs to the verify API, understanding credibility scores, applying weighting schemes, using plugins. +- **Blockchain integration** — EAS attestations, delegated signing, onchain submission via SDK, chain configuration, schema UIDs. Everything Ethereum lives here. +- **Agent integration** — Using Astral as a spatial oracle in agent workflows. How to call it from an agent framework, how to use the result in decision-making. +- **Building verification plugins** — Writing a new PoL system adapter for the verify module. Plugin interface, registration, testing. + +### Use Cases +Complete, runnable scenarios. Each one is a story with a protagonist, a problem, and a solution. +- **Delivery verification** — "Was the courier within the delivery geofence?" (within/contains) +- **Parametric insurance** — "Was the weather event close enough to trigger the policy?" (distance) +- **Geofence compliance** — "Did the drone stay within the approved corridor?" (contains) +- **Onchain attestation** — End-to-end: compute → sign → submit onchain (the full blockchain flow) + +### API Reference +Exhaustive, mechanical, one page per endpoint. +- **Overview** — Base URL, authentication, rate limits, error format, common request fields +- **Compute API** — distance, area, length, contains, within, intersects (one page each) +- **Verify API** — stamp, proof, plugins +- **Records API** — overview, list, get, stats, config (existing) + +### SDK Reference +Mirrors the SDK's module structure. +- **Overview** — What the SDK does, when to use it vs. raw API +- **Installation** — pnpm install, configuration, initialization +- **Location module** — Location format handling, extensions +- **Compute module** — SDK compute methods (wraps the API) +- **Verify module** — SDK verify methods +- **EAS module** — Onchain submission, chain config, schema management +- **Types** — TypeScript type reference +- **Migration** — Upgrading from previous versions + +### Trust Model +Dedicated section because trust is the product. +- **Architecture** — TEE, PostGIS, stateless model, container design, what runs where +- **What is verified** — What the signature covers, input hashing, computation reproducibility +- **What you are trusting** — Honest accounting of assumptions. TEE hardware trust, current state vs. target state with full remote attestation. The gap between "signed by a key" and "signed by an attested enclave." +- **Security considerations** — Threat model, known limitations, responsible disclosure + +### Resources +- **Playground** — Interactive tool +- **Staging environment** — Testing against hosted service +- **Schema registry** — EAS schema UIDs by chain +- **Research** — Links to papers, research agenda, location proofs framework +- **FAQ** +- **Changelog** + +## Concept map → docs page mapping + +How each term from `concept-map.md` maps to a docs page: + +| Concept map term | Docs page | Notes | +|---|---|---| +| Geographic feature (new) | `concepts/geographic-features` | Not in concept map yet — should be added. The input type for all operations. | +| Geocomputation | `concepts/geocomputation` | The six operations + proof of correct execution | +| TEE | `concepts/verifiable-computation` | Implementation detail, not a core concept (per concept map) | +| Location claim | `concepts/location-proofs` | First half of the lifecycle | +| PoL system, signals, location stamp | `concepts/location-proofs` | Evidence production chain | +| Location evidence, location proof | `concepts/location-proofs` | Composition and bundling | +| Evaluation function, credibility scores | `concepts/location-proofs` | Assessment pipeline | +| Weighting scheme | `concepts/location-proofs` | Application-specific decision logic | +| Location proof plugin | `guides/building-plugins` | How to connect a new PoL system | +| EAS, resolver | `guides/blockchain-integration` | Blockchain-specific delivery mechanism | +| Result format (signed output) | `concepts/result-format` | Universal — how any integrator consumes results | + +### Terminology discipline + +- **"Credibility scores"** (casual) / **"credibility vector"** (formal). Use "scores" in guides and use cases, define "vector" on first use in the concepts page. +- **"Attestation"** only in EAS/blockchain contexts. Never use it when you mean stamp, proof, or signed result. +- **"Location stamp"** not "location attestation." Stamps come from PoL systems. Attestations are EAS artifacts. +- **"Geographic feature"** for inputs. Covers raw GeoJSON, verified locations, and UID references. + +## What changes from current structure + +| Current | Proposed | Why | +|---|---|---| +| Name: "Astral Location Services" | Name: "Astral" (or "Astral Docs") | The service is one component, not the whole product | +| Introduction leads with EAS/attestations | Introduction leads with verifiable spatial computation | Wider audience | +| Concepts: location-attestations, policy-attestations, eas-resolvers | These move into guides/blockchain-integration | Blockchain-specific concepts don't belong in universal concepts section | +| Concepts: geospatial-operations | Renamed to "geocomputation," promoted to second concept (after geographic features) | Aligns with concept map terminology | +| No geographic features concept | `concepts/geographic-features` | Foundation concept — what the system operates on, including provenance | +| Concepts: attestation-format | Renamed to "result-format" | The signed result is universal; EAS encoding is blockchain-specific | +| Guides: location-gated-nft, geofenced-token | Move to use-cases (blockchain-specific examples) or fold into blockchain guide | These are blockchain use cases, not general guides | +| No trust model section | Dedicated trust-model section | Trust is the product | +| No agent integration | guides/agent-integration | Peer path with blockchain | +| No privacy docs | concepts/privacy-model | Key differentiator | +| No local setup guide | guides/local-development | The #1 onboarding issue from v0 review | + +## What to preserve + +- All existing API reference content (restructure, don't rewrite) +- All existing SDK reference content +- The playground and staging resources +- The schema registry +- Any existing content that's accurate — just relocate it + +## Migration approach + +1. Add "geographic feature" definition to `concept-map.md` in docs-plan +2. Write the new `mint.json` navigation (done — see `mint.proposed.json`) +3. Create stub files for new pages (title + one-line description) +4. Relocate existing content into the new structure +5. Fill in new pages (introduction rewrite, trust model, guides) +6. Review pass for terminology discipline (attestation→stamp/proof, credibility vector→scores) +7. Review pass for Ethereum jargon above the fold diff --git a/concepts/astral-location-services.mdx b/concepts/astral-location-services.mdx new file mode 100644 index 0000000..ad4f935 --- /dev/null +++ b/concepts/astral-location-services.mdx @@ -0,0 +1,93 @@ +--- +title: "Astral Location Services" +description: "The hosted TEE service that runs verification and computation" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Astral Location Services + +Astral Location Services is the hosted service that performs [location proof verification](/concepts/verify) and [geospatial computation](/concepts/compute). It runs inside a Trusted Execution Environment (TEE), which is what makes the results verifiable rather than merely signed. + +## What the Service Provides + +Two endpoints, one TEE: + +- **[Verify](/concepts/verify)** — Submit a location proof, get back a verified location proof: the original proof, a [credibility vector](/concepts/location-proof-evaluation#the-credibility-vector), and a signed EAS attestation +- **[Compute](/concepts/compute)** — Submit location data with geographic features and a specified spatial operation, get a signed result representing the computed relationship between those features + +Both endpoints accept requests via the [Astral SDK](/sdk/overview) or directly through the [API](/api-reference/overview). + +## Verifiability Properties + +The TEE provides four properties that together make computation verifiable: + +| Property | What it guarantees | +|----------|-------------------| +| **Input verification** | Attestation signatures are verified at the TEE boundary. Inputs are validated before computation begins. | +| **Deterministic computation** | Same inputs always produce the same result. PostGIS version is pinned, precision is fixed at centimeter level, and no persistent state exists between requests. | +| **Signed output** | Results are signed by a key that only exists inside the TEE. The key cannot be extracted by the operator. | +| **TEE attestation** | EigenCompute provides hardware-generated attestation that specific code executed on specific inputs inside the enclave. | + +Together: the code is attested, the inputs are verified, the computation is deterministic, and the output is signed by a key the operator cannot access. An observer can verify that the result came from the correct code running on the correct inputs — without re-executing the computation. + +## Privacy Properties + +The TEE provides meaningful privacy guarantees. Inputs and outputs are encrypted in transit and decrypted only inside the enclave — the infrastructure operator never sees raw location data. + +- **Raw input coordinates** are processed inside the enclave and never exposed to the operator +- **Exact geometries** (polygon boundaries, line paths) exist only during computation and are discarded after signing +- **The operator** — whoever runs the Astral service — cannot access the plaintext data at any point + +The signed result reveals the operation type, the answer, and hashed input references, but not the raw input data itself. Some information leaks from the result (a `contains` answer of `true` tells you the point is inside the polygon), but this is inherent to the computation, not a limitation of the privacy model. + +## EigenCompute TEE stack + +The service runs on [EigenCompute](https://blog.eigencloud.xyz/eigencloud-brings-verifiable-ai-to-mass-market-with-eigenai-and-eigencompute-launches/), part of the EigenCloud ecosystem: + +```mermaid +sequenceDiagram + participant C as Client + participant T as EigenCompute TEE + C->>T: Encrypted request + Note over T: Decrypt inside enclave + Note over T: Validate inputs + Note over T: PostGIS computation + Note over T: Sign result with TEE key + T->>C: Signed result +``` + +PostGIS runs **inside** the TEE container, not as an external service — no external dependencies means the entire execution environment is attested. The GEOS library under PostGIS is the same C++ geometry engine used by QGIS, GDAL, and most professional geospatial software. + +## The Signing Key + +The signing key is generated and provisioned inside the TEE. It cannot be extracted by the operator, even with physical access to the machine. All signed results are produced by this key, and downstream consumers (smart contracts, applications, agents) can verify that a result was signed by the Astral service by checking the signature against the known public key. + + + Signing key publication is not yet finalized — key management and rotation are still being worked out for production deployment. This page will be updated with the public key and verification instructions when available. + + + + For key rotation and management details in smart contract integrations, see the [SDK: EAS module](/sdk/eas). + + +## Stateless Model + +Each request brings all required inputs. There is no persistent state between requests. This ensures determinism — the same request always produces the same result, regardless of when it's submitted or what other requests have been processed. + +## Future Directions + +The current TEE-based approach provides verifiable computation today. Future enhancements include: + +- **AVS consensus** — Multiple operators independently verify computations +- **ZK proofs** — Cryptographic proof of correct execution without trusted hardware +- **Decentralized signers** — Multi-party result signing + + + The verification endpoint in detail + + +--- + +**See also:** +- [API Reference](/api-reference/overview) — full endpoint documentation diff --git a/concepts/compute.mdx b/concepts/compute.mdx new file mode 100644 index 0000000..03e7264 --- /dev/null +++ b/concepts/compute.mdx @@ -0,0 +1,94 @@ +--- +title: "Compute" +description: "Geospatial operations inside the TEE — distance, containment, and more" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Compute + +The Compute endpoint runs geospatial operations on location data inside the [TEE](/concepts/astral-location-services) and returns [signed results](/concepts/signed-results). It answers spatial questions — how far apart are these locations? is this point inside that boundary? — with cryptographically verifiable answers. + +## Accepted Inputs + +The compute endpoint accepts location data at any level of the [verifiability spectrum](/concepts/location-data#the-verifiability-spectrum): + +| Input type | What it is | Trust properties | +|-----------|-----------|-----------------| +| **Raw GeoJSON** | Unsigned geometry | Proves computation was correct; does not prove input provenance | +| **Signed location record** | EAS attestation UID (onchain) or UID + URI (offchain) | Attribution + integrity + correct computation | +| **Verified location proof** | UID of a verified location proof | Evidence of physical correspondence + correct computation | + +You can mix input types in a single operation. A geofencing check might use raw GeoJSON for the boundary (a publicly known polygon) and a verified location proof for the user's position. + +## Inside the TEE + +The compute engine is PostGIS backed by GEOS — the same C++ library that powers QGIS, GDAL, and most professional geospatial software. PostGIS runs inside the Docker container within the TEE, not as an external service. + +Computation is stateless and deterministic. Each request brings all required inputs. Results are rounded to centimeter precision before signing to ensure reproducibility. Same inputs always produce the same output. + +## Available Operations + +### Measurements (Numeric Results) + +| Operation | What it computes | Unit | PostGIS function | +|-----------|-----------------|------|------------------| +| **Distance** | Nearest distance between two geometries | meters | `ST_Distance` | +| **Area** | Area of a polygon | square meters | `ST_Area` | +| **Length** | Length of a line | meters | `ST_Length` | + +### Predicates (Boolean Results) + +| Operation | What it checks | PostGIS function | +|-----------|---------------|------------------| +| **Contains** | Is geometry B entirely inside geometry A? | `ST_Contains` | +| **Within** | Is geometry within a specified radius of target? | `ST_DWithin` | +| **Intersects** | Do geometries share any space? | `ST_Intersects` | + +All measurements use metric units. No unit conversion is provided — convert client-side if needed. + +## Precision and Determinism + +Results are stored with centimeter precision as scaled integers: + +| Type | Precision | Scaling | +|------|-----------|---------| +| Distance / Length | 0.01 m | 523.45 meters → stored as 52345 | +| Area | 0.0001 m² | 1234.5678 m² → stored as 12345678 | + +This scaling ensures deterministic integer representation, which is important for smart contract integration where floating-point arithmetic isn't available. + +## Output + +Every compute operation returns a [signed result](/concepts/signed-results) containing: +- The computed answer (boolean or numeric) +- References to the specific inputs used +- A timestamp +- The operation name +- A cryptographic signature from the TEE-held signing key + +The signed result can be used offchain (in an agent, application, or database) or submitted onchain via EAS delegated attestation. EAS resolvers allow those attestations to trigger smart contract logic. + +## What's Next + +The current operation set covers the most common spatial questions, but PostGIS exposes a much larger surface. Areas we're exploring: + +- **More predicates and measurements** — `disjoint`, `touches`, `crosses`, nearest-neighbor queries +- **Geometry transformations** — buffers, centroids, unions, intersections. These return **new geometries** rather than scalar or boolean values, which raises open design questions about signed result format and storage +- **Spatial selection queries** — operations over sets of geometries, like "which of these polygons contains this point?" or "find all zones within 1km of this location" +- **Compositional queries** — chaining multiple operations into a single verified request, so you could express something like "is this point within 500m of any geometry in this set that intersects this boundary?" without multiple round trips + + + The operation set is extensible. If you need a spatial operation that isn't listed here, [open an issue](https://github.com/AstralProtocol/astral-location-services/issues). + + + + Output formats and how to use them + + +--- + +**See also:** +- [API: Compute endpoints](/api-reference/compute/distance) — endpoint reference for each operation +- [SDK: Compute module](/sdk/compute) — programmatic access to compute operations +- [API: Types](/api-reference/types) — result type reference diff --git a/concepts/geocomputation.mdx b/concepts/geocomputation.mdx new file mode 100644 index 0000000..5a781e1 --- /dev/null +++ b/concepts/geocomputation.mdx @@ -0,0 +1,51 @@ +--- +title: "Overview" +description: "Processing location data with proof of correct execution" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Geocomputation + +Geocomputation is the processing of geospatial data to answer spatial questions: How far apart are these two locations? Is this point inside that boundary? Do these regions overlap? The answers seem simple, but computing them correctly — on a curved, non-Euclidean surface, with consistent precision and deterministic results — is harder than it appears. + +## The computational challenge + +Spatial computation involves real complexity: + +- **Reference systems** — The Earth is not flat, and different coordinate systems represent it differently. Computing distance on a sphere vs. a projected plane vs. a geoid gives different results. +- **Computational geometry** — Operations like polygon containment, intersection detection, and area calculation require robust algorithms that handle edge cases (self-intersecting polygons, antipodal points, degenerate geometries). +- **Precision and determinism** — Floating-point arithmetic is not associative. The same computation can produce different results depending on operation order, hardware, or library version. For verifiable results, computation must be deterministic. + +Professional geospatial software (QGIS, GDAL, PostGIS) has spent decades solving these problems. Astral builds on that foundation. (This is a pragmatic shift from our original plan to reimplement these [algorithms](https://github.com/DecentralizedGeo/spatial-sol) in Solidity.) + +## Scope of geocomputation in Astral + +Astral's geocomputation capabilities span three areas, at different stages of maturity: + +| Capability | Status | What it does | +|-----------|--------|-------------| +| **[Location proof verification](/concepts/verify)** | Shipping | Evaluates location proof credibility | +| **[Geospatial operations](/concepts/compute)** | Shipping | Distance, containment, intersection, area, length | +| **Geospatial AI/ML** | Planned | Spatial analysis, prediction, pattern detection | + +## Verifiable geocomputation + +Computation is only useful in adversarial contexts if you can trust the result. For most applications, that means running code on a server and trusting the operator. For applications where the spatial answer triggers real-world consequences that may carry an incentive to lie — a smart contract execution, a compliance determination, an autonomous agent decision — that trust model isn't sufficient. + +Astral makes geocomputation verifiable through three approaches, at different stages of development: + +**Trusted Execution Environments (v0 — shipped).** Astral's Compute engine runs inside a TEE (currently [EigenCompute](https://blog.eigencloud.xyz/eigencloud-brings-verifiable-ai-to-mass-market-with-eigenai-and-eigencompute-launches/)), which provides hardware-level isolation. The TEE guarantees that the attested code executed on the attested inputs, and that the signing key never left the enclave. This is the foundation of [Astral Location Services](/concepts/astral-location-services), our hosted service. + +**Zero-knowledge circuits (research).** ZK proofs would allow verification of correct computation without any trusted hardware — a verifier could confirm the result was computed correctly without re-executing the computation or trusting a TEE manufacturer. This is an active research direction, not yet implemented. + +**Smart contract verification (limited).** Some spatial operations could theoretically run onchain, but gas costs and computational limitations make this impractical for most geospatial operations today. + + + The hosted TEE service that runs verification and computation + + +--- + +**See also:** +- [API Reference](/api-reference/overview) — full endpoint documentation diff --git a/concepts/geojson.mdx b/concepts/geojson.mdx new file mode 100644 index 0000000..a9ffec9 --- /dev/null +++ b/concepts/geojson.mdx @@ -0,0 +1,76 @@ +--- +title: "GeoJSON" +description: "Raw unsigned geospatial data — the simplest input format" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# GeoJSON + +GeoJSON is raw, unsigned geospatial data. It's the simplest way to pass location data into Astral — and the least verifiable. + +## The format + +GeoJSON ([RFC 7946](https://datatracker.ietf.org/doc/html/rfc7946)) is a standard format for encoding geographic data structures. The full spec defines three object types — Geometry, Feature (geometry + properties), and FeatureCollection — but **Astral v0 works with bare Geometry objects only**, not Feature or FeatureCollection wrappers. + +A Geometry object has two keys: + +- **`type`** — the geometry type: `Point`, `LineString`, `Polygon`, `MultiPoint`, `MultiLineString`, `MultiPolygon`, or `GeometryCollection` +- **`coordinates`** — nested arrays of `[longitude, latitude]` positions + +Geometries are built from coordinate arrays. A Point is a single `[lon, lat]` pair. A LineString is an array of positions. A Polygon is an array of linear rings — where each ring is an array of positions and the **first and last position must be identical** (closing the ring to form a valid area). + +```json +{ + "type": "Polygon", + "coordinates": [[ + [-122.42, 37.78], + [-122.42, 37.76], + [-122.40, 37.76], + [-122.40, 37.78], + [-122.42, 37.78] + ]] +} +``` + + + Astral APIs accept **bare Geometry objects** — pass the geometry directly, not wrapped in a Feature. If you're exporting from tools like geojson.io, extract the `geometry` field from the Feature before passing it to Astral. + + +## What GeoJSON does not provide + +- **Attribution** — No record of who created it +- **Integrity** — No way to detect if it's been modified +- **Correspondence** — No evidence that it reflects reality + +A signed result that uses raw GeoJSON as input proves "Astral computed the relationship between geometry A and geometry B" — but it does not prove who provided those geometries or whether they correspond to anything in the physical world. + +For inputs where provenance matters, use [signed location records](/concepts/location-records) or [location proofs](/concepts/location-proofs). + +## Coordinate conventions + +| Convention | Value | +|-----------|-------| +| **Format** | GeoJSON (RFC 7946) | +| **CRS** | WGS84 (`http://www.opengis.net/def/crs/OGC/1.3/CRS84`) | +| **Coordinate order** | `[longitude, latitude]` | +| **Altitude** | Optional third coordinate, meters above WGS84 ellipsoid | + + + Many mapping APIs use `[latitude, longitude]` order. GeoJSON uses `[longitude, latitude]`. Mixing these up is a common source of bugs — your point ends up in the wrong hemisphere. + + +## Tools for working with GeoJSON + +- **[geojson.io](https://geojson.io)** — Draw, edit, and export GeoJSON on a map. The standard quick-start tool. +- **[Placemark Play](https://play.placemark.io)** — Open-source geodata editor with drawing and algorithmic operations (buffering, simplification). +- **[MapShaper](https://mapshaper.org)** — Simplify, convert, and inspect vector data. Useful for reducing file size and format conversion. + + + Signed, verifiable location data + + +--- + +**See also:** +- [API: Types — Input](/api-reference/types) — how raw GeoJSON is passed to API endpoints diff --git a/concepts/geospatial-operations.mdx b/concepts/geospatial-operations.mdx deleted file mode 100644 index d2c1e2a..0000000 --- a/concepts/geospatial-operations.mdx +++ /dev/null @@ -1,235 +0,0 @@ ---- -title: "Geospatial Operations" -description: "Spatial computations powered by PostGIS" ---- - - - **Research Preview** — Under active development. - - -# Geospatial Operations - -Astral provides verifiable spatial computations backed by **PostGIS** — the gold standard in geospatial databases with 20+ years of production use. - -All operations return [Policy Attestations](/concepts/policy-attestations) — signed results that can be used offchain or submitted onchain to trigger smart contract logic. - - - PostGIS uses [GEOS](https://libgeos.org/) (Geometry Engine Open Source) under the hood for geometry operations. GEOS is the C++ library that powers most professional geospatial software. - - -## Available Operations - -### Measurements - -Operations that return **numeric** results (as [NumericPolicyAttestations](/concepts/policy-attestations#numericpolicyattestation)): - -| Operation | Description | Unit | Geometry Types | PostGIS Function | -|-----------|-------------|------|----------------|------------------| -| `distance` | Nearest distance between two geometries | meters | Any | `ST_Distance` | -| `area` | Area of a polygon | square meters | Polygon, MultiPolygon | `ST_Area` | -| `length` | Length of a line | meters | LineString, MultiLineString | `ST_Length` | - -### Predicates - -Operations that return **boolean** results (as [BooleanPolicyAttestations](/concepts/policy-attestations#booleanpolicyattestation)): - -| Operation | Description | PostGIS Function | -|-----------|-------------|------------------| -| `contains` | Is geometry B **entirely** inside geometry A? | `ST_Contains` | -| `within` | Is geometry within a specified radius of target? | `ST_DWithin` | -| `intersects` | Do geometries share any space? | `ST_Intersects` | - -## Operation Examples - - - **Code snippets need testing** — Verify against actual implementation before use. - - -### Distance - -Calculate the nearest distance between two geometries in meters. For polygons, this returns the minimum distance between their boundaries. - -```typescript -const result = await astral.compute.distance({ - from: userLocationUID, - to: landmarkUID, - chainId: 84532, - schema: SCHEMA_UID -}); - -console.log(result.result); // 523.45 (meters) -console.log(result.units); // "meters" -``` - -### Contains - -Check if a geometry is **entirely** inside a container geometry. - -```typescript -const result = await astral.compute.contains({ - container: sfBayAreaPolygonUID, // Container polygon - geometry: userLocationUID, // Geometry to check - chainId: 84532, - schema: SCHEMA_UID -}); - -console.log(result.result); // true or false -``` - -### Within - -Check if a geometry is within a specified radius of a target — essentially a **radius check**. - -```typescript -const result = await astral.compute.within({ - geometry: userLocationUID, // Geometry to check - target: landmarkUID, // Target location - radius: 500, // Radius in meters - chainId: 84532, - schema: SCHEMA_UID -}); - -console.log(result.result); // true if within 500m -``` - -### Intersects - -Check if two geometries share any space (overlap, touch, or cross). - -```typescript -const result = await astral.compute.intersects({ - geometry1: polygon1UID, - geometry2: polygon2UID, - chainId: 84532, - schema: SCHEMA_UID -}); - -console.log(result.result); // true if they share space -``` - -### Area - -Calculate the area of a polygon (must be Polygon or MultiPolygon). - -```typescript -const result = await astral.compute.area({ - geometry: propertyBoundaryUID, - chainId: 84532, - schema: SCHEMA_UID -}); - -console.log(result.result); // 5432.10 (square meters) -console.log(result.units); // "square_meters" -``` - -### Length - -Calculate the length of a line (must be LineString or MultiLineString). - -```typescript -const result = await astral.compute.length({ - geometry: routeUID, - chainId: 84532, - schema: SCHEMA_UID -}); - -console.log(result.result); // 2345.67 (meters) -console.log(result.units); // "meters" -``` - -## Units - -All measurements use **metric units only**: - -| Measurement | Unit | -|-------------|------| -| Distance | meters | -| Length | meters | -| Area | square meters | -| Radius (in `within`) | meters | - - - No unit conversion options are provided. Developers should convert client-side if needed. - - -## Precision - -Results are stored with centimeter precision to enable **deterministic, reproducible** computation: - -| Type | Precision | Storage | -|------|-----------|---------| -| Distance/Length | 0.01m | Scaled integer (cm) | -| Area | 0.0001 m² | Scaled integer (cm²) | - -``` -523.45 meters → stored as 52345 (centimeters) -1234.5678 m² → stored as 12345678 (cm²) -``` - - - We're actively testing the reliability of deterministic geospatial computation with this precision approach. This is an area of ongoing research in the beta implementation. - - -## Compute Options - -All operations require these parameters: - -```typescript -interface ComputeOptions { - chainId: number; // Target chain for attestation signing - schema: string; // EAS schema UID (required) - recipient?: string; // Attestation recipient address -} -``` - - - **`chainId` is required.** The service needs to know which chain to sign the policy attestation for. - - - - We only support official EAS deployments on supported chains. See the SDK overview for the chain list. - - -## Future Operations - -Post-MVP, we plan to add: - -| Operation | Type | Description | -|-----------|------|-------------| -| `disjoint` | Predicate | Do geometries not touch? | -| `buffer` | Transformation | Create buffer zone around geometry | -| `centroid` | Transformation | Find center point | -| `union` | Transformation | Merge geometries | -| `intersection` | Transformation | Overlapping area | - -We're also exploring **nested/composable operations** — combining multiple predicates in a single attestation, such as "is inside region A AND within 500m of landmark B". - -## Client-Side Operations with Turf.js - -We recommend **[Turf.js](https://turfjs.org/)** for client-side geospatial operations. Use Turf.js for instant UX feedback, and Astral for verifiable attestations: - -```typescript -import * as turf from '@turf/turf'; - -// Client-side: instant UX feedback (unverified, but instant) -const localDistance = turf.distance(point1, point2); -showUI(`${localDistance}km away`); - -// Verifiable: signed attestation for onchain use -const attestedResult = await astral.compute.distance({ - from: uid1, - to: uid2, - chainId: 84532, - schema: SCHEMA_UID -}); -await astral.eas.submitDelegated(attestedResult.delegatedAttestation); -``` - -| Use Case | Tool | Why | -|----------|------|-----| -| Real-time UI feedback | Turf.js | Instant, free, runs client-side | -| Verifiable computation | Astral | Signed attestation for onchain actions | - - - Learn about the signed outputs of geospatial operations - diff --git a/concepts/location-attestations.mdx b/concepts/location-attestations.mdx deleted file mode 100644 index 5581503..0000000 --- a/concepts/location-attestations.mdx +++ /dev/null @@ -1,183 +0,0 @@ ---- -title: "Location Records" -description: "Location data that serves as input to geospatial operations" ---- - - - **Research Preview** — Under active development. - - -# Location Records - -Location records are the **inputs** to geospatial operations. They represent spatial data — points, polygons, routes, or any geometry — that Astral can compute against. - -## Supported Input Types - -Astral currently supports two types of location records: - -1. **Location Attestations** — EAS attestations conforming to the [Location Protocol v0.2](https://github.com/DecentralizedGeo/location-protocol-spec/tree/v0.2-draft) schema -2. **Raw GeoJSON** — Unsigned geometry data for reference locations or prototyping - -We plan to expand support for other location record formats in the future, including AT Protocol location lexicon records and other standards. - -## Location Attestations - -Location Attestations are **signed spatial records** stored as EAS attestations. They contain: - -- **Geometry**: The spatial data (GeoJSON format) -- **Metadata**: Optional descriptive information -- **Signature**: Cryptographic proof of the attester's identity - - - **Code snippets need testing** — Verify against actual implementation before use. - - -```typescript -// Creating a location attestation -const landmark = await astral.location.create({ - type: 'Point', - coordinates: [2.2945, 48.8584] -}, { - chainId: 84532, - submitOnchain: true, - metadata: { - name: "Eiffel Tower", - description: "Iconic Paris landmark" - } -}); - -console.log(landmark.uid); // 0xabc123... -``` - -## Storage Options - -Location Attestations can be stored **onchain** or **offchain**: - - - - - Stored on EAS contracts - - Referenced by UID alone - - Higher gas cost - - Permanent, immutable - - - - Stored on IPFS, servers, etc. - - Referenced by UID + URI - - No gas cost to create - - EIP-712 signed - - - -### Onchain - -```typescript -// Create onchain attestation -const location = await astral.location.create(geojson, { - submitOnchain: true -}); - -// Reference by UID only -await astral.compute.distance(location.uid, otherUID); -``` - -### Offchain - -```typescript -// Create offchain attestation -const location = await astral.location.create(geojson, { - submitOnchain: false, - storage: 'ipfs' // or 'arweave', 'https', etc. -}); - -// Reference by UID + URI -await astral.compute.distance( - { uid: location.uid, uri: location.uri }, - otherUID -); -``` - -## Schema - -Location Attestations follow the [Location Protocol v0.2](https://github.com/DecentralizedGeo/location-protocol-spec/tree/v0.2-draft) schema: - - - **Verification in progress** — We are actively verifying that deployed schemas conform to Location Protocol v0.2. There may be inconsistencies between the documentation, deployed schemas, and the spec. See [GitHub issue #11](https://github.com/AstralProtocol/astral-location-services/issues/11) for status. - - -| Field | Type | Description | -|-------|------|-------------| -| `lp_version` | string | Location Protocol version (e.g., "0.2") | -| `location_type` | string | The type of location data (e.g., "GeoJSON") | -| `location` | bytes | Encoded location data | -| `srs` | string | Spatial reference system as OGC URI | - -## Using Location Attestations - -Location Attestations serve as **inputs** to geospatial operations: - -```typescript -// As direct UID (onchain attestation) -const result = await astral.compute.distance( - '0xabc123...', // UID of first location - '0xdef456...' // UID of second location -); - -// As UID + URI (offchain attestation) -const result = await astral.compute.contains( - { uid: '0xabc123...', uri: 'ipfs://Qm...' }, - userLocationUID -); - -// As inline attestation object -const result = await astral.compute.within( - { attestation: signedOffchainAttestation }, - landmarkUID, - 500 -); -``` - -## Raw GeoJSON Alternative - -You can also use **raw GeoJSON** without creating an attestation first: - -```typescript -// Raw GeoJSON as input -const result = await astral.compute.contains( - { - type: 'Polygon', - coordinates: [[[...]]] // SF Bay Area boundary - }, - userLocationUID -); -``` - - - Raw GeoJSON is **not attested**. The Policy Attestation proves "Astral computed the relationship between inputs A and B" but does NOT prove **who signed** those geometries. Use raw GeoJSON for reference geometries (official boundaries) or prototyping. - - -## Fetching Location Attestations - -```typescript -// Get by UID -const location = await astral.location.get('0xabc123...'); -console.log(location.geometry); // { type: 'Point', coordinates: [...] } - -// Query with filters -const locations = await astral.location.query({ - bbox: [-122.5, 37.7, -122.4, 37.8], // Bounding box - limit: 100 -}); -``` - -## Coordinate System - -Location Attestations honor the `srs` field, using OGC URIs per [Location Protocol v0.2](https://github.com/DecentralizedGeo/location-protocol-spec/tree/v0.2-draft): - -- **Default**: `http://www.opengis.net/def/crs/OGC/1.3/CRS84` (WGS84, lon/lat order) -- **Custom**: Any OGC CRS URI supported by PostGIS - -Coordinates should be in **[longitude, latitude]** order (GeoJSON standard). - - - Learn about the spatial computations you can perform - diff --git a/concepts/location-claims.mdx b/concepts/location-claims.mdx new file mode 100644 index 0000000..5a2220e --- /dev/null +++ b/concepts/location-claims.mdx @@ -0,0 +1,63 @@ +--- +title: "Location Claims" +description: "Assertions about the timing and location of an event" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Location Claims + +A location claim is an assertion about where and when an event occurred. It's the "I was here" statement that [location stamps](/concepts/location-stamps) provide evidence for or against. + +## What a Location Claim Contains + +A location claim extends the [Location Protocol v0.2](https://github.com/DecentralizedGeo/location-protocol-spec/tree/v0.2-draft) specification with verification-specific fields: + +**Asserted location.** The location being claimed — a GeoJSON geometry with a spatial reference system. This is where the subject claims the event occurred. + +**Spatial uncertainty (radius).** How precisely the claim defines the location, in meters. This field is required — you cannot claim presence at an exact point. Both physical measurement and honest reporting involve uncertainty, and pretending otherwise produces misleading verification results. + +**Temporal bounds.** A time range (start and end) during which the event is claimed to have occurred. Like spatial uncertainty, temporal bounds acknowledge that events have duration. + +**Subject.** Who or what was at the location. This could be a person (identified by an Ethereum address, DID, or other scheme), a device, an asset, or an organization. + +**Event type.** What kind of event is being claimed. The event could be: + +- **Presence** — a person or device was at a location +- **Transaction origin** — a transaction originated from a location +- **Asset location** — a physical asset was at a location +- **Delivery** — a delivery occurred at a location + +## The Uncertainty Tradeoff + +Location claims involve a fundamental tension between precision and confidence: + +- "Somewhere in California during 2024" — easy to verify with high confidence, but not very useful for many use cases +- "Within 10 meters at 14:32:07" — precise and useful, but harder to verify confidently + +Broader claims are easier to verify because the evidence just needs to fall anywhere within a larger target. Narrower claims require more precise evidence. Applications decide what precision/confidence balance they need. + +This tradeoff also has privacy implications. Broader spatial and temporal bounds reveal less about exact location, which can be a feature rather than a limitation for privacy-sensitive applications. + +Ultimately, spatial and temporal precision, forgery cost, privacy, latency and other factors are application-specific — different combinations of location stamps will be useful in different contexts. + +## Relationship to Location Stamps + +A location claim is an assertion. A [location stamp](/concepts/location-stamps) is evidence. They use the same Location Protocol format for their location data, but represent different things: + +| | Location claim | Location stamp | +|---|--------------|----------------| +| **Location** | Asserted — where the subject claims to have been | Observed — where evidence indicates the subject was | +| **Created by** | The claimant | A proof-of-location system | +| **Purpose** | States what needs to be verified | Provides evidence for verification | + +[Evaluation](/concepts/location-proof-evaluation) compares the two: does the observed location (from location stamps) support the asserted location (from the location claim)? + + + Bundling location stamps with a location claim + + +--- + +**See also:** +- [API: Types](/api-reference/types) — LocationClaim type reference diff --git a/concepts/location-data.mdx b/concepts/location-data.mdx new file mode 100644 index 0000000..bcde048 --- /dev/null +++ b/concepts/location-data.mdx @@ -0,0 +1,57 @@ +--- +title: "Overview" +description: "Spatial data in Astral — from raw coordinates to verifiable records" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Location Data + +Location data is what Astral operates on. Every API call starts with spatial data — points, polygons, lines, or references to previously recorded locations. But not all location data is created equal. The provenance of a piece of location data determines how much trust you can place in it. + +## The Verifiability Spectrum + +Astral works with location data at three levels of verifiability: + +| Level | What it is | What you know | +|-------|-----------|---------------| +| **Raw GeoJSON** | Unsigned geospatial data | The vector geometry itself — nothing about who created it or whether it's been tampered with | +| **Signed location record** | EAS attestation with cryptographic signature | Who attested to this location, plus assurances that data hasn't been changed | +| **Location proof** | Signed location record + evidence from proof-of-location systems | All of the above, plus independently evaluable evidence about a claim about the location of an event | + +Each level adds verifiability. Raw GeoJSON is fine for some use cases; signed location records add attribution and integrity. Location proofs add evidence of physical correspondence — the hardest part. + +Astral doesn't take a stance on what's trustworthy — that's not our role. Instead, we provide a system to organize, harmonize, and evaluate location data and evidence — to make spatial decision parameters legible – so that applications can make informed, risk-appropriate choices about what to trust and how much. + + + **v0 supports vector geospatial data only.** Raster support (GeoTIFFs, imagery analysis) is planned. + + +## Geospatial data sources + +Location data comes from many sources, each with different properties: + +- **Device sensors** — GPS, Wi-Fi, cellular positioning. Ubiquitous but often spoofable. +- **Network infrastructure** — Cell tower triangulation, IP geolocation, internet latency measurements. Coarse but hard to manipulate without network access. +- **Hardware attestation** — Secure enclave readings, hardware keystores. Harder to forge but still device-dependent. +- **Institutional records** — Land registries, shipping manifests, IoT telemetry. Trusted because of the institution, not cryptography. +- **Reference data** — Official boundaries, standard geometries, public datasets. Trusted because they're publicly auditable. + +No single source is sufficient for high-stakes applications. [Location proofs](/concepts/location-proofs) address this through defense-in-depth: combining evidence from multiple independent sources to raise the cost of forgery. + +## Coordinate system + +All location data in v0 of the Astral Protocol uses [GeoJSON](https://datatracker.ietf.org/doc/html/rfc7946) as the geometry format. Coordinates follow the GeoJSON standard: + +- **Default Coordinate Reference System (CRS):** WGS84 (`http://www.opengis.net/def/crs/OGC/1.3/CRS84`) +- **Coordinate order:** `[longitude, latitude]` — note that this is the opposite of what many mapping APIs use (a common challenge in GIS systems — [here's why](https://macwright.com/lonlat/).) + + + Raw unsigned geospatial data + + +--- + +**See also:** +- [SDK: Location module](/sdk/location) — creating and fetching location records +- [API: Types](/api-reference/types) — input format reference diff --git a/concepts/location-proof-evaluation.mdx b/concepts/location-proof-evaluation.mdx new file mode 100644 index 0000000..59b7812 --- /dev/null +++ b/concepts/location-proof-evaluation.mdx @@ -0,0 +1,85 @@ +--- +title: "Location Proof Evaluation" +description: "How Astral assesses the credibility of a location proof" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Location Proof Evaluation + +When a [location proof](/concepts/location-proof-structure) is submitted for verification, Astral evaluates it through a three-phase process. The output is a **credibility vector** — a structured assessment describing how strongly the evidence supports the claim across multiple dimensions. + + + Why not a binary yes/no? Different applications value different properties — a logistics platform cares most about spatial precision, while a compliance system may prioritize source independence. The credibility vector provides a dense quantification of the evidence so applications can apply their own weighting. + + +## The Three Evaluation Phases + +### Phase 1: Stamp Checks + +Each [location stamp](/concepts/location-stamps) is evaluated independently for internal validity: + +- **Signatures** — Are the cryptographic signatures valid? Do they chain to a trusted source for this [proof-of-location system](/concepts/pol-systems)? +- **Structure** — Does the location stamp conform to the expected format for its [plugin](/plugins/overview)? +- **Signal consistency** — Are the raw signals internally consistent? Do multiple sensor readings within a single location stamp agree with each other? + +A location stamp that fails these checks provides no useful evidence — its results are reported but won't contribute positively to the overall credibility. + +### Phase 2: Correlation Checks (Multi-Stamp Location Proofs) + +For location proofs with multiple location stamps from independent proof-of-location systems, the evaluator cross-correlates the evidence: + +- **Independence** — Are the location stamps from genuinely independent sources? Two location stamps from the same proof-of-location system or the same device aren't independent. Independence is assessed based on plugin type, device identity, and trust model. +- **Agreement** — Do the independent location stamps agree on location and timing? Evidence from unrelated sources that converges on the same location is substantially more convincing than any single source. + +This phase is what makes multi-factor location proofs powerful. Agreement between independent sources is hard to forge because an attacker would need to compromise multiple unrelated systems simultaneously. + +### Phase 3: Claim Assessment + +The evaluator compares the observed evidence (from the location stamps) against the asserted claim: + +- **Spatial consistency** — Does the observed location from the location stamps fall within the claimed location and radius? +- **Temporal consistency** — Does the temporal footprint of the evidence overlap with the claimed time range? +- **Overall support** — Given the strength of the evidence (phases 1 and 2), how well does it support this specific claim? + +## The Credibility Vector + +The output of evaluation is a credibility vector with four dimensions. Each dimension is a structured object containing multiple metrics — not a single score. + +| Dimension | What it measures | Example metrics | +|-----------|-----------------|----------------| +| **Spatial** | How well observed locations support the claimed location | Mean distance from claim center, fraction of location stamps within claimed radius | +| **Temporal** | How well observation times align with the claimed time range | Mean overlap between location stamp and claim time windows, fraction with full overlap | +| **Validity** | Internal validity of the location stamps | Fraction with valid signatures, valid structure, consistent signals | +| **Independence** | How independent and corroborating the evidence sources are | Ratio of unique plugins to total location stamps, spatial agreement across sources | + +There is no single overall score. This is deliberate — collapsing a multidimensional assessment into one number requires value judgments about which dimensions matter most, and that's the application's call, not ours. + + + These dimensions and their constituent metrics are an active area of [research](https://github.com/AstralProtocol/research). We expect them to evolve as we learn more about what's useful in practice. + + +## Interpreting the Vector + +The credibility vector quantifies the strength of the evidence, not the probability that the claim is true. Strong metrics across all four dimensions mean the evidence is internally valid, spatially and temporally consistent with the claim, and drawn from independent sources. Weak metrics tell you *where* the evidence falls short. + +What the vector cannot tell you: whether the proof-of-location systems themselves are trustworthy for your use case. A credibility vector with strong metrics from a single device attestation reflects different underlying assurance than one with equally strong metrics from three independent proof-of-location systems. The per-dimension breakdown — especially independence — makes this visible. + +## Application-Level Decisions + +Astral evaluates and reports. Applications decide. + +The credibility vector gives applications enough information to make risk-appropriate decisions. A social check-in app might accept weak independence metrics. A compliance system might require strong validity and spatial metrics from at least two independent sources. A land title registry might require the strongest available assurance across all dimensions. + +The threshold is always the application's choice — Astral does not impose minimum requirements. + + + Spatial operations with proof of correct execution + + +--- + +**See also:** +- [SDK: Location proofs](/sdk/location-proofs) — verifying location proofs programmatically +- [API: Verify proof](/api-reference/verify/proof) — verification endpoint reference +- [API: Types](/api-reference/types) — CredibilityVector type reference diff --git a/concepts/location-proof-structure.mdx b/concepts/location-proof-structure.mdx new file mode 100644 index 0000000..041d174 --- /dev/null +++ b/concepts/location-proof-structure.mdx @@ -0,0 +1,70 @@ +--- +title: "Composing Location Proofs" +description: "Bundling location stamps with a location claim into a verifiable artifact" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Composing Location Proofs + +A location proof bundles one or more [location stamps](/concepts/location-stamps) with a [location claim](/concepts/location-claims). It's the verifiable artifact — a claim paired with its supporting evidence. + +## Structure + +A location proof contains: + +- **One location claim** — the assertion being made (where, when, who, what event) +- **One or more location stamps** — evidence from proof-of-location systems + +That's it. The simplicity is intentional. A location proof is a container that brings together the assertion and the evidence so they can be [evaluated](/concepts/location-proof-evaluation) together. + +## Single-Stamp vs. Multi-Stamp Location Proofs + +A location proof with a single location stamp is valid. It represents evidence from one proof-of-location system supporting one location claim. The evaluation can still assess the location stamp's internal validity and how well it supports the claim. + +Multi-stamp location proofs from independent proof-of-location systems are stronger. When evidence from unrelated sources agrees, the cost of forgery rises sharply — an attacker would need to simultaneously compromise multiple independent systems. This is where cross-correlation adds value: the evaluation assesses how independent the evidence sources actually are. + +Redundant location stamps from the *same* proof-of-location system don't meaningfully increase confidence. Cross-correlation looks for independence between sources. + + + We are building a taxonomy of proof-of-location systems and quantifying these dimensions as an area of active [research](https://github.com/AstralProtocol/research/blob/feature/research-agenda/docs/research-agenda.md#cross-cutting-research-questions). + + +## Location Proof Plugins + +**Location proof plugins** are the abstraction layer that makes composition practical. Each plugin wraps a [proof-of-location system](/concepts/pol-systems) behind a common interface with five standard methods: + +1. **Collect** — Gather raw signals from the proof-of-location system +2. **Create** — Process signals into an unsigned location stamp +3. **Sign** — Cryptographically sign the location stamp +4. **Verify** — Check a location stamp's internal validity +5. **Evaluate** — Assess how well a location stamp supports a location claim + +This common interface means a developer can compose evidence from multiple proof-of-location systems — and any future system — using the same SDK patterns. The plugin handles the system-specific details; the SDK handles composition and orchestration. + +Currently available plugins: + +| Plugin | Proof-of-location system | Environment | +|--------|------------------------|-------------| +| [ProofMode](/plugins/proofmode) | Device attestation + sensor fusion | iOS, Android, React Native | +| [WitnessChain](/plugins/witnesschain) | Infrastructure latency triangulation | Node.js, any HTTP client | +| [Mock](/plugins/mock) | Configurable test evidence | Any JavaScript/TypeScript environment | + + + Different devices and environments support different plugins. A mobile app might use ProofMode for on-device attestation, while a backend service uses WitnessChain for network-based verification. See [Plugins](/plugins/overview) for details on each. + + +## Composing From Multiple Plugins + +To create a multi-stamp location proof, collect location stamps from each plugin independently, then bundle them with a single location claim. The location stamps don't need to know about each other — they just provide independent evidence that the evaluation process cross-correlates. + + + How Astral assesses the credibility of a location proof + + +--- + +**See also:** +- [SDK: Location proofs](/sdk/location-proofs) — creating and composing location proofs +- [Plugins overview](/plugins/overview) — the full plugin ecosystem +- [Build a custom plugin](/plugins/custom) — implementing the plugin interface diff --git a/concepts/location-proofs.mdx b/concepts/location-proofs.mdx index 29156b3..fc9c696 100644 --- a/concepts/location-proofs.mdx +++ b/concepts/location-proofs.mdx @@ -1,289 +1,81 @@ --- -title: "Location Proofs" -description: "Evidence-based verification of location claims" +title: "Overview" +description: "Composable, multi-factor evidence that something was somewhere" --- - - **Research Preview** — Under active development. - +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) # Location Proofs -Location Proofs provide **evidence-based verification** of location claims. They answer the question: *How confident can we be that a subject was at a claimed location during a claimed time?* - -## Core Concepts - -### Location Claims - -A **Location Claim** is an assertion about the timing and location of an event. Claims extend the [Location Protocol](https://github.com/DecentralizedGeo/location-protocol-spec) specification: - -```typescript -interface LocationClaim { - // Location Protocol v0.2 fields - lpVersion: string; // "0.2" - locationType: string; // "geojson-point", "h3-index", etc. - location: LocationData; // The asserted location - srs: string; // Spatial reference system - - // Verification-specific fields - subject: SubjectIdentifier; // Who/what was at the location - radius: number; // Spatial uncertainty (meters) — required - time: { start: number; end: number }; // Temporal bounds - eventType?: string; // "presence", "transaction", "delivery" -} -``` - - - `radius` is **required** — you cannot claim presence at an exact point. Both spatial and temporal bounds involve uncertainty. - - -The event could be: a person's presence, a transaction's origin, an asset's location, a delivery, etc. - -### Location Stamps - -A **Location Stamp** is evidence from a proof-of-location system. Stamps are **independent of claims** — they provide evidence about an observed location, which may come from: - -- **Direct observation**: Hardware attestation, network measurements -- **Indirect/derived sources**: Documents, records, institutional attestations - -```typescript -interface LocationStamp { - // Location data (LP v0.2) — the OBSERVED location - lpVersion: string; - locationType: string; - location: LocationData; // Where evidence indicates subject was - srs: string; - - // Temporal footprint - temporalFootprint: { start: number; end: number }; - - // Plugin identification - plugin: string; // "proofmode" | "witnesschain" - pluginVersion: string; - - // Evidence and signatures - signals: Record; - signatures: Signature[]; -} -``` - -Both claims and stamps use Location Protocol format for their location data: -- **Claim location** = asserted location (where subject claims to have been) -- **Stamp location** = observed location (where evidence indicates subject was) - -Verification compares: *does the observed support the asserted?* - -### Location Proofs - -A **Location Proof** bundles stamps with a claim — this is the verifiable artifact: - -```typescript -interface LocationProof { - claim: LocationClaim; - stamps: LocationStamp[]; // One or more stamps -} -``` - -Single-stamp proofs are valid. Multi-stamp proofs from independent systems enable cross-correlation analysis. - -### Verification - -**Verification (Evidence Evaluation)** is a three-phase process: - -1. **Verify each stamp** — Check signatures, structure, signal consistency -2. **Assess stamps against claim** — Does the observed location support the asserted location? -3. **Assess cross-correlation** — Do multiple stamps corroborate each other? - -The output is a **Credibility Assessment**. - -## Credibility Assessment - -Verification outputs a structured assessment, not just a yes/no: - -```typescript -interface CredibilityAssessment { - confidence: number; // 0-1 overall score - stampResults: StampResult[]; // Per-stamp verification - correlation?: { // For multi-stamp proofs - independence: number; // Are sources uncorrelated? - agreement: number; // Do sources agree? - }; -} -``` - - - **Confidence ≠ Probability.** A confidence of 0.8 does NOT mean "80% probability the claim is true." It means "the evidence is reasonably strong." Calibrating to true probability is future work. - - -### Multi-Factor Proofs - -Multiple stamps from **independent systems** increase confidence: - -```typescript -// Single stamp: moderate confidence -const result1 = await astral.verify.proof({ - claim, - stamps: [proofmodeStamp] -}); -// result1.credibility.confidence → 0.7 - -// Multi-stamp from independent systems: higher confidence -const result2 = await astral.verify.proof({ - claim, - stamps: [proofmodeStamp, witnessStamp] -}); -// result2.credibility.confidence → 0.9 -// result2.credibility.correlation.independence → 0.95 -``` +A location proof is a location record that represents a **location claim**, coupled with **location evidence** supporting that claim. A location proof provides independently evaluable evidence that the claim corresponds to physical reality. - **Key principle**: Independent, corroborating evidence increases confidence. Redundant stamps from the same system don't add confidence — but they don't subtract either. + A location proof is a verifiable digital artifact that represents a claim about the timing and position of some event, plus one or more pieces of corroborating evidence that would require technical manipulation, collusion, or fraud to forge. -## Plugins - -Stamps come from a range of **plugins** — an extensible library of proof-of-location systems: - - - - **Device attestation + sensor fusion** - - iOS (Swift, Secure Enclave) - - Android (Kotlin, hardware keystore) - - React Native wrapper - - Trust: Device integrity - - - **Infrastructure verification** - - Node.js server - - UDP latency triangulation - - Challenger network - - Trust: Speed of light + cryptoeconomic - - - -Each plugin documents its own threat model and trust assumptions. - -## The Uncertainty Tradeoff +The key aim is to **raise the cost of lying about where things happen**, so we can build more interesting applications on more credible spatial facts. -Location proofs involve uncertainty in both space and time: +Astral's Location Proof Framework provides a library of [location proof plugins](/plugins/overview) that collect evidence from different systems and compose them in a way that can be parsed and verified. We are building to support the widest possible range of ways to verify location — we don't want to make assumptions about what forms of evidence system designers will or won't accept. -**Larger bounds = Higher confidence, Lower precision** +One of Astral's core innovations: composable, multi-factor location proofs that bundle evidence collected from independent [proof-of-location systems](/concepts/pol-systems) into a single verifiable artifact. -- "Somewhere in California during 2024" — easy to verify, not very useful -- "Within 10m at 14:32:07" — precise, but hard to verify confidently +## Why Location Proofs Matter -Applications decide what precision/confidence balance they need. Broader claims may be valuable for **privacy preservation**. +Most location data today is self-reported. A device says "I'm at these coordinates" and the receiving system takes it on faith. This works until the stakes are high enough to justify forgery — and the cost of forging GPS coordinates is essentially zero. -## Forgery Resistance +Location proofs change the economics. By requiring evidence from independent proof-of-location systems, they raise the cost of forgery. The goal is not absolute certainty (which we believe is not achievable for physical location), but making the cost of a convincing forgery exceed the value of the fraud. -> **The cost of forging a location proof should exceed the economic value of the transaction it underpins.** +## The Certainty Spectrum -This is application-specific: -- $10 check-in reward → lower forgery resistance needed -- $10M land title transfer → higher forgery resistance needed +Not all location proofs are equally strong. The level of assurance depends on the number, diversity, and quality of the evidence sources. -The credibility assessment helps applications make this determination. +To reason about the relative security of location proofs, we are developing a 5-level **certainty spectrum**: -## SDK Usage +| Level | Description | +|-------|------------| +| **L0** | No evidence — bare self-reported claim | +| **L1** | One externally sourced piece of evidence (e.g. IP-based or single-sensor) | +| **L2** | One substantive strategy or 2–3 lighter-weight independent sources | +| **L3** | Multiple independent sources, at least one substantive | +| **L4** | Diverse high-integrity signals with strong cryptographic backing and cross-correlation | +| **L5** | Nation-state resistance — advanced cryptographic proofs with multiple cross-domain endorsements (research frontier) | - - **Code snippets need testing** — Verify against actual implementation before use. - +Most real-world applications today operate at L1 or L2. Higher levels require proof-of-location systems that don't yet exist at scale — this is an active area of research. -### Collecting Evidence +## Design Principles -```typescript -import { AstralSDK } from '@decentralized-geo/astral-sdk'; +**No one-size-fits-all.** Different applications need different levels of assurance. A social check-in app has different requirements than a land title registry. Location proofs are designed to be composable so applications can choose the right level for their use case. -// Configure plugin -const pluginOptions = { - name: 'proofmode', - version: '0.1.0', - config: { timeout: 5000 } -}; +**Neutral framework.** Astral provides the structure for collecting, composing, and evaluating location evidence — not opinions about which evidence sources to trust. Some applications will accept government-issued attestations as sufficient; others will give them no weight. The framework accommodates both without preference. -// Collect signals -const signals = await astral.stamps.collect(pluginOptions); +**Probabilistic, not deterministic.** Location proofs produce credibility scores, not binary verdicts. The evidence either supports the claim strongly, weakly, or not at all — and applications decide what threshold they need. -// Create and sign stamp -const unsigned = await astral.stamps.create(pluginOptions, signals); -const stamp = await astral.stamps.sign(unsigned, deviceSigner); -``` +**Compose for the use case.** A single location stamp from one proof-of-location system coupled with a claim is a valid location proof. Multiple location stamps from independent systems are stronger. The architecture supports both without requiring a minimum. -### Creating and Verifying a Proof +**Information monotonicity.** Adding valid, independent evidence to a location proof may raise or lower confidence in the underlying claim, but it should always increase certainty about that confidence. More evidence means a clearer picture, even if the picture is unfavorable. -```typescript -// Define claim (extends Location Protocol) -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: '0x...' }, - radius: 100, // meters — required - time: { start: Date.now() / 1000 - 60, end: Date.now() / 1000 }, - eventType: 'presence' -}; +**Verifiability throughout.** Location stamps should be cryptographically signed, evidence bundles should maintain clear provenance, and evaluation functions should be identifiable. Every layer of the location proof should be independently checkable. -// Bundle into proof -const proof = astral.proofs.create(claim, [stamp]); +**Forgery cost principle.** The cost of forging a location proof should exceed the economic value of the transaction it underpins. A \$10 check-in reward needs less forgery resistance than a \$10M land title transfer. -// Verify -const result = await astral.verify.proof(proof); +## The Location Proof Pipeline -console.log(result.credibility.confidence); // 0.85 -console.log(result.uid); // 0xabc123... -``` +Location proofs are built from a series of concepts that compose together: -## Output: Verified Location Proof +1. **[Proof-of-location systems](/concepts/pol-systems)** — The technical and social systems that produce location evidence +2. **[Location stamps](/concepts/location-stamps)** — Individual pieces of evidence from a single proof-of-location system +3. **[Location claims](/concepts/location-claims)** — Assertions about where and when an event occurred +4. **[Composing location proofs](/concepts/location-proof-structure)** — Bundling location stamps with a location claim into a verifiable artifact +5. **[Location proof evaluation](/concepts/location-proof-evaluation)** — How Astral assesses the credibility of a location proof -The final output is an **EAS attestation**: +Each of the following pages covers one of these concepts in detail. -```typescript -interface VerifiedLocationProof { - proof: LocationProof; - credibility: CredibilityAssessment; - uid: string; - attester: string; // Astral service address - timestamp: number; - chainId?: number; // If submitted onchain -} -``` - -Can be onchain or offchain — no requirement to submit onchain. + + The technical and social systems that produce location evidence + --- -## Relationship to Geospatial Operations - -Location Verification is **upstream** of [Geospatial Operations](/concepts/geospatial-operations): - -| Location Verification | Geospatial Operations | -|----------------------|----------------------| -| Evaluates evidence credibility | Computes spatial relationships | -| Input: location proof (claim + stamps) | Input: locations (claimed or verified) | -| Output: credibility assessment | Output: policy attestation | -| "How confident are we that X was at L?" | "Is A inside B?" | - -Verified location proofs can serve as **trusted inputs** to geospatial operations, increasing confidence in computed results. - -### Integration with Compute - -Verified Location Proofs can be used as **inputs** to Geospatial Operations: - -```typescript -// Verify user's location -const verifiedProof = await astral.verify.proof(proof); - -// Use as trusted input to geospatial operation -const result = await astral.compute.contains({ - container: sfBayAreaPolygonUID, - geometry: { verifiedProof: verifiedProof.uid }, - chainId: 84532, - schema: SCHEMA_UID -}); -``` - - - Learn about spatial computations on location data - +**See also:** +- [SDK: Location proofs](/sdk/location-proofs) — creating, signing, and verifying location proofs +- [Plugins overview](/plugins/overview) — the location proof plugin ecosystem diff --git a/concepts/location-records.mdx b/concepts/location-records.mdx new file mode 100644 index 0000000..021d266 --- /dev/null +++ b/concepts/location-records.mdx @@ -0,0 +1,81 @@ +--- +title: "Location Records" +description: "Signed, verifiable location data with attribution and integrity" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Location records + +A location record is signed, verifiable location data artifact. It takes raw geospatial data and adds what [GeoJSON](/concepts/geojson) alone cannot provide: attribution (who created it) and integrity (proof it hasn't been tampered with). + +In the Astral ecosystem, location records conform to v0.2 of the [Location Protocol](https://spec.decentralizedgeo.org) specification. The Location Protocol provides a lightweight data schema that wraps spatial data in any format, to improve interoperability so that spatial data can be parsed and interpreted across distributed systems. + +## What signing adds + +| Property | Raw GeoJSON | Signed location record | +|----------|------------|----------------------| +| **Geometry** | Yes | Yes | +| **Attribution** | No — anyone could have created it | Yes — cryptographic signature ties it to a specific identity | +| **Integrity** | No — could be modified silently | Yes — any modification invalidates the signature | +| **Verifiable timestamp** | No | Sometimes — if the attestation was anchored or registered on a blockchain or other timestamp server | +| **Correspondence** | No | No — signing proves who *claimed* this location, not that they *were* there | + +The last row is important. A signed location record proves that a specific identity attested to a specific location at a specific time. It does not prove the attester was physically present. That's what [location proofs](/concepts/location-proofs) add. + +//@claude: callout: We use the term "correspondence" to refer to how well a digital spatial data record *corresponds* to physical reality. + +## EAS attestations + +Astral's v0 implementation uses [Ethereum Attestation Service (EAS)](https://attest.org/) for signed location records. EAS provides: + +- **Onchain attestations** — stored directly on EAS contracts, referenced by UID (unique identifier), permanent and immutable +- **Offchain attestations** — stored on user devices, centralized servers, IPFS or other infrastructure, referenced by UID + URI, no gas cost, EIP-712 signed + +Both carry the same cryptographic guarantees. The difference is storage: onchain attestations live on the blockchain; offchain attestations live wherever you put them but are still cryptographically verifiable. + +Other implementations — for example, on ATProto — are in development, and we plan to add support in time. + +## Location Protocol v0.2 schema + +Location records conform to the [Location Protocol v0.2](https://github.com/DecentralizedGeo/location-protocol-spec/tree/v0.2-draft) schema: + +| Field | Type | Description | +|-------|------|-------------| +| `lp_version` | string | Protocol version (e.g., "0.2") | +| `location_type` | string | Type of location data (e.g., "GeoJSON") | +| `location` | bytes | Encoded location data | +| `srs` | string | Spatial reference system as OGC URI | + + + We are actively verifying that deployed schemas conform to Location Protocol v0.2. There may be inconsistencies between the documentation, deployed schemas, and the spec. See [GitHub issue #11](https://github.com/AstralProtocol/astral-location-services/issues/11) for status. + + +## Storage options + + + + - Stored on EAS contracts + - Referenced by chain ID + UID //@claude: right? + - Higher gas cost + - Permanent, immutable + + + - Held on user devices or stored on IPFS, servers, etc. + - Referenced by UID + URI + - No gas cost to create + - EIP-712 signed + + + +For offchain attestations, the UID is deterministically derived from the attestation data. Even when fetching from HTTPS (not content-addressed), Astral verifies that the fetched attestation produces the expected UID. Mismatch means rejection. + + + Adding evidence of physical correspondence + + +--- + +**See also:** +- [SDK: Location module](/sdk/location) — creating, fetching, and querying location records +- [API: Types](/api-reference/types) — input format reference diff --git a/concepts/location-stamps.mdx b/concepts/location-stamps.mdx new file mode 100644 index 0000000..d44cf2f --- /dev/null +++ b/concepts/location-stamps.mdx @@ -0,0 +1,53 @@ +--- +title: "Location Stamps" +description: "Evidence from a single proof-of-location system about an observed location" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Location Stamps + +A location stamp is a signed piece of evidence from a single [proof-of-location system](/concepts/pol-systems) about an observed location. It's the atomic unit of location evidence in Astral — the building block from which the [location evidence](/concepts/location-proof-structure) component of a location proof is composed. + +## Location Stamp Contents + +Conceptually, a location stamp carries four things: + +**Observed location.** Where the proof-of-location system's evidence indicates the subject was. This uses the same [Location Protocol v0.2](https://github.com/DecentralizedGeo/location-protocol-spec/tree/v0.2-draft) format as location records — a geometry with a spatial reference system. + +**Temporal footprint.** When the observation was made. Both location stamps and location claims use time ranges (start and end), not single instants — observations take time, and evidence may span a window. + +**Plugin identification.** Which [location proof plugin](/concepts/location-proof-structure#location-proof-plugins) + version was used to create this evidence. This is necessary for verification — the verifier needs to know what kind of evidence to expect and how to evaluate it. + +**Signals and signatures.** The raw evidence data (signals) collected by the proof-of-location system, plus cryptographic signatures that bind the evidence to its source. The signals are plugin-specific — each proof-of-location system produces different kinds of raw data (sensor readings, latency measurements, attestation tokens, etc.). + +## Independence From Claims + +This is a key design decision. A location stamp says "here is evidence about an observed location" — it does not say "this evidence supports a particular claim." The separation matters because: + +- The same location stamp could be evaluated against different location claims +- Location stamps can be collected before a location claim is formulated +- Verification can assess each location stamp on its own merits before comparing it to the claim + +The connection between location stamps and location claims happens at the [location proof](/concepts/location-proof-structure) level, where they're bundled together. The [evaluation](/concepts/location-proof-evaluation) process then assesses whether the observed evidence (from location stamps) support the asserted location (from the location claim). + +## Internal validity + +Each location stamp carries enough information to verify its own internal validity — independent of any location claim. Stamp verification checks: + +- **Signatures** — Are the cryptographic signatures valid? Do they chain back to a trusted source? +- **Structure** — Does the location stamp conform to the expected format for its plugin? +- **Signal consistency** — Are the raw signals internally consistent? (e.g., do multiple sensor readings agree?) + +This is the first phase of [location proof evaluation](/concepts/location-proof-evaluation) — checking each location stamp before assessing how well it supports the claim. + + + Assertions about where and when an event occurred + + +--- + +**See also:** +- [SDK: Stamps](/sdk/stamps) — collecting, creating, and signing location stamps +- [API: Verify stamp](/api-reference/verify/stamp) — verifying individual location stamps +- [API: Types](/api-reference/types) — LocationStamp type reference diff --git a/concepts/pol-systems.mdx b/concepts/pol-systems.mdx new file mode 100644 index 0000000..f3473d2 --- /dev/null +++ b/concepts/pol-systems.mdx @@ -0,0 +1,66 @@ +--- +title: "Proof-of-Location Systems" +description: "Strategies and technologies for producing location evidence" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Proof-of-Location Systems + +A proof-of-location system is any system that produces evidence about physical location. These systems are the foundation of location proofs — each [location stamp](/concepts/location-stamps) comes from a proof-of-location system, wrapped in a [location proof plugin](/concepts/location-proof-structure#location-proof-plugins). + +// callout: Location verification is ubiquitous online, but it is usually ad hoc or unverifiable. Things ranging from GeoIP lookups, scanning a QR code, inputting a passphrase, checking in with an event host, even submitting a bank statement as proof-of-address... the Location Proof Framework is intended to make these different techniques more transparent and legible ... + +## Categories of proof-of-location systems + +Proof-of-location systems span a wide range of approaches, from hardware-based measurement to social attestation: + +| Category | Mechanism | Example | Strengths | Limitations | +|----------|-----------|---------|-----------|-------------| +| **Near-field machine** | Physical proximity verification via short-range signals | RFID, NFC, Bluetooth beacons | Hard to forge without physical presence | Short range; requires infrastructure | +| **Network machine** | Position derived from network measurements | Time of Flight, TDOA, latency triangulation | Independent of device; based on physics | Requires distributed infrastructure | +| **Sensor data** | Location inferred from environmental readings | Magnetometer signatures, image/audio analysis | Rich contextual evidence | Computationally expensive to verify, difficult to detect generated sensor data | +| **Delegated** | Trusted third party attests to location | Notarized presence, institutional witness | Leverages existing trust relationships | Only as trustworthy as the delegate | +| **Social** | Peer confirmations of co-location | Mutual attestation, group check-in | Distributed trust; no infrastructure needed | Collusion risk | +| **Authority-based** | Authorized entity confirms location | Government agency, licensed surveyor | High institutional trust | Centralized; requires authority access | +| **Legal** | Location established through legal process | Court records, notarized documents, affadavits | Strong evidentiary weight, legal liability of fraud | Slow; expensive; not real-time | + +## Current state of the field + +Honesty matters here: few "hard" proof-of-location systems exist at scale today, and even fewer are decentralized. Most deployed location infrastructure (GPS, Wi-Fi positioning, cell tower triangulation) was designed for navigation, not proof. These systems tell you where you are but don't produce cryptographically verifiable evidence that you were there. + +The proof-of-location systems that do exist with meaningful cryptographic properties — hardware attestation, network latency triangulation, secure enclave readings, [Galileo's OSNMA authentication feature](https://www.gsc-europa.eu/galileo/services/galileo-open-service-navigation-message-authentication-osnma) — are still maturing. Each has real limitations and known attack vectors. + +That said, significant value comes from "softer" proof-of-location systems too. Even a single device attestation with sensor readings, while not unbreakable, raises the cost of forgery substantially compared to self-reported GPS. And combining multiple independent sources — even individually weak ones — creates meaningful assurance through cross-correlation. Our vision is to build a community ecosystem of location proof plugins, and over time enhance our capability to create location proofs further and further up the certainty spectrum. + +## Available plugins + +v0 of the Astral Protocol is launching with limited support for these proof-of-location systems through [location proof plugins](/plugins/overview): + + + + Device attestation + sensor fusion. Uses iOS Secure Enclave and Android hardware keystore to attest to device sensor readings (GPS, Wi-Fi, cellular, magnetometer). Trust comes from device hardware integrity. + + + Infrastructure verification. Uses UDP latency triangulation across a distributed challenger network. Trust comes from the speed of light — you can't fake being close to multiple geographically distributed nodes simultaneously. + + + +The [Mock plugin](/plugins/mock) is available for development and testing. + +We are actively [researching](https://github.com/location-proofs/research) many more location proofs plugins, and invite collaboration here. + +## Building new plugins + +The location proof plugin interface is extensible by design. If you have a proof-of-location system that isn't covered by existing plugins, you can [build a custom plugin](/plugins/custom) that implements the standard interface. + + + Evidence from a single proof-of-location system + + +--- + +**See also:** +- [Plugins overview](/plugins/overview) — the full plugin ecosystem +- [ProofMode plugin](/plugins/proofmode) — device attestation details +- [WitnessChain plugin](/plugins/witnesschain) — network verification details diff --git a/concepts/policy-attestations.mdx b/concepts/policy-attestations.mdx deleted file mode 100644 index 48e7598..0000000 --- a/concepts/policy-attestations.mdx +++ /dev/null @@ -1,220 +0,0 @@ ---- -title: "Policy Attestations" -description: "Signed computation results for offchain and onchain use" ---- - - - **Research Preview** — Under active development. - - -# Policy Attestations - -Policy Attestations are the **signed outputs** of geospatial operations. They are signed by the Astral signing key, which is held exclusively inside the TEE (trusted execution environment). This is how you know the computation was performed correctly — only code running inside the TEE can produce valid signatures. - - - When submitted onchain via EAS resolvers, policy attestations can trigger any smart contract logic on EVM-compatible chains. - - -## What is a Policy Attestation? - -When you call a compute operation, Astral returns a Policy Attestation: - - - **Code snippets need testing** — Verify against actual implementation before use. - - -```typescript -const result = await astral.compute.within({ - geometry: userLocationUID, - target: landmarkUID, - radius: 500, - chainId: 84532, - schema: SCHEMA_UID, - recipient: userAddress -}); - -// result is a PolicyAttestationResult -console.log(result.result); // true -console.log(result.operation); // "within" -console.log(result.attestation); // { uid, schema, signature, ... } -``` - -The attestation proves: -- The computation was performed by the Astral signing key -- The inputs were exactly as specified -- The result is accurate - -## Schema Types - -Policy Attestations use per-result-type schemas. The SDK auto-selects based on the operation. - -### BooleanPolicyAttestation - -For predicates (`contains`, `within`, `intersects`): - -```solidity -// Schema: "bool result, bytes32[] inputRefs, uint64 timestamp, string operation" -struct BooleanPolicyAttestation { - bool result; // The boolean result - bytes32[] inputRefs; // Input references (UIDs or hashes) - uint64 timestamp; // When computation was performed - string operation; // "contains", "within", "intersects" -} -``` - -### NumericPolicyAttestation - -For measurements (`distance`, `length`, `area`): - -```solidity -// Schema: "uint256 result, string units, bytes32[] inputRefs, uint64 timestamp, string operation" -struct NumericPolicyAttestation { - uint256 result; // Scaled integer (centimeters) - string units; // "meters" or "square_meters" - bytes32[] inputRefs; // Input references - uint64 timestamp; // When computation was performed - string operation; // "distance", "length", "area" -} -``` - -### GeometryPolicyAttestation (Future) - -For transformations (`buffer`, `centroid`, `union`): - -```solidity -// Schema: "bytes geometry, string geometryType, bytes32[] inputRefs, uint64 timestamp, string operation" -struct GeometryPolicyAttestation { - bytes geometry; // Encoded geometry result - string geometryType; // "Point", "Polygon", etc. - bytes32[] inputRefs; // Input references - uint64 timestamp; // When computation was performed - string operation; // "buffer", "centroid", "union" -} -``` - -## Input References - -The `inputRefs` array contains a `bytes32` reference for each input: - -| Input Type | Reference Value | -|------------|-----------------| -| Location Attestation UID | The UID itself | -| Offchain Attestation | The attestation UID | -| Raw GeoJSON | `keccak256(abi.encode(geojson))` | - -This enables verification that specific inputs were used: - -```solidity -// In your resolver -(bool result, bytes32[] memory inputRefs, , ) = abi.decode(...); - -// Verify the expected location was checked -require(inputRefs[1] == EXPECTED_LANDMARK_UID, "Wrong location checked"); -``` - - - For raw GeoJSON, the hash allows verification if the original geometry is known, but does not reveal the geometry itself. - - -## SDK Return Object - -```typescript -interface PolicyAttestationResult { - // Decoded result (convenience) - result: T; // boolean | number | GeoJSON.Geometry - units?: string; // For measurements - operation: string; // Operation name - timestamp: number; // Unix timestamp - inputRefs: string[]; // Input references - - // Full attestation data - attestation: { - uid: string; // EAS UID (if submitted onchain) - schema: string; // Schema UID - attester: string; // Astral signer address - recipient: string; // Developer-specified recipient - data: string; // ABI-encoded attestation data - signature: string; // EIP-712 signature - }; - - // For delegated onchain submission - delegatedAttestation: { - signature: string; // Astral's signature - attester: string; // Astral's address - deadline: number; // Signature expiry - }; -} -``` - -## Using Policy Attestations - -### Offchain - -Use the result directly in your application: - -```typescript -const result = await astral.compute.within({ - geometry: uid1, - target: uid2, - radius: 500, - chainId: 84532, - schema: SCHEMA_UID -}); - -if (result.result) { - // User is nearby — enable feature - enableFeature(); -} -``` - -### Onchain - -Submit using [delegated attestation](/sdk/eas): - -```typescript -const result = await astral.compute.within({ - geometry: uid1, - target: uid2, - radius: 500, - chainId: 84532, - schema: RESOLVER_SCHEMA_UID, - recipient: userAddress -}); - -// Submit to EAS — triggers your resolver -const tx = await astral.eas.submitDelegated(result.delegatedAttestation); -await tx.wait(); -``` - - - To connect policy attestations to smart contract logic, you need to [register an EAS resolver](/concepts/eas-resolvers) with your schema first. - - -## Result Scaling - -Numeric results are stored as scaled integers for determinism: - -| Original | Stored | To Recover | -|----------|--------|------------| -| 523.45 meters | 52345 | divide by 100 | -| 1234.5678 m² | 12345678 | divide by 10000 | - -```solidity -// In your resolver -(uint256 resultCm, , , , ) = abi.decode(...); -uint256 meters = resultCm / 100; -``` - -## Attestation Expiry - -The `delegatedAttestation.deadline` indicates when the signature expires. Submissions after the deadline will fail. - -```typescript -if (Date.now() / 1000 < result.delegatedAttestation.deadline) { - await astral.eas.submitDelegated(result.delegatedAttestation); -} -``` - - - Learn how to gate smart contracts with Policy Attestations - diff --git a/concepts/privacy.mdx b/concepts/privacy.mdx new file mode 100644 index 0000000..8fbfdf7 --- /dev/null +++ b/concepts/privacy.mdx @@ -0,0 +1,82 @@ +--- +title: "Privacy" +description: "Privacy properties of verification and computation" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Privacy + +Privacy is designed into Astral from the start — not bolted on afterward. The [TEE](/concepts/astral-location-services) provides meaningful privacy guarantees today, and zero-knowledge circuits for location proof verification and geospatial computations are an active research direction for stronger properties in the future. + +## TEE Privacy Guarantees + +The Trusted Execution Environment processes location data inside hardware-isolated memory that the operator cannot inspect, even with physical access to the machine: + +**What stays private:** +- Raw input coordinates — the exact lat/lng values submitted +- Exact geometries — polygon boundaries, line paths, and other spatial data used in computation +- Location stamp signals — the raw evidence data from proof-of-location systems + +These values exist only inside the TEE during computation and are discarded after the result is signed. + +**What is visible:** +- The signed result — a boolean, numeric value, or credibility vector +- The operation type — which computation was performed +- Input references — hashes of the inputs, not the raw data itself + +However, in v0 the signed results **do include input data in plaintext**. Compute results can carry the full location claim and credibility vector via `proofInputs`, and verified location proofs include the original proof with all stamps and claim data. This means anyone who receives a signed result can see the raw location inputs. + + + v0 does not strip input data from signed results. A privacy-preserving output mode — returning only the answer, operation type, and hashed input references — is planned. See [astral-location-services#57](https://github.com/AstralProtocol/astral-location-services/issues/57) for progress. + + +## Information Leakage From Results + +The result itself may reveal information about the inputs. This is inherent to the computation, not a limitation of the privacy model: + +| Operation | What the result reveals | +|-----------|------------------------| +| `contains` (true) | The point is somewhere inside the polygon | +| `within` (true, 500m) | The point is within 500m of the target | +| `distance` (exact value) | The precise distance between two geometries | + +More specific operations leak more. A `contains` check against a country-sized polygon reveals less than a `within` check with a 10-meter radius. + +## Spatial and Temporal Uncertainty as Privacy Tools + +The [uncertainty tradeoff](/concepts/location-claims#the-uncertainty-tradeoff) in location claims has a privacy dimension. Broader spatial bounds (larger radius) and wider temporal bounds reveal less about exact location and timing. Applications that want to preserve user privacy can intentionally use coarser claims — "was this user in San Francisco sometime today?" rather than "was this user within 5m 37.7749°N 122.4194°W at 14:32:07?" + +This isn't a hack — it's a principled privacy-preserving approach. If the application only needs to know "roughly where, roughly when," there's no reason to collect or process exact coordinates. + +## ZK Location Proofs (Research) + +Zero-knowledge proofs would allow verification of location claims without revealing the underlying location data to anyone — including the verifier. A ZK location proof could prove "I was inside this boundary" without revealing where inside the boundary, or even what the boundary was. + +| Property | TEE (today) | ZK (future) | +|----------|------------|-------------| +| Raw inputs hidden from operator | Yes | Yes | +| Raw inputs hidden from verifier | No | Yes | +| No trusted hardware required | No | Yes | +| Verification without re-execution | No | Yes | +| Production-ready | Yes | Not yet | + +ZK location proofs are an active area of research. The challenge is constructing efficient circuits for geospatial operations — polygon containment and distance calculations are expensive to express in arithmetic circuits. This is future work, not a current capability. + +## TEE Limitations + +The TEE provides strong but not absolute privacy: + +- **Hardware trust** — You are trusting that the TEE hardware (Intel SGX / AMD SEV) correctly isolates the enclave. Side-channel attacks on TEEs are an active area of security research. +- **Result leakage** — As described above, the result itself carries information about the inputs. +- **Input reference hashes** — Hashed input references are visible. If an observer knows the possible input space, they could attempt to match hashes (though this is computationally expensive for arbitrary geometries). + + + Walk through common workflows step by step + + +--- + +**See also:** +- [Trust model](/trust-model/architecture) — what's verified vs. what you're trusting +- [Astral Location Services](/concepts/astral-location-services) — TEE architecture details diff --git a/concepts/signed-results.mdx b/concepts/signed-results.mdx new file mode 100644 index 0000000..8a40db0 --- /dev/null +++ b/concepts/signed-results.mdx @@ -0,0 +1,71 @@ +--- +title: "Signed Results" +description: "Output formats from verification and computation — and how to use them" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Signed Results + +Both the [Verify](/concepts/verify) and [Compute](/concepts/compute) endpoints return signed results — cryptographically signed records of what was computed, what inputs were used, and what the answer was. The signature comes from the Astral signing key, held exclusively inside the [TEE](/concepts/astral-location-services). + +## What a Signed Result Proves + +A signed result proves three things: + +1. **Correct execution** — The computation was performed by attested code inside the TEE +2. **Specific inputs** — The result references the exact inputs that were used (via hashes or UIDs) +3. **Authentic output** — The signing key that produced the signature cannot be extracted by the operator + +Any downstream consumer can verify these properties by checking the signature against the known Astral public key — without re-executing the computation or trusting an intermediary. + +## Result Types + +The signed result format depends on the operation: + +| Operation type | Result schema | Result value | +|---------------|--------------|-------------| +| **Predicates** (contains, within, intersects) | Boolean | `true` or `false` | +| **Measurements** (distance, area, length) | Numeric | Scaled integer (centimeters or cm²) | +| **Verification** | Credibility | Credibility vector with dimensional scores | + +Each result also includes: input references (UIDs or hashes), a timestamp, the operation name, and the Astral signing key's signature. + +## Using Signed Results Offchain + +A signed result is immediately usable in any application: + +- **Agent workflows** — An autonomous agent branches on the spatial answer. The signed result is the audit trail. +- **Backend storage** — Store the signed result as evidence alongside the business action it triggered. +- **Compliance reports** — The signed result proves what was computed and when, with cryptographic backing. +- **Peer-to-peer** — Share the signed result with a counterparty who can verify it independently. + +A verified location proof (from the verify endpoint) is valuable on its own — it doesn't need to flow into a compute operation. Many applications only need to know "how credible is this location claim?" and can act on that directly. + +## Using Signed Results Onchain + +Signed results can be submitted onchain via EAS (Ethereum Attestation Service) delegated attestations. The flow: + +1. **Astral signs** the result inside the TEE +2. **The developer submits** the signed result onchain with Astral's signature (developer pays gas) +3. **EAS verifies** the signature and records Astral as the attester +4. **Resolver contracts** execute business logic based on the result + +This is the **delegated attestation pattern** — Astral produces the attestation offchain, and the developer submits it when ready. The signed result includes a deadline for submission; after the deadline, the signature expires. + +Resolver contracts can verify that the result came from Astral by checking `attestation.attester == astralSigner`, and can inspect the input references to confirm the expected locations were checked. + +## Not Every Result Needs to Go Onchain + +The onchain path is available but not required. Most applications will use signed results offchain — the cryptographic signature provides verifiability regardless of whether the result is submitted to a blockchain. + + + Privacy properties of the system + + +--- + +**See also:** +- [SDK: EAS module](/sdk/eas) — submitting delegated attestations onchain +- [SDK: Compute module](/sdk/compute) — working with compute results +- [API: Types](/api-reference/types) — result type schemas diff --git a/concepts/verifiable-computation.mdx b/concepts/verifiable-computation.mdx deleted file mode 100644 index dd20eaa..0000000 --- a/concepts/verifiable-computation.mdx +++ /dev/null @@ -1,177 +0,0 @@ ---- -title: "Verifiable Computation" -description: "How EigenCompute provides trust in geospatial operations" ---- - - - **Research Preview** — Under active development. - - -# Verifiable Computation - -Astral Location Services run in a **Trusted Execution Environment (TEE)** via EigenCompute, providing cryptographic guarantees that computations were performed correctly. - -## The Verification Pipeline - -Building trustworthy location-based smart contracts requires verification at multiple stages: - -```mermaid -graph LR - A["1. Location Verification
(Where is the user?)
In development"] --> B["2. Geospatial Computation
(Policy evaluation)
✓ Astral Location Services"] - B --> C["3. Onchain Verification
(Smart contract logic)
✓ EAS Resolvers"] -``` - -**Astral Location Services solve the middle step** — verifiable geospatial computation. Given location inputs, we can provably check spatial relationships and produce signed attestations for onchain use. - - - **What about step 1?** Verifying *where* a user actually is remains an open problem. GPS is spoofable. We're developing the [Location Proof framework](https://collective.flashbots.net/t/towards-stronger-location-proofs/5323) to address this. Location proofs will plug into Astral Location Services to complete the pipeline. - - -## Why Verification Matters - -Without verification, you're trusting a black box. Anyone could claim "the user is nearby" without proof. - -With verifiable computation, the signed attestation proves the result came from executing specific code on specific inputs inside a TEE. Smart contracts can verify that the Astral signing key performed the computation correctly, not just that someone *claims* it did. - -## EigenCompute - -[EigenCompute](https://blog.eigencloud.xyz/eigencloud-brings-verifiable-ai-to-mass-market-with-eigenai-and-eigencompute-launches/) is part of the EigenCloud ecosystem. It provides: - - - - Code runs in a hardware-isolated environment that even the operator can't tamper with - - - Hardware-generated proof that specific code executed on specific inputs - - - Same inputs always produce same outputs — reproducible and auditable - - - Path to additional verifiability (ZK proofs, AVS consensus) - - - -## The Execution Model - -```mermaid -flowchart TB - subgraph TEE["EigenCompute TEE Environment"] - subgraph Container["Docker Container"] - subgraph Engine["Astral Geospatial Policy Engine"] - E1["Validates input attestations"] - E2["Executes PostGIS queries"] - E3["Signs results with TEE-held key"] - end - subgraph DB["PostgreSQL + PostGIS"] - D1["All spatial computations"] - D2["Ephemeral, no persistent state"] - end - Engine --> DB - end - G1["TEE guarantees:"] - G2["• Code hasn't been modified"] - G3["• Inputs weren't tampered with"] - G4["• Outputs came from executing that code"] - end -``` - - - Verify EigenCompute details against the [official EigenCompute documentation](https://blog.eigencloud.xyz/eigencloud-brings-verifiable-ai-to-mass-market-with-eigenai-and-eigencompute-launches/). Hardware attestation (remote attestation) specifics may vary. - - -## Verifiability Properties - -| Property | How It's Achieved | -|----------|-------------------| -| **Input Integrity** | Attestation signatures verified at TEE boundary before processing | -| **Execution Integrity** | TEE ensures code runs as deployed, can't be modified | -| **Output Authenticity** | Signing key held inside TEE, can't be extracted | -| **Determinism** | Stateless model + fixed precision = same inputs → same outputs | - -## The Astral Signing Key - -The service holds a signing key **inside** the TEE: - -- Key is generated within the TEE or securely provisioned -- Cannot be extracted by the operator -- All Policy Attestations are signed with this key -- Resolver contracts verify `attestation.attester` matches the known Astral signer - -```solidity -// In your resolver -require(attestation.attester == ASTRAL_SIGNER, "Not from Astral"); -``` - -## Trust Model - -### Current (MVP) - -Why can you trust the computation results? - -- **Open source code**: The compute service code is public and auditable -- **TEE execution**: Code runs inside EigenCompute's trusted execution environment -- **Remote attestation**: EigenCompute provides hardware attestation that the expected code is running -- **Deterministic operations**: Same inputs always produce same outputs (additional testing in progress) - -### Future Enhancements - - - - Multiple independent operators run the computation. Results must match to be accepted. No single operator can lie. - - - Cryptographic proof that the computation was correct. Verifiable by anyone without trusting the prover. - - - Multi-party computation for attestation signing. No single party holds the full key. - - - -## Why This Matters - -Consider a location-gated NFT. The question is: how do we check "is the user within 500m of the Eiffel Tower?" in a way that smart contracts can trust? - -**The computation problem (what Astral Location Services solve):** - -If we have location data for the user and the Eiffel Tower, how do we verifiably check if they're close enough? The geospatial policy engine runs the computation in a TEE and produces a signed attestation: - -``` -Location A + Location B → TEE (PostGIS) → Signed attestation: "distance = 450m" -``` - -**The input problem (location proofs, still being developed):** - -Even with verifiable computation, users can lie about their location. GPS is spoofable. The attestation proves the *computation* was correct, not that the *inputs* were honest. - -This is why we're developing the [Location Proof framework](https://collective.flashbots.net/t/towards-stronger-location-proofs/5323) — to provide evidence-based location claims that can feed into Astral Location Services for a fully verifiable pipeline. - - - **Be upfront with your users**: Until location proof plugins are integrated, location verification relies on trusting the user's GPS. Astral Location Services provides verifiable *computation*, not verifiable *location*. - - -## Why Build the Geospatial Layer First? - -Location proofs become useful when they can trigger onchain actions through geospatial policies. Astral Location Services apply those policies — checking if a location is inside a boundary, within range of a target, or satisfies other spatial constraints. - -By building the geospatial policy infrastructure now, we can experiment with use cases and iterate while developing the [Location Proof plugins](https://collective.flashbots.net/t/towards-stronger-location-proofs/5323). As proof mechanisms mature, they plug directly into the existing pipeline. - -## Determinism Guarantees - -To ensure reproducibility: - -- **Precision**: Results rounded to centimeter precision before signing -- **Stateless**: No persistent state between requests -- **Fixed algorithms**: PostGIS version pinned in container - -```typescript -// Same inputs always produce same outputs -const result1 = await astral.compute.distance(uid1, uid2); -const result2 = await astral.compute.distance(uid1, uid2); - -result1.result === result2.result // Always true -``` - - - See the full architecture - diff --git a/concepts/verify.mdx b/concepts/verify.mdx new file mode 100644 index 0000000..890f33d --- /dev/null +++ b/concepts/verify.mdx @@ -0,0 +1,57 @@ +--- +title: "Verify" +description: "The verification endpoint — submit a location proof, get a verified location proof" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Verify + +The Verify service accepts a [location proof](/concepts/location-proof-structure) and returns a **verified location proof** — the original proof, a [credibility vector](/concepts/location-proof-evaluation#the-credibility-vector), and a signed EAS attestation. It's a hosted, verifiable implementation of the [evaluation function](/concepts/location-proof-evaluation). This page describes *how the service works*; the evaluation page describes *what is checked and why*. + +## How It Works + +When a location proof is submitted to the verify endpoint: + +1. **Plugin resolution** — The service identifies which [location proof plugins](/plugins/overview) produced each [location stamp](/concepts/location-stamps) in the proof and loads the appropriate verification logic. + +2. **Stamp verification** — Each location stamp is verified independently: signature validation, structural checks, and signal consistency assessment. Verification is plugin-specific — each plugin knows how to check its own evidence. + +3. **Cross-correlation** (multi-stamp location proofs) — For location proofs with multiple location stamps from independent proof-of-location systems, the service assesses independence and agreement across sources. + +4. **Claim assessment** — The service evaluates how well the evidence from the location stamps supports the [location claim](/concepts/location-claims): spatial overlap, temporal overlap, and overall consistency. + +5. **Credibility vector generation** — The per-stamp results, correlation analysis, and claim assessment are combined into a structured credibility vector with spatial, temporal, validity, and independence dimensions. + +All of this happens inside the [TEE](/concepts/astral-location-services), which means the evaluation logic is attested and the signing key is protected. + +## Two Endpoints + +| Endpoint | Input | Output | Use case | +|----------|-------|--------|----------| +| **[Verify proof](/api-reference/verify/proof)** | Full location proof (claim + stamps) | Verified location proof | Full evaluation — the primary verification path | +| **[Verify stamp](/api-reference/verify/stamp)** | Single location stamp | Stamp validity result | Quick check of a location stamp's internal validity without a claim | + +The stamp endpoint is useful for confirming that a location stamp's signatures are valid and its structure is correct — without the overhead of claim assessment and cross-correlation. + +## What Each Endpoint Returns + +**Verify proof** returns a verified location proof containing: +- **The original proof** — the location claim and location stamps as submitted +- **Credibility vector** — structured assessment across spatial, temporal, validity, and independence dimensions, with per-stamp detail +- **Signed EAS attestation** — the verification result signed by the TEE key, independently verifiable by any downstream consumer + +**Verify stamp** returns a validity result: whether signatures, structure, and signals pass their checks. + +A verified location proof is valuable on its own. It doesn't need to flow into a [Compute](/concepts/compute) operation — many applications only need to know "how credible is this location claim?" without asking any further spatial questions. + + + Geospatial operations inside the TEE + + +--- + +**See also:** +- [API: Verify proof](/api-reference/verify/proof) — endpoint reference +- [API: Verify stamp](/api-reference/verify/stamp) — stamp verification endpoint +- [SDK: Location proofs](/sdk/location-proofs) — programmatic verification diff --git a/guides/agent-integration.mdx b/guides/agent-integration.mdx new file mode 100644 index 0000000..7b412a1 --- /dev/null +++ b/guides/agent-integration.mdx @@ -0,0 +1,181 @@ +--- +title: "Agent Integration" +description: "Using Astral as a spatial oracle in agent workflows" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Agent integration + +Autonomous agents need spatial reasoning — is this delivery within the geofence? How far is the nearest facility? Does this route intersect a restricted zone? Astral provides verified spatial answers that agents can use in decision-making. + +## The pattern + +The integration pattern is straightforward: the agent needs a spatial answer, calls Astral, gets a signed result, and uses the result in its decision logic. + +```mermaid +graph LR + A[Agent needs spatial answer] --> B[Calls Astral API] + B --> C[Gets signed result] + C --> D[Uses result in decision] +``` + +The signed result provides an audit trail. Anyone reviewing the agent's decisions can verify that the spatial reasoning was based on correctly computed data, not fabricated or hallucinated. + +## Raw HTTP from any framework + +Astral is a REST API, so any language or framework that can make HTTP requests works. No SDK required. + +### Python + +```python +import httpx + +response = httpx.post("https://api.astral.global/compute/distance", json={ + "from": {"type": "Point", "coordinates": [2.2945, 48.8584]}, + "to": {"type": "Point", "coordinates": [2.3522, 48.8566]}, + "chainId": 84532 +}) + +result = response.json() +distance_meters = result["result"] +``` + +### TypeScript + +```typescript +const response = await fetch('https://api.astral.global/compute/distance', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + from: { type: 'Point', coordinates: [2.2945, 48.8584] }, + to: { type: 'Point', coordinates: [2.3522, 48.8566] }, + chainId: 84532 + }) +}); + +const result = await response.json(); +const distanceMeters = result.result; +``` + +## Using the result in decisions + +Once the agent has a signed spatial answer, it can branch on the result: + +```python +result = response.json() + +if result["result"] < 500: + # Courier is within 500 meters of the delivery address + proceed_with_delivery_confirmation(result) +else: + # Too far — wait or reroute + schedule_retry(result["result"]) +``` + +For boolean operations like containment or intersection, the result is `true` or `false`: + +```python +response = httpx.post("https://api.astral.global/compute/contains", json={ + "container": geofence_polygon, + "geometry": {"type": "Point", "coordinates": agent_location}, + "chainId": 84532 +}) + +result = response.json() + +if result["result"]: + # Agent is inside the geofence + execute_geofenced_action() +else: + # Agent is outside — restricted + log_boundary_violation() +``` + +## Example: delivery verification agent + +Here is a complete agent workflow that checks whether a courier has arrived at the delivery address. The agent polls the courier's location and confirms delivery when the courier is close enough. + +```python +import httpx +import time + +ASTRAL_URL = "https://api.astral.global" +DELIVERY_RADIUS_METERS = 100 + +def check_delivery_proximity( + courier_coords: list[float], + destination_coords: list[float], + chain_id: int = 84532 +) -> dict: + """Check if courier is within delivery radius of destination.""" + response = httpx.post(f"{ASTRAL_URL}/compute/distance", json={ + "from": {"type": "Point", "coordinates": courier_coords}, + "to": {"type": "Point", "coordinates": destination_coords}, + "chainId": chain_id + }) + response.raise_for_status() + return response.json() + + +def delivery_agent( + courier_coords: list[float], + destination_coords: list[float] +) -> dict: + """ + Delivery verification agent. + Checks proximity and returns a decision with the signed result. + """ + result = check_delivery_proximity(courier_coords, destination_coords) + distance = result["result"] + + if distance <= DELIVERY_RADIUS_METERS: + return { + "decision": "confirm_delivery", + "distance_meters": distance, + "signed_result": result, + "reason": f"Courier is {distance:.1f}m from destination (within {DELIVERY_RADIUS_METERS}m threshold)" + } + + return { + "decision": "wait", + "distance_meters": distance, + "signed_result": result, + "reason": f"Courier is {distance:.1f}m from destination (need to be within {DELIVERY_RADIUS_METERS}m)" + } + + +# Run the agent +decision = delivery_agent( + courier_coords=[2.3522, 48.8566], + destination_coords=[2.3525, 48.8568] +) + +print(f"Decision: {decision['decision']}") +print(f"Reason: {decision['reason']}") +``` + +The `signed_result` in the decision object contains the full Astral response, including the cryptographic signature. This means the decision is auditable — anyone can verify the spatial computation that led to it. + +## Why verified answers matter for agents + +When an agent makes a decision based on spatial data, the signed result provides an audit trail. Anyone can verify that the agent's spatial reasoning was based on correctly computed data. + +This matters for several reasons: + +- **Accountability** — if an agent approves a delivery payout based on location proximity, the signed result proves the distance was computed correctly on the stated inputs. +- **Dispute resolution** — when a decision is challenged, the signed result is independent evidence. It does not depend on trusting the agent's own logs. +- **Composability** — signed results can be submitted onchain to trigger smart contract logic, bridging the agent's offchain reasoning with onchain actions. + +Without verified computation, an agent's spatial claims are self-reported. With Astral, they are independently verifiable. + +## Next steps + + + + Full details on request format and error handling + + + Submit agent decisions onchain via EAS + + diff --git a/concepts/eas-resolvers.mdx b/guides/blockchain-integration.mdx similarity index 51% rename from concepts/eas-resolvers.mdx rename to guides/blockchain-integration.mdx index d7b6af8..717754b 100644 --- a/concepts/eas-resolvers.mdx +++ b/guides/blockchain-integration.mdx @@ -1,29 +1,28 @@ --- -title: "EAS Resolvers" -description: "Gate smart contract actions with location-based policies" +title: "Blockchain Integration" +description: "Submit verified spatial results onchain via EAS" --- - - **Research Preview** — Under active development. - +**Research Preview** — Smart contract patterns need audit. [GitHub](https://github.com/AstralProtocol) -# EAS Resolvers +# Blockchain integration -EAS Resolvers **unlock location-based smart contracts**. They allow you to execute arbitrary logic whenever an attestation is created — turning geospatial policy attestations into triggers for onchain actions. +Astral's signed results can be submitted onchain as EAS attestations, making them available to smart contracts. This guide covers the full blockchain integration flow. - - For background on the Ethereum Attestation Service, see the [EAS documentation](https://docs.attest.org/). - +## What is EAS? + +The [Ethereum Attestation Service](https://docs.attest.org/) (EAS) is an open protocol for making onchain and offchain attestations. Attestations are structured, signed claims — "entity X attests that Y is true." -## What is a Resolver? +EAS supports two storage modes: -In the Ethereum Attestation Service, a **resolver** is a smart contract that gets called whenever an attestation is made against a specific schema. This enables: +- **Onchain attestations** — stored directly in EAS contracts, referenced by UID +- **Offchain attestations** — signed with EIP-712, stored on IPFS or other storage, verifiable without gas costs -- **Validation**: Accept or reject attestations based on custom logic -- **Side Effects**: Execute actions atomically with attestation creation -- **Composability**: Combine attestations with any onchain logic +Astral uses EAS to package signed spatial results as attestations that smart contracts can consume and act on. -## The Pattern +## The pattern + +The core flow: compute a spatial result, sign it, submit it onchain, and let a resolver contract react. ```mermaid sequenceDiagram @@ -35,21 +34,77 @@ sequenceDiagram User->>SDK: compute.within(...) SDK->>Engine: Request computation - Engine-->>SDK: Signed Policy Attestation + Engine-->>SDK: Signed attestation SDK->>EAS: Submit delegated attestation EAS->>Resolver: onAttest() callback Resolver->>Resolver: Verify + Execute logic Resolver-->>EAS: return true/false ``` -## Basic Resolver Contract +## Delegated attestation flow - - **Code snippets need testing** — Verify against actual implementation before use. - +Astral uses EAS's **delegated attestation** pattern to separate signing from submission: + +```mermaid +sequenceDiagram + participant Dev as Developer App + participant SDK as Astral SDK + participant Svc as Compute Service + participant EAS as EAS Contracts + participant Res as Resolver + + Dev->>SDK: compute.within(...) + SDK->>Svc: POST /compute/within + Svc->>Svc: Execute PostGIS operation + Svc->>Svc: Sign attestation + Svc-->>SDK: Signed attestation + delegated sig + SDK->>EAS: Submit with Astral's signature + EAS->>Res: onAttest() callback + Res->>Res: Execute business logic +``` + +The delegated attestation pattern means: + +- **Astral signs** the attestation data offchain (inside the TEE) +- **Developer submits** with Astral's signature (pays gas) +- **EAS verifies** the signature and records Astral as attester +- **Resolver contracts** can verify `attestation.attester == astralSigner` + +```typescript +const result = await astral.compute.within({ + geometry: uid1, + target: uid2, + radius: 500, + chainId: 84532, + schema: RESOLVER_SCHEMA_UID, + recipient: userAddress +}); + +// Submit to EAS — triggers your resolver +const tx = await astral.eas.submitDelegated(result.delegatedAttestation); +await tx.wait(); +``` + +The `delegatedAttestation.deadline` indicates when the signature expires. Submissions after the deadline will fail: + +```typescript +if (Date.now() / 1000 < result.delegatedAttestation.deadline) { + await astral.eas.submitDelegated(result.delegatedAttestation); +} +``` + +## Writing a resolver contract + +In EAS, a **resolver** is a smart contract that gets called whenever an attestation is made against a specific schema. This enables: + +- **Validation** — accept or reject attestations based on custom logic +- **Side effects** — execute actions atomically with attestation creation +- **Composability** — combine attestations with any onchain logic + +### Basic resolver (LocationGatedAction) - **About the Astral signer**: The `astralSigner` address is the key that signs policy attestations inside the TEE. Signer management (multisig, key rotation, etc.) is on the roadmap. See [Security](/resources/security) for more details. + **About the Astral signer**: The `astralSigner` address is the key that signs attestations inside the TEE. Signer management (multisig, key rotation, etc.) is on the roadmap. See [Security](/resources/security) for more details. ```solidity @@ -108,9 +163,9 @@ contract LocationGatedAction is SchemaResolver, Ownable { } ``` -## Common Patterns +### Common patterns -### NFT Minting +#### NFT minting ```solidity contract LocationNFT is LocationGatedAction, ERC721 { @@ -125,7 +180,7 @@ contract LocationNFT is LocationGatedAction, ERC721 { } ``` -### Token Distribution +#### Token distribution ```solidity contract LocationAirdrop is LocationGatedAction { @@ -138,7 +193,7 @@ contract LocationAirdrop is LocationGatedAction { } ``` -### Access Control +#### Access control ```solidity contract LocationGate is LocationGatedAction { @@ -155,9 +210,9 @@ contract LocationGate is LocationGatedAction { } ``` -### Numeric Policies (Distance-Based) +#### Numeric policies (distance-based) -Use numeric policy attestations (distance, area, length) for more sophisticated logic like [spatial demurrage](https://www.johnx.co/notes/spatial-demurrage) — where transfer fees vary based on distance from a target location. +Use numeric attestations (distance, area, length) for more sophisticated logic like [spatial demurrage](https://www.johnx.co/notes/spatial-demurrage) — where transfer fees vary based on distance from a target location. ```solidity // Decode numeric policy attestation @@ -174,9 +229,9 @@ uint256 distanceKm = distanceCm / 100000; uint256 fee = (amount * distanceKm) / 1000; ``` -## Decoding Attestation Data +### Decoding attestation data -### Boolean Policies +#### Boolean policies ```solidity ( @@ -190,7 +245,7 @@ uint256 fee = (amount * distanceKm) / 1000; ); ``` -### Numeric Policies +#### Numeric policies ```solidity ( @@ -208,28 +263,101 @@ uint256 fee = (amount * distanceKm) / 1000; uint256 meters = result / 100; ``` -## Verification Best Practices +## Registering your schema + +Before submitting attestations, register your schema with EAS and point it at your resolver: + +```typescript +import { SchemaRegistry } from '@ethereum-attestation-service/eas-sdk'; + +const schemaRegistry = new SchemaRegistry(SCHEMA_REGISTRY_ADDRESS); + +// Boolean policy schema +const boolSchema = "bool result,bytes32[] inputRefs,uint64 timestamp,string operation"; + +const tx = await schemaRegistry.connect(signer).register({ + schema: boolSchema, + resolverAddress: yourResolver.address, + revocable: false +}); + +const receipt = await tx.wait(); +const schemaUID = receipt.logs[0].args.uid; +``` + +## Onchain vs offchain location records + +Location attestations — the spatial inputs to computations — can be stored onchain or offchain: + + + + - Stored on EAS contracts + - Referenced by UID alone + - Higher gas cost + - Permanent, immutable + + + - Stored on IPFS, servers, etc. + - Referenced by UID + URI + - No gas cost to create + - EIP-712 signed + + + +### Onchain + +```typescript +// Create onchain attestation +const location = await astral.location.create(geojson, { + submitOnchain: true +}); + +// Reference by UID only +await astral.compute.distance(location.uid, otherUID); +``` + +### Offchain + +```typescript +// Create offchain attestation +const location = await astral.location.create(geojson, { + submitOnchain: false, + storage: 'ipfs' // or 'arweave', 'https', etc. +}); + +// Reference by UID + URI +await astral.compute.distance( + { uid: location.uid, uri: location.uri }, + otherUID +); +``` + +## Chain configuration + +Astral supports EAS on multiple EVM-compatible chains. See [Schema registry](/resources/schemas) for schema UIDs by chain. + +## Verification best practices - + Always check `attestation.attester == astralSigner`: ```solidity require(attestation.attester == astralSigner, "Not from Astral"); ``` - + Prevent replay of old attestations: ```solidity require(timestamp > block.timestamp - 1 hours, "Attestation too old"); ``` - + Ensure the right locations were checked: ```solidity require(inputRefs[1] == EXPECTED_LANDMARK_UID, "Wrong location"); ``` - + Prevent reuse of attestations: ```solidity mapping(bytes32 => bool) public usedAttestations; @@ -243,29 +371,9 @@ uint256 meters = result / 100; -## Registering Your Schema - -```typescript -import { SchemaRegistry } from '@ethereum-attestation-service/eas-sdk'; +## Key rotation -const schemaRegistry = new SchemaRegistry(SCHEMA_REGISTRY_ADDRESS); - -// Boolean policy schema -const boolSchema = "bool result,bytes32[] inputRefs,uint64 timestamp,string operation"; - -const tx = await schemaRegistry.connect(signer).register({ - schema: boolSchema, - resolverAddress: yourResolver.address, - revocable: false -}); - -const receipt = await tx.wait(); -const schemaUID = receipt.logs[0].args.uid; -``` - -## Key Rotation - -Resolver contracts should support updating the Astral signer address. For the research preview, we recommend a simple owner-controlled approach: +Resolver contracts should support updating the Astral signer address. For the research preview, a simple owner-controlled approach works: ```solidity function updateAstralSigner(address newSigner) external onlyOwner { @@ -277,7 +385,3 @@ function updateAstralSigner(address newSigner) external onlyOwner { For production deployments, you may want the owner to be a multisig. We plan to provide more graceful key rotation mechanisms in future releases. - - - Learn how EigenCompute provides trust - diff --git a/guides/building-plugins.mdx b/guides/building-plugins.mdx new file mode 100644 index 0000000..e409355 --- /dev/null +++ b/guides/building-plugins.mdx @@ -0,0 +1,284 @@ +--- +title: "Building Verification Plugins" +description: "Connect a new proof-of-location system to Astral" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Building verification plugins + +A plugin connects a proof-of-location (PoL) system to Astral's verification framework. Plugins collect signals from a PoL system, produce location stamps, and verify those stamps. + + + The plugin interface is under active development. The patterns shown here reflect the current design direction, but specifics may change as we iterate on the verification framework. + + +## What is a plugin? + +Proof-of-location systems vary widely — hardware attestation, network triangulation, sensor fusion, institutional records. A plugin is a standardized adapter that translates a specific PoL system's output into the common stamp format that Astral can verify and cross-correlate. + +Each plugin handles three responsibilities: + +1. **Collect signals** from the PoL system (GPS readings, network measurements, device attestations) +2. **Create stamps** from those signals (structured evidence artifacts) +3. **Verify stamps** for authenticity and structural integrity + +## Plugin interface + +```typescript +interface LocationProofPlugin { + name: string; + version: string; + + // Collect raw signals from the PoL system + collectSignals(config: PluginConfig): Promise; + + // Process signals into a location stamp + createStamp(signals: Signal[]): Promise; + + // Verify a stamp's authenticity and structure + verifyStamp(stamp: LocationStamp): Promise; +} + +interface PluginConfig { + timeout: number; + [key: string]: unknown; +} + +interface Signal { + type: string; + value: unknown; + timestamp: number; + source: string; +} + +interface StampVerificationResult { + valid: boolean; + errors: string[]; + details: Record; +} +``` + +## How stamps work + +A stamp is a signed artifact from a PoL system. It encodes the system's conclusion about where and when an event occurred, along with the raw signals that support that conclusion. + +Stamps follow the [Location Protocol](https://github.com/DecentralizedGeo/location-protocol-spec) format: + +```typescript +interface LocationStamp { + // Location data (LP v0.2) + lpVersion: string; + locationType: string; + location: LocationData; // Where evidence indicates the subject was + srs: string; + + // Temporal footprint + temporalFootprint: { start: number; end: number }; + + // Plugin identification + plugin: string; // "proofmode", "witnesschain", etc. + pluginVersion: string; + + // Evidence and signatures + signals: Record; + signatures: Signature[]; +} +``` + +The distinction between a stamp's location and a claim's location is important. The stamp records where the PoL system *observed* the subject. The claim records where the subject *asserts* they were. Verification compares the two. + +## Implementation guide + +Here is a step-by-step walkthrough for building a hypothetical plugin that uses Wi-Fi access point data. + +### Step 1: Define signal collection + +```typescript +import type { LocationProofPlugin, PluginConfig, Signal } from '@decentralized-geo/astral-sdk'; + +const wifiPlugin: LocationProofPlugin = { + name: 'wifi-triangulation', + version: '0.1.0', + + async collectSignals(config: PluginConfig): Promise { + const networks = await scanNearbyNetworks(config.timeout); + + return networks.map((network) => ({ + type: 'wifi-ap', + value: { + bssid: network.bssid, + ssid: network.ssid, + rssi: network.signalStrength, + frequency: network.frequency + }, + timestamp: Date.now() / 1000, + source: 'device-wifi-scan' + })); + }, + + // ... continued below +}; +``` + +### Step 2: Create stamps from signals + +```typescript + async createStamp(signals: Signal[]): Promise { + // Triangulate position from Wi-Fi signals + const position = triangulateFromAccessPoints( + signals.map((s) => s.value as WifiSignal) + ); + + return { + lpVersion: '0.2', + locationType: 'geojson-point', + location: { + type: 'Point', + coordinates: [position.longitude, position.latitude] + }, + srs: 'http://www.opengis.net/def/crs/OGC/1.3/CRS84', + temporalFootprint: { + start: Math.min(...signals.map((s) => s.timestamp)), + end: Math.max(...signals.map((s) => s.timestamp)) + }, + plugin: 'wifi-triangulation', + pluginVersion: '0.1.0', + signals: { + accessPoints: signals.map((s) => s.value), + triangulationMethod: 'weighted-centroid' + } + }; + }, +``` + +### Step 3: Implement stamp verification + +```typescript + async verifyStamp(stamp: LocationStamp): Promise { + const errors: string[] = []; + + // Check plugin identification + if (stamp.plugin !== 'wifi-triangulation') { + errors.push(`Expected plugin 'wifi-triangulation', got '${stamp.plugin}'`); + } + + // Verify signal structure + const signals = stamp.signals as { accessPoints?: unknown[] }; + if (!signals.accessPoints || signals.accessPoints.length < 3) { + errors.push('Wi-Fi triangulation requires at least 3 access points'); + } + + // Verify temporal bounds are reasonable + const duration = stamp.temporalFootprint.end - stamp.temporalFootprint.start; + if (duration > 60) { + errors.push('Signal collection window exceeds 60 seconds'); + } + + // Verify signatures + const signaturesValid = await verifyStampSignatures(stamp); + if (!signaturesValid) { + errors.push('Stamp signatures are invalid'); + } + + return { + valid: errors.length === 0, + errors, + details: { + accessPointCount: signals.accessPoints?.length ?? 0, + collectionDuration: duration, + signaturesChecked: stamp.signatures.length + } + }; + } +``` + +## Registration + +Register your plugin with the Astral SDK so it can be used in stamp collection and verification: + +```typescript +import { AstralSDK } from '@decentralized-geo/astral-sdk'; + +const astral = new AstralSDK({ chainId: 84532 }); + +astral.plugins.register(wifiPlugin); + +// Now you can collect stamps using your plugin +const signals = await astral.stamps.collect({ + name: 'wifi-triangulation', + version: '0.1.0', + config: { timeout: 5000 } +}); + +const stamp = await astral.stamps.create( + { name: 'wifi-triangulation', version: '0.1.0', config: {} }, + signals +); +``` + +## Testing + +Test each plugin responsibility independently: + +```typescript +import { describe, it, expect } from 'vitest'; + +describe('wifi-triangulation plugin', () => { + it('collects signals from nearby networks', async () => { + const signals = await wifiPlugin.collectSignals({ timeout: 5000 }); + + expect(signals.length).toBeGreaterThan(0); + for (const signal of signals) { + expect(signal.type).toBe('wifi-ap'); + expect(signal.timestamp).toBeGreaterThan(0); + } + }); + + it('creates a valid stamp from signals', async () => { + const signals = mockWifiSignals(5); + const stamp = await wifiPlugin.createStamp(signals); + + expect(stamp.lpVersion).toBe('0.2'); + expect(stamp.plugin).toBe('wifi-triangulation'); + expect(stamp.location.type).toBe('Point'); + expect(stamp.location.coordinates).toHaveLength(2); + }); + + it('rejects stamps with fewer than 3 access points', async () => { + const stamp = createMockStamp({ accessPointCount: 2 }); + const result = await wifiPlugin.verifyStamp(stamp); + + expect(result.valid).toBe(false); + expect(result.errors).toContainEqual( + expect.stringContaining('at least 3 access points') + ); + }); +}); +``` + +## Existing plugins + +Two plugins are currently in development: + + + + Device attestation and sensor fusion. Uses Secure Enclave (iOS) or hardware keystore (Android) to sign location observations. + + + Infrastructure verification using UDP latency triangulation and a challenger network. Trust derives from speed-of-light constraints and cryptoeconomic incentives. + + + +Each plugin documents its own threat model and trust assumptions. When building a new plugin, you should clearly document what an attacker would need to do to forge a stamp from your system. + +## Next steps + + + + Understand how stamps combine into verifiable proofs + + + Submit proofs and understand credibility scores + + diff --git a/guides/calling-the-api.mdx b/guides/calling-the-api.mdx new file mode 100644 index 0000000..46e92e2 --- /dev/null +++ b/guides/calling-the-api.mdx @@ -0,0 +1,216 @@ +--- +title: "Calling the API" +description: "Request format, authentication, and response handling" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Calling the API + +This guide covers the mechanics of making requests to Astral — how to format inputs, what comes back, and how to handle errors. + +## Base URL + +| Environment | URL | +|-------------|-----| +| Production | `https://api.astral.global` | +| Local development | `http://localhost:3004` | + +See [Local development](/guides/local-development) for setup instructions. + +## Request format + +All compute endpoints accept `POST` requests with a JSON body. The required fields depend on the operation, but every request needs a `chainId` to identify which chain to sign for. + +### Distance, length + +```json +{ + "from": { "type": "Point", "coordinates": [2.2945, 48.8584] }, + "to": { "type": "Point", "coordinates": [2.3522, 48.8566] }, + "chainId": 84532 +} +``` + +### Contains, intersects + +```json +{ + "container": { + "type": "Polygon", + "coordinates": [[[2.28, 48.85], [2.30, 48.85], [2.30, 48.87], [2.28, 48.87], [2.28, 48.85]]] + }, + "geometry": { "type": "Point", "coordinates": [2.2945, 48.8584] }, + "chainId": 84532 +} +``` + +### Optional fields + +| Field | Description | +|-------|-------------| +| `schema` | EAS schema UID — required if you plan to submit the result onchain | +| `recipient` | Ethereum address to set as the attestation recipient | + +## Geographic feature inputs + +You can pass geographic features in four formats: + +### Raw GeoJSON + +A GeoJSON geometry object directly in the request: + +```json +{ + "from": { "type": "Point", "coordinates": [2.2945, 48.8584] } +} +``` + +### UID string + +A reference to an onchain location attestation: + +```json +{ + "from": "0xabc123...def456" +} +``` + +### UID + URI (offchain attestation) + +A reference to an offchain attestation stored on IPFS or another location: + +```json +{ + "from": { "uid": "0xabc123...", "uri": "ipfs://Qm..." } +} +``` + +### Inline signed attestation + +A full offchain attestation object: + +```json +{ + "from": { "attestation": { "uid": "0x...", "schema": "0x...", "data": "0x..." } } +} +``` + +## Authentication + +No authentication is currently required. Rate limits apply by IP address (100 requests/hour for unauthenticated clients). Wallet-based authentication with higher limits is planned. + +## Response format + +A successful response includes the computed result, proof of computation, and a pre-signed attestation for optional onchain submission. + +```json +{ + "result": 4520.37, + "units": "meters", + "operation": "distance", + "timestamp": 1706400000, + "inputRefs": [ + "0xabc123...input_a_hash", + "0xdef456...input_b_hash" + ], + "attestation": { + "uid": "0x...", + "schema": "0x...", + "attester": "0x...", + "recipient": "0x...", + "data": "0x...", + "signature": "0x..." + }, + "delegatedAttestation": { + "signature": "0x7f8e9d...tee_signature", + "attester": "0x590fdb53ed3f0B52694876d42367192a5336700F", + "deadline": 1706403600 + } +} +``` + +Here is what each field means: + +- **`result`** — The computed answer. A number for distance/area/length, a boolean for contains/within/intersects. +- **`units`** — Unit of measurement (for numeric results). `"meters"` or `"square_meters"`. +- **`operation`** — The spatial operation that was performed. +- **`timestamp`** — Unix timestamp of when the computation ran. +- **`inputRefs`** — Hashes of the input geographic features. These let you verify which inputs were used in the computation. +- **`attestation`** — The full EAS attestation data, including the TEE's cryptographic signature. +- **`delegatedAttestation`** — A pre-signed attestation ready for onchain submission via EAS. The `deadline` indicates when the signature expires. + +## Error handling + +Errors follow [RFC 7807](https://tools.ietf.org/html/rfc7807) (Problem Details for HTTP APIs): + +```json +{ + "type": "https://api.astral.global/errors/invalid-input", + "title": "Invalid Input", + "status": 400, + "detail": "Field 'from' must be a valid GeoJSON geometry or attestation UID" +} +``` + +### Common error types + +| Type | Status | What it means | +|------|--------|---------------| +| `invalid-input` | 400 | Bad request data, missing fields, or invalid geometry | +| `attestation-not-found` | 404 | The UID does not exist on the specified chain | +| `verification-failed` | 401 | Signature verification failed | +| `computation-error` | 500 | The PostGIS operation failed | +| `rate-limited` | 429 | Too many requests — wait and retry | + +### Handling errors in code + +```typescript +const response = await fetch('https://api.astral.global/compute/distance', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + from: { type: 'Point', coordinates: [2.2945, 48.8584] }, + to: { type: 'Point', coordinates: [2.3522, 48.8566] }, + chainId: 84532 + }) +}); + +if (!response.ok) { + const error = await response.json(); + console.error(`${error.title}: ${error.detail}`); + return; +} + +const result = await response.json(); +console.log(`Distance: ${result.result} ${result.units}`); +``` + +## Full example: curl + +```bash +curl -X POST https://api.astral.global/compute/contains \ + -H "Content-Type: application/json" \ + -d '{ + "container": { + "type": "Polygon", + "coordinates": [[[2.28, 48.85], [2.30, 48.85], [2.30, 48.87], [2.28, 48.87], [2.28, 48.85]]] + }, + "geometry": { "type": "Point", "coordinates": [2.2945, 48.8584] }, + "chainId": 84532 + }' +``` + +## Next steps + + + + Use Astral as a spatial oracle in agent workflows + + + Submit signed results onchain via EAS + + + Full endpoint documentation + + diff --git a/guides/local-development.mdx b/guides/local-development.mdx new file mode 100644 index 0000000..ed20271 --- /dev/null +++ b/guides/local-development.mdx @@ -0,0 +1,149 @@ +--- +title: "Local Development" +description: "Run Astral locally for development and testing" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Local development + +This guide walks you through running Astral on your machine. By the end, you will have a working local instance that responds to geocomputation requests. + +## Prerequisites + +- **Node.js 20+** — the service uses the `--env-file` flag, which requires Node 20 +- **Docker** — for PostgreSQL with PostGIS +- **pnpm** — the monorepo uses pnpm workspaces + +## Clone and install + +```bash +git clone https://github.com/AstralProtocol/astral-location-services.git +cd astral-location-services +pnpm install +``` + +## Start PostgreSQL + PostGIS + +The repo includes a development Docker Compose file that runs PostgreSQL with PostGIS: + +```bash +docker compose -f docker-compose.dev.yml up -d +``` + +Verify the database is running: + +```bash +docker compose -f docker-compose.dev.yml ps +``` + +### Port conflicts + +If port 5432 is already in use by another PostgreSQL instance, create a `docker-compose.override.yml` that maps to a different port (e.g., 5434:5432) and update your `DATABASE_URL` accordingly: + +```yaml +# docker-compose.override.yml +services: + postgres: + ports: + - "5434:5432" +``` + +Then set `DATABASE_URL=postgresql://postgres:postgres@localhost:5434/astral` in your environment file. + +## Environment setup + +```bash +cp packages/astral-service/.env.example packages/astral-service/.env.local +``` + +Edit `.env.local` with your configuration. You will need a `SIGNER_PRIVATE_KEY` — any Ethereum private key works for development. You can generate one with `openssl rand -hex 32`. + +The important fields: + +| Variable | Description | Example | +|----------|-------------|---------| +| `DATABASE_URL` | PostgreSQL connection string | `postgresql://postgres:postgres@localhost:5432/astral` | +| `PORT` | HTTP server port | `3004` | +| `SIGNER_PRIVATE_KEY` | Ethereum private key for signing results | `0xac0974bec...` | +| `CHAIN_ID` | Default chain ID | `84532` (Base Sepolia) | + +## Start the service + + +The service does not use dotenv. You must pass the env file explicitly using Node's `--env-file` flag. + + +```bash +node --env-file=packages/astral-service/.env.local --import tsx packages/astral-service/src/index.ts +``` + +The `npm run dev` script may not work reliably. The command above is the most reliable way to start the service. + +You should see output indicating the server is listening on the configured port. + +## Health check + +Confirm the service is running: + +```bash +curl http://localhost:3004/health +``` + +A successful response means the service is up and connected to the database. + +## Smoke test + +Run a distance computation between the Eiffel Tower and a point across the Seine: + +```bash +curl -X POST http://localhost:3004/compute/distance \ + -H "Content-Type: application/json" \ + -d '{ + "from": { "type": "Point", "coordinates": [2.2945, 48.8584] }, + "to": { "type": "Point", "coordinates": [2.3522, 48.8566] }, + "chainId": 84532 + }' +``` + +The response includes the distance in meters, a cryptographic signature, and input references. If you see a `result` field with a numeric value, everything is working. + +## Platform notes + + + + PostGIS Docker images may need an explicit platform flag. If the container fails to start, add `platform: linux/amd64` to the postgres service in your compose file: + + ```yaml + services: + postgres: + platform: linux/amd64 + image: postgis/postgis:16-3.4 + ``` + + This runs under Rosetta emulation, which is slower but functional. + + + + These ports may collide with other local services: + + | Port | Used by | Common conflict | + |------|---------|----------------| + | 5432 | PostgreSQL | Other Postgres instances, Homebrew Postgres | + | 3004 | Astral service | Other dev servers | + | 3000-3003 | Other monorepo packages | Next.js, React dev servers | + + Use the `docker-compose.override.yml` approach for database port conflicts, and change `PORT` in `.env.local` for service port conflicts. + + + +## Next steps + + + + Learn the request format and response structure + + + Your first verified spatial computation + + diff --git a/guides/verifying-location-proofs.mdx b/guides/verifying-location-proofs.mdx new file mode 100644 index 0000000..d9205dc --- /dev/null +++ b/guides/verifying-location-proofs.mdx @@ -0,0 +1,248 @@ +--- +title: "Verifying Location Proofs" +description: "Submit location proofs and understand credibility scores" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# 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 +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"` | + + + The `radius` field is required. Every location claim involves spatial uncertainty. Claiming a smaller radius requires stronger evidence to achieve the same credibility score. + + +## Collecting stamps + +Stamps are evidence from proof-of-location plugins. Each plugin collects signals from its PoL system and produces a stamp: + +```typescript +import { AstralSDK } from '@decentralized-geo/astral-sdk'; + +const astral = new AstralSDK({ chainId: 84532 }); + +// Collect signals from ProofMode (device attestation) +const proofmodeSignals = await astral.stamps.collect({ + name: 'proofmode', + version: '0.1.0', + config: { timeout: 5000 } +}); + +// Create and sign the stamp +const unsignedStamp = await astral.stamps.create( + { name: 'proofmode', version: '0.1.0', config: {} }, + proofmodeSignals +); +const stamp1 = await astral.stamps.sign(unsignedStamp, deviceSigner); +``` + +For stronger verification, collect stamps from multiple independent systems: + +```typescript +// Collect from a second independent system +const witnessSignals = await astral.stamps.collect({ + name: 'witnesschain', + version: '0.1.0', + config: { timeout: 10000 } +}); + +const unsignedWitnessStamp = await astral.stamps.create( + { name: 'witnesschain', version: '0.1.0', config: {} }, + witnessSignals +); +const stamp2 = await astral.stamps.sign(unsignedWitnessStamp, nodeSigner); +``` + +## Bundling into a proof + +Combine the claim and stamps into a location proof: + +```typescript +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 +const result = await astral.verify.proof(proof); +``` + +Or via raw HTTP: + +```bash +curl -X POST https://api.astral.global/verify/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: + +```json +{ + "credibility": { + "confidence": 0.85, + "stampResults": [ + { + "plugin": "proofmode", + "valid": true, + "spatialAccuracy": 0.9, + "temporalIntegrity": 0.8, + "forgeryResistance": 0.7 + }, + { + "plugin": "witnesschain", + "valid": true, + "spatialAccuracy": 0.85, + "temporalIntegrity": 0.9, + "forgeryResistance": 0.8 + } + ], + "correlation": { + "independence": 0.95, + "agreement": 0.92 + } + }, + "uid": "0xabc123...", + "attester": "0x590fdb53...", + "timestamp": 1706400000 +} +``` + +### Credibility dimensions + +Credibility scores are a multidimensional assessment. Each stamp is evaluated along several dimensions: + +| Dimension | What it measures | +|-----------|-----------------| +| **Spatial accuracy** | How precisely the stamp's observed location matches the claimed location | +| **Temporal integrity** | Whether the stamp's timestamps are consistent and within the claimed time window | +| **Forgery resistance** | How difficult it would be to fabricate this stamp (depends on the PoL system's threat model) | +| **Source independence** | Whether the stamp comes from a system that is independent of other stamps in the proof | + +### Overall confidence + +The top-level `confidence` score (0 to 1) is a summary of the per-stamp assessments and cross-correlation analysis. It reflects the overall strength of evidence for the claim. + + + **Confidence is not probability.** A confidence of 0.85 does not mean "85% chance the claim is true." It means the evidence is strong. Calibrating confidence scores to true probabilities is future work. + + +### 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 +// Single stamp: moderate confidence +const singleResult = await astral.verify.proof( + astral.proofs.create(claim, [proofmodeStamp]) +); +// singleResult.credibility.confidence → 0.7 + +// Multi-stamp from independent systems: higher confidence +const multiResult = await astral.verify.proof( + astral.proofs.create(claim, [proofmodeStamp, witnessStamp]) +); +// multiResult.credibility.confidence → 0.9 +// multiResult.credibility.correlation.independence → 0.95 +``` + +The improvement comes from source independence. Redundant stamps from the same system do not meaningfully increase confidence, but they do not decrease 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 +const verifiedProof = await astral.verify.proof(proof); + +// Use the verified proof as input to a spatial operation +const result = await astral.compute.contains({ + container: approvedZonePolygonUID, + geometry: { verifiedProof: verifiedProof.uid }, + chainId: 84532, + schema: SCHEMA_UID +}); +``` + +## Next steps + + + + Deeper dive into claims, stamps, and the verification model + + + Connect a new proof-of-location system to Astral + + diff --git a/how-it-works.mdx b/how-it-works.mdx index cb5c12c..180d7df 100644 --- a/how-it-works.mdx +++ b/how-it-works.mdx @@ -1,179 +1,119 @@ --- title: "How It Works" -description: "The architecture behind verifiable geospatial computation" +description: "The full pipeline from location evidence to signed results" --- - - **Research Preview** — Architecture is under active development. - +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) -# How Astral Location Services Work +# How it works -Astral provides verifiable geospatial computation by combining three key technologies: **PostGIS** for spatial operations, **EigenCompute TEE** for verifiable execution, and **EAS** for attestation management. +Location data is easy to fake and hard to verify. GPS can be spoofed with a \$20 app. IP geolocation is trivially manipulated. Self-reported coordinates carry no proof of origin. When a delivery platform confirms a drop-off, a compliance system checks a geofence, or an autonomous agent makes a spatial decision — the location data underneath is taken on faith. -## System Architecture +Astral exists to change that. The system provides a pipeline that collects location evidence, bundles it into verifiable artifacts, evaluates its credibility, runs spatial computations on it, and delivers signed results that any downstream system can independently verify. -```mermaid -flowchart LR - subgraph Client["Client"] - App[Developer App] - SDK[Astral SDK] - end +## The Pipeline - subgraph Astral["Astral Services"] - API[API Gateway] - subgraph TEE["EigenCompute TEE"] - Engine[Policy Engine + PostGIS] - end - end +### 1. Collect location evidence - subgraph Chain["Ethereum / Base"] - EAS[EAS Contracts] - Resolver[Resolver Contract] - end +Devices collect signals from **proof-of-location systems** — independent systems that produce evidence about where something is. A phone's secure enclave can attest to sensor readings. A network of infrastructure nodes can triangulate position via latency measurements. Each system has different strengths, weaknesses, and trust properties. - App --> SDK - SDK --> API - API --> Engine - Engine --> SDK - SDK --> EAS - EAS --> Resolver -``` +Each [location proof plugin](/plugins/overview) connects to a proof-of-location system, collects signals, and processes them to produce a [location stamp](/concepts/location-stamps) — an individual piece of evidence about the location of a device, person, asset or event. -## Key Architectural Decisions + + **Why this step matters:** Self-reported location is trivially spoofable. Composing evidence from independent proof-of-location systems raises the cost of forgery — which is the real goal, since absolute certainty about physical location is not achievable. + - - - PostGIS runs **inside** the Docker container, not as an external service. This is essential for verifiable computation in the TEE — no external dependencies means the entire execution environment is attested. - - - Each request brings all required inputs. No persistent state between requests. This ensures determinism and simplifies verification — same inputs always produce same outputs. (Note: additional testing is needed to fully validate determinism across different environments.) - - - Developers submit Policy Attestations onchain using EAS's delegated attestation pattern. Astral signs, developer pays gas, Astral is recorded as the attester. - - +### 2. Create a location proof -## The Computation Flow - - - - Developer's app calls `astral.compute.within(uid1, uid2, 500)` via the SDK - - - The compute service fetches/validates location attestations by UID, or accepts raw GeoJSON - - - PostGIS executes the spatial operation (e.g., `ST_DWithin`) inside the TEE - - - The service creates a Policy Attestation and signs it with the TEE-held key - - - Signed attestation returned to SDK, ready for offchain use or onchain submission - - - -## Verifiable Execution with EigenCompute - -The Geospatial Policy Engine runs in **EigenCompute**, part of the EigenCloud ecosystem: +A **location stamp** is a piece of signed, structured evidence from a single proof-of-location system about an observed location. Each location stamp carries enough information to verify its internal validity: signatures, temporal footprint, and plugin identification. -```mermaid -flowchart LR - subgraph TEE["EigenCompute TEE"] - subgraph Container["Docker Container"] - API[Node.js API] --> PostGIS[PostgreSQL + PostGIS] - PostGIS --> GEOS[GEOS Library] - end - end +One or more location stamps bundle with a **location claim** — an assertion about where and when an event occurred — to form a **location proof**. The location proof is the verifiable artifact: a claim paired with its supporting evidence. - Input[Request] --> API - API --> Output[Signed Attestation] -``` +`proof = {claim, [stamps...]}` - - PostGIS uses [GEOS](https://libgeos.org/) for geometry operations — the same C++ library used by QGIS, GDAL, and most professional geospatial software. See the [GEOS documentation](https://libgeos.org/usage/download/) for more on its reliability and widespread adoption. - + + **Why this step matters:** Attaching structured, composable evidence to claims give people and systems receiving location proofs what they need to verify and assess credibility. A location proof separates the assertion ("I was here") from the evidence ("here's why you should believe me"), which makes both independently evaluable. + -### Verifiability Properties +### 3. Verify the location proof -| Property | How It's Achieved | -|----------|-------------------| -| **Input Verification** | Attestation signatures verified at TEE boundary | -| **Deterministic Computation** | Same inputs always produce same result | -| **Signed Output** | Service key signs Policy Attestation inside TEE | -| **TEE Attestation** | EigenCompute provides hardware attestation of execution | +Submit the location proof to Astral's Verify endpoint. Inside a Trusted Execution Environment (TEE), the system evaluates the location stamp — checking signatures, structure, and signal consistency — then assesses how well the evidence supports the location claim. For multi-stamp location proofs, it also cross-correlates across independent sources. -## Input Resolution +The output is a **credibility vector**: structured scores quantifying how well the evidence supports the claim across multiple dimensions (spatial, temporal, validity, independence, and more — this is an active research area). Location proof verification does not output a binary yes/no — application developers decide what dimensions they value and the threshold they need to meet. -The service accepts multiple input formats: + + **Why this step matters:** Independent, structured evaluation of location evidence. The credibility vector gives applications enough information to make risk-appropriate decisions — a \$10 check-in reward can accept lower confidence than a \$10M physical asset verification. + -| Input Format | Resolution | -|--------------|------------| -| UID string | Fetch from EAS contracts (onchain attestations) | -| `{ uid, uri }` | Fetch from URI, verify UID matches, verify signature | -| `{ attestation }` | Verify signature, extract geometry directly | -| Raw GeoJSON | Use directly, hash for `inputRefs` | +### 4. Compute spatial relationships - - For offchain attestations, the UID is deterministically derived from the attestation data. Even when fetching from HTTPS (not content-addressed), we verify the fetched attestation produces the expected UID. Mismatch = reject. - +Run geospatial operations — distance, containment, intersection, area, length — on location data inside the Astral TEE. The compute endpoints accept raw GeoJSON, signed location records, or verified location proofs as inputs. [PostGIS](https://postgis.net/) (backed by the [GEOS](https://libgeos.org/) library, the same computational geometry engine used by QGIS and other open-source geospatial tools) performs the computation; the TEE signs the result. -## Delegated Attestation Flow + + **Why this step matters:** Verifiable spatial answers. The signed result proves not just "what was the answer" but "the answer was computed correctly by trusted code on these specific inputs." + -```mermaid -sequenceDiagram - participant Dev as Developer App - participant SDK as Astral SDK - participant Svc as Compute Service - participant EAS as EAS Contracts - participant Res as Resolver - - Dev->>SDK: compute.within(...) - SDK->>Svc: POST /compute/within - Svc->>Svc: Execute PostGIS operation - Svc->>Svc: Sign attestation - Svc-->>SDK: Signed attestation + delegated sig - SDK->>EAS: Submit with Astral's signature - EAS->>Res: onAttest() callback - Res->>Res: Execute business logic -``` +### 5. Use the signed result + +The signed result goes wherever it needs to — an autonomous agent's decision loop, a backend database, a compliance report, or a smart contract via an EAS attestation. It carries its own proof of correctness, so any downstream consumer can verify it independently without re-executing the computation or trusting the intermediary. + + + **Why this step matters:** Portable, independently verifiable spatial facts. The result is useful whether it stays offchain or goes onchain. + -The delegated attestation pattern means: -- **Astral signs** the attestation data offchain -- **Developer submits** with Astral's signature (pays gas) -- **EAS verifies** the signature and records Astral as attester -- **Resolver contracts** can verify `attestation.attester == astralSigner` +## Architecture - - Resolver contracts must be deployed and registered by the developer. Astral does not provide pre-built resolver contracts — you create them to implement your specific business logic. See [EAS Resolvers](/concepts/eas-resolvers) for patterns and examples. - +```mermaid +graph LR + subgraph Client["🔵 Client"] + D[Device / Sensor] --> P1[Plugin A] + D --> P2[Plugin B] + P1 --> S1[Stamp] + P2 --> S2[Stamp] + S1 --> LP[Location Proof] + S2 --> LP + CL[Claim] --> LP + end -## Trust Model + subgraph TEE["🟡 Astral TEE"] + LP --> VE[Verify] + VE --> CV[Credibility Vector] + LD[Location Data] --> CO[Compute] + CV -.-> CO + CO --> SR[Signed Result] + end -There are two distinct trust questions in location-based systems: + SR --> AG[Agent] + SR --> AP[Application] + SR --> SC[Smart Contract] -### 1. Are the inputs truthful? + style Client fill:#e8f4fd,stroke:#4a90d9,color:#000 + style TEE fill:#fef9e7,stroke:#d4a63a,color:#000 +``` -This is the **location verification problem** — how do you know a user was actually at the location they claim? GPS is spoofable, and proving physical presence remains an open research problem. + + + Evidence collection → composition → verification. Produces a credibility vector that tells you how much to trust the location claim. The verified location proof can then flow into the Compute service, or be used on its own. + + + Location data (raw, signed, or verified) → spatial operation → signed result. Produces a cryptographically signed spatial answer. The dashed arrow from the credibility vector to Compute indicates that verified proofs *can* feed into computation, but don't have to. + + -We're actively working on this through our [Location Proof framework](https://collective.flashbots.net/t/towards-stronger-location-proofs/5323). For now, Astral Location Services trust the location data provided as input. + + The two capabilities compose but don't require each other. A verified location proof is valuable on its own — it doesn't need to flow into the Compute service. And a compute operation can run on any location data, not just verified location proofs. + -### 2. Was the computation performed correctly? +## What's verified vs. what's trusted -This is what Astral Location Services solve. Given location inputs, how do you prove the geospatial computation (distance, containment, etc.) was performed correctly? +The TEE guarantees that computation executes correctly — the code that ran is the code that was attested, inputs weren't tampered with, and the signing key never leaves the enclave. That's the "verifiable" part. -**Current approach:** -- **Open source code**: The compute service code is public and auditable -- **TEE execution**: Code runs inside EigenCompute's trusted execution environment -- **Astral signing key**: Attestations are signed by a key held inside the TEE -- **Deterministic operations**: Same inputs produce same outputs (additional testing in progress) + + The truthfulness of location inputs is a separate question. It depends on the strength of the location proof: how many independent proof-of-location systems contributed evidence, how resistant those systems are to forgery, and whether the evidence is consistent. Astral evaluates this and reports it honestly via the credibility vector — but it cannot make weak evidence strong. + -**Future improvements:** -- **AVS Consensus**: Multiple operators verify computations -- **ZK Proofs**: Cryptographic proof of correct execution -- **Decentralized Signers**: Multi-party attestation signing +For a detailed accounting of what exactly is verified and what trust assumptions remain, see the [Trust Model](/trust-model/architecture). - - Deep dive into Location Attestations + + How Astral represents and verifies spatial data diff --git a/mint.json b/mint.json index faf9edb..9d90e87 100644 --- a/mint.json +++ b/mint.json @@ -1,6 +1,6 @@ { "$schema": "https://mintlify.com/schema.json", - "name": "Astral Location Services", + "name": "Astral", "redirects": [ { "source": "/", @@ -32,7 +32,7 @@ }, { "name": "GitHub", - "url": "https://github.com/AstralProtocol/astral-location-services" + "url": "https://github.com/AstralProtocol" } ], "topbarCtaButton": { @@ -49,7 +49,7 @@ "url": "sdk" }, { - "name": "API Reference", + "name": "API", "url": "api-reference" } ], @@ -68,35 +68,61 @@ "navigation": [ { "group": "Getting Started", - "pages": [ - "introduction", - "quickstart", - "how-it-works" - ] + "pages": ["introduction", "quickstart", "how-it-works"] }, { "group": "Core Concepts", "pages": [ - "concepts/location-attestations", - "concepts/location-proofs", - "concepts/geospatial-operations", - "concepts/policy-attestations", - "concepts/eas-resolvers", - "concepts/verifiable-computation" + { + "group": "Location Data", + "pages": [ + "concepts/location-data", + "concepts/geojson", + "concepts/location-records" + ] + }, + { + "group": "Location Proofs", + "pages": [ + "concepts/location-proofs", + "concepts/pol-systems", + "concepts/location-stamps", + "concepts/location-claims", + "concepts/location-proof-structure", + "concepts/location-proof-evaluation" + ] + }, + { + "group": "Geocomputation", + "pages": [ + "concepts/geocomputation", + "concepts/astral-location-services", + "concepts/verify", + "concepts/compute" + ] + }, + "concepts/signed-results", + "concepts/privacy" ] }, { "group": "Guides", "pages": [ - "guides/location-gated-nft", - "guides/geofenced-token", - "guides/delivery-verification" + "guides/local-development", + "guides/calling-the-api", + "guides/verifying-location-proofs", + "guides/blockchain-integration", + "guides/agent-integration", + "guides/building-plugins" ] }, { "group": "Use Cases", "pages": [ - "use-cases" + "use-cases/delivery-verification", + "use-cases/parametric-insurance", + "use-cases/geofence-compliance", + "use-cases/onchain-attestation" ] }, { @@ -136,6 +162,7 @@ "sdk/plugins", "sdk/compute", "sdk/eas", + "sdk/types", "sdk/migration" ] }, @@ -149,15 +176,24 @@ "plugins/custom" ] }, + { + "group": "Trust Model", + "pages": [ + "trust-model/architecture", + "trust-model/what-is-verified", + "trust-model/what-you-are-trusting", + "trust-model/security" + ] + }, { "group": "Resources", "pages": [ "resources/playground", "resources/staging", "resources/schemas", - "resources/security", - "resources/roadmap", - "resources/faq" + "resources/research", + "resources/faq", + "resources/changelog" ] } ], diff --git a/mint.proposed.json b/mint.proposed.json new file mode 100644 index 0000000..15f3467 --- /dev/null +++ b/mint.proposed.json @@ -0,0 +1,174 @@ +{ + "$schema": "https://mintlify.com/schema.json", + "name": "Astral", + "redirects": [ + { + "source": "/", + "destination": "/introduction" + } + ], + "logo": { + "dark": "/logo/dark.svg", + "light": "/logo/light.svg" + }, + "favicon": "/favicon.svg", + "colors": { + "primary": "#D4A63A", + "light": "#FEE9B3", + "dark": "#B8860B", + "anchors": { + "from": "#D4A63A", + "to": "#FEE9B3" + } + }, + "topbarLinks": [ + { + "name": "Playground", + "url": "https://playground.astral.global" + }, + { + "name": "GitHub", + "url": "https://github.com/AstralProtocol" + } + ], + "topbarCtaButton": { + "name": "Get Started", + "url": "/quickstart" + }, + "tabs": [ + { + "name": "SDK", + "url": "sdk" + }, + { + "name": "API Reference", + "url": "api-reference" + } + ], + "anchors": [ + { + "name": "GitHub", + "icon": "github", + "url": "https://github.com/AstralProtocol" + }, + { + "name": "Community", + "icon": "telegram", + "url": "https://t.me/+UkTOSXnDcDM5ZTBk" + } + ], + "navigation": [ + { + "group": "Getting Started", + "pages": [ + "introduction", + "quickstart", + "how-it-works" + ] + }, + { + "group": "Core Concepts", + "pages": [ + "concepts/geographic-features", + "concepts/geocomputation", + "concepts/verifiable-computation", + "concepts/location-proofs", + "concepts/result-format", + "concepts/privacy-model" + ] + }, + { + "group": "Guides", + "pages": [ + "guides/local-development", + "guides/calling-the-api", + "guides/verifying-location-proofs", + "guides/blockchain-integration", + "guides/agent-integration", + "guides/building-plugins" + ] + }, + { + "group": "Use Cases", + "pages": [ + "use-cases/delivery-verification", + "use-cases/parametric-insurance", + "use-cases/geofence-compliance", + "use-cases/onchain-attestation" + ] + }, + { + "group": "API Reference", + "pages": [ + "api-reference/overview", + { + "group": "Compute API", + "pages": [ + "api-reference/compute/distance", + "api-reference/compute/area", + "api-reference/compute/length", + "api-reference/compute/contains", + "api-reference/compute/within", + "api-reference/compute/intersects" + ] + }, + { + "group": "Verify API", + "pages": [ + "api-reference/verify/stamp", + "api-reference/verify/proof", + "api-reference/verify/plugins" + ] + }, + { + "group": "Records API", + "pages": [ + "api-reference/records/overview", + "api-reference/records/list", + "api-reference/records/get", + "api-reference/records/stats", + "api-reference/records/config" + ] + } + ] + }, + { + "group": "SDK Reference", + "pages": [ + "sdk/overview", + "sdk/installation", + "sdk/location", + "sdk/compute", + "sdk/verify", + "sdk/eas", + "sdk/types", + "sdk/migration" + ] + }, + { + "group": "Trust Model", + "pages": [ + "trust-model/architecture", + "trust-model/what-is-verified", + "trust-model/what-you-are-trusting", + "trust-model/security" + ] + }, + { + "group": "Resources", + "pages": [ + "resources/playground", + "resources/staging", + "resources/schemas", + "resources/research", + "resources/faq", + "resources/changelog" + ] + } + ], + "footerSocials": { + "twitter": "https://twitter.com/AstralProtocol", + "github": "https://github.com/AstralProtocol", + "telegram": "https://t.me/+UkTOSXnDcDM5ZTBk" + } +} diff --git a/quickstart.mdx b/quickstart.mdx index 45b353b..b7fa277 100644 --- a/quickstart.mdx +++ b/quickstart.mdx @@ -141,15 +141,15 @@ const tx = await astral.eas.submitDelegated(result.delegatedAttestation); console.log('Onchain:', tx.hash); ``` -For the full blockchain flow — writing resolver contracts, registering schemas, chain configuration — see [EAS Resolvers](/concepts/eas-resolvers). +For the full blockchain flow — writing resolver contracts, registering schemas, chain configuration — see [Blockchain Integration](/guides/blockchain-integration). ## Next Steps - - Understand location proofs, attestations, and credibility scores + + Understand location data, location proofs, and geocomputation - + Walk through common workflows step by step diff --git a/resources/changelog.mdx b/resources/changelog.mdx new file mode 100644 index 0000000..1695ff5 --- /dev/null +++ b/resources/changelog.mdx @@ -0,0 +1,16 @@ +--- +title: "Changelog" +description: "What's new in Astral" +--- + +# Changelog + +## v0.1.0 — Research preview + +Initial release of the Astral research preview. + +- Six geocomputation operations: distance, area, length, contains, within, intersects +- TEE execution via EigenCompute +- EAS integration for onchain attestation +- TypeScript SDK +- Location proof verification (experimental) diff --git a/resources/research.mdx b/resources/research.mdx new file mode 100644 index 0000000..66557ef --- /dev/null +++ b/resources/research.mdx @@ -0,0 +1,42 @@ +--- +title: "Research" +description: "Papers, research agenda, and academic context" +--- + +# Research + +Astral sits at the intersection of geospatial science, cryptography, and trusted computing. We're actively publishing research and welcome academic collaboration. + +## Papers + +### Towards stronger location proofs + +Our foundational paper on composable location proofs — how to combine evidence from multiple proof-of-location systems into credible, verifiable claims about physical presence. + +[Read on Flashbots Collective](https://collective.flashbots.net/t/towards-stronger-location-proofs/5323) + +## Research agenda + +We're working on three interconnected problems: + +### Composable location proofs + +How do you combine evidence from independent proof-of-location systems (device attestation, network triangulation, institutional records) into a single credibility assessment? Our [location proofs](/concepts/location-proofs) framework addresses this — defining claims, stamps, and multi-factor verification. + +### Verifiable geocomputation + +How do you prove that a spatial computation (distance, containment, intersection) was performed correctly on specific inputs? Today we use TEE execution with signed results. Future work explores zero-knowledge proofs for spatial predicates. + +### Spatial accountability for autonomous systems + +As autonomous agents (drones, vehicles, robots) operate in physical space, how do you create verifiable records of where they were and what spatial constraints they respected? This connects geofence compliance, corridor verification, and auditable spatial logs. + +## Open source + +All of Astral's core infrastructure is open source. Contributions, issues, and research collaborations are welcome. + +[GitHub](https://github.com/AstralProtocol) + +## Get involved + +If you're working on related problems — verifiable computation, location privacy, spatial data integrity, or autonomous systems accountability — we'd like to hear from you. Open an issue on GitHub or reach out through the channels listed there. diff --git a/sdk/types.mdx b/sdk/types.mdx new file mode 100644 index 0000000..29d7325 --- /dev/null +++ b/sdk/types.mdx @@ -0,0 +1,159 @@ +--- +title: "Types" +description: "TypeScript type definitions for the Astral SDK" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Types + +Core TypeScript types exported by `@decentralized-geo/astral-sdk`. + +## SDK configuration + +```typescript +interface AstralSDKConfig { + chainId: number; // Target chain ID (e.g., 84532 for Base Sepolia) + apiUrl?: string; // API base URL (default: https://api.astral.global) + signer?: ethers.Signer; // Ethers signer for onchain submissions +} +``` + +## Compute types + +### Input types + +Geographic features can be provided as raw GeoJSON, EAS attestation UIDs, or offchain references. + +```typescript +/** GeoJSON geometry */ +type GeoJSONInput = { + type: 'Point' | 'LineString' | 'Polygon' | 'MultiPoint' | 'MultiLineString' | 'MultiPolygon'; + coordinates: number[] | number[][] | number[][][] | number[][][][]; +}; + +/** Onchain attestation UID */ +type UIDInput = string; // "0x..." + +/** Offchain attestation reference */ +type OffchainInput = { + uid: string; + uri: string; // IPFS or HTTP URI +}; + +/** Inline attestation object */ +type InlineAttestationInput = { + attestation: { + uid: string; + schema: string; + data: string; + }; +}; + +type Input = GeoJSONInput | UIDInput | OffchainInput | InlineAttestationInput; +``` + +### Compute request types + +```typescript +interface DistanceRequest { + from: Input; + to: Input; + chainId: number; + schema?: string; + recipient?: string; +} + +interface AreaRequest { + geometry: Input; + chainId: number; + schema?: string; + recipient?: string; +} + +interface LengthRequest { + geometry: Input; + chainId: number; + schema?: string; + recipient?: string; +} + +interface ContainsRequest { + container: Input; + geometry: Input; + chainId: number; + schema?: string; + recipient?: string; +} + +interface WithinRequest { + geometry: Input; + target: Input; + radius: number; // meters + chainId: number; + schema?: string; + recipient?: string; +} + +interface IntersectsRequest { + geometry1: Input; + geometry2: Input; + chainId: number; + schema?: string; + recipient?: string; +} +``` + +### Compute result + +```typescript +interface ComputeResult { + result: T; + operation: string; + units?: string; // "meters" | "square_meters" + timestamp: number; + inputRefs: string[]; + attestation: AttestationData; + delegatedAttestation: DelegatedAttestationData; +} +``` + +## Attestation types + +```typescript +interface AttestationData { + schema: string; + attester: string; + recipient: string; + data: string; // ABI-encoded + signature: string; +} + +interface DelegatedAttestationData { + signature: string; + attester: string; + deadline: number; // Unix timestamp +} +``` + +## Verify types + +See the [Verify API reference](/api-reference/verify/proof#data-types) for the full `LocationClaim`, `LocationStamp`, `LocationProof`, and `CredibilityAssessment` type definitions. + +## EAS types + +```typescript +interface SubmitDelegatedOptions { + signature: string; + attester: string; + schema: string; + data: string; + recipient: string; + deadline: number; +} + +interface SubmitResult { + hash: string; // Transaction hash + uid: string; // Attestation UID +} +``` diff --git a/trust-model/architecture.mdx b/trust-model/architecture.mdx new file mode 100644 index 0000000..02af3ed --- /dev/null +++ b/trust-model/architecture.mdx @@ -0,0 +1,79 @@ +--- +title: "Architecture" +description: "TEE, PostGIS, and the stateless execution model" +--- + +**Research Preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Architecture + +Astral runs geocomputation inside a self-contained Docker container, executing within a Trusted Execution Environment (TEE) via EigenCompute. This page describes how the system is built. + +## Execution model + +```mermaid +flowchart TB + subgraph TEE["EigenCompute TEE Environment"] + subgraph Container["Docker Container"] + subgraph Engine["Astral Geocomputation Engine"] + E1["Validates inputs"] + E2["Executes PostGIS queries"] + E3["Signs results with TEE-held key"] + end + subgraph DB["PostgreSQL + PostGIS"] + D1["All spatial computations"] + D2["Ephemeral, no persistent state"] + end + Engine --> DB + end + G1["TEE guarantees:"] + G2["• Code hasn't been modified"] + G3["• Inputs weren't tampered with"] + G4["• Outputs came from executing that code"] + end +``` + +## Container design + + + + PostGIS runs **inside** the Docker container, not as an external service. This is essential for verifiable computation in the TEE — no external dependencies means the entire execution environment is attested. + + + Each request brings all required inputs. No persistent state between requests. This ensures determinism and simplifies verification — same inputs always produce same outputs. + + + The service holds a signing key that is generated within the TEE or securely provisioned. It cannot be extracted by the operator. All signed results are produced with this key. + + + +## Internal computation flow + +```mermaid +flowchart LR + subgraph TEE["EigenCompute TEE"] + subgraph Container["Docker Container"] + API[Node.js API] --> PostGIS[PostgreSQL + PostGIS] + PostGIS --> GEOS[GEOS Library] + end + end + + Input[Request] --> API + API --> Output[Signed Result] +``` + + + PostGIS uses [GEOS](https://libgeos.org/) for geometry operations — the same C++ library used by QGIS, GDAL, and most professional geospatial software. + + +## Why this architecture + +The design choices above serve a single goal: making geocomputation results verifiable. + +- **Self-contained** means the TEE attestation covers the entire execution environment. No external database calls that could be intercepted or altered. +- **Stateless** means determinism is straightforward. Given the same inputs, the container produces the same output every time. +- **Key inside TEE** means the signing key cannot be extracted or used outside the attested environment. If you trust the TEE, you trust the signature. + + + What the signature covers and what computation reproducibility means + diff --git a/trust-model/security.mdx b/trust-model/security.mdx new file mode 100644 index 0000000..1c61b48 --- /dev/null +++ b/trust-model/security.mdx @@ -0,0 +1,167 @@ +--- +title: "Security Considerations" +description: "Threat model, known limitations, and responsible disclosure" +--- + +**Research Preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Security considerations + +This page documents the security model, known limitations, and best practices for building with Astral. + +## Astral signer address + + + **Current Astral Signer (Base Sepolia):** `0x590fdb53ed3f0B52694876d42367192a5336700F` + + Resolver contracts must verify that `attestation.attester` equals this address. See [Staging](/resources/staging) for the full configuration. + + +## Known considerations + +### Replay attacks + +**Status:** Documented, resolver responsibility + +Signed results could potentially be reused: +- **Temporal replay:** Old result used for current benefit +- **Cross-context replay:** Result for one resolver used at another + +**Mitigations (your responsibility):** + +```solidity +contract SecureResolver is SchemaResolver { + mapping(bytes32 => bool) public usedAttestations; + + function onAttest(Attestation calldata attestation, uint256) + internal override returns (bool) + { + // 1. Check not already used + bytes32 attHash = keccak256(abi.encode(attestation.uid)); + require(!usedAttestations[attHash], "Already used"); + usedAttestations[attHash] = true; + + // 2. Check timestamp freshness + (, , uint64 timestamp, ) = abi.decode(...); + require(timestamp > block.timestamp - 1 hours, "Too old"); + + // 3. Verify expected inputs + (, bytes32[] memory inputRefs, , ) = abi.decode(...); + require(inputRefs[1] == EXPECTED_LOCATION, "Wrong location"); + + // ... business logic + } +} +``` + +### Input trust + +**Status:** Raw GeoJSON not verified + +Raw GeoJSON inputs are accepted for flexibility, but are **not verified for authenticity**: + +```typescript +// This works but geometry source is unverified +const result = await astral.compute.contains( + { type: 'Polygon', coordinates: [...] }, // Raw, unverified + userLocationUID +); +``` + +The signed result proves: +- "Astral computed the relationship between A and B" + +It does **not** prove: +- "Geometry A came from a trusted source" +- "User was actually at location B" + +**Best practice:** For high-security applications, require attested inputs (UIDs) rather than raw GeoJSON. + +### GPS spoofing + +**Status:** Out of scope for MVP + +Astral can verify that a computation was correct, but cannot verify that the input location represents where the user actually was. GPS can be spoofed. + +**Future:** Location proofs with multiple corroborating stamps will make spoofing harder. + +## Best practices + +### For resolver authors + + + + ```solidity + require(attestation.attester == astralSigner, "Not from Astral"); + ``` + + + ```solidity + require(timestamp > block.timestamp - MAX_AGE, "Attestation too old"); + ``` + + + ```solidity + require(inputRefs[1] == EXPECTED_LANDMARK, "Wrong location checked"); + ``` + + + ```solidity + require(!usedAttestations[uid], "Already used"); + usedAttestations[uid] = true; + ``` + + + ```solidity + function updateSigner(address newSigner) external onlyOwner { + astralSigner = newSigner; + } + ``` + + + +### For application developers + +- **Prefer UIDs** over raw GeoJSON for sensitive operations +- **Set appropriate timeouts** — don't accept stale results +- **Validate recipient** — ensure the result is for the right user +- **Handle signature expiry** — delegated attestation signatures have deadlines + +## Key management + +### Service signing key + +- Key generated or provisioned within TEE +- Cannot be extracted by operators +- Used to sign all results + +### Key rotation + +Resolver contracts should support key rotation: + +```solidity +address public astralSigner; + +event SignerUpdated(address oldSigner, address newSigner); + +function updateAstralSigner(address newSigner) external onlyOwner { + emit SignerUpdated(astralSigner, newSigner); + astralSigner = newSigner; +} +``` + + + For the research preview, a simple owner-controlled update is sufficient. For production, consider making the owner a multisig. + + +## Audit status + +| Component | Status | +|-----------|--------| +| Compute Service | Pending | +| SDK | Pending | +| Example Contracts | Pending | + + + Full security audit will be conducted before mainnet deployment. + diff --git a/trust-model/what-is-verified.mdx b/trust-model/what-is-verified.mdx new file mode 100644 index 0000000..345f6b0 --- /dev/null +++ b/trust-model/what-is-verified.mdx @@ -0,0 +1,64 @@ +--- +title: "What Is Verified" +description: "What the signature covers and what computation reproducibility means" +--- + +**Research Preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# What is verified + +When Astral produces a signed result, that signature makes specific claims. This page spells out exactly what is covered. + +## Verifiability properties + +| Property | How it's achieved | +|----------|-------------------| +| **Input integrity** | Input signatures verified at TEE boundary before processing | +| **Execution integrity** | TEE ensures code runs as deployed, can't be modified | +| **Output authenticity** | Signing key held inside TEE, can't be extracted | +| **Determinism** | Stateless model + fixed precision = same inputs produce same outputs | + +## What the signature covers + +A signed result from Astral proves three things: + +1. **The computation ran inside the TEE.** EigenCompute provides hardware attestation that the expected code is executing in the attested environment. +2. **The inputs were hashed and recorded.** Every signed result includes `inputRefs` — hashes of the inputs used. You can verify which inputs went into the computation. +3. **The output was produced by that computation.** The signing key exists only inside the TEE. If the signature is valid, the output came from the attested code running on the referenced inputs. + +## Input references (inputRefs) + +Every signed result includes an array of `inputRefs` — deterministic references to the inputs used in the computation. These let downstream consumers verify which inputs were used: + +- For geographic features referenced by UID, the `inputRef` is the UID itself +- For raw GeoJSON inputs, the `inputRef` is a hash of the geometry + +This means you can check not just *that* a computation was performed, but *what data* it operated on. + +## Computation reproducibility + +Determinism is what makes signed results meaningful. If someone else runs the same computation on the same inputs, they should get the same answer. + +Astral achieves this through: + +- **Centimeter precision rounding** before signing — eliminates floating-point variance +- **Pinned PostGIS version** in the container — no algorithm changes between builds +- **Stateless execution** — no accumulated state that could affect results + +## The signing key + +The service holds a signing key **inside** the TEE: + +- Key is generated within the TEE or securely provisioned +- Cannot be extracted by the operator +- All results are signed with this key +- Downstream consumers verify that results came from the known Astral signer + +```solidity +// In a resolver contract +require(attestation.attester == ASTRAL_SIGNER, "Not from Astral"); +``` + + + Honest accounting of current assumptions + diff --git a/trust-model/what-you-are-trusting.mdx b/trust-model/what-you-are-trusting.mdx new file mode 100644 index 0000000..d332af7 --- /dev/null +++ b/trust-model/what-you-are-trusting.mdx @@ -0,0 +1,64 @@ +--- +title: "What You Are Trusting" +description: "Honest accounting of current assumptions and the path forward" +--- + +**Research Preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# What you are trusting + +We're transparent about what's verified and what's assumed because trust is the product. This page gives an honest accounting of the current trust assumptions and where they're headed. + +## Current trust assumptions + +| Assumption | Status | Notes | +|------------|--------|-------| +| TEE executes code correctly | Verified | EigenCompute provides hardware attestation | +| Astral operates service honestly | Required | Single operator in MVP | +| Signing key held securely in TEE | Verified | Key cannot be extracted by operator | +| Input locations are truthful | **Not verified** | GPS is spoofable; future work | + +The MVP uses a **centralized trust model**: a single service with a known signer, running inside a TEE. The TEE provides execution attestation, and deterministic operations ensure reproducibility. But you are trusting Astral to operate the service honestly. + +## Location inputs are not verified + +Astral verifies computation, not location. Until location proof plugins are integrated, you are trusting that the GPS coordinates provided as input are honest. + +GPS is spoofable. A user can claim to be anywhere. The signed result proves that *if* the user was at location A, *then* they were within 500m of location B. It does not prove the user was actually at location A. + +This is why we're developing the [Location Proof framework](https://collective.flashbots.net/t/towards-stronger-location-proofs/5323) — to provide evidence-based location claims that feed into Astral for a fully verifiable pipeline. As proof mechanisms mature, they plug directly into the existing system. + +**Be direct with your users**: if your application depends on truthful location inputs, communicate that the location data is currently trust-based. + +## What verification buys you today + +Even with these assumptions, verifiable computation is meaningful: + +- **The computation is correct.** You know the spatial relationship was evaluated faithfully, not fabricated. +- **The inputs are recorded.** `inputRefs` let you audit which data went into the computation. +- **The result is tamper-evident.** The signature proves the output came from the attested environment. + +This is substantially better than trusting an opaque API that returns `true` or `false` with no proof. + +## The path forward + + + + Multiple independent operators run the computation. Results must match to be accepted. No single operator can lie. + + + Cryptographic proof that the computation was correct. Verifiable by anyone without trusting the prover. + + + Multi-party computation for result signing. No single party holds the full key. + + + Evidence-based location claims replace raw GPS. Multiple corroborating stamps make spoofing harder. + + + +Each enhancement reduces the trust surface. AVS consensus removes the single-operator assumption. ZK proofs make verification independent of hardware trust. Decentralized signers eliminate the single key. Location proofs address the input honesty gap. + + + Threat model, known limitations, and responsible disclosure + diff --git a/use-cases/delivery-verification.mdx b/use-cases/delivery-verification.mdx new file mode 100644 index 0000000..77e67b6 --- /dev/null +++ b/use-cases/delivery-verification.mdx @@ -0,0 +1,333 @@ +--- +title: "Delivery Verification" +description: "Escrow that releases when the courier arrives" +--- + +**Research preview** — APIs may change. [GitHub](https://github.com/AstralProtocol) + +# Delivery verification + +You're building a delivery platform. Buyers lock payment in escrow. When the courier arrives at the delivery address, the escrow releases automatically. No manual confirmation, no disputes about whether the courier actually showed up. + + + **About location verification**: This guide uses GPS coordinates as input. GPS is spoofable. Astral is developing [location proof plugins](https://collective.flashbots.net/t/towards-stronger-location-proofs/5323) for stronger verification — these are still in development. + + +## How it works + +1. Buyer creates a delivery order and locks payment in an escrow contract +2. The delivery address is registered as a location record onchain +3. When the courier arrives, their app checks proximity using `compute.within` +4. Astral returns a signed result confirming the courier is within range +5. The signed result is submitted onchain, triggering the escrow to release funds + +## The escrow contract + +The resolver contract holds funds and releases them when it receives a valid signed result proving the courier arrived. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@eas/contracts/resolver/SchemaResolver.sol"; +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; + +contract DeliveryEscrow is SchemaResolver, ReentrancyGuard { + address public astralSigner; + + struct Delivery { + address buyer; + address courier; + bytes32 destinationUID; // Location record of delivery address + uint256 amount; + uint256 radius; // Acceptable radius in meters + uint256 deadline; + bool completed; + bool refunded; + } + + mapping(bytes32 => Delivery) public deliveries; + mapping(bytes32 => bool) public usedAttestations; + + event DeliveryCreated(bytes32 indexed deliveryId, address buyer, uint256 amount); + event DeliveryCompleted(bytes32 indexed deliveryId, address courier); + event DeliveryRefunded(bytes32 indexed deliveryId, address buyer); + + constructor(IEAS eas, address _astralSigner) SchemaResolver(eas) { + astralSigner = _astralSigner; + } + + // Buyer creates delivery escrow + function createDelivery( + bytes32 deliveryId, + address courier, + bytes32 destinationUID, + uint256 radiusMeters, + uint256 deadline + ) external payable { + require(msg.value > 0, "Must send payment"); + require(deliveries[deliveryId].buyer == address(0), "Already exists"); + require(deadline > block.timestamp, "Invalid deadline"); + + deliveries[deliveryId] = Delivery({ + buyer: msg.sender, + courier: courier, + destinationUID: destinationUID, + amount: msg.value, + radius: radiusMeters * 100, // Convert to cm + deadline: deadline, + completed: false, + refunded: false + }); + + emit DeliveryCreated(deliveryId, msg.sender, msg.value); + } + + function onAttest( + Attestation calldata attestation, + uint256 /*value*/ + ) internal override returns (bool) { + require(attestation.attester == astralSigner, "Not from Astral"); + require(!usedAttestations[attestation.uid], "Already used"); + usedAttestations[attestation.uid] = true; + + // Decode the signed result + ( + bool isWithinRadius, + bytes32[] memory inputRefs, + uint64 timestamp, + string memory operation + ) = abi.decode( + attestation.data, + (bool, bytes32[], uint64, string) + ); + + // Verify operation type + require( + keccak256(bytes(operation)) == keccak256(bytes("within")), + "Wrong operation" + ); + + // Extract delivery ID from recipient field + bytes32 deliveryId = bytes32(uint256(uint160(attestation.recipient))); + Delivery storage delivery = deliveries[deliveryId]; + + require(delivery.buyer != address(0), "Delivery not found"); + require(!delivery.completed, "Already completed"); + require(!delivery.refunded, "Already refunded"); + require(block.timestamp <= delivery.deadline, "Deadline passed"); + + // Verify correct destination was checked + require(inputRefs.length >= 2, "Invalid inputs"); + require(inputRefs[1] == delivery.destinationUID, "Wrong destination"); + + // Verify courier is within radius + require(isWithinRadius, "Not at delivery location"); + + // Verify timestamp is recent + require(timestamp > block.timestamp - 30 minutes, "Result too old"); + + // Complete delivery + delivery.completed = true; + + // Release funds to courier + (bool success, ) = delivery.courier.call{value: delivery.amount}(""); + require(success, "Transfer failed"); + + emit DeliveryCompleted(deliveryId, delivery.courier); + return true; + } + + // Buyer can refund after deadline + function refund(bytes32 deliveryId) external nonReentrant { + Delivery storage delivery = deliveries[deliveryId]; + + require(msg.sender == delivery.buyer, "Not buyer"); + require(!delivery.completed, "Already completed"); + require(!delivery.refunded, "Already refunded"); + require(block.timestamp > delivery.deadline, "Deadline not passed"); + + delivery.refunded = true; + + (bool success, ) = delivery.buyer.call{value: delivery.amount}(""); + require(success, "Refund failed"); + + emit DeliveryRefunded(deliveryId, delivery.buyer); + } + + function onRevoke(Attestation calldata, uint256) internal pure override returns (bool) { + return false; + } +} +``` + +## Buyer flow + +The buyer creates a delivery order by registering the destination onchain and locking payment. + +```typescript +import { AstralSDK } from '@decentralized-geo/astral-sdk'; +import { ethers } from 'ethers'; + +async function createDeliveryOrder( + destinationCoords: [number, number], + courierAddress: string, + paymentAmount: bigint, + wallet: Signer +) { + const astral = new AstralSDK({ chainId: 84532, signer: wallet }); + const escrow = new Contract(ESCROW_ADDRESS, ESCROW_ABI, wallet); + + // Create destination location record + const destination = await astral.location.onchain.create({ + location: { type: 'Point', coordinates: destinationCoords }, + memo: "Delivery Address" + }); + + // Generate delivery ID + const deliveryId = ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode( + ['address', 'address', 'uint256'], + [await wallet.getAddress(), courierAddress, Date.now()] + )); + + // Create escrow + const tx = await escrow.createDelivery( + deliveryId, + courierAddress, + destination.uid, + 100, // 100 meter radius + Math.floor(Date.now() / 1000) + 86400, // 24 hour deadline + { value: paymentAmount } + ); + + await tx.wait(); + + return { + deliveryId, + destinationUID: destination.uid + }; +} +``` + +## Courier flow + +When the courier arrives, their app checks proximity and submits the signed result to release payment. + +```typescript +async function confirmDelivery( + deliveryId: string, + destinationUID: string, + wallet: Signer +) { + const astral = new AstralSDK({ chainId: 84532, signer: wallet }); + + // Get current location + const coords = await getCurrentLocation(); + + // Create location record + const courierLocation = await astral.location.onchain.create({ + location: { type: 'Point', coordinates: coords } + }); + + // Prove within radius of destination + const proof = await astral.compute.within( + courierLocation.uid, + destinationUID, + 100, // Must match contract radius + { + schema: SCHEMA_UID, + recipient: deliveryId // Pass delivery ID + } + ); + + if (!proof.result) { + throw new Error('Not close enough to delivery address'); + } + + // Submit signed result — triggers payment release + const tx = await astral.compute.submit(proof.delegatedAttestation); + await tx.wait(); + + return { success: true, transactionHash: tx.hash }; +} +``` + +## Mobile integration + +```typescript +// React Native example +import Geolocation from '@react-native-community/geolocation'; + +function DeliveryConfirmButton({ deliveryId, destinationUID }) { + const [status, setStatus] = useState<'idle' | 'checking' | 'confirming' | 'done'>('idle'); + + const handleConfirm = async () => { + setStatus('checking'); + + Geolocation.getCurrentPosition( + async (position) => { + const coords: [number, number] = [ + position.coords.longitude, + position.coords.latitude + ]; + + try { + setStatus('confirming'); + await confirmDelivery(deliveryId, destinationUID, wallet); + setStatus('done'); + Alert.alert('Success', 'Delivery confirmed! Payment released.'); + } catch (error) { + setStatus('idle'); + Alert.alert('Error', error.message); + } + }, + (error) => { + setStatus('idle'); + Alert.alert('Location Error', 'Could not get your location'); + }, + { enableHighAccuracy: true } + ); + }; + + return ( +