Web of Trust Scenario

This document provides a complete walkthrough of using TrustWeave’s web of trust features, including trust registries, delegation chains, and proof purpose validation.

1
2
3
4
5
dependencies {
    implementation("com.trustweave:trustweave-core:1.0.0-SNAPSHOT")
    implementation("com.trustweave:trustweave-trust:1.0.0-SNAPSHOT")
    implementation("com.trustweave:trustweave-testkit:1.0.0-SNAPSHOT")
}

Result: These modules give you the trust-layer DSLs, registries, and in-memory mocks used throughout the walkthrough.

Overview

The web of trust scenario demonstrates how to:

  1. Set up trust anchors
  2. Issue trusted credentials
  3. Verify trust paths
  4. Delegate capabilities
  5. Verify delegation chains
  6. Use proof purpose validation
  7. Integrate all features together

Step-by-Step Walkthrough

Step 1: Configure Trust Layer with Trust Registry

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
import com.trustweave.trust.dsl.*
import java.time.Instant
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    val trustLayer = trustLayer {
        keys {
            provider("inMemory")
            algorithm(KeyAlgorithms.ED25519)
        }

        did {
            method(DidMethods.KEY) {
                algorithm(KeyAlgorithms.ED25519)
            }
        }

        credentials {
            defaultProofType(ProofTypes.ED25519)
        }

        trust {
            provider("inMemory")
        }
    }
}

What this does: Builds a trustLayer instance with in-memory KMS, DID method, credential defaults, and trust registry providers—perfect for local demos.

Outcome: Returns a configured trust layer you reuse in subsequent steps to create DIDs, issue credentials, and resolve trust anchors.

Step 2: Create DIDs for Entities

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
import com.trustweave.trust.types.DidCreationResult

val universityDidResult = trustLayer.createDid {
    method(DidMethods.KEY)
    algorithm(KeyAlgorithms.ED25519)
}
val universityDid = when (universityDidResult) {
    is DidCreationResult.Success -> universityDidResult.did
    else -> throw IllegalStateException("Failed to create university DID: ${universityDidResult.reason}")
}

val companyDidResult = trustLayer.createDid {
    method(DidMethods.KEY)
    algorithm(KeyAlgorithms.ED25519)
}
val companyDid = when (companyDidResult) {
    is DidCreationResult.Success -> companyDidResult.did
    else -> throw IllegalStateException("Failed to create company DID: ${companyDidResult.reason}")
}

val studentDidResult = trustLayer.createDid {
    method(DidMethods.KEY)
    algorithm(KeyAlgorithms.ED25519)
}
val studentDid = when (studentDidResult) {
    is DidCreationResult.Success -> studentDidResult.did
    else -> throw IllegalStateException("Failed to create student DID: ${studentDidResult.reason}")
}

val hrDeptDidResult = trustLayer.createDid {
    method(DidMethods.KEY)
    algorithm(KeyAlgorithms.ED25519)
}
val hrDeptDid = when (hrDeptDidResult) {
    is DidCreationResult.Success -> hrDeptDidResult.did
    else -> throw IllegalStateException("Failed to create HR dept DID: ${hrDeptDidResult.reason}")
}

What this does: Issues four DIDs—university, company, student, and HR department—using the configured DID method.

Outcome: Each actor has a resolvable identifier that upcoming trust and delegation steps can reference.

Step 3: Set Up Trust Anchors

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
trustLayer.trust {
    // Add university as trusted anchor for education credentials
    addAnchor(universityDid) {
        credentialTypes("EducationCredential", "DegreeCredential")
        description("Trusted university for education credentials")
    }

    // Add company as trusted anchor for employment credentials
    addAnchor(companyDid) {
        credentialTypes("EmploymentCredential")
        description("Trusted company for employment credentials")
    }

    // Verify trust anchors were added
    val isUniversityTrusted = isTrusted(universityDid, "EducationCredential")
    println("University trusted for EducationCredential: $isUniversityTrusted")
}

What this does: Seeds the trust registry with anchors for the university and company, then confirms the university anchor is active for education credentials.

Outcome: Subsequent verifications can rely on trust registry lookups instead of hard-coded issuer lists.

Step 4: Issue Credentials with Trust 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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// Issue degree credential from university
import com.trustweave.trust.types.IssuanceResult

// First, resolve university DID to get key ID
val universityResolution = trustLayer.resolveDid(universityDid)
val universityDoc = when (universityResolution) {
    is DidResolutionResult.Success -> universityResolution.document
    else -> throw IllegalStateException("Failed to resolve university DID")
}
val universityKeyId = universityDoc.verificationMethod.firstOrNull()?.id?.substringAfter("#")
    ?: throw IllegalStateException("No verification method found")

val degreeIssuanceResult = trustLayer.issue {
    credential {
        id("https://university.edu/credentials/degree-123")
        type(CredentialType.Education, CredentialType.Degree)
        issuer(universityDid.value)
        subject {
            id(studentDid.value)
            "degree" {
                "type" to "Bachelor"
                "field" to "Computer Science"
                "university" to "Example University"
            }
        }
        issued(Instant.now())
        expires(Instant.now().plusSeconds(31536000)) // 1 year
    }
    signedBy(issuerDid = universityDid.value, keyId = universityKeyId)
}

val degreeCredential = when (degreeIssuanceResult) {
    is IssuanceResult.Success -> degreeIssuanceResult.credential
    else -> throw IllegalStateException("Failed to issue credential: ${degreeIssuanceResult.reason}")
}

// Verify with trust registry
val verification = trustLayer.verify {
    credential(degreeCredential)
    checkTrustRegistry(true)
    checkExpiration(true)
}

println("Verification Results:")
println("  Valid: ${verification.valid}")
println("  Trust Registry Valid: ${verification.trustRegistryValid}")
println("  Proof Valid: ${verification.proofValid}")
println("  Not Expired: ${verification.notExpired}")

if (verification.trustRegistryValid) {
    println("✅ Issuer is trusted!")
} else {
    println("❌ Issuer is not trusted")
}

What this does: Issues a degree credential and verifies it with trust registry and expiration checks enabled.

Outcome: A positive result confirms both the issuer’s trust anchor and the credential’s validity window are satisfied.

Step 5: Set Up Delegation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Company delegates credential issuance to HR department
trustLayer.updateDid {
    did(companyDid)
    method(DidMethods.KEY)
    addCapabilityDelegation("$hrDeptDid#key-1")
}

// Verify delegation chain
val delegationResult = trustLayer.delegation {
    verifyChain(delegatorDid = companyDid, delegateDid = hrDeptDid)
}

if (delegationResult.valid) {
    println("✅ Delegation verified:")
    println("   Path: ${delegationResult.path.joinToString(" -> ")}")
} else {
    println("❌ Delegation failed:")
    delegationResult.errors.forEach { println("   - $it") }
}

What this does: Adds capability delegation from the company to its HR department and validates the resulting delegation path.

Outcome: A valid result proves the HR department may act on behalf of the company when issuing credentials.

Step 6: Issue Credential Using Delegated Authority

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
// HR department issues credential using delegated authority
// First resolve HR DID to get key ID
val hrResolution = trustLayer.resolveDid(hrDeptDid)
val hrDoc = when (hrResolution) {
    is DidResolutionResult.Success -> hrResolution.document
    else -> throw IllegalStateException("Failed to resolve HR DID")
}
val hrKeyId = hrDoc.verificationMethod.firstOrNull()?.id?.substringAfter("#")
    ?: throw IllegalStateException("No verification method found")

val employmentIssuanceResult = trustLayer.issue {
    credential {
        id("https://company.com/credentials/employment-456")
        type("EmploymentCredential")
        issuer(hrDeptDid.value) // HR issues on behalf of company
        subject {
            id(studentDid.value)
            "employment" {
                "company" to "Tech Corp"
                "role" to "Software Engineer"
                "startDate" to "2024-01-01"
            }
        }
        issued(Instant.now())
    }
    signedBy(issuerDid = hrDeptDid.value, keyId = hrKeyId)
}

val employmentCredential = when (employmentIssuanceResult) {
    is IssuanceResult.Success -> employmentIssuanceResult.credential
    else -> throw IllegalStateException("Failed to issue credential: ${employmentIssuanceResult.reason}")
}

// Verify credential with delegation check
val employmentVerification = trustLayer.verify {
    credential(employmentCredential)
    checkTrustRegistry(true)
    verifyDelegation(true)
    checkExpiration(true)
}

println("Employment Credential Verification:")
println("  Valid: ${employmentVerification.valid}")
println("  Trust Registry Valid: ${employmentVerification.trustRegistryValid}")
println("  Delegation Valid: ${employmentVerification.delegationValid}")

What this does: Issues an employment credential from the delegated HR DID and verifies it with both trust registry and delegation checks enabled.

Outcome: Successful verification confirms the delegation chain and trust anchors are respected before the credential is accepted.

Step 7: Find Trust Paths

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
trustLayer.trust {
    // Get all trusted issuers for a credential type
    val educationIssuers = getTrustedIssuers("EducationCredential")
    println("Trusted education issuers: ${educationIssuers.joinToString(", ")}")

    // Find trust path between two DIDs (if trust relationships exist)
    val trustPath = getTrustPath(universityDid, companyDid)
    if (trustPath != null) {
        println("Trust path found:")
        println("  Path: ${trustPath.path.joinToString(" -> ")}")
        println("  Trust Score: ${trustPath.trustScore}")
        println("  Valid: ${trustPath.valid}")
    } else {
        println("No trust path found between university and company")
    }
}

What this does: Lists all trusted issuers for education credentials and attempts to compute a trust path between the university and company anchors.

Outcome: If a path exists you receive the ordered DIDs and trust score, helping you diagnose trust relationships before accepting credentials.

Step 8: Update DID Documents with New Fields

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
trustLayer.updateDid {
    did(studentDid)
    method(DidMethods.KEY)

    // Add capability invocation for signing documents
    addCapabilityInvocation("$studentDid#key-1")

    // Add capability delegation for delegating to assistants
    addCapabilityDelegation("$studentDid#key-2")

    // Set JSON-LD context
    context("https://www.w3.org/ns/did/v1", "https://example.com/context/v1")
}

println("✅ DID document updated with capability relationships and context")

What this does: Augments the student DID document with capability invocation/delegation relationships and an expanded JSON-LD context.

Outcome: The student can now sign documents and delegate capabilities while verifiers understand the DID document structure.

Step 9: Use Proof Purpose Validation

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
// Update issuer DID to have assertionMethod relationship
trustLayer.updateDid {
    did(universityDid)
    method(DidMethods.KEY)
    addAssertionMethod("$universityDid#key-1")
}

// Issue credential with assertionMethod proof purpose
val validatedIssuanceResult = trustLayer.issue {
    credential {
        id("https://university.edu/credentials/validated-789")
        type("EducationCredential")
        issuer(universityDid.value)
        subject {
            id(studentDid.value)
            "certification" {
                "name" to "Certified Developer"
                "level" to "Advanced"
            }
        }
        issued(Instant.now())
    }
    signedBy(issuerDid = universityDid.value, keyId = universityKeyId)
    proofPurpose(ProofPurposes.ASSERTION_METHOD)
}

val validatedCredential = when (validatedIssuanceResult) {
    is IssuanceResult.Success -> validatedIssuanceResult.credential
    else -> throw IllegalStateException("Failed to issue credential: ${validatedIssuanceResult.reason}")
}

// Verify with proof purpose validation
val proofPurposeVerification = trustLayer.verify {
    credential(validatedCredential)
    validateProofPurpose(true)
    checkTrustRegistry(true)
}

println("Proof Purpose Validation:")
println("  Valid: ${proofPurposeVerification.valid}")
println("  Proof Purpose Valid: ${proofPurposeVerification.proofPurposeValid}")
println("  Trust Registry Valid: ${proofPurposeVerification.trustRegistryValid}")

**What this does:** Ensures the issuers DID advertises `assertionMethod`, issues a credential with that proof purpose, and validates the proof purpose alongside trust and expiration checks.

**Outcome:** A `proofPurposeValid` flag of `true` confirms the credentials proof aligns with the declared purpose, preventing misuse in other contexts.

### Step 10: Complete Integration Example

```kotlin
// Complete workflow combining all features
fun completeWebOfTrustWorkflow() = runBlocking {
    val trustLayer = trustLayer {
        keys { provider("inMemory") }
        did { method(DidMethods.KEY) }
        trust { provider("inMemory") }
    }

    // 1. Create DIDs
    val issuerDidResult = trustLayer.createDid { method(DidMethods.KEY) }
    val issuerDid = when (issuerDidResult) {
        is DidCreationResult.Success -> issuerDidResult.did
        else -> throw IllegalStateException("Failed to create issuer DID: ${issuerDidResult.reason}")
    }
    
    val holderDidResult = trustLayer.createDid { method(DidMethods.KEY) }
    val holderDid = when (holderDidResult) {
        is DidCreationResult.Success -> holderDidResult.did
        else -> throw IllegalStateException("Failed to create holder DID: ${holderDidResult.reason}")
    }

    // 2. Set up trust anchor
    trustLayer.trust {
        addAnchor(issuerDid.value) {
            credentialTypes("TestCredential")
        }
    }

    // 3. Update issuer DID with assertionMethod
    val updateResult = trustLayer.updateDid {
        did(issuerDid.value)
        method(DidMethods.KEY)
        addAssertionMethod("${issuerDid.value}#key-1")
    }
    when (updateResult) {
        is DidUpdateResult.Success -> { /* Success */ }
        else -> throw IllegalStateException("Failed to update DID: ${updateResult.reason}")
    }

    // 4. Resolve issuer DID to get key ID
    val issuerResolution = trustLayer.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")

    // 5. Issue credential
    val issuanceResult = trustLayer.issue {
        credential {
            id("https://example.com/credential-1")
            type("TestCredential")
            issuer(issuerDid.value)
            subject {
                id(holderDid.value)
                "test" to "value"
            }
            issued(Instant.now())
        }
        signedBy(issuerDid = issuerDid.value, keyId = issuerKeyId)
        proofPurpose(ProofPurposes.ASSERTION_METHOD)
    }
    
    val credential = when (issuanceResult) {
        is IssuanceResult.Success -> issuanceResult.credential
        else -> throw IllegalStateException("Failed to issue credential: ${issuanceResult.reason}")
    }

    // 6. Verify with all checks enabled
    val result = trustLayer.verify {
        credential(credential)
        checkTrustRegistry(true)
        validateProofPurpose(true)
        checkExpiration(true)
    }

    // 7. Check results
    println("Complete Verification Results:")
    println("  Valid: ${result.valid}")
    println("  Trust Registry Valid: ${result.trustRegistryValid}")
    println("  Proof Purpose Valid: ${result.proofPurposeValid}")
    println("  Proof Valid: ${result.proofValid}")
    println("  Not Expired: ${result.notExpired}")

    if (result.valid && result.trustRegistryValid && result.proofPurposeValid) {
        println("✅ Credential verified successfully with all checks!")
    }
}

What this does: Demonstrates the full trust workflow in miniature—build a trust layer, establish anchors, issue a credential with proof purpose, and verify it with all checks enabled.

Outcome: Use this function as a regression test to ensure future changes preserve the trust-layer guarantees.

Real-World Use Cases

University Credential Verification

Universities can be added as trust anchors, allowing verifiers to automatically trust credentials issued by recognized institutions.

1
2
3
4
5
6
trustLayer.trust {
    addAnchor(universityDid) {
        credentialTypes("EducationCredential", "DegreeCredential", "CertificateCredential")
        description("Accredited university")
    }
}

Outcome: Any verifier consulting the trust registry now recognises the university’s credentials automatically, reducing ad-hoc whitelists across institutions.

Corporate Delegation

Companies can delegate credential issuance to HR departments, creating a hierarchical authority structure while maintaining centralized trust.

1
2
3
4
5
6
7
8
9
10
11
// CEO delegates to HR Director
trustLayer.updateDid {
    did(ceoDid)
    addCapabilityDelegation("$hrDirectorDid#key-1")
}

// HR Director delegates to HR Manager
trustLayer.updateDid {
    did(hrDirectorDid)
    addCapabilityDelegation("$hrManagerDid#key-1")
}

Outcome: A delegation ladder forms so HR staff can issue credentials while the executive team retains ultimate control over capability revocation.

Multi-Party Trust Networks

Multiple organizations can form trust networks where credentials issued by one trusted party are automatically trusted by others in the network.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Create trust network
trustLayer.trust {
    addAnchor(organization1Did) {
        credentialTypes("PartnershipCredential")
    }
    addAnchor(organization2Did) {
        credentialTypes("PartnershipCredential")
    }
    addAnchor(organization3Did) {
        credentialTypes("PartnershipCredential")
    }

    // Get all trusted partners
    val partners = getTrustedIssuers("PartnershipCredential")
    println("Trusted partners: ${partners.joinToString(", ")}")
}

Outcome: A single query returns every organisation trusted for partnership credentials, making it easy to surface network relationships to auditors or dashboards.

Best Practices

  1. Use Credential Type Filtering: Specify credential types when adding trust anchors to limit trust scope
  2. Verify Delegation Chains: Always verify delegation chains when accepting delegated credentials
  3. Check Trust Paths: Verify trust paths before accepting credentials from unknown issuers
  4. Update DID Documents: Keep DID documents up-to-date with capability relationships
  5. Use Proof Purpose Validation: Enable proof purpose validation to ensure proofs are used correctly
  6. Monitor Trust Scores: Use trust scores to make informed decisions about credential acceptance
  7. Regular Audits: Periodically review and update trust anchors and delegation relationships

Error Handling

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
try {
    val result = trustLayer.verify {
        credential(credential)
        checkTrustRegistry(true)
        validateProofPurpose(true)
        verifyDelegation(true)
    }

    if (!result.valid) {
        println("Verification failed:")
        result.errors.forEach { println("  - $it") }
        result.warnings.forEach { println("  Warning: $it") }
    }
} catch (e: Exception) {
    println("Verification error: ${e.message}")
}

Outcome: Errors and warnings are surfaced explicitly so you can distinguish hard failures (invalid proofs, missing trust anchors) from advisory messages during troubleshooting.

See Also