Beginner Tutorial Series
A structured learning path for developers new to TrustWeave and decentralized identity. Each tutorial builds on the previous one, introducing concepts progressively.
Learning Path Overview
This series takes you from zero to building production-ready applications with TrustWeave:
- Tutorial 1: Your First DID - Create and understand DIDs
- Tutorial 2: Issuing Your First Credential - Issue and verify credentials
- Tutorial 3: Managing Credentials with Wallets - Store and organize credentials
- Tutorial 4: Building a Complete Workflow - End-to-end issuer-holder-verifier flow
- Tutorial 5: Adding Blockchain Anchoring - Anchor data for tamper evidence
Prerequisites
Before starting:
- Kotlin basics: Variables, functions, classes, coroutines
- Development environment: Kotlin 2.2.0+, Java 21+, Gradle
- Installation: Follow the Installation Guide
Tutorial 1: Your First DID
Duration: 15-20 minutes Goal: Create and resolve your first DID
What You’ll Learn
- What DIDs are and why they matter
- How to create a DID using
did:key - How to resolve a DID to get its document
- Basic error handling with
Result<T>
Step 1: Setup
Create a new Kotlin project and add dependencies:
1
2
3
4
// build.gradle.kts
dependencies {
implementation("com.trustweave:trustweave-all:1.0.0-SNAPSHOT")
}
Step 2: Create TrustWeave Instance
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import com.trustweave.trust.TrustWeave
import com.trustweave.trust.dsl.credential.DidMethods
import com.trustweave.trust.dsl.credential.KeyAlgorithms
import com.trustweave.testkit.services.*
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
// Build TrustWeave instance (for tutorials, using testkit factories)
val trustWeave = TrustWeave.build {
factories(didMethodFactory = TestkitDidMethodFactory()) // Test-only factory
keys { provider("inMemory"); algorithm(KeyAlgorithms.ED25519) }
did { method(DidMethods.KEY) { algorithm(KeyAlgorithms.ED25519) } }
}
println("✅ TrustWeave initialized")
}
What this does: Creates a TrustWeave instance with default configuration, including the did:key method.
Step 3: Create Your First DID
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
import com.trustweave.trust.TrustWeave
import com.trustweave.trust.dsl.credential.DidMethods
import com.trustweave.trust.dsl.credential.KeyAlgorithms
import com.trustweave.did.resolver.DidResolutionResult
import com.trustweave.testkit.services.*
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
// Build TrustWeave instance (for tutorials, using testkit factories)
val trustWeave = TrustWeave.build {
factories(didMethodFactory = TestkitDidMethodFactory()) // Test-only factory
keys { provider("inMemory"); algorithm(KeyAlgorithms.ED25519) }
did { method(DidMethods.KEY) { algorithm(KeyAlgorithms.ED25519) } }
}
// Create a DID using the default method (did:key)
val did = trustWeave.createDid { method(DidMethods.KEY) }
val resolution = trustWeave.resolveDid(did)
when (resolution) {
is DidResolutionResult.Success -> {
println("✅ Created DID: ${did.value}")
println(" Verification Methods: ${resolution.document.verificationMethod.size}")
}
is DidResolutionResult.Failure -> {
println("❌ Failed to create DID: ${resolution.reason}")
}
}
}
What this does: Creates a new DID using the default did:key method. The DID document contains public keys for signing and verification.
Result: A DidDocument containing the DID identifier and verification methods.
Step 4: Resolve the DID
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fun main() = runBlocking {
// Build TrustWeave instance (for tutorials, using testkit factories)
val trustWeave = TrustWeave.build {
factories(didMethodFactory = TestkitDidMethodFactory()) // Test-only factory
keys { provider("inMemory"); algorithm(KeyAlgorithms.ED25519) }
did { method(DidMethods.KEY) { algorithm(KeyAlgorithms.ED25519) } }
}
// Create a DID using the modern DSL
val did = trustWeave.createDid { method(DidMethods.KEY) }
println("Created DID: ${did.value}")
// Resolve the DID we just created
val resolution = trustWeave.resolveDid(did)
when (resolution) {
is DidResolutionResult.Success -> {
println("✅ Resolved DID: ${did.value}")
println(" Methods: ${resolution.document.verificationMethod.size}")
}
is DidResolutionResult.Failure -> {
println("❌ Failed to resolve: ${resolution.reason}")
}
}
}
What this does: Resolves the DID to retrieve its document, demonstrating that DIDs are resolvable identifiers.
Step 5: Handle Errors Properly
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
fun main() = runBlocking {
// Build TrustWeave instance (for tutorials, using testkit factories)
val trustWeave = TrustWeave.build {
factories(didMethodFactory = TestkitDidMethodFactory()) // Test-only factory
keys { provider("inMemory"); algorithm(KeyAlgorithms.ED25519) }
did { method(DidMethods.KEY) { algorithm(KeyAlgorithms.ED25519) } }
}
// Try to resolve a non-existent DID
val resolution = trustWeave.resolveDid("did:key:invalid")
when (resolution) {
is DidResolutionResult.Success -> {
println("Resolved: ${resolution.document.id}")
}
is DidResolutionResult.Failure -> {
when (resolution) {
is DidResolutionResult.Failure.NotFound -> {
println("❌ DID not found: did:key:invalid")
}
is DidResolutionResult.Failure.InvalidFormat -> {
println("❌ Invalid DID format: ${resolution.reason}")
}
else -> {
println("❌ Error: ${resolution.reason}")
}
}
}
}
}
What this does: Demonstrates proper error handling using TrustWeave’s structured error types.
Key Takeaways
- DIDs are self-sovereign identifiers that you control
did:keyis included by default and works offline- Always use
fold()for error handling in production code - Error types provide actionable information for recovery
Next Steps
- Try creating DIDs with different methods (requires registration)
- Explore the DID document structure
- Read Core Concepts: DIDs
Tutorial 2: Issuing Your First Credential
Duration: 20-25 minutes Goal: Issue and verify a verifiable credential
What You’ll Learn
- What verifiable credentials are
- How to issue a credential
- How to verify a credential
- Understanding credential structure
Step 1: Create Issuer and Holder DIDs
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
fun main() = runBlocking {
// Build TrustWeave instance (for tutorials, using testkit factories)
val trustWeave = TrustWeave.build {
factories(didMethodFactory = TestkitDidMethodFactory()) // Test-only factory
keys { provider("inMemory"); algorithm(KeyAlgorithms.ED25519) }
did { method(DidMethods.KEY) { algorithm(KeyAlgorithms.ED25519) } }
}
// Create issuer DID (the organization issuing credentials)
import com.trustweave.trust.types.DidCreationResult
val issuerDidResult = trustWeave.createDid { method(DidMethods.KEY) }
val issuerDid = when (issuerDidResult) {
is DidCreationResult.Success -> {
println("Issuer DID: ${issuerDidResult.did.value}")
issuerDidResult.did
}
else -> {
println("Failed to create issuer DID: ${issuerDidResult.reason}")
return@runBlocking
}
}
// Create holder DID (the person receiving the credential)
val holderDidResult = trustWeave.createDid { method(DidMethods.KEY) }
val holderDid = when (holderDidResult) {
is DidCreationResult.Success -> {
println("Holder DID: ${holderDidResult.did.value}")
holderDidResult.did
}
else -> {
println("Failed to create holder DID: ${holderDidResult.reason}")
return@runBlocking
}
}
}
What this does: Sets up the two parties needed for credential issuance: issuer and holder.
Step 2: Issue a Credential
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
// Kotlin stdlib
import kotlinx.coroutines.runBlocking
// TrustWeave core
import com.trustweave.trust.TrustWeave
import com.trustweave.trust.dsl.credential.DidMethods
import com.trustweave.trust.dsl.credential.KeyAlgorithms
import com.trustweave.credential.*
import com.trustweave.testkit.services.*
fun main() = runBlocking {
// Build TrustWeave instance (for tutorials, using testkit factories)
val trustWeave = TrustWeave.build {
factories(
kmsFactory = TestkitKmsFactory(), // Test-only factory
didMethodFactory = TestkitDidMethodFactory() // Test-only factory
)
keys { provider("inMemory"); algorithm(KeyAlgorithms.ED25519) }
did { method(DidMethods.KEY) { algorithm(KeyAlgorithms.ED25519) } }
}
// Create DIDs
val issuerDidResult = trustWeave.createDid { method(DidMethods.KEY) }
val issuerDid = when (issuerDidResult) {
is DidCreationResult.Success -> issuerDidResult.did
else -> {
println("Failed to create issuer DID: ${issuerDidResult.reason}")
return@runBlocking
}
}
val holderDidResult = trustWeave.createDid { method(DidMethods.KEY) }
val holderDid = when (holderDidResult) {
is DidCreationResult.Success -> holderDidResult.did
else -> {
println("Failed to create holder DID: ${holderDidResult.reason}")
return@runBlocking
}
}
// Get the first verification method from issuer's DID document
val issuerResolution = trustWeave.resolveDid(issuerDid)
val issuerDoc = when (issuerResolution) {
is DidResolutionResult.Success -> issuerResolution.document
else -> throw IllegalStateException("Failed to resolve issuer DID")
}
val issuerKeyId = issuerDoc.verificationMethod.firstOrNull()?.id?.substringAfter("#")
?: throw IllegalStateException("No verification method found")
// Issue a credential using DSL
import com.trustweave.trust.types.IssuanceResult
import java.time.Instant
val issuanceResult = trustWeave.issue {
credential {
type("VerifiableCredential", "EducationalCredential")
issuer(issuerDid.value)
subject {
id(holderDid.value)
"name" to "Alice"
"degree" to "Bachelor of Science"
"university" to "Example University"
}
issued(Instant.now())
}
signedBy(issuerDid = issuerDid.value, keyId = issuerKeyId)
}
when (issuanceResult) {
is IssuanceResult.Success -> {
println("✅ Credential issued")
println(" ID: ${issuanceResult.credential.id}")
println(" Issuer: ${issuanceResult.credential.issuer}")
println(" Subject: ${issuanceResult.credential.credentialSubject}")
println(" Types: ${issuanceResult.credential.type}")
}
else -> {
println("❌ Failed to issue credential: ${issuanceResult.reason}")
}
}
}
What this does: Issues a verifiable credential from the issuer to the holder, containing educational information.
Result: A VerifiableCredential with cryptographic proof that can be verified.
Step 3: Verify the Credential
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
fun main() = runBlocking {
// Build TrustWeave instance (for tutorials, using testkit factories)
val trustWeave = TrustWeave.build {
factories(
kmsFactory = TestkitKmsFactory(), // Test-only factory
didMethodFactory = TestkitDidMethodFactory() // Test-only factory
)
keys { provider("inMemory"); algorithm(KeyAlgorithms.ED25519) }
did { method(DidMethods.KEY) { algorithm(KeyAlgorithms.ED25519) } }
}
// ... (create DIDs and issue credential from Step 2) ...
val credential = credentialResult.getOrThrow()
// Verify the credential
val verificationResult = TrustWeave.verifyCredential(credential)
verificationResult.fold(
onSuccess = { verification ->
if (verification.valid) {
println("✅ Credential is valid")
println(" Proof valid: ${verification.proofValid}")
println(" Not expired: ${verification.notExpired}")
println(" Not revoked: ${verification.notRevoked}")
if (verification.warnings.isNotEmpty()) {
println(" Warnings: ${verification.warnings}")
}
} else {
println("❌ Credential is invalid")
println(" Errors: ${verification.errors}")
}
},
onFailure = { error ->
println("❌ Verification failed: ${error.message}")
}
)
}
What this does: Verifies the credential’s proof, expiration, and revocation status.
Step 4: Add Expiration Date
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
import java.time.Instant
import java.time.temporal.ChronoUnit
fun main() = runBlocking {
// Build TrustWeave instance (for tutorials, using testkit factories)
val trustWeave = TrustWeave.build {
factories(
kmsFactory = TestkitKmsFactory(), // Test-only factory
didMethodFactory = TestkitDidMethodFactory() // Test-only factory
)
keys { provider("inMemory"); algorithm(KeyAlgorithms.ED25519) }
did { method(DidMethods.KEY) { algorithm(KeyAlgorithms.ED25519) } }
}
// ... (create DIDs) ...
// Issue credential with expiration date (1 year from now)
val expirationDate = Instant.now().plus(365, ChronoUnit.DAYS)
val credentialResult = TrustWeave.issueCredential(
issuerDid = issuerDid.value,
issuerKeyId = issuerKeyId,
credentialSubject = mapOf(
"id" to holderDid.value,
"name" to "Alice",
"degree" to "Bachelor of Science"
),
types = listOf("VerifiableCredential", "EducationalCredential"),
expirationDate = expirationDate
)
// ... (verify credential) ...
}
What this does: Adds an expiration date to the credential, demonstrating credential lifecycle management.
Key Takeaways
- Credentials contain claims about a subject (holder)
- Credentials are cryptographically signed by the issuer
- Verification checks proof, expiration, and revocation
- Always check verification warnings, not just validity
Next Steps
- Add more claims to credentials
- Explore different credential types
- Read Core Concepts: Verifiable Credentials
Tutorial 3: Managing Credentials with Wallets
Duration: 25-30 minutes Goal: Store, organize, and query credentials using wallets
What You’ll Learn
- What wallets are and their purpose
- How to create wallets
- How to store credentials in wallets
- How to query and retrieve credentials
- Wallet organization features
Step 1: Create a Wallet
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
// Kotlin stdlib
import kotlinx.coroutines.runBlocking
// TrustWeave core
import com.trustweave.trust.TrustWeave
import com.trustweave.trust.dsl.credential.DidMethods
import com.trustweave.trust.dsl.credential.KeyAlgorithms
import com.trustweave.wallet.*
import com.trustweave.testkit.services.*
fun main() = runBlocking {
// Build TrustWeave instance (for tutorials, using testkit factories)
val trustWeave = TrustWeave.build {
factories(
kmsFactory = TestkitKmsFactory(), // Test-only factory
didMethodFactory = TestkitDidMethodFactory() // Test-only factory
)
keys { provider("inMemory"); algorithm(KeyAlgorithms.ED25519) }
did { method(DidMethods.KEY) { algorithm(KeyAlgorithms.ED25519) } }
}
// Create holder DID
val holderDidResult = trustWeave.createDid { method(DidMethods.KEY) }
val holderDid = when (holderDidResult) {
is DidCreationResult.Success -> holderDidResult.did
else -> {
println("Failed to create holder DID: ${holderDidResult.reason}")
return@runBlocking
}
}
// Create wallet for the holder
import com.trustweave.trust.types.WalletCreationResult
val walletResult = trustWeave.wallet {
holder(holderDid.value)
type("inMemory")
}
val wallet = when (walletResult) {
is WalletCreationResult.Success -> {
println("✅ Wallet created: ${walletResult.wallet.walletId}")
println(" Holder: ${holderDid.value}")
walletResult.wallet
}
else -> {
println("❌ Failed to create wallet: ${walletResult.reason}")
return@runBlocking
}
}
}
What this does: Creates a wallet for storing credentials. Wallets provide secure storage and management.
Step 2: Store Credentials
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
fun main() = runBlocking {
// Build TrustWeave instance (for tutorials, using testkit factories)
val trustWeave = TrustWeave.build {
factories(
kmsFactory = TestkitKmsFactory(), // Test-only factory
didMethodFactory = TestkitDidMethodFactory() // Test-only factory
)
keys { provider("inMemory"); algorithm(KeyAlgorithms.ED25519) }
did { method(DidMethods.KEY) { algorithm(KeyAlgorithms.ED25519) } }
}
// Create DIDs and issue credential (from Tutorial 2)
val issuerDidResult = trustWeave.createDid { method(DidMethods.KEY) }
val issuerDid = when (issuerDidResult) {
is DidCreationResult.Success -> issuerDidResult.did
else -> {
println("Failed to create issuer DID: ${issuerDidResult.reason}")
return@runBlocking
}
}
val holderDidResult = trustWeave.createDid { method(DidMethods.KEY) }
val holderDid = when (holderDidResult) {
is DidCreationResult.Success -> holderDidResult.did
else -> {
println("Failed to create holder DID: ${holderDidResult.reason}")
return@runBlocking
}
}
// Issue credential (simplified - see Tutorial 2 for full example)
val credential = /* ... issue credential ... */
// Create wallet
val walletResult = trustWeave.wallet {
holder(holderDid.value)
type("inMemory")
}
val wallet = when (walletResult) {
is WalletCreationResult.Success -> walletResult.wallet
else -> {
println("Failed to create wallet: ${walletResult.reason}")
return@runBlocking
}
}
// Store credential in wallet (store returns String, not Result)
val storedId = wallet.store(credential)
println("✅ Credential stored: $storedId")
}
What this does: Stores a credential in the wallet, making it available for later retrieval.
Step 3: Query Credentials
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
fun main() = runBlocking {
// Build TrustWeave instance (for tutorials, using testkit factories)
val trustWeave = TrustWeave.build {
factories(
kmsFactory = TestkitKmsFactory(), // Test-only factory
didMethodFactory = TestkitDidMethodFactory() // Test-only factory
)
keys { provider("inMemory"); algorithm(KeyAlgorithms.ED25519) }
did { method(DidMethods.KEY) { algorithm(KeyAlgorithms.ED25519) } }
}
// ... (create wallet and store credentials) ...
// Query all credentials
val allCredentials = wallet.queryCredentials()
println("Total credentials: ${allCredentials.size}")
// Query by type
val educationalCreds = wallet.queryCredentials(
type = "EducationalCredential"
)
println("Educational credentials: ${educationalCreds.size}")
// Query by issuer
val issuerCreds = wallet.queryCredentials(
issuer = issuerDid.value
)
println("Credentials from issuer: ${issuerCreds.size}")
}
What this does: Demonstrates querying credentials by various criteria.
Step 4: Use Organization Features
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
fun main() = runBlocking {
// Build TrustWeave instance (for tutorials, using testkit factories)
val trustWeave = TrustWeave.build {
factories(
kmsFactory = TestkitKmsFactory(), // Test-only factory
didMethodFactory = TestkitDidMethodFactory() // Test-only factory
)
keys { provider("inMemory"); algorithm(KeyAlgorithms.ED25519) }
did { method(DidMethods.KEY) { algorithm(KeyAlgorithms.ED25519) } }
}
// Create wallet with organization features enabled
val wallet = trustWeave.wallet {
holder(holderDid.value)
type("inMemory")
// Organization features can be configured here
}
// Store credentials
val credential = /* ... */
wallet.storeCredential(credential).getOrThrow()
// Use organization features
wallet.withOrganization { org ->
// Create a collection
val collection = org.createCollection("Education")
// Add credential to collection
org.addToCollection(collection.id, credential.id)
// Tag credential
org.tagCredential(credential.id, "diploma")
org.tagCredential(credential.id, "bachelor")
// Query by collection
val educationCreds = org.queryByCollection(collection.id)
println("Education collection: ${educationCreds.size} credentials")
// Query by tag
val diplomaCreds = org.queryByTag("diploma")
println("Diploma credentials: ${diplomaCreds.size}")
}
}
What this does: Demonstrates wallet organization features: collections, tags, and metadata.
Key Takeaways
- Wallets provide secure credential storage
- Use
InMemoryprovider for testing, persistent providers for production - Organization features help manage large numbers of credentials
- Query capabilities enable efficient credential retrieval
Next Steps
- Explore presentation features (selective disclosure)
- Try different wallet providers
- Read Core Concepts: Wallets
- Complete Wallet API Tutorial
Tutorial 4: Building a Complete Workflow
Duration: 30-35 minutes Goal: Build an end-to-end issuer-holder-verifier workflow
What You’ll Learn
- Complete credential lifecycle
- Issuer, holder, and verifier roles
- Presentation creation and verification
- Real-world workflow patterns
Step 1: Setup All Parties
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
import com.trustweave.trust.TrustWeave
import com.trustweave.trust.dsl.credential.DidMethods
import com.trustweave.trust.dsl.credential.KeyAlgorithms
import com.trustweave.did.resolver.DidResolutionResult
import com.trustweave.testkit.services.*
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
// Build TrustWeave instance (for tutorials, using testkit factories)
val trustWeave = TrustWeave.build {
factories(
kmsFactory = TestkitKmsFactory(), // Test-only factory
didMethodFactory = TestkitDidMethodFactory() // Test-only factory
)
keys { provider("inMemory"); algorithm(KeyAlgorithms.ED25519) }
did { method(DidMethods.KEY) { algorithm(KeyAlgorithms.ED25519) } }
}
// Issuer: University issuing degrees
val issuerDidResult = trustWeave.createDid { method(DidMethods.KEY) }
val issuerDid = when (issuerDidResult) {
is DidCreationResult.Success -> issuerDidResult.did
else -> {
println("Failed to create issuer DID: ${issuerDidResult.reason}")
return@runBlocking
}
}
val issuerResolution = trustWeave.resolveDid(issuerDid)
val issuerDoc = when (issuerResolution) {
is DidResolutionResult.Success -> issuerResolution.document
else -> throw IllegalStateException("Failed to resolve issuer DID")
}
val issuerKeyId = issuerDoc.verificationMethod.firstOrNull()?.id?.substringAfter("#")
?: throw IllegalStateException("No verification method found")
// Holder: Student receiving degree
val holderDidResult = trustWeave.createDid { method(DidMethods.KEY) }
val holderDid = when (holderDidResult) {
is DidCreationResult.Success -> holderDidResult.did
else -> {
println("Failed to create holder DID: ${holderDidResult.reason}")
return@runBlocking
}
}
val holderWalletResult = trustWeave.wallet {
holder(holderDid.value)
type("inMemory")
}
val holderWallet = when (holderWalletResult) {
is WalletCreationResult.Success -> holderWalletResult.wallet
else -> {
println("Failed to create wallet: ${holderWalletResult.reason}")
return@runBlocking
}
}
// Verifier: Employer verifying degree
val verifierDidResult = trustWeave.createDid { method(DidMethods.KEY) }
val verifierDid = when (verifierDidResult) {
is DidCreationResult.Success -> verifierDidResult.did
else -> {
println("Failed to create verifier DID: ${verifierDidResult.reason}")
return@runBlocking
}
}
println("✅ All parties set up")
println(" Issuer: ${issuerDid.value}")
println(" Holder: ${holderDid.value}")
println(" Verifier: ${verifierDid.value}")
}
What this does: Sets up the three parties in a typical credential workflow.
Step 2: Issue Credential (Issuer)
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
fun main() = runBlocking {
// Build TrustWeave instance (for tutorials, using testkit factories)
val trustWeave = TrustWeave.build {
factories(
kmsFactory = TestkitKmsFactory(), // Test-only factory
didMethodFactory = TestkitDidMethodFactory() // Test-only factory
)
keys { provider("inMemory"); algorithm(KeyAlgorithms.ED25519) }
did { method(DidMethods.KEY) { algorithm(KeyAlgorithms.ED25519) } }
}
// ... (setup parties) ...
// ISSUER: Issue credential
val credential = TrustWeave.issueCredential(
issuerDid = issuerDid.value,
issuerKeyId = issuerKeyId,
credentialSubject = mapOf(
"id" to holderDid.value,
"name" to "Alice",
"degree" to "Bachelor of Science in Computer Science",
"university" to "Example University",
"graduationDate" to "2024-05-15"
),
types = listOf("VerifiableCredential", "EducationalCredential")
).getOrThrow()
println("✅ Credential issued by issuer")
}
Step 3: Store Credential (Holder)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fun main() = runBlocking {
// Build TrustWeave instance (for tutorials, using testkit factories)
val trustWeave = TrustWeave.build {
factories(
kmsFactory = TestkitKmsFactory(), // Test-only factory
didMethodFactory = TestkitDidMethodFactory() // Test-only factory
)
keys { provider("inMemory"); algorithm(KeyAlgorithms.ED25519) }
did { method(DidMethods.KEY) { algorithm(KeyAlgorithms.ED25519) } }
}
// ... (setup and issue) ...
// HOLDER: Store credential in wallet
val storedId = holderWallet.storeCredential(credential).getOrThrow()
println("✅ Credential stored by holder: $storedId")
}
Step 4: Create Presentation (Holder)
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
// Kotlin stdlib
import kotlinx.coroutines.runBlocking
// TrustWeave core
import com.trustweave.trust.TrustWeave
import com.trustweave.trust.dsl.credential.DidMethods
import com.trustweave.trust.dsl.credential.KeyAlgorithms
import com.trustweave.presentation.*
import com.trustweave.testkit.services.*
fun main() = runBlocking {
// Build TrustWeave instance (for tutorials, using testkit factories)
val trustWeave = TrustWeave.build {
factories(
kmsFactory = TestkitKmsFactory(), // Test-only factory
didMethodFactory = TestkitDidMethodFactory() // Test-only factory
)
keys { provider("inMemory"); algorithm(KeyAlgorithms.ED25519) }
did { method(DidMethods.KEY) { algorithm(KeyAlgorithms.ED25519) } }
}
// ... (setup, issue, store) ...
// HOLDER: Create presentation for verifier
val presentationResult = holderWallet.withPresentation { pres ->
pres.createPresentation(
credentials = listOf(credential),
holderDid = holderDid.value,
challenge = "verifier-challenge-123", // Nonce from verifier
domain = "example-employer.com"
)
}
presentationResult.fold(
onSuccess = { presentation ->
println("✅ Presentation created by holder")
println(" Challenge: ${presentation.challenge}")
println(" Domain: ${presentation.domain}")
},
onFailure = { error ->
println("❌ Failed to create presentation: ${error.message}")
}
)
}
What this does: Creates a verifiable presentation that the holder can share with the verifier.
Step 5: Verify Presentation (Verifier)
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
import com.trustweave.trust.TrustWeave
import com.trustweave.trust.dsl.credential.DidMethods
import com.trustweave.trust.dsl.credential.KeyAlgorithms
import com.trustweave.testkit.services.*
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
// Build TrustWeave instance (for tutorials, using testkit factories)
val trustWeave = TrustWeave.build {
factories(
kmsFactory = TestkitKmsFactory(), // Test-only factory
didMethodFactory = TestkitDidMethodFactory() // Test-only factory
)
keys { provider("inMemory"); algorithm(KeyAlgorithms.ED25519) }
did { method(DidMethods.KEY) { algorithm(KeyAlgorithms.ED25519) } }
}
// ... (complete workflow above) ...
val presentation = presentationResult.getOrThrow()
// VERIFIER: Verify presentation
val verificationResult = trustWeave.verifyPresentation(
presentation = presentation,
challenge = "verifier-challenge-123", // Must match
domain = "example-employer.com" // Must match
)
verificationResult.fold(
onSuccess = { verification ->
if (verification.valid) {
println("✅ Presentation verified by verifier")
println(" All credentials valid: ${verification.allCredentialsValid}")
println(" Proof valid: ${verification.proofValid}")
} else {
println("❌ Presentation invalid")
println(" Errors: ${verification.errors}")
}
},
onFailure = { error ->
println("❌ Verification failed: ${error.message}")
}
)
}
What this does: Verifies the presentation, ensuring credentials are valid and the presentation proof is correct.
Step 6: Selective Disclosure (Advanced)
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
fun main() = runBlocking {
// Build TrustWeave instance (for tutorials, using testkit factories)
val trustWeave = TrustWeave.build {
factories(
kmsFactory = TestkitKmsFactory(), // Test-only factory
didMethodFactory = TestkitDidMethodFactory() // Test-only factory
)
keys { provider("inMemory"); algorithm(KeyAlgorithms.ED25519) }
did { method(DidMethods.KEY) { algorithm(KeyAlgorithms.ED25519) } }
}
// ... (setup and issue) ...
// HOLDER: Create presentation with selective disclosure
// Only reveal degree and university, hide name and graduation date
val presentation = holderWallet.withPresentation { pres ->
pres.createPresentation(
credentials = listOf(credential),
holderDid = holderDid.value,
challenge = "challenge-123",
domain = "example.com",
revealFields = mapOf(
credential.id to listOf("degree", "university")
)
)
}.getOrThrow()
println("✅ Presentation with selective disclosure created")
// Verifier only sees degree and university, not name or graduation date
}
What this does: Demonstrates selective disclosure, allowing holders to reveal only specific fields.
Key Takeaways
- Three-party workflow: issuer → holder → verifier
- Presentations allow holders to share credentials securely
- Challenge and domain prevent replay attacks
- Selective disclosure protects privacy
Next Steps
- Add revocation checking
- Implement credential expiration handling
- Read Common Patterns
- Explore Scenarios for domain-specific workflows
Tutorial 5: Adding Blockchain Anchoring
Duration: 25-30 minutes Goal: Anchor data to blockchain for tamper evidence
What You’ll Learn
- What blockchain anchoring is
- How to anchor data
- How to read anchored data
- When to use anchoring
Step 1: Register Blockchain Client
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
// Kotlin stdlib
import kotlinx.coroutines.runBlocking
// TrustWeave core
import com.trustweave.trust.TrustWeave
import com.trustweave.trust.dsl.credential.DidMethods
import com.trustweave.trust.dsl.credential.KeyAlgorithms
import com.trustweave.anchor.*
import com.trustweave.anchor.options.*
import com.trustweave.testkit.services.*
fun main() = runBlocking {
val trustWeave = TrustWeave.build {
blockchains {
// Register Algorand testnet client
"algorand:testnet" to AlgorandBlockchainAnchorClient(
chainId = "algorand:testnet",
options = AlgorandOptions(
algodUrl = "https://testnet-api.algonode.cloud",
privateKey = "your-private-key"
)
)
}
}
println("✅ Blockchain client registered")
}
What this does: Registers a blockchain client for anchoring operations.
Step 2: Anchor Data
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
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.encodeToString
@Serializable
data class ImportantData(
val id: String,
val timestamp: String,
val value: String
)
fun main() = runBlocking {
val TrustWeave = TrustWeave.create {
// ... (register blockchain client) ...
}
// Create data to anchor
val data = ImportantData(
id = "data-123",
timestamp = Instant.now().toString(),
value = "Important information"
)
// Anchor to blockchain
val anchorResult = trustWeave.blockchains.anchor(
data = data,
serializer = ImportantData.serializer(),
chainId = "algorand:testnet"
)
anchorResult.fold(
onSuccess = { anchor ->
println("✅ Data anchored")
println(" Transaction: ${anchor.ref.txHash}")
println(" Block: ${anchor.ref.blockNumber}")
println(" Timestamp: ${anchor.timestamp}")
},
onFailure = { error ->
println("❌ Anchoring failed: ${error.message}")
}
)
}
What this does: Anchors data to the blockchain, creating a tamper-evident timestamp.
Step 3: Read Anchored Data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fun main() = runBlocking {
val TrustWeave = TrustWeave.create {
// ... (register blockchain client) ...
}
// ... (anchor data) ...
val anchorRef = anchorResult.getOrThrow().ref
// Read anchored data
val readResult = TrustWeave.readAnchor<ImportantData>(
ref = anchorRef,
serializer = ImportantData.serializer()
)
readResult.fold(
onSuccess = { data ->
println("✅ Read anchored data: $data")
},
onFailure = { error ->
println("❌ Failed to read: ${error.message}")
}
)
}
What this does: Reads anchored data from the blockchain and verifies it matches the on-chain digest.
Step 4: Anchor Credential Status List
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
fun main() = runBlocking {
val TrustWeave = TrustWeave.create {
// ... (register blockchain client) ...
}
// Create status list for revocation
val statusList = TrustWeave.createStatusList(
issuerDid = issuerDid.value,
purpose = StatusPurpose.REVOCATION
).getOrThrow()
// Anchor status list to blockchain
// This makes revocation status tamper-evident
val anchorResult = trustWeave.blockchains.anchor(
data = statusList,
serializer = StatusListCredential.serializer(),
chainId = "algorand:testnet"
)
anchorResult.fold(
onSuccess = { anchor ->
println("✅ Status list anchored")
println(" Can verify revocation status on-chain")
},
onFailure = { error ->
println("❌ Failed to anchor status list: ${error.message}")
}
)
}
What this does: Demonstrates anchoring revocation status lists for tamper-evident revocation checking.
Key Takeaways
- Anchoring creates tamper-evident timestamps
- Only digests are stored on-chain, not full data
- Useful for revocation lists, audit logs, and provenance
- Different blockchains have different latency and cost characteristics
Next Steps
- Explore different blockchain options
- Learn about anchoring strategies
- Read Core Concepts: Blockchain Anchoring
- Read Blockchain-Anchored Revocation
What’s Next?
After completing this tutorial series, you’re ready to:
- Build Real Applications: Use the patterns you’ve learned in production
- Explore Advanced Topics:
- Study Domain Scenarios: See how TrustWeave is used in real-world scenarios
- Contribute: Help improve TrustWeave by creating plugins
Additional Resources
- API Reference - Complete API documentation
- Core Concepts - Deep dives into concepts
- Common Patterns - Production patterns
- Troubleshooting - Debugging guide
- FAQ - Frequently asked questions