Skip to main content

Verify a Data Exchange listing offline

When an Edge Agent is about to ingest data offered by a peer organization across the Data Exchange, it must not pull it on trust. It first verifies the peer org's signed cross-org offer listing against a peer-org key it pinned out of band, offline, deny-closed. This tutorial runs that gate end to end with real commands.

Verify-only, by design

The edge holds the verify-only half of the Data Exchange. Signing, minting, settlement, and the live request/approve/provision lifecycle run on a full cell; the edge verifies a hand-carried listing against an out-of-band-pinned peer key, on a possibly disconnected cell. This is a deliberate boundary, not a gap. See Offline verification.

What you will prove​

A listing signed by a pinned, active peer org, inside its validity window, whose scope stays within your pinned ceiling, verifies. Anything else, an unknown signer, a tampered field, an expired window, a swapped payload, refuses with a non-zero exit and a signed deny-receipt, so an ingest wrapper never pulls data behind an unverified offer.

Prerequisites​

  • A device with s9 on its PATH (Quickstart working).
  • A peer-org pins file, the pinned peer-org keys in the wire-JSON shape. The baked-in set ships empty, so a device with no pins deny-closes every listing until the fleet or a release provisions pins.
  • A hand-carried listing JSON file, the peer org's signed cross-org offer, produced on the peer's cell and carried to you (on removable media, or any out-of-band channel).

The gate​

Figure: the edge verify gate. A hand-carried listing is checked against pinned peer keys and the device's anchor-corrected clock; the cascade is deny-closed, so it verifies only if every gate passes and otherwise refuses with a signed receipt.

Table equivalent

InputRole
Hand-carried listing (candidate)The peer's signed cross-org offer
Pinned peer-org keys (verified)The out-of-band trust anchor
Anchor-corrected clock (neutral)Judges validity windows; not a wall clock
s9 exchange verify-listing (edge)The deny-closed cascade
VERIFIED (green)Every gate passed: safe to ingest
REFUSED (red)A gate failed: signed deny-receipt, non-zero exit

Step 1: Verify a listing against your pins​

s9 exchange verify-listing \
--listing /media/usb/peer-offer.json \
--pins ./peer-org-pins.json

On success the gate prints the verified offer and exits zero:

Cross-org listing VERIFIED
publisher org : org:peer-acme
product : product:telemetry-2026
offer : offer:9f2c
reach : cross_org
scope tags : region:emea, class:telemetry
valid window : 1756598400..1759190400

If you omit --pins, the device uses its baked-in set (which ships empty) so every listing deny-closes until pins are provisioned. That is the safe default, not a bug.

Step 2: Bind the offer to the data (defense in depth)​

If you know the content hash of the data you are about to pull, bind the offer to it so a swapped payload is caught even when the offer itself is valid:

s9 exchange verify-listing \
--listing /media/usb/peer-offer.json \
--pins ./peer-org-pins.json \
--content-hash "$EXPECTED_SHA256"

Step 3: See the deny-closed refusal​

Point the gate at a listing whose signer you have not pinned (or a tampered or expired one). The gate refuses, mints a signed deny-receipt, and exits non-zero:

s9 exchange verify-listing --listing ./unknown-signer.json --pins ./peer-org-pins.json
echo "exit=$?" # non-zero, the pull is refused

The refusal is deterministic and offline: an unknown or revoked signer is refused before any cryptography is run; a tampered field reads as a content-hash mismatch; a listing outside its window fails the validity gate. Each refusal is captured as an answer, not a crash.

About the clock

Validity windows are judged against the device's anchor-corrected trusted clock, not the host wall clock, an air-gapped box must not verify against a clock an attacker can wind back. You can override it for testing with --now <unix>.

What you proved​

  • A cross-org data pull is gated on a deny-closed verification of the peer's signed offer: no network, no shared secret, no coordinator to phone.
  • Trust rests on a key you pinned out of band, and the empty default means an unprovisioned device pulls nothing.
  • Every refusal produces a signed deny-receipt with the first failing gate named, so a refusal is auditable evidence, not a silent failure.