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:

  1. Tutorial 1: Your First DID - Create and understand DIDs
  2. Tutorial 2: Issuing Your First Credential - Issue and verify credentials
  3. Tutorial 3: Managing Credentials with Wallets - Store and organize credentials
  4. Tutorial 4: Building a Complete Workflow - End-to-end issuer-holder-verifier flow
  5. 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:key is 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


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 InMemory provider for testing, persistent providers for production
  • Organization features help manage large numbers of credentials
  • Query capabilities enable efficient credential retrieval

Next Steps


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


What’s Next?

After completing this tutorial series, you’re ready to:

  1. Build Real Applications: Use the patterns you’ve learned in production
  2. Explore Advanced Topics:
  3. Study Domain Scenarios: See how TrustWeave is used in real-world scenarios
  4. Contribute: Help improve TrustWeave by creating plugins

Additional Resources