API surface. Participants, records, timelines, grants and attestations are REST. Network-wide access policies are GraphQL Interface.
Every party to a shipment keeps its own copy of the truth, and nobody can see the whole journey. The merchant has the order, the carrier the scans, the broker the entry, the consumer an email. They reconcile by phone when they disagree. The Logistics Data Network is a shared, append-only record of the spec’s canonical objects. Every participant can write the facts it is responsible for, read the facts it is entitled to, and prove that neither has been altered. It is a mode (data, chapter 12) run by nodes. Every other mode feeds it through federation (chapter 09 §6).

1. Design principles

2. Participants

  • A participant is identified by a W3C DID. did:web is enough for organizations that own a domain. Spec-native implementers can use their API domain.
  • kinds is merchant, ecommerce_platform, marketplace, carrier, lsp, airline, three_pl, fleet_operator, import_broker, export_broker, ior_eor_provider, customs_authority, insurer, financier, consumer, or data_node.
  • credentials are W3C Verifiable Credentials issued by recognized authorities (licences, certifications, broker registrations). Policies can require them, as in “only a licensed customs broker may read declarations”.
  • Consumers are participants too, usually through a pairwise DID created for one shipment, so different shipments cannot be correlated to the same person.
  • account links the participant to a spec account (chapter 19) where one exists.
Endpoints: POST /v1/participants (register a DID, proven by signing a challenge), GET /v1/participants/{id}, GET /v1/participants?kind=…, POST /v1/participants/{id}/credentials (attach a VC), POST /v1/participants/{id}/rotate_keys.

3. Records

A record is one signed, content-addressed version of one canonical object.

Writing

Spec implementers do not write records by hand. With data network replication enabled on an account (settings.data_network: { node, replicate: ["shipment", "tracking_event", "document", "ledger_entry", "attestation"] }), every committed object version is replicated as a record automatically, signed by the account’s participant keys.

Anchoring

  • Nodes batch records (by time or count), compute a Merkle root, and write it to the ledger. ledger.network uses CAIP-2 chain identifiers, so any chain works.
  • status is pending, submitted, confirmed, or failed.
  • POST /v1/records/verify with { "record": "rec_…" } or { "object": <JSON>, "subject": … } returns { valid, cid_matches, signature_valid, producer, anchored_at, ledger_tx }. Anyone can verify a record they hold, even without access to the node that wrote it.

4. Access control

Access is decided per participant, per record, per field. It comes from three sources, checked in order.

4.1 Shipment roles (automatic)

Every shipment defines who is involved. Each involvement is a role:

4.2 Access policies (field masks per role)

A policy says what each role may read about each object type:
  • fields uses dotted paths, with * for everything. deny rules win over allow rules.
  • scope: "own_segment" limits a carrier to the segments of the journey it operates, plus the handoff points next to them.
  • require_credentials checks the participant’s verifiable credentials.
  • The spec publishes pol_default (above, abridged). Accounts and networks can attach their own policies, and an interline agreement’s data_sharing.policy applies to the carriers on that agreement.

4.3 Grants (explicit)

Participants can grant access that the policies do not imply: a merchant sharing a shipment with an insurer for a claim, or a consumer sharing their tracking with a neighbour.
A grantor can only grant fields it can read itself, so grants never escalate access.

How a read is decided

Keys are released wrapped to the participant’s DID key, so a node serves ciphertext plus a wrapped key, and never needs to send plaintext over federation. Every key release is itself logged as a record (subject.type: "access_log"). A participant can see who read its data.

5. Query and change feed

  • Every result is filtered by the requester’s access (§4), and queries never see fields the requester cannot read.
  • datasets produce aggregated or anonymized extracts for network analytics: lane transit times, carrier on-time performance, emissions per lane. This is capability data.datasets.

6. Attestations

An attestation is a signed statement by a participant about another record. It covers proof of delivery countersigned by the consignee, customs release by a broker, condition-on-receipt by a downstream carrier, and an insurer accepting a claim.
POST /v1/attestations, GET /v1/attestations?about=rec_…. Attestations are records too. They are anchored and access-controlled in the same way.

7. End-to-end timelines

A cross-border parcel is scanned by six to ten organizations: the shipper’s 3PL, a first-mile courier, a consolidator, an airline, two customs brokers, a gateway, a last-mile courier and a locker. Each sees its own scans under its own identifier (its tracking number, a bag ID, an AWB, a container number), and nobody sees the whole journey. The data network assembles it. Every party writes its events as records, identifiers are linked as custody changes, and the node consolidates the distributed events into one timeline per shipment, with standard milestones and provenance for every entry.

7.1 Identity linking

Each party knows the shipment by a different name. Identifier links are records that say two identifiers refer to the same goods, or that one contains the other:
  • relation is same_as (two tracking numbers for one parcel, such as an upstream and a relabelled local number) or contains (aggregation, with a validity window from load to unload).
  • scheme is tracking_number (with carrier), shipment (shp_), sscc, uld, container (ISO 6346), awb, postal_receptacle, bag, upstream_reference.
  • Links are written automatically by implementers that replicate: injection writes same_as (chapter 18), consolidation add and remove write contains (chapter 24), and relabelling writes same_as.
  • The node maintains an identity graph per shipment. An event recorded against any identifier in the graph, during the window that identifier is linked, belongs to the shipment’s timeline. This is how one airline scan of a ULD becomes a milestone for 700 parcels from 40 shippers, without the airline knowing any of them.

7.2 Milestones

Raw events are many and noisy. Milestones are the small, standard set every party and consumer understands. The node derives them from distributed events using a published definition set:
Rules.
  • Events are merged by occurred_at, deduplicated across producers (same milestone class, the same identifier through the graph, within the dedupe window), and never rewritten. Corrections are new records that supersede old ones by previous.
  • When producers disagree, precedence is: an attested record over others, then the party responsible for that segment at that time (from the journey and interline custody), then the earliest receipt. The losing records stay in conflicts.
  • The timeline is filtered through access control (§4) like everything else. A consignee sees milestones with city-level locations. A carrier sees full detail only for its own segments. A shipper sees everything about its shipments.

7.3 Endpoints

Events: timeline.milestone_reached, timeline.milestone_disputed, timeline.gap_detected. They are delivered to every participant that has a role on the shipment and a subscription, so the shipper, its platform and the consignee’s tracking page all see the same milestone at the same moment, whoever scanned it.

8. Erasure

POST /v1/records/erase with { subject: { shipment } , reason: "data_subject_request" } destroys the payload keys of every record holding personal data for that shipment. Records, CIDs, signatures and anchors remain, so the history stays verifiable, but the personal fields can no longer be decrypted. public_fields never contain personal data, which policy validation enforces at write.

9. Events

participant.registered, participant.credential_added, record.created, timeline.milestone_reached, timeline.milestone_disputed, timeline.gap_detected, anchor.confirmed, access_grant.created, access_grant.revoked, attestation.created, records.erased.