The Universal Object & Resource Attestation (UORA) Protocol defines a complete, vendor-neutral framework for decentralized identity, state verification, and lifecycle tracking of physical assets across untrusted parties. Unlike data-format specifications that define only syntax, UORA defines a protocol: a set of rules for how attestations are issued, discovered, validated, and reconciled — with sufficient detail that a conformant implementation can be built from this document alone.
This specification addresses the four pillars of decentralized supply chain trust:
This is a Community Group Draft. It has been released for review and comment. Implementors are advised against deploying this material in production systems until it reaches Candidate Recommendation status. Please report issues at https://github.com/uora-wg/core/issues.
Global supply chains operate across jurisdictional boundaries, regulatory regimes, and competing commercial interests. Traditional traceability systems rely on centralized databases, proprietary APIs, and bilateral agreements — each a single point of failure, a trust bottleneck, and an interoperability barrier. The UORA Protocol replaces these fragile architectures with a decentralized, cryptographically verifiable framework where trust is derived from governance authority and mathematics, not from any single participant or shared clock.
The protocol is organized into four architectural layers:
Relationship to Companion Specifications: This Core Protocol provides all normative rules required to build a conformant Issuer, Resolver, and Verifier. The companion UORA Secure Physical Binding specification is normatively required only for physical binding verification (BAL declarations, PUF Interface Descriptors, Proximity Verifier Service). It is not required for software-only implementations.
The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL in this document are to be interpreted as described in [[RFC2119]].
null for origin events, a single attestation ID for linear chains, or an ordered array for DAG-based transformations.Extends UORAAttestation subtypes with industry-specific claims (Pharma, CriticalMinerals, FoodBeverage, Customs, Aerospace). All profiles inherit validation rules from Layers 2–3. Profiles MUST NOT override governance, chaining, or conflict rules.
Industry-specific extensions require no core protocol changes.CertificationCredentials issued by Trust Anchors to Issuers, with BitstringStatusList revocation. Trust Frameworks define authority precedence tables. Every attestation MUST reference a valid, unexpired, non-revoked CertificationCredential.
Decoupled from core schemas — trust models evolve independently.Four concrete attestation types: Origin, Transfer, Transformation, Disposition. Antecedent chaining (linear and DAG, with depth limits and cycle detection). Conflict resolution cascade (authority → timestamp → ID). Linear custody chain enforcement.
Complete, automatable supply chain event modeling.DID-based object identification (serial, batch, SKU granularity). Standardized UORA-Query-API with six endpoints fully specified herein. Physical binding metadata (companion spec required only for binding verification).
Every object has a single, uniform discovery interface.Publishes Trust Framework; issues and revokes CertificationCredentials; configures authority precedence table.
Creates and signs attestations; references valid CertificationCredential in every attestation.
Possesses the physical object; presents attestation chains to Verifiers.
Validates structure, governance, chain integrity, and conflict resolution.
Implements UORA-Query-API; stores or proxies attestations; executes the validation pipeline.
Every physical object tracked by UORA SHALL be assigned a Decentralized Identifier (DID) resolvable to a DID Document. The DID Document provides cryptographic verification material, the Resolver service endpoint, and optional physical binding metadata.
DID Pattern:
did:web:{domain}:object:{granularity}:{identifier}
| Granularity | Example DID | Use Case | Antecedent Behavior |
|---|---|---|---|
| serial | did:web:maker.example.com:object:serial:SN-001 | Individual item tracking | Single linear chain |
| lot | did:web:maker.example.com:object:lot:L-2026-04 | Batch-level traceability | May reference DAG antecedents |
| sku | did:web:maker.example.com:object:sku:GTIN-123456 | Product class definition | Lifecycle events at higher granularity |
A UORA-compliant DID Document SHALL include verification material and a service endpoint declaring the UORA-Query-API base URL.
{
"@context": ["https://www.w3.org/ns/did/v1"],
"id": "did:web:maker.example.com:object:serial:SN-001",
"verificationMethod": [
{
"id": "#key-1",
"type": "Ed25519VerificationKey2020",
"controller": "did:web:maker.example.com:object:serial:SN-001",
"publicKeyMultibase": "zH3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"
}
],
"assertionMethod": ["#key-1"],
"service": [
{
"id": "#attestation-history",
"type": "UORAAttestationService",
"serviceEndpoint": "https://maker.example.com/uora/v1"
}
]
}
Every UORA-compliant Resolver SHALL implement this standardized query interface at the serviceEndpoint declared in the object's DID Document.
| Method | Path | Purpose | Idempotent |
|---|---|---|---|
GET | {base}/history | Retrieve attestation history for an object | Yes |
POST | {base}/attestations | Submit and validate a new attestation | No |
POST | {base}/validate | Validate an attestation without storing | Yes |
GET | {base}/chain | Retrieve human-readable custody chain timeline | Yes |
GET | {base}/health | Resolver health check | Yes |
GET | {base}/stats | Resolver store statistics | Yes |
GET /history| Parameter | Required | Type | Description |
|---|---|---|---|
did | MUST | URI | DID of the physical object |
eventType | OPTIONAL | String | Filter: Origin, Transfer, Transformation, Disposition |
from | OPTIONAL | ISO 8601 | Return attestations where validFrom >= from |
to | OPTIONAL | ISO 8601 | Return attestations where validFrom <= to |
limit | OPTIONAL | Integer | Maximum results (default: 100, max: 1000) |
includeSuperseded | OPTIONAL | Boolean | If true, include attestations with status superseded (default: false) |
200 OK for GET /history
{
"did": "did:web:maker.example.com:object:serial:SN-001",
"attestations": [
{
"id": "urn:uuid:origin-attestation-id",
"type": ["VerifiableCredential", "UORAAttestation", "UORAOriginAttestation"],
"validFrom": "2026-01-15T08:00:00Z",
"issuer": "did:web:maker.example.com",
"eventType": "Origin",
"antecedent": null,
"status": "valid",
"chainIntegrity": "intact",
"supersededBy": null,
"proof": { "type": "Ed25519Signature2020" },
"credential": { /* full verifiable credential */ }
}
],
"total": 1
}
201 Created for POST /attestations
{
"status": "accepted",
"attestationId": "urn:uuid:550e8400-e29b-41d4-a716-446655440000",
"chainIntegrity": "intact",
"eventType": "Transfer",
"antecedent": ["urn:uuid:origin-attestation-id"]
}
200 OK for POST /validate
{
"status": "valid",
"error": null,
"chainIntegrity": "intact",
"details": {
"attestationId": "urn:uuid:550e8400-e29b-41d4-a716-446655440000",
"issuer": "did:web:shipper.example.com",
"eventType": "Transfer",
"antecedent": ["urn:uuid:origin-attestation-id"],
"proofType": "Ed25519Signature2020",
"phasesExecuted": [1, 2, 3, 4, 5, 6, 7]
}
}
200 OK for GET /chain
{
"did": "did:web:maker.example.com:object:serial:SN-001",
"chainLength": 3,
"dagDepth": 1,
"isComplete": true,
"chain": [
{
"position": 1,
"eventType": "Origin",
"attestationId": "urn:uuid:origin-attestation-id",
"timestamp": "2026-01-15T08:00:00Z",
"issuer": "did:web:maker.example.com",
"description": "Object created at https://id.gs1.org/414/9521321000010"
}
]
}
200 OK for GET /health
{
"status": "ok",
"version": "1.0",
"conformanceLevel": "UORA-Full-Compliant",
"timestamp": "2026-05-01T00:00:00Z"
}
| HTTP Status | Condition | Body field error |
|---|---|---|
400 | Missing required did parameter or malformed JSON body | bad_request |
404 | DID not found or no attestation history available | not_found |
409 | Duplicate attestation ID detected (Phase 1) | rejected_duplicate_id |
422 | Attestation rejected by any validation pipeline phase | Phase-specific rejection status |
500 | Internal Resolver error | internal_error |
All error responses MUST include a JSON body with fields error (string), message (human-readable string), and phase (integer 1–7 identifying the failing pipeline phase, or null for non-pipeline errors).
A UORA digital identity MUST be bound to its physical counterpart through at least one tamper-evident mechanism. The normative specification is the companion UORA Secure Physical Binding protocol. This companion specification is required only for physical binding verification; it is not required to implement a conformant Resolver or Verifier operating on software-presented credentials.
| Binding Mechanism | Security Level | Typical Application |
|---|---|---|
| Cryptographic NFC Tag | High | Individual high-value items with challenge-response |
| Secure QR with Digital Signature | Medium | Batch-level tracking with visual inspection |
| IoT Hardware Security Module (HSM) | High | Containers and equipment with continuous attestation |
| Physically Unclonable Function (PUF) | Very High | Critical components and luxury goods |
An attestation SHALL NOT be considered valid unless the Issuer possesses a valid, non-revoked CertificationCredential (UORACertificationCredential) for the specific attestation type and product category.
{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://w3id.org/uora/contexts/core.jsonld"
],
"id": "urn:uuid:cert-credential-id",
"type": ["VerifiableCredential", "UORACertificationCredential"],
"issuer": {
"id": "did:web:trust-anchor.example.com",
"name": "Pharmaceutical Trust Anchor Consortium"
},
"validFrom": "2026-01-01T00:00:00Z",
"validUntil": "2027-01-01T00:00:00Z",
"credentialStatus": {
"id": "https://trust-anchor.example.com/status/1#42",
"type": "BitstringStatusListEntry",
"statusPurpose": "revocation",
"statusListIndex": "42",
"statusListCredential": "https://trust-anchor.example.com/status/1"
},
"credentialSubject": {
"id": "did:web:certified-issuer.example.com",
"authorizedAttestations": [
"UORAOriginAttestation",
"UORATransferAttestation",
"UORATransformationAttestation"
],
"authorizedCategories": ["pharmaceuticals", "medical-devices"],
"authorizedRegions": ["global"],
"authorityPrecedence": 50,
"trustFramework": "https://w3id.org/verifiable-supply-chain/trust-frameworks/pharma-v1"
}
}
authorityPrecedence field is the canonical source of an Issuer's precedence rank. It MUST match the value in the Trust Framework's authority precedence table. In case of conflict, the CertificationCredential value is authoritative.Every UORAAttestation MUST include an authorizedBy claim referencing a valid CertificationCredential.
{
"credentialSubject": {
"authorizedBy": {
"certificationId": "urn:uuid:cert-credential-id",
"trustFramework": "https://w3id.org/verifiable-supply-chain/trust-frameworks/pharma-v1",
"verifiedAt": "2026-04-28T10:30:00Z"
}
}
}
A Verifier SHALL reject any attestation where:
| Condition | Rejection Status | Remediation |
|---|---|---|
The authorizedBy claim is absent | rejected_missing_authorization | Issuer MUST add the claim and reissue |
The referenced certificationId cannot be resolved | rejected_unauthorized_issuer | Issuer MUST obtain a valid CertificationCredential |
The attestation's validFrom falls outside the certification's validity window | rejected_expired_certification | Issuer MUST renew certification before issuing |
| The CertificationCredential is revoked (per BitstringStatusList check) | rejected_revoked_certification | Issuer MUST obtain a new CertificationCredential |
The attestation type is not in authorizedAttestations | rejected_unauthorized_category | Issuer MUST obtain certification for this type |
The Issuer DID does not match credentialSubject.id | rejected_unauthorized_issuer | Attestation MUST be issued by the certified entity |
Trust Anchors MUST implement credential revocation using the W3C Bitstring Status List specification [[BITSTRING-STATUS-LIST]]. Verifiers MUST check the revocation status of every CertificationCredential during Phase 5 validation. The revocation check is not optional and MUST NOT be cached for longer than 5 minutes.
/* Revocation check algorithm */
1. Resolve credentialStatus.statusListCredential → StatusListCredential VC
2. Verify the StatusListCredential's proof
3. Decode the compressed bitstring (GZIP + base64url)
4. Check bit at index credentialStatus.statusListIndex
5. If bit = 1: reject with rejected_revoked_certification
6. If bit = 0: credential is valid (proceed with other checks)
Every UORA attestation SHALL reference its immediate predecessor through an antecedent property, creating a cryptographically linked, verifiable history chain. Conflict resolution cascade is governed by authority precedence — not timestamps — as the primary discriminator.
For sequential events, the antecedent is a single attestation ID:
{ "credentialSubject": { "antecedent": "urn:uuid:previous-attestation-id" } }
Implementations SHALL normalize single antecedent strings to arrays during processing. The first attestation in an object's lifecycle SHALL have "antecedent": null.
For transformations and aggregations, antecedent SHALL accept an ordered array:
{
"credentialSubject": {
"antecedent": [
"urn:uuid:component-a-final-state",
"urn:uuid:component-b-final-state",
"urn:uuid:component-c-final-state"
]
}
}
DAG Traversal Limits (Normative):
| Limit | Value | Rationale |
|---|---|---|
| Maximum DAG depth | 64 hops | Prevents unbounded recursion in deeply nested assembly chains |
| Maximum antecedent array width | 256 entries | Limits fan-in for aggregation events |
| Cycle detection | REQUIRED | Resolvers MUST maintain a visited-ID set; reject cycles with rejected_cyclic_chain |
| Traversal timeout | 30 seconds | Reject with rejected_traversal_timeout if exceeded |
DAG Validation Rule: A Verifier SHALL recursively validate every referenced branch. The attestation is valid only if ALL antecedent branches resolve to valid, non-superseded terminal states within the depth and timeout limits.
If a Verifier encounters two attestations claiming to succeed the same antecedent, the conflict SHALL be resolved using this deterministic, three-tier cascade.
The attestation from the Issuer with the higher authorityPrecedence value prevails. Always resolvable without examining timestamps. Governance supersedes chronology.
If Issuers have identical authority precedence, the attestation with the later validFrom timestamp prevails (1-second tolerance). Reached only when co-equal authorities record competing events.
If authority and timestamps are equal, the attestation with the lexicographically smaller id prevails. Deterministic, requires no external input.
Authority Precedence Table Example:
{
"trustFramework": "https://w3id.org/verifiable-supply-chain/trust-frameworks/pharma-v1",
"authorityPrecedenceTable": {
"did:web:fda.example.gov": 100,
"did:web:manufacturer.example.com": 50,
"did:web:wholesaler.example.com": 30,
"did:web:retailer.example.com": 10
}
}
Preservation Requirement: The losing attestation SHALL be preserved with "status": "superseded" and "supersededBy" set to the winning attestation's ID. Superseded attestations SHALL NOT be used for state computation.
For Transfer and Disposition attestations, the Verifier SHALL enforce linear custody chain enforcement. The antecedent MUST reference the most recent valid event.
| Attestation | Antecedent | Most Recent Valid | Result |
|---|---|---|---|
| Origin | null | — | valid |
| Transfer A (maker → distributor) | origin-id | origin-id | valid |
| Transfer B (distributor → retailer) | transfer-a-id | transfer-a-id | valid |
| Transfer C — VIOLATION | origin-id | transfer-b-id | rejected_linear_chain_violation |
Exemptions: Origin events (antecedent: null) and Transformation events (multi-antecedent DAG) are exempt.
All UORA attestations SHALL extend this abstract base type.
["VerifiableCredential", "UORAAttestation", ""]
| Property | Required | Type | Description |
|---|---|---|---|
@context | MUST | Array | VC v2 context + UORA Core context URI |
type | MUST | Array | Full type chain: VerifiableCredential, UORAAttestation, exactly one concrete subtype |
id | MUST | URI | Globally unique identifier: urn:uuid:{uuid-v4} |
issuer.id | MUST | URI | DID of the certified Issuer |
validFrom | MUST | DateTime | ISO 8601 UTC timestamp of the attested event |
credentialSubject.id | MUST | URI | DID of the physical object |
credentialSubject.eventType | MUST | String | Origin, Transfer, Transformation, Disposition |
credentialSubject.antecedent | MUST | URI | Array | null | Predecessor attestation ID(s); null for origin events |
credentialSubject.authorizedBy | MUST | Object | Certification reference |
evidence | MUST | Array | At least one evidence entry |
proof | MUST | Object | W3C Data Integrity Proof |
Documents the creation, manufacture, mining, or harvest of a physical object. Root attestation; antecedent MUST be null.
| Property | Required | Type | Description |
|---|---|---|---|
originType | MUST | String | manufactured, mined, harvested, created |
originLocation | MUST | URI | Physical location of origin (GS1 GLN or equivalent) |
originDate | MUST | DateTime | Timestamp of creation event |
productionBatch | OPTIONAL | String | Batch or lot identifier |
inputMaterials | OPTIONAL | Array<URI> | DID references to source materials |
Documents a change of custody, ownership, or physical location. Antecedent MUST reference the most recent valid event per linear custody chain enforcement.
| Property | Required | Type | Description |
|---|---|---|---|
transferType | MUST | String | custody, ownership, location |
fromParty | MUST | URI | DID of transferring party |
toParty | MUST | URI | DID of receiving party |
fromLocation | OPTIONAL | URI | Origin location before transfer |
toLocation | OPTIONAL | URI | Destination location after transfer |
Documents processing, assembly, or modification. Only type supporting multi-antecedent DAG chains. Exempt from linear custody chain enforcement.
| Property | Required | Type | Description |
|---|---|---|---|
transformationType | MUST | String | assembly, processing, modification, disassembly |
inputObjects | MUST | Array<URI> | DID references to consumed input objects |
outputObjects | MUST | Array<URI> | DID references to produced output objects |
inputQuantities | OPTIONAL | Array | Quantities of each input |
outputQuantities | OPTIONAL | Array | Quantities of each output |
transformationLocation | OPTIONAL | URI | Physical location of transformation |
processCertification | OPTIONAL | URI | Reference to process certification |
Antecedent rule: SHALL be an array with at least one entry, one per inputObject. Subject to DAG traversal limits.
Documents end-of-life, recycling, or decommissioning. Terminal attestation; no attestation SHALL reference a Disposition as its antecedent.
| Property | Required | Type | Description |
|---|---|---|---|
dispositionType | MUST | String | recycled, decommissioned, destroyed, lost |
dispositionLocation | OPTIONAL | URI | Physical location of disposition |
environmentalCertification | OPTIONAL | URI | Reference to environmental compliance certification |
{
"evidence": [
{
"id": "urn:uuid:evidence-unique-id",
"type": ["Evidence"],
"name": "Human-readable evidence name",
"description": "Description of the evidence"
}
]
}
{
"evidence": [
{
"id": "urn:uuid:evidence-unique-id",
"type": ["Evidence", "EPCISObjectEvent"],
"name": "EPCIS Shipping Event",
"description": "Original EPCIS ObjectEvent with bizStep=shipping",
"epcisEventID": "ni:///sha-256;abc123def456?ver=CBV2.0",
"epcisEventType": "ObjectEvent",
"epcisBizStep": "shipping"
}
]
}
| Data Size | Recommendation | Rationale |
|---|---|---|
| < 1 KB | Embed directly in evidence | Negligible overhead; always available |
| 1 KB – 1 MB | Reference via externalData | Balance availability with chain size |
| > 1 MB | MUST reference via externalData | Avoid large payloads in attestation history |
{
"externalData": {
"uri": "https://storage.example.com/report-001.pdf",
"hash": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"size": 1048576,
"accessPolicy": "holder-controlled",
"encryptedFor": null
}
}
The accessPolicy field MUST be one of: public, holder-controlled, or encrypted. Verifiers MUST respect access policies.
Every attestation MUST carry a W3C Data Integrity Proof.
{
"proof": {
"type": "Ed25519Signature2020",
"created": "2026-04-28T10:30:00Z",
"verificationMethod": "did:web:shipper.example.com#key-1",
"proofPurpose": "assertionMethod",
"proofValue": "z5LbLbR3qZ5Y6m8n9o0p1q2r3s4t5u6v7w8x9y0za1b2c3d4..."
}
}
| Field | Required | Description |
|---|---|---|
type | MUST | Ed25519Signature2020, BbsBlsSignature2020, or DataIntegrityProof |
created | MUST | ISO 8601 UTC timestamp of proof creation |
verificationMethod | MUST | DID URL of the signing key |
proofPurpose | MUST | MUST be assertionMethod |
proofValue | MUST | Cryptographic proof value |
BbsBlsSignature2020 [[BBS-2023]] proofs enable selective disclosure. Support for BbsBlsSignature2020 is REQUIRED for Full Compliance.
The UORA Resolver implements a seven-phase validation pipeline. Each phase executes sequentially; a rejection in any phase terminates processing.
id is a valid UUID URN; id is not a duplicate.type array contains VerifiableCredential, UORAAttestation, and exactly one concrete subtype.validFrom is not more than 5 seconds in the future.| Status | Phase | Description |
|---|---|---|
valid | — | All seven phases passed; attestation is authoritative for state computation |
superseded | 7 | Structurally valid; lost conflict resolution cascade; preserved for audit, not used for state |
rejected_missing_field | 1 | Required field absent |
rejected_duplicate_id | 1 | Attestation ID already processed |
rejected_invalid_type | 2 | Type declaration mismatch or missing concrete subtype |
rejected_invalid_event_type | 2 | eventType does not match declared subtype |
rejected_future_timestamp | 3 | validFrom more than 5 seconds in the future |
rejected_invalid_proof | 4 | Proof missing, unrecognized type, or cryptographic verification failure |
rejected_missing_authorization | 5 | No authorizedBy claim |
rejected_unauthorized_issuer | 5 | Issuer lacks valid CertificationCredential or DID mismatch |
rejected_expired_certification | 5 | CertificationCredential expired at attestation time |
rejected_revoked_certification | 5 | CertificationCredential revoked per BitstringStatusList |
rejected_unauthorized_category | 5 | Attestation type not in certification's authorized scope |
rejected_broken_chain | 6 | Antecedent reference cannot be resolved or DAG branch invalid |
rejected_linear_chain_violation | 6 | Transfer/Disposition does not reference the most recent valid event |
rejected_cyclic_chain | 6 | Antecedent graph contains a cycle |
rejected_traversal_timeout | 6 | DAG traversal exceeded 30-second time limit |
| State | Meaning | Action Required |
|---|---|---|
intact | Antecedent chain is complete and all referenced attestations are valid | None — automated processing continues |
broken | Unresolvable antecedent, DAG branch failure, cycle, or traversal timeout | Manual investigation required |
invalid | Attestation failed structural or governance validation (Phases 1–5) | Attestation must be reissued with corrections |
A complete, well-formed UORATransferAttestation that passes all seven validation phases.
{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://w3id.org/uora/contexts/core.jsonld",
"https://w3id.org/verifiable-supply-chain/contexts/shipping.jsonld"
],
"id": "urn:uuid:550e8400-e29b-41d4-a716-446655440000",
"type": [
"VerifiableCredential",
"UORAAttestation",
"UORATransferAttestation",
"VerifiableShippingEvent"
],
"issuer": {
"id": "did:web:shipper.example.com",
"name": "Global Logistics Co."
},
"validFrom": "2026-04-28T10:30:00.000Z",
"credentialSubject": {
"id": "did:web:maker.example.com:object:serial:SN-001",
"eventType": "Transfer",
"transferType": "custody",
"fromParty": "did:web:shipper.example.com",
"toParty": "did:web:receiver.example.com",
"fromLocation": "https://id.gs1.org/414/9521321000010",
"toLocation": "https://id.gs1.org/414/9521321000096",
"antecedent": "urn:uuid:previous-origin-attestation-id",
"authorizedBy": {
"certificationId": "urn:uuid:shipper-cert-credential",
"trustFramework": "https://w3id.org/verifiable-supply-chain/trust-frameworks/logistics-v1",
"verifiedAt": "2026-04-28T10:30:00Z"
}
},
"evidence": [
{
"id": "urn:uuid:evidence-001",
"type": ["Evidence", "EPCISObjectEvent"],
"name": "Custody Transfer Handover",
"description": "Digital handover confirmation at dock door R1",
"epcisEventID": "ni:///sha-256;abc123def456?ver=CBV2.0",
"epcisEventType": "ObjectEvent",
"epcisBizStep": "shipping"
}
],
"proof": {
"type": "Ed25519Signature2020",
"created": "2026-04-28T10:30:00Z",
"verificationMethod": "did:web:shipper.example.com#key-1",
"proofPurpose": "assertionMethod",
"proofValue": "z5LbLbR3qZ5Y6m8n9o0p1q2r3s4t5u6v7w8x9y0za1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
}
}
Produces attestations conforming to the data model. Passes Phases 1–2. Ed25519Signature2020 required. 9 test vectors (Categories A + B).
Meets Data compliance + implements UORA-Query-API with all six endpoints. Passes Phases 1–5 including revocation checks. 16 test vectors (Categories A–E + J).
Meets Protocol compliance + all seven phases including DAG limits, cycle detection, conflict resolution cascade, and BBS+ selective disclosure. All 22 test vectors.
The following test vectors are normative. Test vectors are also published at https://w3id.org/uora/conformance/v1.0/vectors.json.
Well-formed UORAOriginAttestation with Ed25519 proof and valid CertificationCredential. Expected: accepted
Well-formed UORATransferAttestation referencing a valid origin. Expected: accepted
Well-formed UORATransformationAttestation with two-element DAG antecedent. Expected: accepted
Well-formed UORADispositionAttestation as terminal event. Expected: accepted
Missing issuer.id. Expected: rejected — rejected_missing_field
Duplicate UUID submission. Expected: rejected — rejected_duplicate_id, HTTP 409
Type array missing concrete subtype. Expected: rejected — rejected_invalid_type
validFrom 60 seconds in the future. Expected: rejected — rejected_future_timestamp
Origin attestation with non-null antecedent. Expected: rejected — rejected_broken_chain
authorizedBy claim absent. Expected: rejected — rejected_missing_authorization
CertificationCredential expired before attestation. Expected: rejected — rejected_expired_certification
Attestation type not in authorized scope. Expected: rejected — rejected_unauthorized_category
CertificationCredential revoked in BitstringStatusList. Expected: rejected — rejected_revoked_certification
Entire proof section omitted. Expected: rejected — rejected_invalid_proof
Incorrect proofValue — signature verification fails. Expected: rejected — rejected_invalid_proof
Higher authority precedence wins (100 vs 50). Expected: A valid, B superseded. Tier 1 resolves.
Equal authority; later timestamp wins. Expected: A valid, B superseded. Tier 2 resolves.
Equal authority and timestamp; smaller UUID wins. Expected: A valid, B superseded. Tier 3 resolves.
Antecedent references non-existent UUID. Expected: rejected — rejected_broken_chain
DAG branch resolves to superseded attestation. Expected: rejected — rejected_broken_chain
Transfer skips over a more recent valid event. Expected: rejected — rejected_linear_chain_violation
Transfer correctly references most recent valid event. Expected: accepted
Antecedent graph contains cycle (A→B→C→A). Expected: rejected — rejected_cyclic_chain
Transfer attestation with EPCIS ObjectEvent evidence. Expected: accepted
BbsBlsSignature2020 derived presentation with omitted location fields. Expected: accepted
Domain-specific profiles SHALL extend the appropriate UORA attestation subtype and add industry-specific claims. Profiles SHALL NOT modify or override governance validation rules, antecedent chaining, linear custody chain enforcement, DAG traversal limits, or conflict resolution cascade.
| UORA Attestation | Example Domain Profiles | EPCIS Mapping |
|---|---|---|
| UORAOriginAttestation | VerifiableCommissioningEvent | ObjectEvent (ADD, bizStep=commissioning) |
| UORATransferAttestation | VerifiableShippingEvent, VerifiableReceivingEvent | ObjectEvent (OBSERVE, bizStep=shipping/receiving) |
| UORATransformationAttestation | VerifiableManufacturingEvent, VerifiableAssemblyEvent | TransformationEvent |
| UORADispositionAttestation | VerifiableRecyclingEvent, VerifiableDestructionEvent | ObjectEvent (DELETE) |
This section is normative. UORA attestations are designed for supply chain transparency, but the long-lived nature of object identifiers and immutable event histories creates significant privacy risks that implementations MUST address.
Issuers SHOULD use BbsBlsSignature2020 proofs [[BBS-2023]] to enable Holders to present derived credentials disclosing only required claims. Verifiers MUST NOT require disclosure beyond their stated verification purpose and MUST document minimum required claim sets.
Implementations MAY use pairwise DIDs — distinct DIDs presented to different Verifiers for the same physical object — to prevent cross-Verifier correlation. Resolvers MUST support DID aliasing.
Resolvers and Verifiers MUST enforce the accessPolicy field:
public: Any Verifier MAY fetch without authorization.holder-controlled: Verifier MUST obtain explicit Holder authorization.encrypted: Only the designated Verifier can decrypt. Resolvers treat as opaque.The evidence array SHOULD contain only data necessary for verification. Large payloads MUST be referenced via externalData. Evidence entries MUST NOT include PII about natural persons without explicit consent.
Implementations in jurisdictions with data erasure requirements MUST implement Resolver-level access controls rather than modifying or deleting attestations. PII MUST be placed in holder-controlled or encrypted externalData so that access revocation effectively implements erasure without chain corruption.
Issuers MAY encrypt sensitive claims using the intended Verifier's public key. Resolvers MUST store encrypted fields as opaque blobs and MUST NOT attempt decryption.
Authority precedence is the primary conflict resolution cascade discriminator. A forged timestamp can only influence Tier 2, reached only when Issuers have identical authority precedence. The losing attestation is always preserved with supersededBy, making manipulation detectable in audit.
Mitigated by mandatory CertificationCredential validation at Phase 5, Trust Anchor ability to revoke compromised certifications via BitstringStatusList (max 5-minute cache), and cryptographic binding of every attestation to the Issuer's DID.
Attestations with unresolvable antecedents are flagged chainIntegrity: "broken" and require manual investigation.
Linear custody chain enforcement prevents insertion of Transfer attestations that skip over legitimate custody events.
Mitigated by UUID URN uniqueness and duplicate ID rejection at Phase 1.
Normative DAG depth limit (64 hops), width limit (256 entries), and traversal timeout (30 seconds) bound computational cost.
Resolvers MUST implement visited-ID cycle detection and reject cyclic graphs with rejected_cyclic_chain.
The 5-minute revocation cache maximum limits the window during which revoked credentials may be accepted. Trust Anchors SHOULD implement emergency revocation procedures.
The canonical JSON-LD context is published at:
https://w3id.org/uora/contexts/core.jsonld
{
"@context": {
"@version": 1.1,
"@protected": true,
"id": "@id",
"type": "@type",
"UORAAttestation": { "@id": "https://w3id.org/uora#UORAAttestation" },
"UORAOriginAttestation": { "@id": "https://w3id.org/uora#UORAOriginAttestation" },
"UORATransferAttestation": { "@id": "https://w3id.org/uora#UORATransferAttestation" },
"UORATransformationAttestation": { "@id": "https://w3id.org/uora#UORATransformationAttestation" },
"UORADispositionAttestation": { "@id": "https://w3id.org/uora#UORADispositionAttestation" },
"UORACertificationCredential": { "@id": "https://w3id.org/uora#UORACertificationCredential" },
"eventType": "uora:eventType",
"antecedent": { "@id": "uora:antecedent", "@type": "@id" },
"authorizedBy": "uora:authorizedBy",
"originType": "uora:originType",
"originLocation": { "@id": "uora:originLocation", "@type": "@id" },
"originDate": { "@id": "uora:originDate", "@type": "xsd:dateTime" },
"productionBatch": "uora:productionBatch",
"inputMaterials": { "@id": "uora:inputMaterials", "@type": "@id", "@container": "@set" },
"transferType": "uora:transferType",
"fromParty": { "@id": "uora:fromParty", "@type": "@id" },
"toParty": { "@id": "uora:toParty", "@type": "@id" },
"fromLocation": { "@id": "uora:fromLocation", "@type": "@id" },
"toLocation": { "@id": "uora:toLocation", "@type": "@id" },
"transformationType": "uora:transformationType",
"inputObjects": { "@id": "uora:inputObjects", "@type": "@id", "@container": "@set" },
"outputObjects": { "@id": "uora:outputObjects", "@type": "@id", "@container": "@set" },
"inputQuantities": "uora:inputQuantities",
"outputQuantities": "uora:outputQuantities",
"transformationLocation": { "@id": "uora:transformationLocation", "@type": "@id" },
"processCertification": { "@id": "uora:processCertification", "@type": "@id" },
"dispositionType": "uora:dispositionType",
"dispositionLocation": { "@id": "uora:dispositionLocation", "@type": "@id" },
"environmentalCertification": { "@id": "uora:environmentalCertification", "@type": "@id" },
"certificationId": { "@id": "uora:certificationId", "@type": "@id" },
"trustFramework": { "@id": "uora:trustFramework", "@type": "@id" },
"verifiedAt": { "@id": "uora:verifiedAt", "@type": "xsd:dateTime" },
"authorityPrecedence": "uora:authorityPrecedence",
"authorizedAttestations": "uora:authorizedAttestations",
"authorizedCategories": "uora:authorizedCategories",
"authorizedRegions": "uora:authorizedRegions",
"accessPolicy": "uora:accessPolicy",
"encryptedFor": { "@id": "uora:encryptedFor", "@type": "@id" },
"externalData": "uora:externalData",
"physicalBinding": "uora:physicalBinding",
"epcisEventID": "uora:epcisEventID",
"epcisEventType": "uora:epcisEventType",
"epcisBizStep": "uora:epcisBizStep",
"uora": "https://w3id.org/uora#",
"xsd": "http://www.w3.org/2001/XMLSchema#"
}
}