Stargaze
/ Docs
GitHub ↗ Request demo
Technical Documentation

Stargaze — private attestations for physical AI

Physical AI systems — drones, industrial robots, autonomous ground vehicles — emit continuous, sensitive telemetry: routes, sensor readings, task logs, performance metrics. Operators must prove claims about that telemetry to insurers, regulators, and autonomous counterparties. The problem is that proving a claim typically means handing over the evidence.

Stargaze is the privacy-first attestation layer that breaks this trade-off. It lets operators prove cryptographically that a claim is true — a UAV stayed within its corridor, a robot completed a task, a fleet met its SLA — without revealing the underlying telemetry.

Built natively on EAS (Ethereum Attestation Service) on Base, with a Groth16 ZK circuit tier for predicate proofs and optional TEE signing as a hardware root of trust.

Design-partner phase. Stargaze is in active development. The geofence circuit and EAS schema definitions are production-ready; the trajectory and aggregate circuits are on the roadmap. SDK packages are in pre-release. Apply to the design-partner program →

Core concepts

Attestation

An attestation is a signed, on-chain record — created by an attester, bound to a schema, optionally targeted at a recipient. Stargaze uses EAS as its attestation primitive: a widely-deployed standard on Base with a maintained SDK and tooling.

Private attestation

An attestation in which the data bundle is stored off-chain (Stargaze: Arweave) and only its Merkle root (disclosureRoot) is written on-chain. The data is never published; the chain holds a tamper-evident commitment to it.

Selective disclosure

The attester later reveals any subset of the original fields to a chosen verifier by producing Merkle inclusion proofs against the disclosureRoot. The verifier checks the proofs against the on-chain root — no decryption, no full-bundle exposure.

ZK predicate proof

Selective disclosure reveals field values. ZK proofs go further: they prove a predicate over field values without revealing the values themselves. Stargaze's Groth16 circuits prove claims like "this trajectory stayed inside this corridor" with zero knowledge of the actual trajectory.

TEE signing

A Trusted Execution Environment binds telemetry to a specific hardware device at capture time, before any user-space software can tamper with it. The TEE produces a deterministic deviceId and a teeSignature over the data hash; both travel with the attestation as evidence of device-bound provenance.

Quick start

Install the SDK, connect a signer, and create your first private attestation. The snippet below uses pre-computed values for the device ID, corridor hash, and ZK proof hash — see the worked example for how those are produced.

TypeScript
import { StargazeAttest, schemas } from '@stargaze/sdk';
import { JsonRpcProvider, Wallet } from 'ethers';

const provider = new JsonRpcProvider('https://mainnet.base.org');
const signer   = new Wallet(process.env.PRIVATE_KEY!, provider);

const client = new StargazeAttest({
  chain:   'base',         // 'base' | 'base-sepolia'
  signer,
  storage: 'arweave',      // off-chain store for private data
});

// Pre-computed inputs (see the Worked example for how these are produced)
const deviceId       = '0x9c1a…';   // bytes32 from DeviceSigner (TEE)
const corridorHash   = '0x4f8b…';   // Poseidon hash of the registered corridor
const zkProofHash    = '0x71d2…';   // keccak256 of the accepted Groth16 proof
const insurerAddress = '0x1234…';

const { uid } = await client.attest({
  schema:    schemas.corridorCompliance,
  recipient: insurerAddress,
  private:   true,
  data: {
    deviceId,
    corridorId:       corridorHash,
    proofHash:        zkProofHash,
    verifierContract: process.env.GEOFENCE_VERIFIER_ADDRESS!,
    flightStart:      1716_400_000_000n,   // Unix ms
    flightEnd:        1716_401_800_000n,
    complianceStatus: true,
  },
});

console.log('Attestation UID:', uid);
Base Sepolia first. Set chain: 'base-sepolia' during development. EAS schema UIDs are deterministic and identical across testnet and mainnet; verifier contract addresses differ per network — keep them in env vars (see Network & contracts).

Installation

npm / pnpm / yarn
# Core SDK
npm install @stargaze/sdk

# ZK circuit package — prover, witness generator, verification keys
npm install @stargaze/vault-circuits

# TEE device-signing helpers (platform-specific, optional)
npm install @stargaze/sdk-tee

Peer dependencies

PackageVersionNotes
ethers^6.0or viem ^2.0 — either works
snarkjs^0.7required by @stargaze/vault-circuits
Node.js≥ 18native fetch, crypto.subtle

Architecture

Stargaze is a three-tier privacy stack. Each tier adds an independent cryptographic guarantee; tiers can be deployed individually or composed end-to-end.

┌─────────────────────────────────────────┐ Tier 1 · Edge / TEE • Sign telemetry hash on device • Emit deterministic deviceId └────────────────────┬────────────────────┘ deviceId · teeSignature ┌─────────────────────────────────────────┐ Tier 2 · EAS Attestation Layer • Encrypt bundle → Arweave • Compute disclosureRoot (Merkle) • Anchor root on EAS (Base) └────────────────────┬────────────────────┘ attestation UID + Merkle root ┌─────────────────────────────────────────┐ Tier 3 · ZK Predicate Proofs • Groth16 circuit (e.g. geofence) • On-chain verifier accepts proof • proofHash referenced by attestation └────────────────────┬────────────────────┘ proofHash via emit event ┌─────────────────────────────────────────┐ Third-party Verifier insurer · regulator · contract • Merkle disclosure against root • ZK proof lookup via on-chain event └─────────────────────────────────────────┘

Verification flow

The full attestation lifecycle is six steps. Anyone holding the attestation UID can verify both the data integrity (via Merkle root) and the predicate (via the ZK proof event) — without trusting the attester to be honest about either.

DEVICE SDK VERIFIER (Base) INSURER │ │ │ │ │ 1. sign │ │ │ ├──────────────▶ │ │ │ │ 2. prove │ │ │ ├───────────────────▶ │ │ │ 3. ProofAccepted │ │ │ ◀───────────────────┤ │ │ │ 4. attest (EAS) │ │ │ ├───────────────────▶ │ │ │ │ 5. disclose │ │ ├───────────────────┼────────────────▶ │ │ │ 6. verify │ │ │ ◀────────────────┤
  1. Sign — device captures telemetry; TEE produces deviceId and a signature over the dataHash.
  2. Prove — SDK runs the relevant Groth16 circuit (e.g. geofence) and submits the proof to the on-chain verifier.
  3. ProofAccepted — on a valid proof, the verifier emits ProofAccepted(bytes32 proofHash, address attester, bytes32 corridorHash, bytes32 trajectoryHash). This is the on-chain record of acceptance.
  4. Attest — SDK builds the data bundle (including proofHash), encrypts it to Arweave, computes the Merkle disclosureRoot, and submits the EAS attestation referencing the root.
  5. Disclose — attester sends a selective disclosure (subset of fields + Merkle proofs) to the verifying party.
  6. Verify — the verifier checks (a) each Merkle proof against the on-chain root, and (b) that a ProofAccepted event exists for the included proofHash.

EAS layer

EAS is the on-chain attestation primitive. Stargaze registers schemas in the EAS SchemaRegistry and creates attestations against those schemas via the EAS contract on Base.

"Private" in Stargaze means the data bundle lives on Arweave and only the Merkle root is anchored on-chain. The on-chain attestation tx still happens — that is what gives the attestation a verifiable timestamp, schema, and attester address. Selective disclosure is a Stargaze convention layered on top of EAS, not a native EAS feature.

ZK circuit tier

The ZK tier enables predicate proofs — proving a statement about data without revealing the data. Circuits are written in Circom 2 and compiled to Groth16 via snarkjs. Verifier contracts are auto-generated and deployed to Base.

The verifier emits a ProofAccepted event with the proofHash (a deterministic hash of the proof + public signals). The EAS attestation references this hash. To verify the attestation's predicate claim, a third party queries the chain for a matching event — turning "I proved this" into a cheap, public lookup.

TEE layer

The TEE layer is the input root of trust. A device-resident Trusted Execution Environment (TPM, StrongBox, Nitro Enclave) signs the sensor data hash at capture time — before any user-space software can modify it. The resulting teeSignature and deterministic deviceId are committed inside the attestation's data bundle.

TEE signing is optional but strongly recommended: without it, an attester can fabricate sensor data and the rest of the stack will faithfully attest to the fabrication. See Trust model for the honest limits.

EAS Schema library

Stargaze registers a curated set of physical-AI schemas on EAS. All schemas share two structural conventions:

  • deviceId: bytes32 — TEE-derived device identity; deterministic per hardware key.
  • disclosureRoot: bytes32 — Merkle root over the full attested data bundle, enabling selective disclosure of any field.

Schema UIDs are deterministic on EAS — computed as keccak256(schema || resolverAddress || revocableFlag) — and therefore identical on Base mainnet and Base Sepolia for the same definition.

stargaze.corridor-compliance.v1 Live

Proves a UAV or robot stayed within a defined operational corridor during a mission — without revealing the route. Backed by the geofence Groth16 circuit.

FieldTypeReqDescription
deviceIdbytes32TEE-derived hardware identity
corridorIdbytes32Poseidon hash of the registered corridor polygon
proofHashbytes32Hash of the Groth16 proof + public signals accepted by GeofenceVerifier
verifierContractaddressGeofenceVerifier address that emitted ProofAccepted
flightStartuint64Mission start, Unix milliseconds
flightEnduint64Mission end, Unix milliseconds
complianceStatusbooltrue if proof verified on-chain
disclosureRootbytes32Merkle root over the full data bundle

stargaze.task-completion.v1 Live

Attests that a robot completed a defined task with a verifiable outcome. The full evidence bundle (logs, sensor snapshots, actuator traces) is stored privately on Arweave; only its hash is committed.

FieldTypeReqDescription
deviceIdbytes32Hardware identity of executing device
taskIdbytes32Keccak256 hash of the task definition
outcomeHashbytes32Hash of the outcome evidence bundle
completedAtuint64Completion timestamp, Unix milliseconds
successboolWhether the task succeeded
evidenceRefstringArweave URI of the encrypted evidence bundle
disclosureRootbytes32Merkle root over the full data bundle

stargaze.sensor-provenance.v1 Live

Chain-of-custody attestation for a sensor data bundle. The TEE signature binds the data hash to specific hardware at capture time, so downstream consumers can verify the data wasn't substituted post-capture.

FieldTypeReqDescription
deviceIdbytes32Hardware identity
sensorTypeuint80=GPS · 1=IMU · 2=LiDAR · 3=Camera · 4=Custom
dataHashbytes32Keccak256 of the encrypted data bundle
capturedAtuint64Capture timestamp, Unix milliseconds
teeSignaturebytesTEE signature over dataHash
storageRefstringArweave URI of the encrypted data bundle
disclosureRootbytes32Merkle root over the full data bundle

stargaze.performance-sla.v1 Requires aggregate circuit

ZK-proven aggregate performance claim — fleet uptime, task success rate, latency P95 — over a reporting period. Individual unit metrics stay private; only the aggregate is attested.

FieldTypeReqDescription
operatorIdbytes32Operator identity — hash of operator address
perioduint64Reporting period start, Unix epoch (seconds)
metricHashbytes32ZK-proven aggregate commitment
proofHashbytes32Hash of the aggregate Groth16 proof
slaTargetuint16Target in basis points — e.g. 9900 = 99.00%
metboolWhether the SLA target was met
disclosureRootbytes32Merkle root over the full data bundle

Registration

Schemas are registered once per network via the EAS SchemaRegistry. Registration is permissionless — anyone can register a schema; Stargaze's published schemas use a fixed registrant address documented in Network & contracts.

TypeScript — register a schema
import { SchemaRegistry } from '@ethereum-attestation-service/eas-sdk';

const registry = new SchemaRegistry(SCHEMA_REGISTRY_ADDRESS);
registry.connect(signer);

const schemaString =
  'bytes32 deviceId,bytes32 corridorId,bytes32 proofHash,' +
  'address verifierContract,uint64 flightStart,uint64 flightEnd,' +
  'bool complianceStatus,bytes32 disclosureRoot';

const tx = await registry.register({
  schema:    schemaString,
  resolverAddress: ethers.ZeroAddress,   // optional resolver contract
  revocable: true,
});

const schemaUID = await tx.wait();       // bytes32 UID, deterministic across chains
console.log('Schema UID:', schemaUID);

Versioning

Schemas evolve by registering new versions (v2, v3, …) rather than mutating v1. Old attestations remain valid against their original schema. Stargaze maintains both schemas and SDK helpers during a transition window of at least 6 months; deprecation is announced in the changelog and reflected in schemas. exports.

Naming convention: stargaze..v. Major version bumps for breaking field changes, additive changes get a new schema rather than a backward-compatible patch (EAS schemas are immutable post-registration).

Revocation

All Stargaze schemas are registered with revocable: true. The original attester can revoke an attestation when:

  • A device-key compromise is discovered — historical attestations from that key are repudiated.
  • The attested predicate is later disproven (e.g. corridor definition was incorrect).
  • A dispute resolves against the attester through the staking layer (StargazeStaking, when deployed).

Revocation preserves the historical record: the attestation stays on-chain with an added revocationTime. Verifiers should always check revocation status before relying on an attestation; the SDK does this automatically in verifyAttestation().

TypeScript — revoke
await client.revoke(uid, {
  reason: 'Device key rotated — replaced with attestation 0xabc…',
});
// Emits EAS Revoked event; subsequent verifyAttestation(uid) returns false

ZK Circuits

All circuits are written in Circom 2 and compiled to Groth16 via snarkjs. Each circuit ships with a proving key, verification key, an auto-generated Solidity verifier, and a TypeScript prover wrapper in @stargaze/vault-circuits.

The verifier wraps the snarkjs-generated verifyProof with a verifyAndRecord entry point that emits a ProofAccepted event on success — this is what the EAS attestation references via proofHash.

geofence.circom Live

Proves that a trajectory — a sequence of GPS waypoints — stayed entirely within a convex polygon corridor. The waypoints are private; only the corridor definition and a hash binding the proof to a specific trajectory are public.

Signal specification

SignalVisibilityTypeDescription
trajectoryprivatefield[N][2]Lat/lng pairs, scaled ×10⁶ (microdegrees as integers). N = MAX_WAYPOINTS.
nWaypointsprivatefieldActual count; remaining slots padded with the last valid waypoint.
corridorPolyprivatefield[V][2]Polygon vertices (V ≤ 32); used to derive constraints and to verify corridorHash in-circuit.
corridorHashpublicfieldPoseidon hash of corridorPoly. Registered alongside the corridor definition; binds the proof to a specific corridor.
trajectoryHashpublicfieldPoseidon hash of trajectory. Prevents proof reuse across routes.
compliantoutputfield1 if every waypoint satisfies the half-plane constraints of corridorPoly, else 0.

Performance

~47k
R1CS constraints
2.1s
Prove time (M3)
~285k
Verify gas (Base)
256
Max waypoints
Convex corridors only. The current circuit uses signed-area half-plane checks, which require convexity. Non-convex corridors must be decomposed into a union of convex sub-regions and proven separately (then logically OR'd off-chain). Native concave-polygon support is on the roadmap.

Trajectory circuit Q3 2026

Will prove properties of a continuous path without revealing the path: maximum speed at every segment, maximum heading change between adjacent segments, altitude window. Useful for "flew within speed and altitude limits" without exposing the route.

Planned public inputs: speedLimit, headingChangeLimit, altMin, altMax, trajectoryHash. Private inputs: 4-tuple per waypoint (lat, lng, alt, t).

Scaled-aggregate circuit Q3 2026

Will prove fleet-level aggregates — mean, P95, counts, sums — over a set of private scalar inputs. Enables the PerformanceSLA schema: an operator proves fleet uptime was above 99% without revealing which individual units were down.

Planned public inputs: aggregateCommitment (Poseidon hash of the input vector), slaTarget, aggregateValue. Private inputs: per-unit metric array.

Proof generation

TypeScript
import { proveGeofence, hashCorridor, hashTrajectory } from '@stargaze/vault-circuits';

// 1. Pre-compute the corridor hash (once, when registering the corridor)
const corridorPoly = [
  [51_500_000, -150_000],  // [lat ×10^6, lng ×10^6]
  [51_510_000, -150_000],
  [51_510_000, -100_000],
  [51_500_000, -100_000],
];
const corridorHash = hashCorridor(corridorPoly);

// 2. Hash the trajectory (binds the proof to this exact route)
const trajectory = [
  [51_506_400, -127_800],
  [51_506_750, -128_050],
  [51_507_100, -127_600],
  // … up to MAX_WAYPOINTS (256)
];
const trajectoryHash = hashTrajectory(trajectory);

// 3. Generate the proof
const { proof, publicSignals } = await proveGeofence({
  trajectory,                       // private — never leaves the device
  corridorPoly,                     // private (verified in-circuit against corridorHash)
  corridorHash,                     // public
  trajectoryHash,                   // public
});

// proof          → { pi_a: [...], pi_b: [...], pi_c: [...] }  (Groth16)
// publicSignals  → [corridorHash, trajectoryHash, compliant]

On-chain verification

The verifier wraps the snarkjs-generated raw verifyProof with a verifyAndRecord entry point. verifyAndRecord emits a ProofAccepted event with the proofHash on success — this is the on-chain record that the attestation later references.

Solidity — GeofenceVerifier (abridged)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IGeofenceVerifier {
    event ProofAccepted(
        bytes32 indexed proofHash,
        address indexed attester,
        bytes32 corridorHash,
        bytes32 trajectoryHash
    );

    /// @notice Verify and record a geofence compliance proof.
    /// @return proofHash  keccak256(abi.encode(a, b, c, input))
    function verifyAndRecord(
        uint[2]    calldata a,
        uint[2][2] calldata b,
        uint[2]    calldata c,
        uint[3]    calldata input   // [corridorHash, trajectoryHash, compliant]
    ) external returns (bytes32 proofHash);

    /// @notice Pure verification (no event, no state) — for off-chain checks.
    function verifyProof(
        uint[2]    calldata a,
        uint[2][2] calldata b,
        uint[2]    calldata c,
        uint[3]    calldata input
    ) external view returns (bool);
}
TypeScript — SDK helper
// Submit proof, wait for ProofAccepted, return proofHash
const { proofHash, txHash } = await client.verifyOnChain({
  circuit:          circuits.geofence,
  proof,
  publicSignals,
  verifierContract: process.env.GEOFENCE_VERIFIER_ADDRESS!,
});

// `proofHash` is now ready to embed in the EAS attestation.

SDK Reference

@stargaze/sdk is the TypeScript client for the full attestation + proof pipeline. It wraps the EAS SDK, the vault-circuits prover, and Arweave storage behind a single composable API.

Client setup

TypeScript — viem
import { StargazeAttest } from '@stargaze/sdk';
import { createWalletClient, http } from 'viem';
import { base } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const walletClient = createWalletClient({ account, chain: base, transport: http() });

const client = new StargazeAttest({
  chain:   'base',         // 'base' | 'base-sepolia'
  signer:  walletClient,   // ethers Wallet or viem WalletClient
  storage: 'arweave',      // 'arweave' (default) | 'ipfs'
  arweave: {
    host:     'arweave.net',
    port:     443,
    protocol: 'https',
    key:      arweaveJWK,  // JWK for writes; omit for read-only
  },
});

Creating attestations

client.attest() creates an EAS attestation. When private: true, the data bundle is serialized, Merkle-encoded, encrypted, stored on Arweave, and only the Merkle root is submitted on-chain.

TypeScript
const { uid, txHash, merkleRoot, storageRef } = await client.attest({
  schema:         schemas.corridorCompliance,
  recipient:      insurerAddress,
  expirationTime: 0n,        // 0n = no expiry
  revocable:      true,
  private:        true,
  data: {
    deviceId,                                    // bytes32 from DeviceSigner
    corridorId:       corridorHash,
    proofHash:        zkProofHash,
    verifierContract: process.env.GEOFENCE_VERIFIER_ADDRESS!,
    flightStart:      BigInt(missionStartMs),
    flightEnd:        BigInt(missionEndMs),
    complianceStatus: true,
  },
});

// uid         → EAS attestation UID (bytes32)
// txHash      → Base transaction hash
// merkleRoot  → on-chain Merkle root (= disclosureRoot in the data bundle)
// storageRef  → Arweave URI of the encrypted bundle

Generating proofs

TypeScript
import { circuits } from '@stargaze/sdk';

// 1. Compute hashes (the SDK uses the same Poseidon impl as the circuit)
const corridorHash   = client.hashCorridor(corridorPoly);
const trajectoryHash = client.hashTrajectory(waypoints);

// 2. Generate the Groth16 proof
const { proof, publicSignals } = await client.prove({
  circuit: circuits.geofence,
  inputs: {
    trajectory:   waypoints,    // private
    corridorPoly,               // private (verified in-circuit)
    corridorHash,               // public
    trajectoryHash,             // public
  },
});

// 3. Submit to the on-chain verifier, get proofHash from the ProofAccepted event
const { proofHash } = await client.verifyOnChain({
  circuit:          circuits.geofence,
  proof,
  publicSignals,
  verifierContract: process.env.GEOFENCE_VERIFIER_ADDRESS!,
});

// `proofHash` is what goes into the attestation's `proofHash` field

Verification

TypeScript
// Local proof verification (no gas, no tx — for testing)
const localOk = await client.verifyLocal({
  circuit: circuits.geofence,
  proof,
  publicSignals,
});

// Full attestation verification:
//   1. fetch attestation from EAS
//   2. check revocation status
//   3. confirm a ProofAccepted event exists for attestation.proofHash
//   4. (if disclosures provided) verify Merkle proofs against disclosureRoot
const result = await client.verifyAttestation(uid, {
  disclosure,                  // optional — verifies Merkle proofs too
});

// result: {
//   valid: boolean,
//   revoked: boolean,
//   proofAccepted: boolean,
//   disclosureValid: boolean | null,   // null if no disclosure supplied
//   attestation: { ... },              // raw EAS attestation
// }

Selective disclosure

Reveal a subset of fields from a private attestation. The result is a self-contained object that any party can verify against the on-chain Merkle root — no SDK required on the verifier side beyond a Base RPC.

TypeScript — attester
// Disclose only compliance status + flight window
const disclosure = await client.disclose(uid, [
  'complianceStatus',
  'flightStart',
  'flightEnd',
]);

// disclosure = {
//   fields:  { complianceStatus: true, flightStart: 1716_400_000_000n, … },
//   proofs:  { complianceStatus: MerkleProof, flightStart: …, flightEnd: … },
//   root:    '0x4f…',     // matches on-chain disclosureRoot
//   uid:     '0xabc…',
// }

// Send `disclosure` to the verifier (insurer, regulator, etc.)
TypeScript — verifier
import { verifyDisclosure } from '@stargaze/sdk/verify';

const valid = await verifyDisclosure({
  disclosure,
  provider: new JsonRpcProvider('https://mainnet.base.org'),
});
// Checks each field's Merkle proof against the on-chain disclosureRoot
// for the attestation UID. Returns true iff all proofs verify and the
// attestation is not revoked.

TEE Integration

The @stargaze/sdk-tee package wraps platform-specific Trusted Execution Environments behind a single DeviceSigner interface. Its output — a deterministic deviceId and a teeSignature over the data hash — is consumed by the main SDK when building attestations.

Supported platforms

PlatformTEEStatus
Android (API 28+)Android Keystore + StrongBoxSupported
Linux x86_64TPM 2.0 (tpm2-tools)Supported
AWS Nitro EnclavesNSM SDKSupported
Intel TDXDCAP remote attestationIn progress
Custom hardwareITEESigner interfaceBYO

Device signing

TypeScript — TPM 2.0 (Linux)
import { DeviceSigner } from '@stargaze/sdk-tee';

const signer = await DeviceSigner.fromTPM({
  keyHandle: '/tpm/stargaze-device-key',
  pcrs:      [0, 7],   // PCRs to bind into the quote (firmware, secure boot)
});

const { deviceId, signature, quote } = await signer.sign(dataHash);

// deviceId  → bytes32, deterministic from the TPM-resident EK
// signature → bytes, ECDSA over dataHash using the device-bound key
// quote     → bytes, raw TPM2_Quote — store with the evidence bundle
TypeScript — AWS Nitro Enclave
import { DeviceSigner } from '@stargaze/sdk-tee';
import { randomBytes } from 'node:crypto';

const signer = await DeviceSigner.fromNitro({
  nsmDevicePath: '/dev/nsm',
  nonce:         randomBytes(32),   // included in the NSM attestation doc
});

const { deviceId, signature, quote } = await signer.sign(dataHash);
// `quote` is the raw NSM attestation CBOR — verifiable against AWS root CA.

Quote verification

The teeSignature alone proves "someone with the device key signed this." A TEE quote goes further — it proves the signing key resides in a genuine, unmodified secure environment. Quotes ship inside the encrypted evidence bundle (not in the on-chain attestation) and are verified by the third-party recipient before they trust the deviceId.

Quote formatVerifier trust anchor
TPM 2.0 QuoteDevice EK certificate chain → vendor root (Infineon, Nuvoton, …)
Android Key Attestationx.509 chain → Google hardware-attestation root
AWS NSM Attestation DocCOSE_Sign1 → AWS Nitro root certificate
Intel TDX DCAPIntel PCS / collateral chain
TypeScript — verify a quote
import { verifyQuote } from '@stargaze/sdk-tee/verify';

const result = await verifyQuote({
  format:   'tpm2',     // 'tpm2' | 'android' | 'nitro' | 'tdx'
  quote,
  expectedDeviceId: deviceId,
  expectedPcrs:     { 0: pcr0Expected, 7: pcr7Expected },  // policy
});

// result: { valid, vendor, firmwareVersion, attestedAt, pcrs }

Custom TEE

Implement the ITEESigner interface to integrate any hardware enclave or key management system:

TypeScript
interface ITEESigner {
  /**
   * Sign a data hash using the device-resident TEE.
   * @param dataHash  32-byte hash to sign
   * @returns         deviceId (32 bytes), signature, optional TEE quote
   */
  sign(dataHash: Uint8Array): Promise<{
    deviceId:  Uint8Array;   // 32 bytes — stable device identifier
    signature: Uint8Array;   // variable length
    quote?:    Uint8Array;   // optional raw TEE attestation quote
  }>;
}

const signer = DeviceSigner.fromCustom(myTEEImpl);

Worked example

End-to-end: a delivery-drone operator proves to their insurer that flight #4711 stayed inside its assigned corridor — without revealing the route. Every variable name in this example matches the rest of the docs.

Step 1 — Pre-flight (one-time corridor registration)

TypeScript
// Run once, at corridor registration time, off-chain.
import { hashCorridor } from '@stargaze/vault-circuits';

const corridorPoly = [
  [51_500_000, -150_000],
  [51_510_000, -150_000],
  [51_510_000, -100_000],
  [51_500_000, -100_000],
];
const corridorHash = hashCorridor(corridorPoly);
// → store corridorHash in your fleet management system; share with the insurer.

Step 2 — During flight (on the drone)

The drone's flight controller captures GPS waypoints at 1 Hz. The on-device TEE signs the rolling waypoint buffer at each capture; the resulting deviceId and per-capture signatures are stored alongside the raw waypoints.

Step 3 — Post-flight (on the operator's machine)

TypeScript
import { StargazeAttest, schemas, circuits } from '@stargaze/sdk';
import { DeviceSigner } from '@stargaze/sdk-tee';
import { Wallet, JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://mainnet.base.org');
const signer   = new Wallet(process.env.PRIVATE_KEY!, provider);
const client   = new StargazeAttest({ chain: 'base', signer, storage: 'arweave' });

// 3a. Load the captured waypoints + the TEE signature over the final dataHash
const waypoints      = loadWaypointsFromFlight('flight-4711');   // private
const missionStartMs = waypoints[0].t;
const missionEndMs   = waypoints.at(-1).t;
const dataHash       = client.hashWaypoints(waypoints);

const tee = await DeviceSigner.fromTPM({ keyHandle: '/tpm/drone-key', pcrs: [0, 7] });
const { deviceId, signature: teeSignature, quote } = await tee.sign(dataHash);

// 3b. Generate the geofence ZK proof
const trajectoryHash = client.hashTrajectory(waypoints);
const { proof, publicSignals } = await client.prove({
  circuit: circuits.geofence,
  inputs: { trajectory: waypoints, corridorPoly, corridorHash, trajectoryHash },
});

// 3c. Submit the proof to the on-chain verifier — get the proofHash
const { proofHash } = await client.verifyOnChain({
  circuit:          circuits.geofence,
  proof,
  publicSignals,
  verifierContract: process.env.GEOFENCE_VERIFIER_ADDRESS!,
});

// 3d. Create the private EAS attestation
const { uid } = await client.attest({
  schema:    schemas.corridorCompliance,
  recipient: insurerAddress,
  private:   true,
  data: {
    deviceId,
    corridorId:       corridorHash,
    proofHash,
    verifierContract: process.env.GEOFENCE_VERIFIER_ADDRESS!,
    flightStart:      BigInt(missionStartMs),
    flightEnd:        BigInt(missionEndMs),
    complianceStatus: true,
  },
  // The full bundle (waypoints, teeSignature, quote) is encrypted and stored
  // on Arweave; only the Merkle root reaches the chain.
  attachments: { waypoints, teeSignature, quote },
});

console.log('Flight 4711 attestation:', uid);

Step 4 — Disclosure to the insurer

TypeScript — operator side
const disclosure = await client.disclose(uid, [
  'complianceStatus',
  'flightStart',
  'flightEnd',
]);
// Send `disclosure` to the insurer over any channel (email, S3, signed message).

Step 5 — Insurer verification

TypeScript — insurer side
import { StargazeAttest, verifyDisclosure } from '@stargaze/sdk';
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://mainnet.base.org');
const client   = new StargazeAttest({ chain: 'base', provider });  // read-only

const result = await client.verifyAttestation(uid, { disclosure });

if (!result.valid)            throw new Error('Attestation invalid');
if (result.revoked)           throw new Error('Attestation has been revoked');
if (!result.proofAccepted)    throw new Error('No matching ProofAccepted event');
if (!result.disclosureValid)  throw new Error('Disclosure Merkle proofs failed');

console.log('Flight was compliant:', disclosure.fields.complianceStatus);
console.log('Mission window:', disclosure.fields.flightStart, '→', disclosure.fields.flightEnd);
// Insurer now has cryptographic proof of compliance + a verifiable mission
// window, without ever seeing a single GPS waypoint.

Trust model

Stargaze's security model is layered, and honest about where each layer's guarantees end. We do not claim "verifiable inference" or perfect hardware attestation.

What ZK proofs guarantee

A Groth16 proof that verifies on-chain is sound: no computationally bounded adversary can produce a valid proof for a false statement. If GeofenceVerifier.verifyAndRecord() emits ProofAccepted, the submitted trajectory hash satisfies the circuit constraints for the given corridor.

Limit: ZK proofs guarantee integrity over submitted inputs. They do not guarantee that the submitted inputs reflect real-world sensor data. Soundness is over the circuit's arithmetic, not over physical reality.

What TEE signing provides

TEE signing binds the data hash to a specific hardware key established at manufacture or provisioning. An attacker who doesn't control the physical device cannot forge a valid teeSignature for a fabricated hash — raising the cost of fraud from software to physical hardware compromise.

Limit: TEE security depends on vendor trust (Intel, Qualcomm, ARM TrustZone, AMD SEV) and firmware integrity. Side-channel attacks (speculative execution, power analysis) exist against all major implementations. Not all robotics hardware has a usable secure enclave; DeviceSigner falls back gracefully when a TEE is unavailable but the attestation should be flagged as non-TEE in that case.

What EAS anchoring provides

An EAS attestation is an immutable on-chain record: timestamped by Base, signed by a specific attester address, bound to a schema. It cannot be back-dated or altered after creation. The disclosureRoot commits to the full data bundle at attestation time, and proofHash commits to a specific accepted ZK proof.

Limit: EAS records what was attested — not whether the attested claim is physically true. The attester is the trust anchor for inputs; the schema constrains what they can lie about (e.g. complianceStatus requires a matching ProofAccepted event, but the attester can still lie about which corridor was the "correct" one).

Economic deterrence

The StargazeStaking contract (optional, pre-deployment) allows operators to post a slashable bond. A dispute mechanism — built on top of the schemas — can slash the bond if a false attestation is proven (e.g. an authorized regulator publishes counter-evidence). This complements the cryptographic guarantees with a financial deterrent calibrated to the stakes of the attestation.

What Stargaze cannot claim

  • It cannot prove that sensor hardware was functioning correctly or was properly calibrated.
  • It cannot prove that TEE firmware was uncompromised at signing time.
  • It is not a substitute for physical inspection, regulatory audit, or insurance underwriting.
  • ZK proofs over GPS coordinates do not authenticate GPS signals — receiver-side spoofing is a separate threat vector that requires anti-spoofing hardware or multi-constellation cross-checks.

Network & contracts

Base Mainnet (chain ID 8453)

ContractAddress
EAS0x4200000000000000000000000000000000000021
SchemaRegistry0x4200000000000000000000000000000000000020
GeofenceVerifierdeployment pending
corridor-compliance schema UIDregistration pending
task-completion schema UIDregistration pending
sensor-provenance schema UIDregistration pending

Base Sepolia (chain ID 84532)

ContractAddress
EAS0x4200000000000000000000000000000000000021
SchemaRegistry0x4200000000000000000000000000000000000020
GeofenceVerifierdeployable via forge script — see below
Schema UIDs match across networks. Because SchemaRegistry.register derives the UID deterministically from the schema string + resolver + revocable flag, a schema registered with identical parameters on Sepolia and Mainnet shares one UID. Verifier contract addresses do not — keep them in environment variables.

Deploying GeofenceVerifier to Base Sepolia

Shell
forge script scripts/DeployVerifier.s.sol:DeployGeofenceVerifier \
  --rpc-url https://sepolia.base.org \
  --broadcast \
  --verify \
  --etherscan-api-key $BASESCAN_API_KEY \
  --private-key $PRIVATE_KEY

Gas & storage costs

Indicative costs on Base mainnet (≈ $0.10 / Mgas at typical gas price). Storage costs assume the encrypted bundle is 1–3 KB compressed.

OperationGasUSD est.
Schema registration (one-time)~140k$0.014
GeofenceVerifier deploy (one-time)~1.4M$0.14
verifyAndRecord (geofence proof)~285k$0.029
Create attestation (private)~85k$0.009
Revoke attestation~50k$0.005
Arweave storage (per attestation)$0.005 – $0.02

End-to-end per-flight cost (proof + attestation + storage): ~$0.04. Schema and verifier deploys are one-time amortized costs.

Batching. The aggregate circuit (planned) lets you attest fleet-wide claims in a single proof + attestation, which is cheaper than per-unit attestation when the verifier only cares about the aggregate.

Design partners

Stargaze is working with a small number of industrial and logistics operators in the design-partner phase. We're looking for fleets where a compliance, performance, or provenance claim needs to be shared with a third party — insurer, regulator, settlement contract, or supply-chain counterparty — without exposing the underlying data.

What design partners get

  • Direct access to the engineering team throughout your pilot.
  • Custom schema design for your specific compliance or disclosure requirements.
  • Early access to SDK builds and circuit updates.
  • A full attestation pilot on your own telemetry — from TEE signing to on-chain proof.
  • Co-authorship on the use-case writeup (anonymized by default).

Good fit if you operate

  • UAV or drone fleets subject to corridor, altitude, or no-fly-zone compliance requirements.
  • Autonomous ground vehicles or industrial robots with task-completion SLAs.
  • DePIN nodes that need to prove coverage, uptime, or signal quality without revealing location.
  • Any fleet where sensor data is sensitive IP but compliance must be demonstrable.

Apply at stargaze.cc/demo or email enquiries@stargaze.cc. We respond to every qualified request within two business days.