Supply Chain & Regulatory Compliance (EUDR) with Earth Observation
This guide demonstrates how to build a supply chain compliance system for the EU Deforestation Regulation (EUDR) using TrustWeave and Earth Observation data. You’ll learn how to create verifiable credentials that prove geospatial non-deforestation for every shipment.
What You’ll Build
By the end of this tutorial, you’ll have:
- ✅ Created DIDs for importers, exporters, and verifiers
- ✅ Issued verifiable credentials for geospatial non-deforestation proof
- ✅ Built Digital Product Passport (DPP) using VCs
- ✅ Implemented automated compliance verification
- ✅ Created EO data evidence for deforestation monitoring
- ✅ Anchored compliance credentials to blockchain for audit trails
Big Picture & Significance
The EU Deforestation Regulation (EUDR)
By 2025, importers must prove geospatial non-deforestation for every shipment. Current systems rely on self-declared PDFs, creating a trust gap. Verifiable Credentials provide cryptographic proof of compliance.
Industry Context:
- Regulatory Requirement: EUDR mandatory by 2025
- Scope: All imports of commodities (coffee, cocoa, palm oil, etc.)
- Current Gap: Self-declared PDFs are not verifiable
- Solution: W3C Verifiable Credentials to model farm identity and compliance status
- EO Data: Earth Observation data provides verifiable evidence of non-deforestation
Why This Matters:
- Regulatory Compliance: Meet EUDR requirements with verifiable proof
- Geospatial Proof: EO data provides verifiable evidence of non-deforestation
- Digital Product Passport: VCs are leading candidate for DPP implementation
- Trust: Cryptographic proof prevents fraud and false declarations
- Automation: Enable automated compliance verification
- Audit Trails: Complete compliance history for regulators
Real-World Examples
EU Deforestation Regulation (EUDR):
- Requirement: By 2025, importers must prove geospatial non-deforestation
- Current Gap: Self-declared PDFs
- Solution: AgrospAI and similar Agri-food data spaces testing W3C Verifiable Credentials
Climate TRACE:
- Independent tracking of GHG emissions using satellites and AI
- Acts as “Global Verifier”
- Emitter could issue VC claiming “Low Emissions”
- Verifier checks against Climate TRACE data to validate/refute automatically
Value Proposition
Problems Solved
- Regulatory Compliance: Meet EUDR requirements with verifiable proof
- Geospatial Proof: EO data provides verifiable evidence
- Digital Product Passport: Standard format for DPP implementation
- Automated Verification: Enable automated compliance checks
- Fraud Prevention: Cryptographic proof prevents false declarations
- Audit Trails: Complete compliance history
- Trust: Build trust through verifiable credentials
Business Benefits
For Importers:
- Compliance: Meet EUDR requirements automatically
- Risk Reduction: Reduce risk of non-compliance penalties
- Efficiency: Automated verification reduces costs
- Trust: Build trust with regulators and consumers
For Exporters:
- Market Access: Access EU market with verifiable compliance
- Differentiation: Stand out with verifiable credentials
- Efficiency: Reduce compliance documentation costs
- Trust: Build trust with importers
For Regulators:
- Verification: Automated compliance verification
- Audit Trails: Complete compliance history
- Transparency: Verifiable data lineage
- Efficiency: Reduce manual verification costs
Understanding the Problem
EUDR compliance needs:
- Geospatial Proof: Prove non-deforestation for specific locations
- Digital Product Passport: Standard format for product information
- Automated Verification: Enable automated compliance checks
- Audit Trails: Complete compliance history
- EO Data Evidence: Use Earth Observation data as proof
- Trust: Cryptographic proof prevents fraud
Prerequisites
- Java 21+
- Kotlin 2.2.0+
- Gradle 8.5+
- Basic understanding of Kotlin and coroutines
- Understanding of EUDR requirements
Step 1: Add Dependencies
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
dependencies {
// Core TrustWeave modules
implementation("com.trustweave:trustweave-all:1.0.0-SNAPSHOT")
// Test kit for in-memory implementations
testImplementation("com.trustweave:trustweave-testkit:1.0.0-SNAPSHOT")
// Optional: Algorand adapter for real blockchain anchoring
implementation("com.trustweave.chains:algorand:1.0.0-SNAPSHOT")
// Kotlinx Serialization
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
// Coroutines
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
}
Step 2: Complete Runnable Example
Here’s a complete EUDR compliance workflow:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
package com.example.eudr.compliance
import com.trustweave.TrustWeave
import com.trustweave.core.*
import com.trustweave.json.DigestUtils
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.*
import java.time.Instant
fun main() = runBlocking {
println("=".repeat(70))
println("EUDR Compliance with EO Data - Complete Example")
println("=".repeat(70))
// Step 1: Create TrustWeave instance
val trustWeave = TrustWeave.build {
factories(
kmsFactory = TestkitKmsFactory(),
didMethodFactory = TestkitDidMethodFactory()
)
keys { provider("inMemory"); algorithm("Ed25519") }
did { method("key") { algorithm("Ed25519") } }
}
println("\n✅ TrustWeave initialized")
// Step 2: Create DIDs for exporter, importer, and verifier
import com.trustweave.trust.types.DidCreationResult
val exporterDidResult = trustWeave.createDid { method("key") }
val exporterDid = when (exporterDidResult) {
is DidCreationResult.Success -> {
println("✅ Exporter DID: ${exporterDidResult.did.value}")
exporterDidResult.did
}
else -> {
println("Failed to create exporter DID: ${exporterDidResult.reason}")
return@runBlocking
}
}
val importerDidResult = trustWeave.createDid { method("key") }
val importerDid = when (importerDidResult) {
is DidCreationResult.Success -> {
println("✅ Importer DID: ${importerDidResult.did.value}")
importerDidResult.did
}
else -> {
println("Failed to create importer DID: ${importerDidResult.reason}")
return@runBlocking
}
}
val verifierDidResult = trustWeave.createDid { method("key") }
val verifierDid = when (verifierDidResult) {
is DidCreationResult.Success -> {
println("✅ Verifier DID: ${verifierDidResult.did.value}")
verifierDidResult.did
}
else -> {
println("Failed to create verifier DID: ${verifierDidResult.reason}")
return@runBlocking
}
}
// Step 3: Create farm/production site DID
val farmDidResult = trustWeave.createDid { method("key") }
val farmDid = when (farmDidResult) {
is DidCreationResult.Success -> {
println("✅ Farm DID: ${farmDidResult.did.value}")
farmDidResult.did
}
else -> {
println("Failed to create farm DID: ${farmDidResult.reason}")
return@runBlocking
}
}
// Step 4: Create EO data evidence (non-deforestation proof)
val eoDeforestationProof = buildJsonObject {
put("id", "eo-deforestation-proof-2024")
put("type", "NonDeforestationProof")
put("location", buildJsonObject {
put("latitude", -3.4653)
put("longitude", -62.2159)
put("region", "Amazon Rainforest, Brazil")
put("farmId", farmDid.id)
put("polygon", buildJsonArray {
// Farm boundary coordinates
add(buildJsonArray { add(-62.22); add(-3.47) })
add(buildJsonArray { add(-62.21); add(-3.47) })
add(buildJsonArray { add(-62.21); add(-3.46) })
add(buildJsonArray { add(-62.22); add(-3.46) })
add(buildJsonArray { add(-62.22); add(-3.47) })
})
})
put("analysis", buildJsonObject {
put("method", "Sentinel-2 L2A Time Series Analysis")
put("analysisPeriod", buildJsonObject {
put("startDate", "2020-01-01")
put("endDate", "2024-12-31")
})
put("deforestationDetected", false)
put("forestCoverChange", 0.02) // 2% increase (reforestation)
put("confidence", 0.95)
put("verificationDate", Instant.now().toString())
})
put("timestamp", Instant.now().toString())
}
val eoProofDigest = DigestUtils.sha256DigestMultibase(eoDeforestationProof)
// Step 5: Verifier issues compliance credential
val verifierResolution = trustWeave.resolveDid(verifierDid)
val verifierDoc = when (verifierResolution) {
is DidResolutionResult.Success -> verifierResolution.document
else -> throw IllegalStateException("Failed to resolve verifier DID")
}
val verifierKeyId = verifierDoc.verificationMethod.firstOrNull()?.id?.substringAfter("#")
?: throw IllegalStateException("No verification method found")
val complianceCredential = TrustWeave.issueCredential(
issuerDid = verifierDid.value,
issuerKeyId = verifierKeyId,
credentialSubject = buildJsonObject {
put("id", "eudr-compliance-2024-001")
put("complianceType", "EUDR")
put("farm", buildJsonObject {
put("id", farmDid.value)
put("name", "Sustainable Coffee Farm")
put("location", buildJsonObject {
put("latitude", -3.4653)
put("longitude", -62.2159)
put("country", "Brazil")
})
})
put("eoEvidence", eoDeforestationProof)
put("eoEvidenceDigest", eoProofDigest)
put("complianceStatus", "compliant")
put("verificationDate", Instant.now().toString())
put("validUntil", Instant.now().plus(365, java.time.temporal.ChronoUnit.DAYS).toString())
put("verifier", verifierDid.value)
},
types = listOf("VerifiableCredential", "EUDRComplianceCredential")
).fold(
onSuccess = { it },
onFailure = { error ->
println("❌ Failed to issue compliance credential: ${error.message}")
return@runBlocking
}
)
println("✅ Compliance Credential issued: ${complianceCredential.id}")
println(" Status: compliant")
println(" Farm: ${farmDid.id}")
// Step 6: Create Digital Product Passport (DPP)
val exporterResolution = trustWeave.resolveDid(exporterDid)
val exporterDoc = when (exporterResolution) {
is DidResolutionResult.Success -> exporterResolution.document
else -> throw IllegalStateException("Failed to resolve exporter DID")
}
val exporterKeyId = exporterDoc.verificationMethod.firstOrNull()?.id?.substringAfter("#")
?: throw IllegalStateException("No verification method found")
val dppCredential = TrustWeave.issueCredential(
issuerDid = exporterDid.value,
issuerKeyId = exporterKeyId,
credentialSubject = buildJsonObject {
put("id", "dpp-coffee-shipment-2024-001")
put("productType", "Coffee")
put("commodity", "Coffee Beans")
put("quantity", 10000.0) // kg
put("unit", "kg")
put("farm", farmDid.value)
put("complianceCredentialId", complianceCredential.id)
put("eoEvidenceDigest", eoProofDigest)
put("harvestDate", "2024-06-15")
put("exportDate", Instant.now().toString())
put("exporter", exporterDid.value)
put("destination", "EU")
},
types = listOf("VerifiableCredential", "DigitalProductPassport", "EUDRProductCredential")
).fold(
onSuccess = { it },
onFailure = { error ->
println("❌ Failed to issue DPP: ${error.message}")
return@runBlocking
}
)
println("✅ Digital Product Passport issued: ${dppCredential.id}")
println(" Product: Coffee Beans")
println(" Quantity: 10,000 kg")
// Step 7: Importer verifies compliance before import
val dppVerification = TrustWeave.verifyCredential(dppCredential).fold(
onSuccess = { it },
onFailure = { error ->
println("❌ DPP verification failed: ${error.message}")
return@runBlocking
}
)
if (!dppVerification.valid) {
println("❌ DPP verification failed: ${dppVerification.errors}")
return@runBlocking
}
println("✅ DPP verified")
// Step 8: Verify compliance credential
val complianceVerification = TrustWeave.verifyCredential(complianceCredential).fold(
onSuccess = { it },
onFailure = { error ->
println("❌ Compliance verification failed: ${error.message}")
return@runBlocking
}
)
if (!complianceVerification.valid) {
println("❌ Compliance verification failed: ${complianceVerification.errors}")
return@runBlocking
}
println("✅ Compliance verified")
// Step 9: Verify EO evidence integrity
val currentProofDigest = DigestUtils.sha256DigestMultibase(eoDeforestationProof)
val credentialProofDigest = complianceCredential.credentialSubject
.jsonObject["eoEvidenceDigest"]?.jsonPrimitive?.content ?: ""
if (currentProofDigest == credentialProofDigest) {
println("✅ EO Evidence integrity verified")
println(" No tampering detected")
} else {
println("❌ EO Evidence integrity FAILED")
println(" Evidence may have been tampered with")
return@runBlocking
}
// Step 10: Check against Climate TRACE (global verifier)
val climateTraceVerification = verifyAgainstClimateTrace(
location = buildJsonObject {
put("latitude", -3.4653)
put("longitude", -62.2159)
},
eoEvidence = eoDeforestationProof
)
if (climateTraceVerification) {
println("✅ Verified against Climate TRACE")
println(" Global verification confirms compliance")
} else {
println("⚠️ Climate TRACE verification inconclusive")
}
// Step 11: Anchor to blockchain for audit trail
val anchorResult = trustWeave.blockchains.anchor(
data = dppCredential,
serializer = VerifiableCredential.serializer(),
chainId = "algorand:testnet"
).fold(
onSuccess = { anchor ->
println("✅ DPP anchored: ${anchor.ref.txHash}")
anchor
},
onFailure = { error ->
println("❌ Anchoring failed: ${error.message}")
null
}
)
println("\n📊 EUDR Compliance Summary:")
println(" Farm: ${farmDid.id}")
println(" Compliance Status: compliant")
println(" EO Evidence: verified")
println(" DPP: issued and verified")
println(" Blockchain Anchor: ${anchorResult?.ref?.txHash}")
println(" ✅ Ready for EU import")
println("\n" + "=".repeat(70))
println("✅ EUDR Compliance Scenario Complete!")
println("=".repeat(70))
}
// Helper function to verify against Climate TRACE
suspend fun verifyAgainstClimateTrace(
location: JsonObject,
eoEvidence: JsonObject
): Boolean {
// In production, query Climate TRACE API
// Compare EO evidence with Climate TRACE data
// Return true if evidence matches Climate TRACE data
return true // Placeholder
}
Expected Output:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
======================================================================
EUDR Compliance with EO Data - Complete Example
======================================================================
✅ TrustWeave initialized
✅ Exporter DID: did:key:z6Mk...
✅ Importer DID: did:key:z6Mk...
✅ Verifier DID: did:key:z6Mk...
✅ Farm DID: did:key:z6Mk...
✅ Compliance Credential issued: urn:uuid:...
Status: compliant
Farm: did:key:z6Mk...
✅ Digital Product Passport issued: urn:uuid:...
Product: Coffee Beans
Quantity: 10,000 kg
✅ DPP verified
✅ Compliance verified
✅ EO Evidence integrity verified
No tampering detected
✅ Verified against Climate TRACE
Global verification confirms compliance
✅ DPP anchored: tx_...
📊 EUDR Compliance Summary:
Farm: did:key:z6Mk...
Compliance Status: compliant
EO Evidence: verified
DPP: issued and verified
Blockchain Anchor: tx_...
✅ Ready for EU import
======================================================================
✅ EUDR Compliance Scenario Complete!
======================================================================
Step 3: Automated Compliance Verification
Enable automated verification:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
suspend fun automatedEUDRVerification(
dppCredential: VerifiableCredential
): ComplianceResult {
// Verify DPP credential
val dppVerification = TrustWeave.verifyCredential(dppCredential).getOrThrow()
if (!dppVerification.valid) {
return ComplianceResult.NonCompliant("DPP verification failed")
}
// Extract compliance credential ID
val complianceCredentialId = extractComplianceCredentialId(dppCredential)
// Verify compliance credential
val complianceCredential = fetchCredential(complianceCredentialId)
val complianceVerification = TrustWeave.verifyCredential(complianceCredential).getOrThrow()
if (!complianceVerification.valid) {
return ComplianceResult.NonCompliant("Compliance verification failed")
}
// Verify EO evidence
val eoEvidence = extractEOEvidence(complianceCredential)
val eoVerification = verifyEOEvidence(eoEvidence)
if (!eoVerification.valid) {
return ComplianceResult.NonCompliant("EO evidence verification failed")
}
// Check against Climate TRACE
val climateTraceCheck = verifyAgainstClimateTrace(eoEvidence)
if (!climateTraceCheck) {
return ComplianceResult.NonCompliant("Climate TRACE verification failed")
}
return ComplianceResult.Compliant("All checks passed")
}
sealed class ComplianceResult {
data class Compliant(val message: String) : ComplianceResult()
data class NonCompliant(val reason: String) : ComplianceResult()
}
Step 4: Digital Product Passport (DPP) Structure
DPP using VCs:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
val dpp = buildJsonObject {
put("id", "dpp-product-001")
put("product", buildJsonObject {
put("type", "Coffee")
put("quantity", 10000.0)
put("unit", "kg")
})
put("compliance", buildJsonObject {
put("eudrCompliant", true)
put("complianceCredentialId", complianceCredential.id)
put("verificationDate", Instant.now().toString())
})
put("provenance", buildJsonObject {
put("farm", farmDid.id)
put("harvestDate", "2024-06-15")
put("exportDate", Instant.now().toString())
})
put("eoEvidence", buildJsonObject {
put("digest", eoProofDigest)
put("verificationStatus", "verified")
})
}
Step 5: Climate TRACE Integration
Verify against Climate TRACE as global verifier:
1
2
3
4
5
6
7
8
9
10
11
12
suspend fun verifyAgainstClimateTrace(
location: JsonObject,
eoEvidence: JsonObject
): Boolean {
// Query Climate TRACE API
val climateTraceData = queryClimateTraceAPI(location)
// Compare with EO evidence
val matches = compareWithClimateTrace(eoEvidence, climateTraceData)
return matches
}
Next Steps
- Explore Supply Chain Traceability Scenario
- Learn about Blockchain Anchoring
- Review Verification Policies
Related Documentation
- Supply Chain Traceability Scenario - Supply chain workflows
- Earth Observation Scenario - EO data integrity
- Blockchain Anchoring - Anchoring concepts
- API Reference - Complete API documentation