Skip to main content
Credo’s module system provides a flexible architecture for extending agent functionality. Modules are self-contained packages that register services, APIs, and configuration with the agent’s dependency injection system.

What is a Module?

A module in Credo is a class that implements the Module interface and provides:
  • Services: Business logic and functionality
  • API: Public interface for interacting with the module
  • Configuration: Module-specific settings
  • Repositories: Data persistence and retrieval
export interface Module {
  api?: Constructor<unknown>
  register(dependencyManager: DependencyManager): void
}

Default Modules

Credo includes several default modules that are automatically registered with every agent:
function getDefaultAgentModules() {
  return {
    dcql: () => new DcqlModule(),
    genericRecords: () => new GenericRecordsModule(),
    dids: () => new DidsModule(),
    w3cCredentials: () => new W3cCredentialsModule(),
    cache: () => new CacheModule({ cache: new SingleContextStorageLruCache({ limit: 500 }) }),
    pex: () => new DifPresentationExchangeModule(),
    sdJwtVc: () => new SdJwtVcModule(),
    x509: () => new X509Module(),
    mdoc: () => new MdocModule(),
    kms: () => new KeyManagementModule({}),
  } as const
}
Source: packages/core/src/agent/AgentModules.ts:104
Default modules are always available on the agent without explicit registration. You can override their configuration by providing your own instance in the agent constructor.

Default Module APIs

Default modules are accessible directly on the agent instance:
// Access default module APIs
await agent.dids.create({ method: 'key' })
await agent.w3cCredentials.createCredential({ /* ... */ })
await agent.sdJwtVc.sign({ /* ... */ })
await agent.x509.createSelfSignedCertificate({ /* ... */ })
await agent.mdoc.sign({ /* ... */ })
await agent.kms.createKey({ keyType: 'ed25519' })

Custom Modules

Custom modules extend agent functionality beyond the defaults. Common custom modules include:

DIDComm Module

import { DidCommModule } from '@credo-ts/didcomm'

const agent = new Agent({
  config: { /* ... */ },
  modules: {
    didcomm: new DidCommModule(),
  },
  dependencies: agentDependencies,
})

// Access DIDComm API
await agent.didcomm.connections.createConnection({ /* ... */ })
The DIDComm module is exposed at the top level of the agent for convenience: agent.didcomm is equivalent to agent.modules.didcomm.

OpenID4VC Module

import { OpenId4VcModule } from '@credo-ts/openid4vc'

const agent = new Agent({
  config: { /* ... */ },
  modules: {
    openid4vc: new OpenId4VcModule(),
  },
  dependencies: agentDependencies,
})

// Access OpenID4VC API
await agent.openid4vc.issuer.createCredentialOffer({ /* ... */ })
The OpenID4VC module is also exposed at the top level: agent.openid4vc is equivalent to agent.modules.openid4vc.

AnonCreds Module

import { AnonCredsModule } from '@credo-ts/anoncreds'
import { anoncreds } from '@hyperledger/anoncreds-nodejs'

const agent = new Agent({
  config: { /* ... */ },
  modules: {
    anoncreds: new AnonCredsModule({ anoncreds }),
  },
  dependencies: agentDependencies,
})

// Access via modules namespace
await agent.modules.anoncreds.createSchema({ /* ... */ })

Storage Modules

Storage modules provide the persistence layer:
import { AskarModule } from '@credo-ts/askar'
import { ariesAskar } from '@hyperledger/aries-askar-nodejs'

const agent = new Agent({
  config: { /* ... */ },
  modules: {
    askar: new AskarModule({
      ariesAskar,
      multiWalletDatabasePath: './wallet-db',
    }),
  },
  dependencies: agentDependencies,
})
A storage module (AskarModule or DrizzleStorageModule) is required for the agent to function. The agent will throw an error during initialization if no storage service is registered.

Module Structure

Here’s how to create a custom module:

1. Define the Module Class

import type { DependencyManager, Module } from '@credo-ts/core'

export class MyCustomModule implements Module {
  public readonly config: MyCustomModuleConfig
  public readonly api = MyCustomApi

  public constructor(config?: MyCustomModuleConfigOptions) {
    this.config = new MyCustomModuleConfig(config)
  }

  public register(dependencyManager: DependencyManager) {
    // Register config
    dependencyManager.registerInstance(MyCustomModuleConfig, this.config)

    // Register services
    dependencyManager.registerSingleton(MyCustomService)

    // Register repositories
    dependencyManager.registerSingleton(MyCustomRepository)
  }
}

2. Create the API Class

The API class provides the public interface for your module:
import { injectable, AgentContext } from '@credo-ts/core'

@injectable()
export class MyCustomApi {
  private agentContext: AgentContext
  private myCustomService: MyCustomService

  public constructor(agentContext: AgentContext, myCustomService: MyCustomService) {
    this.agentContext = agentContext
    this.myCustomService = myCustomService
  }

  public async doSomething(options: DoSomethingOptions) {
    return await this.myCustomService.doSomething(this.agentContext, options)
  }
}

3. Implement Services

Services contain the business logic:
import { injectable, AgentContext } from '@credo-ts/core'

@injectable()
export class MyCustomService {
  public async doSomething(agentContext: AgentContext, options: DoSomethingOptions) {
    // Business logic here
    const repository = agentContext.dependencyManager.resolve(MyCustomRepository)
    // ...
  }
}

4. Create Repositories

Repositories handle data persistence:
import { Repository, StorageService, EventEmitter, injectable } from '@credo-ts/core'

@injectable()
export class MyCustomRepository extends Repository<MyCustomRecord> {
  public constructor(storageService: StorageService<MyCustomRecord>, eventEmitter: EventEmitter) {
    super(MyCustomRecord, storageService, eventEmitter)
  }
}

Module Configuration

Basic Configuration

export interface MyCustomModuleConfigOptions {
  // Configuration options
  option1?: string
  option2?: number
}

export class MyCustomModuleConfig {
  private options: MyCustomModuleConfigOptions

  public constructor(options?: MyCustomModuleConfigOptions) {
    this.options = options ?? {}
  }

  public get option1() {
    return this.options.option1 ?? 'default-value'
  }
}

Module Lifecycle

Modules can implement lifecycle hooks:
export class MyCustomModule implements Module {
  // ... other methods ...

  // Called during agent initialization
  public async initialize?(agentContext: AgentContext): Promise<void> {
    // Initialization logic
  }

  // Called during agent shutdown
  public async shutdown?(agentContext: AgentContext): Promise<void> {
    // Cleanup logic
  }
}

Module Examples

DidsModule

The DidsModule manages DID creation, resolution, and registration:
export class DidsModule implements Module {
  public readonly config: DidsModuleConfig
  public readonly api = DidsApi

  public constructor(config?: DidsModuleConfigOptions) {
    this.config = new DidsModuleConfig(config)
  }

  public register(dependencyManager: DependencyManager) {
    dependencyManager.registerInstance(DidsModuleConfig, this.config)
    dependencyManager.registerSingleton(DidResolverService)
    dependencyManager.registerSingleton(DidRegistrarService)
    dependencyManager.registerSingleton(DidRepository)
  }
}
Source: packages/core/src/modules/dids/DidsModule.ts:8

W3cCredentialsModule

The W3cCredentialsModule handles W3C Verifiable Credentials:
export class W3cCredentialsModule implements Module {
  public readonly config: W3cCredentialsModuleConfig
  public readonly api = W3cCredentialsApi

  public constructor(config?: W3cCredentialsModuleConfigOptions) {
    this.config = new W3cCredentialsModuleConfig(config)
  }

  public register(dependencyManager: DependencyManager) {
    dependencyManager.registerSingleton(W3cCredentialService)
    dependencyManager.registerSingleton(W3cJwtCredentialService)
    dependencyManager.registerSingleton(W3cJsonLdCredentialService)
    dependencyManager.registerSingleton(W3cCredentialRepository)
    dependencyManager.registerSingleton(SignatureSuiteRegistry)
    dependencyManager.registerInstance(W3cCredentialsModuleConfig, this.config)
  }
}
Source: packages/core/src/modules/vc/W3cCredentialsModule.ts:20

SdJwtVcModule

The SdJwtVcModule implements SD-JWT VC support:
export class SdJwtVcModule implements Module {
  public readonly config: SdJwtVcModuleConfig
  public readonly api = SdJwtVcApi

  public constructor(options?: SdJwtVcModuleConfigOptions) {
    this.config = new SdJwtVcModuleConfig(options)
  }

  public register(dependencyManager: DependencyManager) {
    dependencyManager.registerInstance(SdJwtVcModuleConfig, this.config)
    dependencyManager.registerSingleton(SdJwtVcService)
    dependencyManager.registerSingleton(SdJwtVcRepository)
  }
}
Source: packages/core/src/modules/sd-jwt-vc/SdJwtVcModule.ts:10

Dependency Injection

Modules leverage Credo’s dependency injection system:

Registering Dependencies

// Singleton: One instance shared across the application
dependencyManager.registerSingleton(MyService)

// Instance: Specific instance to use
dependencyManager.registerInstance(MyConfig, configInstance)

// Transient: New instance created each time
dependencyManager.register(MyClass, { useClass: MyClass })

Resolving Dependencies

// In a service or API class
const service = agentContext.dependencyManager.resolve(MyService)

// Using the @injectable decorator
@injectable()
export class MyApi {
  public constructor(
    private agentContext: AgentContext,
    private myService: MyService
  ) {}
}

Best Practices

Module Design:
  • Keep modules focused on a single domain or feature
  • Use clear naming conventions (MyFeatureModule, MyFeatureApi, MyFeatureService)
  • Document configuration options and their defaults
  • Implement lifecycle hooks for proper initialization and cleanup
API Design:
  • APIs should be the only public interface to module functionality
  • Services should remain internal implementation details
  • Use TypeScript for strong typing and better developer experience
  • Provide clear error messages
Configuration:
  • Provide sensible defaults for all configuration options
  • Document required vs. optional configuration
  • Validate configuration during module construction

Module Registry

The DependencyManager maintains a registry of all registered modules:
// Access registered modules
const registeredModules = agent.dependencyManager.registeredModules

// Check if a module is registered
if (agent.dependencyManager.isRegistered(MyModuleApi)) {
  const api = agent.dependencyManager.resolve(MyModuleApi)
}

Build docs developers (and LLMs) love