This guide explains how to create custom plugins for TrustWeave by implementing the various plugin interfaces.
Overview
TrustWeave is designed with a plugin architecture that allows you to extend functionality by implementing specific interfaces. Plugins can be registered manually or discovered automatically via the Service Provider Interface (SPI).
Plugin Types
TrustWeave supports the following plugin interfaces:
DidMethod - Implement custom DID methods (e.g., did:web, did:key, did:ion)
BlockchainAnchorClient - Add support for new blockchain networks
packagecom.example.TrustWeave.pluginsimportcom.trustweave.did.*importcom.trustweave.kms.KeyManagementServiceimportjava.time.Instantimportjava.util.UUID/**
* Example implementation of a custom DID method.
* This creates simple did:example DIDs stored in memory.
*/classExampleDidMethod(privatevalkms:KeyManagementService):DidMethod{overridevalmethod="example"// In-memory storage (use a database in production)privatevaldocuments=mutableMapOf<String,DidDocument>()overridesuspendfuncreateDid(options:DidCreationOptions):DidDocument{// Generate a key using the KMSvalalgorithm=options.algorithm.algorithmNamevalkeyHandle=kms.generateKey(algorithm,options.additionalProperties)// Create DID identifiervaldidId=UUID.randomUUID().toString().replace("-","")valdid="did:$method:$didId"// Create verification methodvalverificationMethodId="$did#${keyHandle.id}"valverificationMethod=VerificationMethodRef(id=verificationMethodId,type=when(algorithm.uppercase()){"ED25519"->"Ed25519VerificationKey2020""SECP256K1"->"EcdsaSecp256k1VerificationKey2019"else->"JsonWebKey2020"},controller=did,publicKeyJwk=keyHandle.publicKeyJwk,publicKeyMultibase=keyHandle.publicKeyMultibase)// Build DID Documentvaldocument=DidDocument(id=did,verificationMethod=listOf(verificationMethod),authentication=listOf(verificationMethodId),assertionMethod=if(options.purposes.contains(KeyPurpose.ASSERTION)){listOf(verificationMethodId)}elseemptyList())// Store documentdocuments[did]=documentreturndocument}overridesuspendfunresolveDid(did:String):DidResolutionResult{// Validate DID formatif(!did.startsWith("did:$method:")){returnDidResolutionResult(document=null,resolutionMetadata=mapOf("error"to"invalidDid"))}valdocument=documents[did]valnow=Instant.now()returnif(document!=null){DidResolutionResult(document=document,documentMetadata=DidDocumentMetadata(created=now,updated=now),resolutionMetadata=emptyMap())}else{DidResolutionResult(document=null,resolutionMetadata=mapOf("error"to"notFound"))}}overridesuspendfunupdateDid(did:String,updater:(DidDocument)->DidDocument):DidDocument{valcurrent=documents[did]?:throwIllegalArgumentException("DID not found: $did")valupdated=updater(current)documents[did]=updatedreturnupdated}overridesuspendfundeactivateDid(did:String):Boolean{returndocuments.remove(did)!=null}}
packagecom.example.TrustWeave.pluginsimportcom.trustweave.anchor.*importcom.trustweave.core.exception.NotFoundExceptionimportkotlinx.serialization.json.JsonElementimportjava.util.concurrent.ConcurrentHashMapimportjava.util.concurrent.atomic.AtomicLong/**
* Example blockchain anchor client implementation.
* This stores anchors in memory (use actual blockchain SDK in production).
*/classExampleBlockchainAnchorClient(privatevalchainId:String,privatevalcontract:String?=null):BlockchainAnchorClient{privatevalstorage=ConcurrentHashMap<String,AnchorResult>()privatevaltxCounter=AtomicLong(0)overridesuspendfunwritePayload(payload:JsonElement,mediaType:String):AnchorResult{// Generate transaction hashvaltxHash="tx_${txCounter.incrementAndGet()}_${System.currentTimeMillis()}"// Create anchor referencevalref=AnchorRef(chainId=chainId,txHash=txHash,contract=contract)// Create anchor resultvalresult=AnchorResult(ref=ref,payload=payload,mediaType=mediaType,timestamp=System.currentTimeMillis()/1000)// Store (in production, submit to blockchain)storage[txHash]=resultreturnresult}overridesuspendfunreadPayload(ref:AnchorRef):AnchorResult{if(ref.chainId!=chainId){throwIllegalArgumentException("Chain ID mismatch")}returnstorage[ref.txHash]?:throwNotFoundException("Anchor not found: ${ref.txHash}")}}
Using AbstractBlockchainAnchorClient
For production implementations, extend AbstractBlockchainAnchorClient which provides fallback storage and common patterns:
importcom.trustweave.anchor.AbstractBlockchainAnchorClientclassMyBlockchainClient(chainId:String,options:Map<String,Any?>):AbstractBlockchainAnchorClient(chainId,options){overrideprotectedfuncanSubmitTransaction():Boolean{// Check if credentials are configuredreturnoptions["privateKey"]!=null}overrideprotectedsuspendfunsubmitTransactionToBlockchain(payloadBytes:ByteArray):String{// Submit to actual blockchain// Return transaction hashreturn"0x..."}overrideprotectedsuspendfunreadTransactionFromBlockchain(txHash:String):AnchorResult{// Read from actual blockchain// Return AnchorResult}overrideprotectedfunbuildExtraMetadata(mediaType:String):Map<String,String>{returnmapOf("network"to"mainnet","mediaType"tomediaType)}overrideprotectedfungenerateTestTxHash():String{return"test_${System.currentTimeMillis()}"}}
Registration
1
2
3
4
5
6
7
8
9
10
11
valTrustWeave=TrustWeave.create{blockchains{"example:mainnet"toExampleBlockchainAnchorClient("example:mainnet")}}// Or after creationTrustWeave.registerBlockchainClient("example:mainnet",ExampleBlockchainAnchorClient("example:mainnet"))
3. Implementing a Proof Generator
The ProofGenerator interface allows you to implement custom proof types.
packagecom.example.TrustWeave.pluginsimportcom.trustweave.kms.*importjava.security.*importjava.util.concurrent.ConcurrentHashMap/**
* Example KMS implementation using Java's KeyPairGenerator.
*/classExampleKeyManagementService:KeyManagementService{privatevalkeys=ConcurrentHashMap<String,KeyPair>()overridesuspendfungenerateKey(algorithm:String,options:Map<String,Any?>):KeyHandle{valkeyPairGenerator=when(algorithm.uppercase()){"ED25519"->KeyPairGenerator.getInstance("EdDSA")"SECP256K1"->KeyPairGenerator.getInstance("EC")else->throwIllegalArgumentException("Unsupported algorithm: $algorithm")}keyPairGenerator.initialize(256)valkeyPair=keyPairGenerator.generateKeyPair()valkeyIdString="key_${System.currentTimeMillis()}"keys[keyIdString]=keyPair// Convert to JWK format (simplified)valpublicKeyJwk=mapOf("kty"to"EC","crv"toalgorithm,"x"tokeyPair.public.encoded.toString(Charsets.UTF_8))returnKeyHandle(id=KeyId(keyIdString),algorithm=algorithm,publicKeyJwk=publicKeyJwk)}overridesuspendfungetPublicKey(keyId:KeyId):KeyHandle{valkeyPair=keys[keyId.value]?:throwKeyNotFoundException("Key not found: ${keyId.value}")returnKeyHandle(id=keyId,algorithm="Ed25519",// Determine from keyPairpublicKeyJwk=mapOf("kty"to"EC")// Convert properly)}overridesuspendfunsign(keyId:KeyId,data:ByteArray,algorithm:String?):ByteArray{valkeyPair=keys[keyId.value]?:throwKeyNotFoundException("Key not found: ${keyId.value}")valsigner=Signature.getInstance("Ed25519")signer.initSign(keyPair.private)signer.update(data)returnsigner.sign()}overridesuspendfundeleteKey(keyId:KeyId):Boolean{returnkeys.remove(keyId.value)!=null}}
importcom.trustweave.spi.PluginLifecycleclassMyBlockchainClient:BlockchainAnchorClient,PluginLifecycle{overridesuspendfuninitialize(config:Map<String,Any?>):Boolean{// Initialize connections, load configurationreturntrue}overridesuspendfunstart():Boolean{// Start background processesreturntrue}overridesuspendfunstop():Boolean{// Stop accepting new operationsreturntrue}overridesuspendfuncleanup(){// Clean up resources}}
TrustWeave automatically manages lifecycle for all registered plugins:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
valTrustWeave=TrustWeave.create{...}// Initialize all pluginsTrustWeave.initialize().getOrThrow()// Start all pluginsTrustWeave.start().getOrThrow()// ... use TrustWeave ...// Stop all pluginsTrustWeave.stop().getOrThrow()// CleanupTrustWeave.cleanup().getOrThrow()