Skip to main content
Version: 0.7

Askar

Askar is an encrypted store for records and keys. AskarModule registers it as the agent's storage and as a key management backend. It runs on SQLite or PostgreSQL, in Node.js and React Native.

Askar is the default for mobile wallets: it encrypts the wallet, includes key management, and has no migrations to ship with app updates. On a server, Drizzle stores the records and Askar keeps the keys, with storage turned off. Use SQLite, the default, on phones and for a server with one instance, and PostgreSQL when several instances share the keys. To choose between Askar and Drizzle, see Storage.

Install​

npm install @credo-ts/askar@^0.7.2 @openwallet-foundation/askar-nodejs@0.6.1

The platform package pins @openwallet-foundation/askar-shared to its own version, so use a single Askar version across your project. With mixed versions, npm installs a second copy of askar-shared for @credo-ts/askar, and initialize() fails with Native askar has not been registered yet. The Askar to Drizzle migration package needs exactly 0.6.0; see Migrate from Askar.

Configure​

agent.ts
import { AskarModule } from '@credo-ts/askar'
import { Agent } from '@credo-ts/core'
import { agentDependencies } from '@credo-ts/node'
import { NativeAskar } from '@openwallet-foundation/askar-nodejs'

const agent = new Agent({
dependencies: agentDependencies,
modules: {
askar: new AskarModule({
askar: NativeAskar,
store: {
id: 'my-agent',
// Load the key from a secret store in production
key: 'my-agent-store-key',
},
}),
},
})

// Creates the store on the first run, opens it on later runs
await agent.initialize()

Pass the NativeAskar class as askar. The askar instance export of the platform package is deprecated: it's only set once the platform package has run, which depends on import order.

store.id names the store. It's also the root agent's profile, the SQLite directory and the PostgreSQL database name. agent.initialize() opens the store, and creates it if it doesn't exist yet. agent.shutdown() closes it.

With the wrong key, initialize() throws a CredoError caused by AskarStoreInvalidKeyError. A different id or SQLite path doesn't fail: it creates a new, empty store. To open an existing wallet, check that the database exists before you initialize.

Store key​

Askar encrypts the store with store.key. keyDerivationMethod decides how it turns the key into the encryption key:

keyDerivationMethodUse it for
rawRandom keys that you keep somewhere safe, such as the device keychain or a secret manager. Skips key derivation, so opening the store is fast.
kdf:argon2i:mod (default)Passwords and PINs. Argon2i makes guessing the key slow.
kdf:argon2i:intThe same, faster and less secure.

Use raw when you can keep a random key in a keychain or secret manager, and Argon2i when the key is something a person types. Generate a raw key once with Store.generateRawKey() from the platform package: any other string fails when Askar creates the store.

To change the key of an existing store, call agent.modules.askar.rotateStoreKey({ newKey, newKeyDerivationMethod }) and then update the key in your configuration.

Databases​

SQLite​

The store is a file at <data path>/wallet/<store id>/sqlite.db. The data path is ~/.afj/data on Node.js, and .afj in the app's document directory on React Native. To use another file, set the full path of the database file:

database: { type: 'sqlite', config: { path: '/var/lib/my-agent/agent.db' } }

In-memory SQLite​

For tests that need Askar, such as DIDComm tests, keep the store in memory. The data is gone when the agent shuts down. Other tests can use the lighter Drizzle and Node.js setup.

test-agent.ts
import { AskarModule } from '@credo-ts/askar'
import { Agent } from '@credo-ts/core'
import { agentDependencies } from '@credo-ts/node'
import { NativeAskar, Store } from '@openwallet-foundation/askar-nodejs'

const agent = new Agent({
dependencies: agentDependencies,
modules: {
askar: new AskarModule({
askar: NativeAskar,
store: {
id: 'test-agent',
// A random raw key skips the slow key derivation, which keeps tests fast
key: Store.generateRawKey(),
keyDerivationMethod: 'raw',
database: { type: 'sqlite', config: { inMemory: true } },
},
}),
},
})

PostgreSQL​

agent.ts
import { AskarModule } from '@credo-ts/askar'
import { Agent } from '@credo-ts/core'
import { agentDependencies } from '@credo-ts/node'
import { NativeAskar } from '@openwallet-foundation/askar-nodejs'

const agent = new Agent({
dependencies: agentDependencies,
modules: {
askar: new AskarModule({
askar: NativeAskar,
store: {
// Also the name of the Postgres database
id: 'my-agent',
key: 'my-agent-store-key',
database: {
type: 'postgres',
config: {
host: 'localhost:5432',
maxConnections: 10,
connectionParameters: { sslmode: 'require' },
},
credentials: {
account: 'credo',
password: 'credo-password',
},
},
},
}),
},
})

await agent.initialize()

The store id is the database name. On the first initialize() Askar creates that database, so the account needs permission to create databases, or you pass a separate adminAccount and adminPassword in credentials for that.

OptionDescription
config.hostHost and port, for example localhost:5432.
config.maxConnections, config.minConnectionsSize of the connection pool. Also available for SQLite.
config.connectTimeout, config.idleTimeoutTimeouts.
config.connectionParametersExtra connection string parameters, such as sslmode, sslrootcert and application_name. Parameters that Credo sets itself, such as host, user and dbname, throw.
credentials.account, credentials.passwordThe account to connect with.
credentials.adminAccount, credentials.adminPasswordOptional account for creating and deleting databases.

Storage and key management​

AskarModule registers storage and a key management backend named askar. Turn either off:

enableStorageenableKmsUse it when
truetrueAskar stores records and keys. The default.
falsetrueAnother module stores records, such as Drizzle. Askar still creates its store, for the keys.
truefalseYou register AskarKeyManagementService yourself in the KeyManagementModule, for example to make Askar the default next to another backend. See Key management.

If you add AskarKeyManagementService to the KeyManagementModule backends and keep enableKms on, registering the module throws.

Multi-tenancy​

With the tenants module, multiWalletDatabaseScheme decides where tenant data goes: AskarMultiWalletDatabaseScheme.ProfilePerWallet keeps all tenants in the root store with a profile each, DatabasePerWallet (the default) gives each tenant its own store. This also applies when Askar only keeps the keys. Use ProfilePerWallet. See Where tenant data is stored for the trade-offs, and for why you shouldn't set a SQLite path with DatabasePerWallet.

Manage the store​

agent.modules.askar (the module's key in modules) has methods for store-level tasks:

MethodWhat it does
rotateStoreKey({ newKey, newKeyDerivationMethod? })Changes the store key. Update your configuration afterwards.
exportStore({ exportToStore })Copies the open store to a new store, for example a backup with its own id, key and path. On SQLite, the target file must not exist yet.
importStore({ importFromStore })Copies another store, such as a backup, to the configured store location. Call closeStore() first. On SQLite, the configured file must not exist yet.
deleteStore()Deletes the store and all its data.
closeStore(), openStore(), provisionStore(), isStoreOpenManual control. initialize() and shutdown() already do this.

With ProfilePerWallet these methods act on the whole store, including all tenants, so they throw when you call them from a tenant agent.