Skip to main content
Version: v0.4.x

Migrating from AFJ 0.3.x to 0.4.x

This document describes everything you need to know for updating AFJ 0.3.x to 0.4.x. If you're not aware of how updating in AFJ works make sure to first read the guide on Updating AFJ.

First of all, update you dependencies to the 0.4.x versions. This will also update the needed peer dependencies. Extension packages are not updated with this command. You need to update these manually, and make sure they're up to date with the latest version of AFJ.

Aries Framework JavaScript 0.4.0 is a major release that introduces a lot of new features and changes to the public API. Specifically, this release removed the dependency on the Indy SDK from the @aries-framework/core package. Agent setup is more flexible, but it also means the setup is more complex. Follow the mentioned steps in this document carefully to make the upgrade as smooth as possible.

caution

The migration guide only covers how to migrate from 0.3.x to 0.4.x while keeping the same behavior and dependencies. AFJ 0.4.0 introduced a lot of new features and adds support for Aries Askar, Indy VDR and AnonCreds RS as a replacement for the Indy SDK.

Migrating to these new components requires additional migration steps, which need to be closely followed to prevent loss of data. These can be found at the Update Indy SDK to Askar guide.

As noted in the Update Indy SDK to Askar guide, it is very important that the 0.3.x to 0.4.x update is started after migrating from the Indy SDK to Aries Askar.

caution

Multi-tenancy is not covered in the 0.3.x to 0.4.x migration guide. If you're using multi-tenancy in 0.3.x and want to migrate to 0.4.x, please open an issue on Github.

caution

The following APIs, modules and features are experimental and therefore not covered by the semver versioning in Aries Framework JavaScript. If you're using any of these features, make sure to use an exact version of AFJ (0.4.0) instead of a range (^0.4.0):

  • Implementing your own AnonCredsRegistry and AnonCreds service implementation. Using the default implementations (Indy SDK, AnonCreds RS) is fine.
  • Using the shared component libraries from @aries-framework/aries-askar, @aries-framework/indy-vdr and @aries-framework/anoncreds-rs
  • Using OpenID4VC from the @aries-framework/openid4vc-client module
  • W3C JWT Verifiable Credentials
  • Using multi-tenancy from the @aries-framework/tenants module

First install the updated dependencies. Make sure to also install the new @aries-framework/indy-sdk package, which is a wrapper around the Indy SDK and install the indy-sdk-react-native package.

yarn add @aries-framework/react-native@^0.4.0 @aries-framework/core@^0.4.0 @aries-framework/indy-sdk@^0.4.0 @aries-framework/anoncreds@^0.4.0 indy-sdk-react-native@^0.3.1

# or NPM
npn install @aries-framework/react-native@^0.4.0 @aries-framework/core@^0.4.0 @aries-framework/indy-sdk@^0.4.0 @aries-framework/anoncreds@^0.4.0 indy-sdk-react-native@^0.3.1

We also need to install types for the indy-sdk-react-native package, which we take from the @types/indy-sdk package.

yarn add --dev @types/indy-sdk-react-native@npm:@types/indy-sdk

# or NPM
npm install --save-dev @types/indy-sdk-react-native@npm:@types/indy-sdk

Breaking Code Changes

This section will list all breaking changes made to the public API of AFJ between version 0.3.x and 0.4.0.

info

If you have custom modules take into account there could be a lot more breaking changes that aren't documented here. We try to make sure that the biggest breaking changes to the internal API are also documented here, but it is possible some breaking changes are not documented here (feel free to open a pull request).

Agent Creation

With the dependency on the Indy SDK removed from the @aries-framework/core package, we now need to register the IndySdkModule to still leverage the functionality the Indy SDK provides.

import { Agent } from '@aries-framework/core'

// Import from @aries-framework/react-native in React Native
import { agentDependencies } from '@aries-framework/node'

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

Indy Network Configuration

With the Indy SDK being extracted out of core, the indyLedger configuration option is no longer available on the AgentConfig interface. Instead, the networks configuration option is now available on the IndySdkModule configuration.

In addition the connectToIndyLedgersOnStartup property has been removed in favor of a per-network connectOnStartup property that allows more fine-grained control over which networks to connect to on startup.

import { Agent } from '@aries-framework/core'

// Import from @aries-framework/react-native in React Native
import { agentDependencies } from '@aries-framework/node'

const agent = new Agent({
config: {
connectToIndyLedgersOnStartup: true,
indyLedgers: [
{
id: 'Sovrin Mainnet',
indyNamespace: 'sovrin',
isProduction: true,
genesisPath: './genesis/sovrin-genesis.txn',
},
],
},
dependencies: agentDependencies,
})

Changes to wallet

Throughout the framework it was possible to provide a seed for deterministic key generation. Recently it was discovered that the seed property in the Indy SDK was not actually used as a seed, but directly as the private key.

Therefore a new privateKey property was added to the Wallet interface in addition to the seed property. When using the IndySdkModule this now means you can only provide the privateKey property. The seed property is no longer supported by the IndySdkModule.

The type of the seed property has also been changed to Buffer to make it more consistent with privateKey property, and remove the ambiguity of what the encoding of the string variant of the seed property should be.

Did Resolver and Registrar

The did:sov resolver and registrar were registered by default in 0.3.x of the agent. In 0.4.0 they've been moved to the @aries-framework/indy-sdk package and are thus not registered by default on the DIDs module anymore. In addition, the IndySdkSovDidRegistrar has been replaced in favor of the IndySdkIndyDidRegistrar which provides similar behavior, but leverages the new did:indy method, which removes ambiguity about which network to use. You can still use the IndySdkSovDidResolver to resolve did:sov DIDs.

Note that the IndySdkModule MUST be registered when using the Indy SDK did resolvers and registrars (see Agent Creation). The networks supported by the resolvers and registrar is determined by the networks registered on the IndySdkModule (see Indy Network Configuration).

Also note that by default the WebDidResolver, KeyDidResolver and PeerDidResolver are registered, and setting the resolvers property on the DidsModule configuration will override the default resolvers (an exception is the PeerDidResolver as it is required for creating connections, and thus will always be registered).

The same thing is true for the registrars, for which the KeyDidRegistrar and PeerDidRegistrar are registered by default. If defining the resolvers and registrars, make sure to include the default registrars if you want to keep the previous behavior.

import { Agent } from '@aries-framework/core'

// Import from @aries-framework/react-native in React Native
import { agentDependencies } from '@aries-framework/node'

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

Removal of publicDidSeed and publicDid

To make AFJ more generic, and less focused on Hyperledger Indy, and Indy dids, the publicDidSeed and publicDid properties have been removed from the agent configuration, the agent class, and the Wallet interface.

The publicDid property was used as the did to register items in the ledger module. The approach had some limitations:

  • An agent could only have a single publicDid property. This means that if you wanted to write to multiple ledgers you would have to create multiple agents
  • The property assumed only Indy ledgers would be used, and didn't take into account the possibility of other ledgers.

AFJ now provides generic APIs that can work with any ledger, and thus the publicDid property is no longer needed. Different sections of this migration guide will explain the different parts of how to use the new APIs, this section just focuses on how to replace the publicDid property.

The most common use case for the publicDid property was for registering an endorser did that can endorse (read: pay for) transactions on the ledger. This can now be done by importing the did into agent, after which it can be used by the AnonCreds module to register schemas and credential definitions, and the did registrar to register DIDs.

There's a one-time import that needs to be done to import the did into the agent using the DidsApi.import method. If you've previously used the publicDidSeed property, providing the private key is optional, as it is already stored in the wallet. Note that the privateKey is the same as the publicDidSeed, see Changes to Wallet for context). The import method will resolve the did passed to the import method (so make sure to register the IndySdkIndyDidResolver). It is recommended to import the endorser did as an did:indy did rather than a did:sov did, as the did:sov method is deprecated for creation. The did:indy did can be constructed by replacing sov with indy:<indyNamespace>, where <indyNamespace> should be replaced with a namespace as registered in the networks property of the IndySdkModule (see Indy Network Configuration).

An initial list of namespace identifiers can be found in an issue in the Indy Did Networks Repository. In the future this list will be maintained in the Indy DID Networks repository itself.

import { Agent } from '@aries-framework/core'

// Import from @aries-framework/react-native in React Native
import { agentDependencies } from '@aries-framework/node'

const agent = new Agent({
config: {
publicDidSeed: '01eafa4de4e22ed4fc2ee522b6ce2731',
},
dependencies: agentDependencies,
})

More Granular Usage of Legacy did:sov Prefix in DIDComm Messages

AFJ 0.3.0 used the useLegacyDidSovPrefix to use the legacy did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/ as the prefix in the @type of DIDComm message instead of the new https://didcomm.org prefix. Over time it has proven that this approach leads to undesired behavior as all messages (even protocols that were defined after the new prefix was the default) would use the legacy prefix. However, due to not all implementations having support for new prefix, disabling the legacy prefixes proved to be a problem.

Therefore, in AFJ 0.4.0 the useLegacyDidSovPrefix property has been replaced with the useDidSovPrefixWhereAllowed property. This property will only use the legacy prefix for protocols that were defined before the new prefix was the default. This means that protocols that were defined after the new prefix was the default will use the new prefix independent of the value of the useDidSovPrefixWhereAllowed property. We hope this will allow us to slowly migrate away from the legacy prefix as new protocols are defined without breaking backwards compatibility.

import { Agent } from '@aries-framework/core'

// Import from @aries-framework/react-native in React Native
import { agentDependencies } from '@aries-framework/node'

const agent = new Agent({
config: {
useLegacyDidSovPrefix: true,
},
dependencies: agentDependencies,
})

Removal of injectionContainer property from the agent

The injectionContainer property on the Agent has been replaced by the dependencyManager property. The dependencyManager property is an instance of the DependencyManager class and wraps the injection container from tsyringe.

The DependencyManager should provide all functionality that is needed, and it is recommended to not use the injection directly. If you need to use the injection container directly, you can access it via the container property on the DependencyManager instance, but please raise an issue to discuss if the functionality you need should be added to the DependencyManager class.

connection has been updated to connectionId in TransportSession

The connection property on the TransportSession has been changed to connectionId and now only holds a reference to the connection id instead of the connection record itself.

Accessing the connection on a transport session is an advanced case that is mostly only relevant when implementing a custom transport. If you are using the default transports, you should not be affected by this change.

Replacement of Ledger Module with new AnonCreds Module

The ledger module has been available in AFJ since the very beginning, and was due for a big overhaul. With the addition of the dids module a while ago we already had a replacement for the registerPublicDid and getPublicDid methods on the ledger module. The other methods of the ledger module have been replaced by the AnonCreds module.

In AFJ 0.4.0 AnonCreds credential support is not part of the core framework anymore, and needs to be manually registered on the agent. The first part is enabling the AnonCreds module, which allows to manage AnonCreds objects, interact with the ledger, and issuer, hold and verify AnonCreds credentials and is explained in this section. The second part is actually allowing AnonCreds credentials to be exchanged in the Issue Credential and Present Proof protocols, which is explained in the Manually Register AnonCreds Support in Credentials and Proofs Modules section.

There's a few important takeaways based on the code example below:

  • The anoncreds module can be accessed under the agent.modules.anoncreds property.
  • The interfaces are set up as generically as possible based on the DID Registration and DID Resolution specifications.
  • AnonCreds registries need to be manually registered so it's important to register the IndySdkAnonCredsRegistry on the AnonCredsModule.
  • It is now required to pass an issuerId when registering AnonCreds objects, according to the AnonCreds Specification. Only did:indy issuer identifiers are allowed (based on the did:indy AnonCreds Method), so the network can be inferred from the issuer identifier.

As will be explained in the Manually Register AnonCreds Support in Credentials and Proofs Modules, you can still use the unqualified issuer identifiers (best known as the Hyperledger Indy Legacy AnonCreds Method) in credential and proof exchanges.

import { Agent } from '@aries-framework/core'

// Import from @aries-framework/react-native in React Native
import { agentDependencies } from '@aries-framework/node'

const agent = new Agent({
config: {
/* ... */
publicDidSeed: '01eafa4de4e22ed4fc2ee522b6ce2731',
indyLedgers: [
{
id: 'Sovrin Mainnet',
indyNamespace: 'sovrin',
isProduction: true,
genesisPath: './genesis/sovrin-genesis.txn',
},
],
},
dependencies: agentDependencies,
})

await agent.initialize()

const schema = await agent.ledger.registerSchema({
attributes: ['name'],
name: 'Example Schema',
version: '1.0.1',
})

const credentialDefinition = await agent.ledger.registerCredentialDefinition({
schema,
supportRevocation: false,
tag: 'default',
})

Changes to the Credentials and Proofs modules

A lot of small, and some bigger changes have been made to the Credentials, and primarily, the Proofs modules. These changes have been made to make the modules more consistent and generic so that they can be used in a wider range of use cases, removing any focus on AnonCreds and Indy credentials.

Changes to the Proofs module include:

  • getRequestedCredentialsForProofRequest has been renamed to getCredentialsForRequest
  • autoSelectCredentialsForProofRequest has been renamed to selectCredentialsForRequest
  • The config parameter for both methods has been removed and those have been replaced by proof format specific configuration options. In this case, the config.filterByNonRevocationRequirements has been added as proofFormats.indy.filterByNonRevocationRequirements (if the indy format is registered as explained in the next section). The config.filterByPresentationPreview has been removed as the presentation preview was only present in the present proof V1 protocol, and due to it's limited applicability (the holder starts with a proposal) we've decided to remove this method for now. You can still filter the credentials yourself by using the getCredentialsForRequest method.
  • Interfaces have been renamed to be more consistent with the method names. Please refer to the CredentialsApiOptions and ProofsApiOptions for the interface names that can be imported.

If you encounter any other breaking changes in the Proofs and Credentials modules, please open an issue in the Aries JavaScript Docs repository.

Changes to the AnonCreds Credential and Proof Format

With the 0.4.0 release, AFJ now provides a pluggable AnonCreds interface, and requires support AnonCreds credentials to be explicitly registered on the agent. This is also the case for registering the credential and proof formats.

In 0.3.x, the IndyProofFormatService and IndyCredentialFormatService were registered by default. In 0.4.x, these services are no longer registered by default and they should be imported from the @aries-framework/anoncreds package as LegacyIndyProofFormatService and LegacyIndyCredentialFormatService and are based on Aries RFC 0592. In a future version the new AnonCredsCredentialFormatService and AnonCredsProofFormatService will be added to the AnonCreds package, which are based on Aries RFC 0771 and allow for AnonCreds credentials to be exchanged based on the new ledger agnostic AnonCreds Specification.

In addition, the V1CredentialProtocol and V1ProofProtocol have been extracted into the @aries-framework/anoncreds package, as they only support exchange of (legacy Indy) AnonCreds credentials.

import { Agent } from '@aries-framework/core'

// Import from @aries-framework/react-native in React Native
import { agentDependencies } from '@aries-framework/node'

const agent = new Agent({
config: {
/* ... */
indyLedgers: [
{
id: 'Sovrin Mainnet',
indyNamespace: 'sovrin',
isProduction: true,
genesisPath: './genesis/sovrin-genesis.txn',
},
],
},
dependencies: agentDependencies,
})

Removal of AnonCreds Master Secret management from Wallet

The Wallet class no longer manages the creation and deletion of AnonCreds master secrets (which are now called Link Secrets in AnonCreds module and specification). If you haven't provided a custom masterSecretId to the walletConfig before, the storage migration script should have automatically created an AnonCredsLinkSecretRecord for you based on the walletConfig.id.

For new agents however, you now need to explicitly create a link secret before you can create requests for AnonCreds credential offers. You can do this using the AnonCredsApi.createLinkSecret method.

caution

If you previously used a custom masterSecretId in the walletConfig the migration script will have created an incorrect AnonCredsLinkSecretRecord based on the walletConfig.id. You will need to manually override the link secret record with the correct linkSecretId.

You ONLY need to do this if you're not migrating from Indy SDK to Askar, as in that case the migration script will have created the correct AnonCredsLinkSecretRecord for you.

import { AnonCredsLinkSecretRepository } from '@aries-framework/anoncreds'

const linkSecretRepository = agent.dependencyManager.resolve(AnonCredsLinkSecretRepository)
const defaultLinkSecret = await linkSecretRepository.findDefault(agent.context)

if (defaultLinkSecret) {
defaultLinkSecret.linkSecretId = 'my-custom-link-secret-id'
await linkSecretRepository.update(agent.context, defaultLinkSecret)
}
import { AnonCredsModule } from '@aries-framework/anoncreds'
import { Agent } from '@aries-framework/core'
import { IndySdkAnonCredsRegistry, IndySdkModule } from '@aries-framework/indy-sdk'

// Import from @aries-framework/react-native in React Native
import { agentDependencies } from '@aries-framework/node'

// Import from indy-sdk-react-native in React Native
import indySdk from 'indy-sdk'

const agent = new Agent({
config: {
/* ... */
},
dependencies: agentDependencies,
modules: {
indySdk: new IndySdkModule({
indySdk,
}),
anoncreds: new AnonCredsModule({
registries: [new IndySdkAnonCredsRegistry()],
}),
},
})

// the agent MUST be initialized before calling it.
await agent.wallet.initialize({
id: 'wallet-id',
key: 'wallet-key',
})

await agent.modules.anoncreds.createLinkSecret({
// first one will be set to default automatically
setAsDefault: true,

// will be generated if not provided.
// linkSecretId: 'link-secret-id'
})

Default Outbound DIDComm Content Type now application/didcomm-envelope-enc

The default outbound DIDComm content type has been changed from DidCommMimeType.V0 (application/ssi-agent-wire) to DidCommMimeType.V1 (application/didcomm-envelope-enc). V1 is the default for DIDComm V1 (as defined in Aries RFC 0044). In the past, V0 resulted in better interoperability, but since it has been the default for so long it makes sense to change the default behavior.

It is advised to use the default value as configured by the agent (V1). If you want to keep the old behavior, you can configure the didCommMimeType property in the agent configuration.

import { Agent } from '@aries-framework/core'

// Import from @aries-framework/react-native in React Native
import { agentDependencies } from '@aries-framework/node'

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

Generalizing Indy properties in CredentialExchangeRecord

With AnonCreds credentials now being generalized to support multiple ledgers, the properties specific to Hyperledger Indy have now been generalized into a generic AnonCreds properties.

credentialRecordType

First off, the credentialRecordType that was used to reference stored AnonCreds credentials has been renamed from indy to anoncreds. The migration script takes care of the update to the storage, but you need to make sure to update all places that expect a credentialRecordType of indy to be defined.

CredentialExchangeRecord
{
"credentials": [
{
"credentialRecordType": "indy",
"credentialRecordId": "credential-id"
}
]
}

Metadata

Second, the metadata keys and values have been renamed to be AnonCreds specific rather than Indy specific. The CredentialMetadataKeys have been replaced by the AnonCredsCredentialMetadataKey and AnonCredsCredentialRequestMetadataKey constants which can be imported from the @aries-framework/anoncreds package.

CredentialExchangeRecord
{
"metadata": {
"_internal/indyCredential": {
"schemaId": "schema-id",
"credentialDefinitionId": "cred-def-id",
"indyRevocationRegistryId": "rev-reg-id",
"indyCredentialRevocationId": "cred-rev-id"
},
"_internal/indyRequest": {
"master_secret_blinding_data": {
"v_prime": "string",
"vr_prime": "string"
},
"master_secret_name": "string",
"nonce": "string"
}
}
}

More paths in FileSystem

The FileSystem interface has been extended to support multiple base paths. The previous interface had a single basePath property which was used for storing of files across the framework.

With the different lifetimes of different objects, the FileSystem interface has been extended to now support three different base paths:

  • cachePath - files used for caching purposes. It's okay if the files are cleared from time to time.
  • dataPath - files that are used for long-term reliable storage purposes. These files will never be cleared.
  • tempPath - files that are used for temporary storage purposes. It's okay if the files are cleared from time to time.

If you're using the framework, you don't need to worry about this change. The agent may need to download some objects again after the update. If you've made a custom implementation of the FileSystem, make sure to support all three base paths, and make sure to correctly handle the life-cycle of the files. Items stored under the dataPath should never be cleared.

caution

Indy SDK SQLite wallets do not use the paths defined by the FileSystem interface, and thus will not be influenced by this change. When upgrading to Aries Askar, the dataPath will be used as the base path for storing the SQLite wallet data.

Breaking Storage Changes

The 0.4.0 release introduces some breaking changes to the storage format.

Below all breaking storage changes are explained in as much detail as possible. The update assistant provides all tools to migrate without a hassle, but it is important to know what has changed. All examples only show the keys that have changed, unrelated keys in records have been omitted.

See the Update Assistant documentation for a guide on how to use the update assistant.

There are no configuration parameters to be provided to the update assistant to migrate from 0.3.x to 0.4.x.

TODO