Common Patterns

Version: 0.7.0 Learn common usage patterns and best practices for TrustWeave.

Table of Contents

  1. Issuer → Holder → Verifier workflow
  2. Batch Operations
  3. Error Recovery with Fallbacks
  4. Credential Lifecycle Management
  5. Multi-Chain Anchoring
  6. Wallet Organization Patterns
  7. Education trust domain demo (reference wallet)
  8. Spatial Web drone demo (reference wallet)

Issuer → Holder → Verifier workflow

Complete workflow showing all three parties in a credential ecosystem. This example uses production-ready error handling patterns with fold().

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
package com.example.patterns.workflow

import org.trustweave.trust.quickStart
import org.trustweave.trust.TrustWeave
import org.trustweave.trust.dsl.credential.DidMethods.KEY
import org.trustweave.trust.dsl.credential.KeyAlgorithms.ED25519
import org.trustweave.trust.dsl.credential.KmsProviders.IN_MEMORY
import org.trustweave.trust.types.getOrThrowDid
import org.trustweave.trust.types.WalletCreationResult
import org.trustweave.credential.results.IssuanceResult
import org.trustweave.credential.results.VerificationResult
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    val trustWeave = TrustWeave.quickStart()

    // 1. ISSUER
    val issuerDid = trustWeave.createDid { method(KEY); algorithm(ED25519) }.getOrThrowDid()

    val credential = when (
        val issued = trustWeave.issue {
            credential {
                type("VerifiableCredential", "DegreeCredential")
                issuer(issuerDid)
                subject {
                    id("did:key:holder-123")
                    "name" to "Alice"
                    "degree" to "Bachelor of Science"
                }
            }
            signedBy(issuerDid)
        }
    ) {
        is IssuanceResult.Success -> issued.credential
        is IssuanceResult.Failure -> {
            println("Issue failed: ${issued.allErrors.joinToString()}")
            return@runBlocking
        }
    }
    println("Issuer created credential: ${credential.id}")

    // 2. HOLDER
    val holderDid = trustWeave.createDid { method(KEY); algorithm(ED25519) }.getOrThrowDid()

    val wallet = when (val w = trustWeave.wallet { holder(holderDid) }) {
        is WalletCreationResult.Success -> w.wallet
        is WalletCreationResult.Failure -> {
            println("Wallet creation failed: $w")
            return@runBlocking
        }
    }

    val credentialId = wallet.store(credential)
    println("Holder stored credential: $credentialId")

    // 3. VERIFIER
    when (val v = trustWeave.verify(credential)) {
        is VerificationResult.Valid -> println("Verifier accepted credential")
        is VerificationResult.Invalid -> println("Verification failed: ${v.allErrors.joinToString()}")
    }
}

Key Points:

  • Each party has their own DID
  • Issuer signs the credential with their key
  • Holder stores the credential in their wallet
  • Verifier checks the credential without contacting issuer

Workflow Diagram

sequenceDiagram
    participant I as Issuer
    participant VC as TrustWeave
    participant H as Holder
    participant V as Verifier

    Note over I: 1. Issuer Setup
    I->>VC: createDid()
    VC-->>I: DidDocument

    Note over I: 2. Credential Issuance
    I->>VC: trustWeave.issue { ... }
    VC->>VC: Resolve issuer DID
    VC->>VC: Sign credential with issuer key
    VC-->>I: VerifiableCredential (with proof)

    Note over I,H: 3. Credential Transfer
    I->>H: Send VerifiableCredential

    Note over H: 4. Holder Storage
    H->>VC: trustWeave.wallet { holder(...) }
    VC-->>H: Wallet
    H->>VC: wallet.store(credential)
    VC-->>H: credentialId

    Note over H,V: 5. Credential Presentation
    H->>V: Present VerifiableCredential

    Note over V: 6. Verification
    V->>VC: trustWeave.verify(credential)
    VC->>VC: Resolve issuer DID
    VC->>VC: Verify proof signature
    VC->>VC: Check expiration/revocation
    VC-->>V: VerificationResult

    alt Credential Valid
        V->>V: Accept credential
    else Credential Invalid
        V->>V: Reject credential
    end

Batch Operations

Process multiple DIDs or credentials efficiently using coroutines.

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
package com.example.patterns.batch

import org.trustweave.trust.quickStart
import org.trustweave.trust.TrustWeave
import org.trustweave.trust.dsl.credential.DidMethods.KEY
import org.trustweave.trust.types.getOrThrowDid
import org.trustweave.did.resolver.DidResolutionResult
import org.trustweave.did.identifiers.extractKeyId
import org.trustweave.credential.results.getOrThrow
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.runBlocking
import kotlinx.datetime.Clock
import org.trustweave.did.resolver.errorMessage

fun main() = runBlocking {
    val trustWeave = TrustWeave.quickStart()

    val dids = listOf(
        "did:key:z6Mk...",
        "did:key:z6Mk...",
        "did:key:z6Mk..."
    )

    val results = coroutineScope {
        dids.map { did -> async { trustWeave.resolveDid(did) } }.awaitAll()
    }

    results.forEachIndexed { index, resolution ->
        when (resolution) {
            is DidResolutionResult.Success ->
                println("DID ${index + 1} resolved: ${resolution.document.id}")
            else ->
                println("DID ${index + 1} failed: ${resolution.errorMessage ?: "unknown error"}")
        }
    }

    val credentials = coroutineScope {
        (1..10).map { index ->
            async {
                runCatching {
                    val issuerDid = trustWeave.createDid { method(KEY) }.getOrThrowDid()
                    val issuerDoc = when (val res = trustWeave.resolveDid(issuerDid)) {
                        is DidResolutionResult.Success -> res.document
                        else -> throw IllegalStateException(res.errorMessage ?: "Failed to resolve DID")
                    }
                    val issuerKeyId = issuerDoc.verificationMethod.firstOrNull()?.extractKeyId()
                        ?: throw IllegalStateException("No verification method found")

                    trustWeave.issue {
                        credential {
                            issuer(issuerDid)
                            subject {
                                id("did:key:holder-$index")
                                "index" to index
                            }
                            issued(Clock.System.now())
                        }
                        signedBy(issuerDid, issuerKeyId)
                    }.getOrThrow()
                }
            }
        }.awaitAll()
    }

    val successful = credentials.filter { it.isSuccess }
    val failed = credentials.filter { it.isFailure }

    println("Created ${successful.size} credentials out of ${credentials.size}")
    if (failed.isNotEmpty()) {
        println("Failed: ${failed.size} credentials")
        failed.forEach { result ->
            result.fold(
                onSuccess = { },
                onFailure = { error -> println("  Error: ${error.message}") }
            )
        }
    }
}

Key Points:

  • Use async { ... }.awaitAll() for parallel operations
  • Handle each result individually
  • Filter successful results for further processing

Error Recovery with Fallbacks

Handle errors gracefully with fallback strategies.

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
package com.example.patterns.recovery

import org.trustweave.trust.quickStart
import org.trustweave.trust.TrustWeave
import org.trustweave.trust.types.DidCreationResult
import org.trustweave.did.identifiers.Did
import org.trustweave.did.resolver.DidResolutionResult
import org.trustweave.did.resolver.errorMessage
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    val trustWeave = TrustWeave.quickStart()

    suspend fun createDidWithFallback(methods: List<String>): Did? {
        for (method in methods) {
            val didResult = trustWeave.createDid { method(method) }
            when (didResult) {
                is DidCreationResult.Success -> return didResult.did
                is DidCreationResult.Failure.MethodNotRegistered -> {
                    println("Method '$method' not available, trying next...")
                }
                else -> {
                    println("Unexpected error with method '$method': $didResult")
                }
            }
        }
        return null
    }

    val did = createDidWithFallback(listOf("web", "ion", "key"))
        ?: error("All DID methods failed")

    println("Created DID: ${did.value}")

    suspend fun resolveDidWithRetry(did: String, maxRetries: Int = 3): DidResolutionResult? {
        var lastReason: String? = null

        for (attempt in 1..maxRetries) {
            val resolution = trustWeave.resolveDid(did)
            when (resolution) {
                is DidResolutionResult.Success -> return resolution
                is DidResolutionResult.Failure -> {
                    lastReason = resolution.errorMessage
                    if (attempt < maxRetries) {
                        val delay = (attempt * attempt * 100).toLong()
                        kotlinx.coroutines.delay(delay)
                        println("Retry $attempt/$maxRetries after ${delay}ms...")
                    }
                }
            }
        }

        println("Failed after $maxRetries attempts: $lastReason")
        return null
    }
}

Key Points:

  • Try multiple methods/strategies in order
  • Use exponential backoff for retries
  • Always have a fallback plan
  • Log attempts for debugging

Credential Lifecycle Management

Manage credentials through their entire lifecycle: issuance, storage, presentation, verification, and expiration.

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
package com.example.patterns.lifecycle

import org.trustweave.trust.quickStart
import org.trustweave.trust.TrustWeave
import org.trustweave.trust.dsl.credential.DidMethods.KEY
import org.trustweave.trust.dsl.wallet.presentationFromWalletResult
import org.trustweave.trust.types.getOrThrowDid
import org.trustweave.did.resolver.DidResolutionResult
import org.trustweave.did.resolver.errorMessage
import org.trustweave.did.identifiers.extractKeyId
import org.trustweave.credential.results.VerificationResult
import org.trustweave.credential.results.getOrThrow
import org.trustweave.trust.types.getOrThrow
import org.trustweave.wallet.withLifecycle
import org.trustweave.wallet.withOrganization
import kotlinx.coroutines.runBlocking
import kotlinx.datetime.Clock
import kotlin.time.Duration.Companion.days

fun main() = runBlocking {
    val trustWeave = TrustWeave.quickStart()

    val issuerDid = trustWeave.createDid { method(KEY) }.getOrThrowDid()
    val holderDid = trustWeave.createDid { method(KEY) }.getOrThrowDid()

    val expirationDate = Clock.System.now().plus(365.days)
    val issuerDoc = when (val res = trustWeave.resolveDid(issuerDid)) {
        is DidResolutionResult.Success -> res.document
        else -> throw IllegalStateException(res.errorMessage ?: "Failed to resolve DID")
    }
    val issuerKeyId = issuerDoc.verificationMethod.firstOrNull()?.extractKeyId()
        ?: throw IllegalStateException("No verification method found")

    val credential = trustWeave.issue {
        credential {
            issuer(issuerDid)
            subject {
                id(holderDid.value)
                "name" to "Alice"
            }
            issued(Clock.System.now())
            expires(expirationDate)
        }
        signedBy(issuerDid, issuerKeyId)
    }.getOrThrow()

    val wallet = trustWeave.wallet {
        holder(holderDid)
        enableOrganization()
        enablePresentation()
    }.getOrThrow()

    val credentialId = wallet.store(credential)

    wallet.withOrganization { org ->
        val collectionId = org.createCollection("Education", "Academic credentials")
        org.addToCollection(credentialId, collectionId)
        org.tagCredential(credentialId, setOf("degree", "verified"))
    }

    // For real presentations use trustWeave.presentationFromWalletResult(wallet) { ... }
    // or pass a ProofOptions to wallet's CredentialPresentation API.
    val pres = trustWeave.presentationFromWalletResult(wallet) {
        fromWallet(credentialId)
        holder(holderDid.value)
        challenge("job-application-${System.currentTimeMillis()}")
    }

    val verification = try {
        trustWeave.verify {
            credential(credential)
        }
    } catch (error: Exception) {
        println("Verification failed: ${error.message}")
        return@runBlocking
    }

    when (verification) {
        is VerificationResult.Valid -> { /* continue */ }
        is VerificationResult.Invalid -> {
            println("Credential invalid: ${verification.allErrors.joinToString()}")
            return@runBlocking
        }
    }

    val expiration = credential.expirationDate
    if (expiration != null && expiration < Clock.System.now()) {
        println("Credential expired on $expiration")
        wallet.withLifecycle { lifecycle ->
            lifecycle.archive(credentialId)
        }
    }

    println("Credential lifecycle managed successfully")
}

Key Points:

  • Always set expiration dates for time-sensitive credentials
  • Organize credentials for easy retrieval
  • Verify credentials before use
  • Archive expired credentials
  • Use presentations for selective disclosure

Multi-Chain Anchoring

Anchor the same credential to multiple blockchains for redundancy and interoperability.

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
package com.example.patterns.multichain

import org.trustweave.trust.TrustWeave
import org.trustweave.trust.dsl.credential.DidMethods.KEY
import org.trustweave.trust.dsl.credential.KmsProviders.IN_MEMORY
import org.trustweave.trust.dsl.credential.KeyAlgorithms.ED25519
import org.trustweave.trust.types.DidCreationResult
import org.trustweave.credential.results.IssuanceResult
import org.trustweave.credential.model.vc.VerifiableCredential
import org.trustweave.did.resolver.DidResolutionResult
import org.trustweave.did.identifiers.extractKeyId
import kotlinx.coroutines.runBlocking
import kotlinx.datetime.Clock

fun main() = runBlocking {
    val trustWeave = TrustWeave.build {
        keys { provider(IN_MEMORY); algorithm(ED25519) }
        anchor {
            chain("algorand:testnet") { inMemory() }
            chain("polygon:testnet") { inMemory() }
        }
        did { method("key") { algorithm("Ed25519") } }
    }

    val issuerDid = when (val issuerDidResult = trustWeave.createDid { method(KEY) }) {
        is DidCreationResult.Success -> issuerDidResult.did
        is DidCreationResult.Failure -> {
            println("Failed to create issuer DID: $issuerDidResult")
            return@runBlocking
        }
    }

    val issuerDoc = when (val issuerResolution = trustWeave.resolveDid(issuerDid)) {
        is DidResolutionResult.Success -> issuerResolution.document
        else -> throw IllegalStateException("Failed to resolve issuer DID")
    }
    val issuerKeyId = issuerDoc.verificationMethod.firstOrNull()?.extractKeyId()
        ?: throw IllegalStateException("No verification method found")

    val credential = when (
        val issuanceResult = trustWeave.issue {
            credential {
                issuer(issuerDid)
                subject {
                    id("did:key:holder")
                    "data" to "important-data"
                }
                issued(Clock.System.now())
            }
            signedBy(issuerDid, issuerKeyId)
        }
    ) {
        is IssuanceResult.Success -> issuanceResult.credential
        is IssuanceResult.Failure -> {
            println("Failed to issue credential: ${issuanceResult.allErrors.joinToString()}")
            return@runBlocking
        }
    }

    val chains = listOf("algorand:testnet", "polygon:testnet")
    val anchorResults = chains.mapNotNull { chainId ->
        try {
            val anchor = trustWeave.blockchains.anchor(
                data = credential,
                serializer = VerifiableCredential.serializer(),
                chainId = chainId
            )
            println("Anchored to $chainId: ${anchor.ref.txHash}")
            anchor
        } catch (error: Exception) {
            println("Failed to anchor to $chainId: ${error.message}")
            null
        }
    }

    println("Anchored to ${anchorResults.size} out of ${chains.size} chains")
    val anchorRefs = anchorResults.map { it.ref }
}

Key Points:

  • Anchor to multiple chains for redundancy
  • Handle failures gracefully (some chains may be unavailable)
  • Store all anchor references for verification
  • Use different chains for different use cases

Wallet Organization Patterns

Organize credentials efficiently using collections, tags, and metadata.

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
package com.example.patterns.organization

import org.trustweave.trust.quickStart
import org.trustweave.trust.TrustWeave
import org.trustweave.trust.dsl.credential.DidMethods.KEY
import org.trustweave.trust.types.WalletCreationResult
import org.trustweave.trust.types.getOrThrowDid
import org.trustweave.did.resolver.DidResolutionResult
import org.trustweave.did.identifiers.extractKeyId
import org.trustweave.credential.results.getOrThrow
import org.trustweave.wallet.withOrganization
import kotlinx.coroutines.runBlocking
import kotlinx.datetime.Clock

fun main() = runBlocking {
    val trustWeave = TrustWeave.quickStart()

    val holderDid = trustWeave.createDid { method(KEY) }.getOrThrowDid()

    val walletResult = trustWeave.wallet {
        holder(holderDid)
        provider("inMemory")
    }

    val wallet = when (walletResult) {
        is WalletCreationResult.Success -> walletResult.wallet
        is WalletCreationResult.Failure.InvalidHolderDid -> {
            println("Failed to create wallet: invalid holder DID '${walletResult.holderDid}': ${walletResult.reason}")
            return@runBlocking
        }
        is WalletCreationResult.Failure.FactoryNotConfigured -> {
            println("Failed to create wallet: factory not configured: ${walletResult.reason}")
            return@runBlocking
        }
        is WalletCreationResult.Failure.StorageFailed -> {
            println("Failed to create wallet: storage failed: ${walletResult.reason}")
            return@runBlocking
        }
        is WalletCreationResult.Failure.Other -> {
            println("Failed to create wallet: ${walletResult.reason}")
            return@runBlocking
        }
    }

    val issuerDid = trustWeave.createDid { method(KEY) }.getOrThrowDid()

    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()?.extractKeyId()
        ?: throw IllegalStateException("No verification method found")

    val credentials = listOf(
        "Bachelor of Science" to "education",
        "Professional License" to "professional",
        "Membership Card" to "membership"
    )

    val credentialIds = credentials.mapNotNull { (name, category) ->
        val issuanceResult = trustWeave.issue {
            credential {
                issuer(issuerDid)
                subject {
                    id(holderDid.value)
                    "credentialName" to name
                }
                issued(Clock.System.now())
                type("VerifiableCredential", "${category}Credential")
            }
            signedBy(issuerDid)
        }
        
        val credential = try {
            issuanceResult.getOrThrow()
        } catch (error: IllegalStateException) {
            println("Failed to issue credential '$name': ${error.message}")
            return@mapNotNull null
        }

        val credentialId = wallet.store(credential)

        // Organize immediately after storage
        wallet.withOrganization { org ->
            // Create collection by category
            val collectionId = org.createCollection(
                name = category.replaceFirstChar { it.titlecase() },
                description = "$category credentials"
            )
            org.addToCollection(credentialId, collectionId)

            // Add tags
            org.tagCredential(credentialId, setOf(category, "verified", "active"))

            // Add metadata
            org.addMetadata(credentialId, mapOf(
                "category" to category,
                "storedAt" to System.currentTimeMillis()
            ))
        }

        credentialId
    }

    // Query by collection
    wallet.withOrganization { org ->
        val educationCollection = org.listCollections()
            .firstOrNull { it.name == "Education" }

        if (educationCollection != null) {
            val educationCreds = org.getCredentialsInCollection(educationCollection.id)
            println("Education credentials: ${educationCreds.size}")
        }
    }

    // Query by tag
    wallet.withOrganization { org ->
        val verifiedCreds = org.findByTag("verified")
        println("Verified credentials: ${verifiedCreds.size}")
    }

    // Get statistics
    val stats = wallet.getStatistics()
    println("Wallet stats:")
    println("  Total: ${stats.totalCredentials}")
    println("  Collections: ${stats.collectionsCount}")
    println("  Tags: ${stats.tagsCount}")
}

Key Points:

  • Organize credentials immediately after storage
  • Use collections for broad categories
  • Use tags for flexible querying
  • Add metadata for custom properties
  • Query by collection, tag, or metadata

Education trust domain demo (reference wallet)

The reference wallet (reference-wallet/) includes a runnable demo-university trust domain: a CSV-backed registrar that issues SD-JWT degree credentials and accepts them at a demo verifier. Use it to walk through issuer → holder → verifier without writing Kotlin first.

Trust domain data

Asset Path
Domain metadata reference-wallet/data/trust-domains/demo-university/domain.json
Degree roster (5 students) reference-wallet/data/trust-domains/demo-university/degrees.csv
Discovery API GET /api/demo-issuer/trust-domain

Each CSV row becomes a signed credential with selectively disclosable claims (name, degree, major, institution, graduationDate, gpa).

Web demo routes

Route Role
/issuer/degrees Roster of all graduates in the trust domain
/issuer/degree/[studentId] Degree detail page — full registrar record + wallet offer QR
/issuer/offer Combined issuer console (student picker + QR)
/ · /receive · /present Holder wallet — library, scan offer, share to verifier
/verifier Live verifier — show request QR, poll verification status
/verifier/test Verifier test harness — automated E2E without a phone

Typical demo flow

  1. Start the web wallet: cd reference-wallet && npm run dev -- -H 0.0.0.0 -p 3000
  2. Open /issuer/degree/STU-001 (or any student) and scan the offer QR from the Expo wallet Receive tab.
  3. Open /verifier, scan the verifier QR from the wallet Share tab, disclose claims, and tap Share with verifier.
  4. Confirm the verifier page updates, or run /verifier/test to exercise the same APIs headlessly.

For phone wallets, set demoBackendBaseUrl in reference-wallet/expo/app.json to your PC’s LAN IP (e.g. http://192.168.1.252:3000).

Kotlin equivalents: See Academic Credentials scenario and distribution/examples/.../AcademicCredentialsExample.kt for library-level patterns; the reference wallet demo uses the same issuer/holder/verifier roles over HTTP.


Spatial Web drone demo (reference wallet)

The reference wallet includes a runnable Spatial Web scenario: FAA drone identification (with registered aircraft photos), SF Bay airspace activity authorization, and an airspace gatekeeper that verifies presentations plus geographic policy.

Trust domain data

Asset Path
Airspace domain reference-wallet/data/trust-domains/demo-sf-airspace/domain.json
Airspace drone roster reference-wallet/data/trust-domains/demo-sf-airspace/drones.csv
FAA registry reference-wallet/data/trust-domains/demo-faa-drone-registry/domain.json
FAA drone roster + photos reference-wallet/data/trust-domains/demo-faa-drone-registry/drones.csv, reference-wallet/public/drones/*.jpg (480×360 JPEG, ≤50 KiB; regenerate with npm run resize-demo-photos)
Airspace discovery GET /api/demo-issuer/spatial/trust-domain
FAA discovery GET /api/demo-issuer/faa/trust-domain

Web demo routes

Route Role
/demos Demo hub — links to all runnable scenarios
/issuer/faa FAA UAS registration roster
/issuer/faa/[droneId] Drone ID credential — registration record, aircraft photo, offer QR
/issuer/airspace SF Bay airspace authority — activity authorization roster
/issuer/airspace/[droneId] Activity authorization — domain constraints + offer QR
/airspace/gate Airspace gatekeeper — location/activity session + presentation QR
/ · /receive · /present Drone agent wallet

Typical demo flow

  1. Start the web wallet: cd reference-wallet && npm run dev -- -H 0.0.0.0 -p 3000
  2. Open /demos or go directly to /issuer/faa/DRONE-001 — scan the FAA offer QR (Receive) for DroneIdentificationCredential (includes photo URL + digest).
  3. Open /issuer/airspace/DRONE-001 — scan the airspace offer QR for ActivityAuthorizationCredential.
  4. Open /airspace/gate — set activity data-collection and SF coordinates (37.7749, -122.4194), click Open gate & show QR.
  5. Share tab → scan gate QR → disclose claims → submit. Gate shows cleared or denied (try LA coordinates to see denial).

For phone wallets, set demoBackendBaseUrl in reference-wallet/expo/app.json to your PC’s LAN IP.

Kotlin equivalent: ./gradlew :distribution:examples:runSpatialWeb — see Spatial Web Authorization scenario and distribution/examples/.../SpatialWebExample.kt.



This site uses Just the Docs, a documentation theme for Jekyll.