diff --git a/packages/api/package.json b/packages/api/package.json
index 1dd3b9897..7d6e9927e 100644
--- a/packages/api/package.json
+++ b/packages/api/package.json
@@ -84,6 +84,7 @@
   },
   "devDependencies": {
     "@playwright/test": "1.40.1",
+    "@tbd54566975/dwn-sdk-js": "0.2.18",
     "@types/chai": "4.3.6",
     "@types/eslint": "8.44.2",
     "@types/mocha": "10.0.1",
diff --git a/packages/api/src/web5.ts b/packages/api/src/web5.ts
index 7193a988d..9b08dd30e 100644
--- a/packages/api/src/web5.ts
+++ b/packages/api/src/web5.ts
@@ -184,7 +184,7 @@ export class Web5 {
 
       // Initialize, if necessary, and start the agent.
       if (await userAgent.firstLaunch()) {
-        await userAgent.initialize({ password, recoveryPhrase });
+        recoveryPhrase = await userAgent.initialize({ password, recoveryPhrase });
       }
       await userAgent.start({ password });
 
diff --git a/packages/api/tests/web5.spec.ts b/packages/api/tests/web5.spec.ts
index ef22d6bb6..9438fc7b2 100644
--- a/packages/api/tests/web5.spec.ts
+++ b/packages/api/tests/web5.spec.ts
@@ -26,6 +26,24 @@ describe('Web5', () => {
       await testHarness.closeStorage();
     });
 
+    describe('connect()', () => {
+      it('accepts an externally created DID', async () => {
+        const testIdentity = await testHarness.createIdentity({
+          name        : 'Test',
+          testDwnUrls : ['https://dwn.example.com']
+        });
+
+        // Call connect() with the custom agent.
+        const { web5, did } = await Web5.connect({
+          agent        : testHarness.agent,
+          connectedDid : testIdentity.did.uri
+        });
+
+        expect(did).to.exist;
+        expect(web5).to.exist;
+      });
+    });
+
     describe('constructor', () => {
       it('instantiates Web5 API with provided Web5Agent and connectedDid', async () => {
         // Create a new Identity.
@@ -41,23 +59,73 @@ describe('Web5', () => {
         expect(web5).to.have.property('dwn');
         expect(web5).to.have.property('vc');
       });
+
+      it('supports a single agent with multiple Web5 instances and different DIDs', async () => {
+        // Create two identities, each of which is stored in a new tenant.
+        const careerIdentity = await testHarness.agent.identity.create({
+          metadata  : { name: 'Social' },
+          didMethod : 'jwk'
+        });
+        const socialIdentity = await testHarness.agent.identity.create({
+          metadata  : { name: 'Social' },
+          didMethod : 'jwk'
+        });
+
+        // Instantiate a Web5 instance with the "Career" Identity, write a record, and verify the result.
+        const web5Career = new Web5({ agent: testHarness.agent, connectedDid: careerIdentity.did.uri });
+        expect(web5Career).to.exist;
+
+        // Instantiate a Web5 instance with the "Social" Identity, write a record, and verify the result.
+        const web5Social = new Web5({ agent: testHarness.agent, connectedDid: socialIdentity.did.uri });
+        expect(web5Social).to.exist;
+      });
     });
 
-    describe('connect()', () => {
-      it('accepts an externally created DID', async () => {
-        const testIdentity = await testHarness.createIdentity({
-          name        : 'Test',
-          testDwnUrls : ['https://dwn.example.com']
+    describe('scenarios', () => {
+      it('writes records with multiple identities under management', async () => {
+        // First launch and initialization.
+        await testHarness.agent.initialize({ password: 'test' });
+
+        // Start the Agent, which will decrypt and load the Agent's DID from the vault.
+        await testHarness.agent.start({ password: 'test' });
+
+        // Create two identities, each of which is stored in a new tenant.
+        const careerIdentity = await testHarness.agent.identity.create({
+          metadata  : { name: 'Social' },
+          didMethod : 'jwk'
+        });
+        const socialIdentity = await testHarness.agent.identity.create({
+          metadata  : { name: 'Social' },
+          didMethod : 'jwk'
         });
 
-        // Call connect() with the custom agent.
-        const { web5, did } = await Web5.connect({
-          agent        : testHarness.agent,
-          connectedDid : testIdentity.did.uri
+        // Instantiate a Web5 instance with the "Career" Identity, write a record, and verify the result.
+        const web5Career = new Web5({ agent: testHarness.agent, connectedDid: careerIdentity.did.uri });
+        const careerResult = await web5Career.dwn.records.write({
+          data    : 'Hello, world!',
+          message : {
+            schema     : 'foo/bar',
+            dataFormat : 'text/plain'
+          }
         });
+        expect(careerResult.status.code).to.equal(202);
+        expect(careerResult.record).to.exist;
+        expect(careerResult.record?.author).to.equal(careerIdentity.did.uri);
+        expect(await careerResult.record?.data.text()).to.equal('Hello, world!');
 
-        expect(did).to.exist;
-        expect(web5).to.exist;
+        // Instantiate a Web5 instance with the "Social" Identity, write a record, and verify the result.
+        const web5Social = new Web5({ agent: testHarness.agent, connectedDid: socialIdentity.did.uri });
+        const socialResult = await web5Social.dwn.records.write({
+          data    : 'Hello, everyone!',
+          message : {
+            schema     : 'foo/bar',
+            dataFormat : 'text/plain'
+          }
+        });
+        expect(socialResult.status.code).to.equal(202);
+        expect(socialResult.record).to.exist;
+        expect(socialResult.record?.author).to.equal(socialIdentity.did.uri);
+        expect(await socialResult.record?.data.text()).to.equal('Hello, everyone!');
       });
     });
   });
@@ -69,10 +137,13 @@ describe('Web5', () => {
         keyDerivationWorkFactor : 1,
         store                   : new MemoryStore<string, string>()
       });
-      const { web5 } = await Web5.connect({ agentVault });
+      const { web5, recoveryPhrase } = await Web5.connect({ agentVault });
 
       expect(web5).to.exist;
       expect(web5.agent).to.be.instanceOf(Web5UserAgent);
+      // Verify recovery phrase is a 12-word string.
+      expect(recoveryPhrase).to.be.a('string');
+      expect(recoveryPhrase.split(' ')).to.have.lengthOf(12);
     });
   });
 });
\ No newline at end of file
diff --git a/packages/identity-agent/.vscode/launch.json b/packages/identity-agent/.vscode/launch.json
index 4183caa7d..8ff014b0e 100644
--- a/packages/identity-agent/.vscode/launch.json
+++ b/packages/identity-agent/.vscode/launch.json
@@ -13,7 +13,10 @@
       "preLaunchTask": "build tests",
       "skipFiles": [
         "<node_internals>/**"
-      ]
+      ],
+      "env": {
+        "TEST_DWN_URL": "http://localhost:3000"
+      }
     }
   ]
 }
\ No newline at end of file
diff --git a/packages/identity-agent/README.md b/packages/identity-agent/README.md
new file mode 100644
index 000000000..8fb2a118a
--- /dev/null
+++ b/packages/identity-agent/README.md
@@ -0,0 +1,326 @@
+# Web5 Identity Agent
+
+| A library for building Web5 identity management applications |
+| ------------------------------------------------------------ |
+
+[![NPM Package][identity-agent-npm-badge]][identity-agent-npm-link]
+[![NPM Downloads][identity-agent-downloads-badge]][identity-agent-npm-link]
+
+[![Build Status][identity-agent-build-badge]][identity-agent-build-link]
+[![Open Issues][identity-agent-issues-badge]][identity-agent-issues-link]
+[![Code Coverage][identity-agent-coverage-badge]][identity-agent-coverage-link]
+
+---
+
+- [Introduction](#introduction)
+- [Getting Started](#getting-started)
+  - [Node.js](#nodejs)
+  - [Web Browsers](#web-browsers)
+  - [React Native](#react-native)
+- [Contributing](#contributing)
+- [Core Concepts](#core-concepts)
+  - [Launching an Identity Agent](#launching-an-identity-agent)
+  - [Creating an End User Identity](#creating-an-end-user-identity)
+- [Customization](#customization)
+  - [Using Non-default Data Stores](#using-non-default-data-stores)
+
+---
+
+<a id="introduction"></a>
+
+The Identity Agent SDK is a component of the
+[Web5 JS](https://github.com/TBD54566975/web5-js) platform, created to simplify the development of
+applications that manage multiple Web5 identities on behalf of a single entity. Identity agents,
+sometimes called "wallets", are typically
+native desktop (e.g., [Electron](https://www.electronjs.org)) or
+mobile (e.g, [React Native](https://reactnative.dev)) apps that are installed by end users to
+manage one or more decentralized identities.
+
+## Getting Started
+
+This JavaScript library was designed for modern development runtimes, including Node.js, web
+browsers, and React Native.  The package is distributed as `@web5/identity-agent` via
+[npmjs.com][identity-agent-npm-link], [jsdelivr.com][identity-agent-jsdelivr-link],
+[unpkg.com][identity-agent-unpkg-link], and [github.com][identity-agent-repo-link].
+
+### Node.js
+
+This library is designed and tested for the _active_ (`v20`) and _maintenance_
+(`v18`) [LTS releases](https://nodejs.org/en/about/previous-releases) of Node.js
+
+Install the latest version of `@web5/identity-agent` using `npm` or your preferred package manager:
+
+```shell
+npm install @web5/identity-agent
+```
+
+Example ESM import:
+
+```js
+import { Ed25519 } from "@web5/identity-agent";
+```
+
+Example CJS require:
+
+```js
+const { Ed25519 } = require("@web5/identity-agent");
+```
+
+### Web Browsers
+
+A polyfilled distribution is published to [jsdelivr.com][identity-agent-jsdelivr-browser] and
+[unpkg.com][identity-agent-unpkg-browser] that can imported in a module `<script>` tag:
+
+```html
+<!DOCTYPE html>
+<html lang="en">
+  <body>
+    <script type="module">
+      // Example import from JSDELIVR
+      import { Web5IdentityAgent } from "https://cdn.jsdelivr.net/npm/@web5/identity-agent/dist/browser.mjs";
+    </script>
+  </body>
+</html>
+```
+
+<a id="secure-context"></a>
+
+> [!IMPORTANT]
+> The `@web5/identity-agent` library depends on the
+> [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) in web browsers.
+> Web Crypto is available in all modern browsers but is accessible only in a
+> [secure context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts).
+>
+> This means you will not be able to use many `@web5/identity-agent` features in a browser unless
+> the page is served over `https://` or `wss://`. Locally-delivered resources such as those with
+> `http://127.0.0.1`, `http://localhost`, or `http://*.localhost` URLs are also considered to have
+> been delivered securely.
+
+### React Native
+
+For React Native, you may need a
+[polyfill](https://github.com/LinusU/react-native-get-random-values) for `crypto.getRandomValues`.
+
+```js
+import "react-native-get-random-values";
+```
+
+## Contributing
+
+We welcome you to join our open source community. Whether you're new to open source or a seasoned
+contributor, there's a place for you here. From coding to documentation, every contribution matters.
+Check out our [contribution guide][contributing-link] for ways to get started.
+
+For help, discussion about best practices, or to chat with others building on Web5 join our
+[Discord Server][discord-link]:
+
+[![discord-badge]][discord-link]
+
+Remember, contributing is not just about code; it's about building together. Join us in shaping the
+future of the Web!
+
+## Core Concepts
+
+### Launching an Identity Agent
+
+An [agent](https://developer.tbd.website/docs/web5/learn/agents) is software that acts on behalf of
+a user to manage identity, public or private data, and interactions with other apps or services in a
+network. Identity Agents are a specific type of agent focused on simplifying the management of a
+person's online identities, since they often have several.
+
+Many people already use multiple personas when interacting with others online depending on
+whether the context is family, career, or social. This might take the form of a work issued email
+that you use for career apps and a personal email that you use when interacting with family and
+friends. The same is true with Web5 identities except that the unique identifier is a
+[Decentralized Identifier (DID)](https://developer.tbd.website/docs/web5/learn/decentralized-identifiers)
+rather than an email address. Developers can create identity management apps, sometimes called
+"wallets", that focus on making it easy to connect Web5 apps to one or more identities and keep track
+of which apps have access to your data.
+
+Every time an Identity Agent app runs on a computing device, whether desktop or mobile, the
+following should occur:
+
+1. Check whether the agent was previously initialized.
+2. If this is the first run on this device, initialize the agent.
+3. Load the agent's DID and keys to prepare for use.
+
+An example implementation of these steps would be:
+
+```ts
+import { Web5IdentityAgent } from '@web5/identity-agent'
+
+// Create a Web5 Identity Agent instance.
+const agent = await Web5IdentityAgent.create();
+
+// Prompt the end user to enter a password, which prevents unauthorized access to the encrypted
+// identity vault that secures the Agent's DID and cryptographic keys.
+const password = /* input by user */
+
+
+// If its the first launch, initialize the identity vault for the agent, and if not provided, return
+// the generated recovery phrase.
+let recoveryPhrase: string;
+if (await agent.firstLaunch()) {
+  // Unless provided, a 12-word recovery phrase will automatically be generated and the Agent's
+  // cryptographic keys deterministically generated from the phrase.
+  recoveryPhrase = await agent.initialize({ password, recoveryPhrase });
+}
+
+// On every launch unlock the identity vault using the provided password and load the Agent's DID
+// and keys from encrypted vault storage.
+await userAgent.start({ password });
+```
+
+### Creating an End User Identity
+
+In Web5 apps, a user’s unique identifier - like an email address - is called a
+[Decentralized Identifier (DID)](https://developer.tbd.website/docs/web5/learn/decentralized-identifiers).
+You can think of an Identity as an isolated space uniquely identified by a DID that stores and
+manages the data relevant to a particular context or use case.
+
+For example, a person might have one identity for work-related apps and data, another for
+personal/family, and yet another for their social media persona.
+
+By organizing cryptographic keys, profile information, data, and credentials into separate
+identities, an Identity Agent can help users maintain greater control over their personal
+information and how it is shared with others. Users can choose which identity to share with
+different parties, depending on the context and level of trust.
+
+The follow code example walks through how to create a new identity, manage it with the Identity
+Agent, and store data in the
+[Decentralized Web Node (DWN)](https://developer.tbd.website/docs/web5/learn/decentralized-web-nodes/)
+data store controlled by the newly created identity.
+
+```ts
+import { getTechPreviewDwnEndpoints, Web5 } from '@web5/api';
+
+// Retrieve publicly addressable DWNs that other network participants can use to exchange messages
+// and data with the new Identity.
+const serviceEndpointNodes = await getTechPreviewDwnEndpoints();
+
+// Generate a new Identity for the end-user.
+const careerIdentity = await agent.identity.create({
+  didMethod  : 'dht',
+  metadata   : { name: 'Alice' },
+  didOptions : {
+    services: [
+      {
+        id              : 'dwn',
+        type            : 'DecentralizedWebNode',
+        serviceEndpoint : serviceEndpointNodes,
+        enc             : '#enc',
+        sig             : '#sig',
+      }
+    ],
+    verificationMethods: [
+      {
+        algorithm : 'Ed25519',
+        id        : 'sig',
+        purposes  : ['assertionMethod', 'authentication']
+      },
+      {
+        algorithm : 'secp256k1',
+        id        : 'enc',
+        purposes  : ['keyAgreement']
+      }
+    ]
+  }
+});
+
+// Enable management by this Identity Manager.
+await agent.identity.manage({ portableIdentity: await identity.export() });
+```
+
+### Writing Data to an Identity's Data Store
+
+The [Web5 API](https://github.com/TBD54566975/web5-js/tree/main/packages/api) makes it simple to
+store data in an identity's DWN data store by handling all of the message and data preparation and
+processing steps.  Using the `careerIdentity` created earlier, a simple message payload can be
+written as follows:
+
+```ts
+// Instantiate a Web5 instance with the "Career" Identity.
+const web5Career = new Web5({ agent, connectedDid: careerIdentity.did.uri });
+
+// Write a simple text record.
+const { record, status } = await web5Career.dwn.records.write({
+  data    : 'Message',
+  message : {
+    dataFormat : 'text/plain'
+  }
+});
+
+console.log(status.code) // Output: 202
+
+const recordData = await record?.data.text();
+console.log(recordData); // Output: Message
+```
+
+## Customization
+
+### Using Non-default Data Stores
+
+By default, `Web5IdentityAgent` uses a [LevelDB](https://github.com/Level/level) store for both the
+agent's identity vault and the DID resolver cache. For testing and prototyping purposes it may be
+desirable to use an in-memory store that doesn't persist data. There are also runtime environments,
+such as React Native, that don't support using the [level](https://www.npmjs.com/package/level)
+package. Any implementation of the
+[`KeyValueStore`](https://github.com/TBD54566975/web5-js/blob/5f364bc0d859e28f1388524ebe8ef152a71727c4/packages/common/src/types.ts#L4-L43)
+interface can be substituted for the default identity vault and DID resolver cache.
+
+For example, to use the in-memory `KeyValueStore` implementation from `@web5/common`:
+
+```ts
+import { MemoryStore } from '@web5/common';
+import { DidDht, DidJwk } from '@web5/dids';
+import { Web5IdentityAgent } from '@web5/identity-agent';
+import { AgentDidApi, DidResolverCacheLevel, DwnDidStore } from '@web5/agent';
+
+// Instantiate Identity Vault with an in-memory store.
+const agentVault = new HdIdentityVault({
+  keyDerivationWorkFactor: 210_000,
+  store: new MemoryStore<string, string>()
+});
+
+// Instantiate DID API with an in-memory resolver cache.
+const didApi = new AgentDidApi({
+  didMethods    : [DidDht, DidJwk],
+  resolverCache : new DidResolverCacheMemory(),
+  store         : new DwnDidStore()
+});
+
+// Create a Web5 Identity Agent instance.
+const agent = await Web5IdentityAgent.create({ agentVault, didApi});
+```
+
+## Project Resources
+
+| Resource                                | Description                                                                   |
+| --------------------------------------- | ----------------------------------------------------------------------------- |
+| [CODEOWNERS][codeowners-link]           | Outlines the project lead(s)                                                  |
+| [CODE OF CONDUCT][code-of-conduct-link] | Expected behavior for project contributors, promoting a welcoming environment |
+| [CONTRIBUTING][contributing-link]       | Developer guide to build, test, run, access CI, chat, discuss, file issues    |
+| [GOVERNANCE][governance-link]           | Project governance                                                            |
+| [LICENSE][license-link]                 | Apache License, Version 2.0                                                   |
+
+[identity-agent-npm-badge]: https://img.shields.io/npm/v/@web5/identity-agent.svg?style=flat&color=blue&santize=true
+[identity-agent-npm-link]: https://www.npmjs.com/package/@web5/identity-agent
+[identity-agent-downloads-badge]: https://img.shields.io/npm/dt/@web5/identity-agent?&color=blue
+[identity-agent-build-badge]: https://img.shields.io/github/actions/workflow/status/TBD54566975/web5-js/tests-ci.yml?branch=main&label=build
+[identity-agent-build-link]: https://github.com/TBD54566975/web5-js/actions/workflows/tests-ci.yml
+[identity-agent-coverage-badge]: https://img.shields.io/codecov/c/gh/TBD54566975/web5-js/main?style=flat&token=YI87CKF1LI
+[identity-agent-coverage-link]: https://app.codecov.io/github/TBD54566975/web5-js/tree/main/packages%2Fcrypto-aws-kms
+[identity-agent-issues-badge]: https://img.shields.io/github/issues/TBD54566975/web5-js/package:%20identity-agent?label=issues
+[identity-agent-issues-link]: https://github.com/TBD54566975/web5-js/issues?q=is%3Aopen+is%3Aissue+label%3A"package%3A+identity-agent"
+[identity-agent-repo-link]: https://github.com/TBD54566975/web5-js/tree/main/packages/identity-agent
+[identity-agent-jsdelivr-link]: https://www.jsdelivr.com/package/npm/@web5/identity-agent
+[identity-agent-jsdelivr-browser]: https://cdn.jsdelivr.net/npm/@web5/identity-agent/dist/browser.mjs
+[identity-agent-unpkg-link]: https://unpkg.com/@web5/identity-agent
+[identity-agent-unpkg-browser]: https://unpkg.com/@web5/identity-agent/dist/browser.mjs
+[codeowners-link]: https://github.com/TBD54566975/web5-js/blob/main/CODEOWNERS
+[code-of-conduct-link]: https://github.com/TBD54566975/web5-js/blob/main/CODE_OF_CONDUCT.md
+[contributing-link]: https://github.com/TBD54566975/web5-js/blob/main/CONTRIBUTING.md
+[governance-link]: https://github.com/TBD54566975/web5-js/blob/main/GOVERNANCE.md
+[license-link]: https://github.com/TBD54566975/web5-js/blob/main/LICENSE
+[discord-badge]: https://img.shields.io/discord/937858703112155166?color=5865F2&logo=discord&logoColor=white
+[discord-link]: https://discord.com/channels/937858703112155166/969272658501976117
diff --git a/packages/identity-agent/package.json b/packages/identity-agent/package.json
index 7235f22a3..484f452b2 100644
--- a/packages/identity-agent/package.json
+++ b/packages/identity-agent/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@web5/identity-agent",
-  "version": "0.2.6",
+  "version": "0.3.0",
   "type": "module",
   "main": "./dist/cjs/index.js",
   "module": "./dist/esm/index.js",
@@ -68,10 +68,10 @@
     "node": ">=20.9.0"
   },
   "dependencies": {
-    "@web5/agent": "0.2.6",
-    "@web5/common": "0.2.3",
-    "@web5/crypto": "0.2.2",
-    "@web5/dids": "0.2.4"
+    "@web5/agent": "0.3.0",
+    "@web5/common": "0.2.4",
+    "@web5/crypto": "0.4.0",
+    "@web5/dids": "0.4.1"
   },
   "devDependencies": {
     "@playwright/test": "1.40.1",
@@ -85,7 +85,6 @@
     "@typescript-eslint/parser": "6.4.0",
     "@web/test-runner": "0.18.0",
     "@web/test-runner-playwright": "0.11.0",
-    "@web5/api": "0.8.4",
     "c8": "9.0.0",
     "chai": "4.3.10",
     "chai-as-promised": "7.1.1",
diff --git a/packages/identity-agent/src/identity-agent.ts b/packages/identity-agent/src/identity-agent.ts
index aa660628d..78c642ce6 100644
--- a/packages/identity-agent/src/identity-agent.ts
+++ b/packages/identity-agent/src/identity-agent.ts
@@ -4,261 +4,240 @@ import type {
   VcResponse,
   DidResponse,
   DwnResponse,
-  SyncManager,
-  AppDataStore,
+  DidInterface,
+  DwnInterface,
   SendVcRequest,
   SendDwnRequest,
   ProcessVcRequest,
-  Web5ManagedAgent,
   ProcessDwnRequest,
+  Web5PlatformAgent,
 } from '@web5/agent';
 
 import { LevelStore } from '@web5/common';
-import { EdDsaAlgorithm } from '@web5/crypto';
-import { DidIonMethod, DidKeyMethod, DidResolver } from '@web5/dids';
+import { BearerDid, DidDht, DidJwk, DidResolverCacheLevel } from '@web5/dids';
 import {
-  LocalKms,
-  DidManager,
-  DidMessage,
-  DwnManager,
-  KeyManager,
-  DidStoreDwn,
-  KeyStoreDwn,
-  AppDataVault,
+  AgentDidApi,
+  AgentDwnApi,
+  DwnDidStore,
+  DwnKeyStore,
+  AgentSyncApi,
   Web5RpcClient,
-  IdentityManager,
-  IdentityStoreDwn,
-  SyncManagerLevel,
-  PrivateKeyStoreDwn,
-  cryptoToPortableKeyPair,
+  AgentCryptoApi,
+  AgentKeyManager,
+  HdIdentityVault,
+  LocalKeyManager,
+  SyncEngineLevel,
+  AgentIdentityApi,
+  DwnIdentityStore,
 } from '@web5/agent';
 
-export type IdentityAgentOptions = {
-  agentDid: string;
-  appData: AppDataStore;
-  didManager: DidManager;
-  didResolver: DidResolver;
-  dwnManager: DwnManager;
-  identityManager: IdentityManager;
-  keyManager: KeyManager;
+/**
+ * Initialization parameters for {@link Web5IdentityAgent}, including an optional recovery phrase that
+ * can be used to derive keys to encrypt the vault and generate a DID.
+ */
+export type AgentInitializeParams = {
+  /**
+    * The password used to secure the Agent vault.
+    *
+    * The password selected should be strong and securely managed to prevent unauthorized access.
+    */
+   password: string;
+
+  /**
+   * An optional recovery phrase used to deterministically generate the cryptographic keys for the
+   * Agent vault.
+   *
+   * Supplying this phrase enables the vault's contents to be restored or replicated across devices.
+   * If omitted, a new phrase is generated, which should be securely recorded for future recovery needs.
+   */
+   recoveryPhrase?: string;
+ };
+
+export type AgentStartParams = {
+  /**
+   * The password used to unlock the previously initialized Agent vault.
+   */
+  password: string;
+ }
+
+export type AgentParams<TKeyManager extends AgentKeyManager = LocalKeyManager> = {
+  /** Optional. The Decentralized Identifier (DID) representing this Web5 User Agent. */
+  agentDid?: BearerDid;
+  /** Encrypted vault used for managing the Agent's DID and associated keys. */
+  agentVault: HdIdentityVault;
+  /** Provides cryptographic capabilties like signing, encryption, hashing and key derivation. */
+  cryptoApi: AgentCryptoApi;
+  /** Specifies the local path to be used by the Agent's persistent data stores. */
+  dataPath?: string;
+  /** Facilitates DID operations including create, update, and resolve. */
+  didApi: AgentDidApi<TKeyManager>;
+  /** Facilitates DWN operations including processing and sending requests. */
+  dwnApi: AgentDwnApi;
+  /** Facilitates decentralized Identity operations including create, import, and export. */
+  identityApi: AgentIdentityApi<TKeyManager>;
+  /** Responsible for securely managing the cryptographic keys of the agent. */
+  keyManager: TKeyManager;
+  /** Remote procedure call (RPC) client used to communicate with other Web5 services. */
   rpcClient: Web5Rpc;
-  syncManager: SyncManager;
+  /** Facilitates data synchronization of DWN records between nodes. */
+  syncApi: AgentSyncApi;
 }
 
-export class IdentityAgent implements Web5ManagedAgent {
-  agentDid: string;
-  appData: AppDataStore;
-  didManager: DidManager;
-  didResolver: DidResolver;
-  dwnManager: DwnManager;
-  identityManager: IdentityManager;
-  keyManager: KeyManager;
-  rpcClient: Web5Rpc;
-  syncManager: SyncManager;
-
-  constructor(options: IdentityAgentOptions) {
-    this.agentDid = options.agentDid;
-    this.appData = options.appData;
-    this.didManager = options.didManager;
-    this.didResolver = options.didResolver;
-    this.dwnManager = options.dwnManager;
-    this.identityManager = options.identityManager;
-    this.keyManager = options.keyManager;
-    this.rpcClient = options.rpcClient;
-    this.syncManager = options.syncManager;
+export class Web5IdentityAgent<TKeyManager extends AgentKeyManager = LocalKeyManager> implements Web5PlatformAgent<TKeyManager> {
+  public crypto: AgentCryptoApi;
+  public did: AgentDidApi<TKeyManager>;
+  public dwn: AgentDwnApi;
+  public identity: AgentIdentityApi<TKeyManager>;
+  public keyManager: TKeyManager;
+  public rpc: Web5Rpc;
+  public sync: AgentSyncApi;
+  public vault: HdIdentityVault;
+
+  private _agentDid?: BearerDid;
+
+  constructor(params: AgentParams<TKeyManager>) {
+    this._agentDid = params.agentDid;
+    this.crypto = params.cryptoApi;
+    this.did = params.didApi;
+    this.dwn = params.dwnApi;
+    this.identity = params.identityApi;
+    this.keyManager = params.keyManager;
+    this.rpc = params.rpcClient;
+    this.sync = params.syncApi;
+    this.vault = params.agentVault;
 
     // Set this agent to be the default agent.
-    this.didManager.agent = this;
-    this.dwnManager.agent = this;
-    this.identityManager.agent = this;
+    this.did.agent = this;
+    this.dwn.agent = this;
+    this.identity.agent = this;
     this.keyManager.agent = this;
-    this.syncManager.agent = this;
+    this.sync.agent = this;
   }
 
-  static async create(options: Partial<IdentityAgentOptions> = {}): Promise<IdentityAgent> {
-    let {
-      agentDid, appData, didManager, didResolver, dwnManager,
-      identityManager, keyManager, rpcClient, syncManager
-    } = options;
-
-    if (agentDid === undefined) {
-      // An Agent DID was not specified, so set to empty string.
-      agentDid = '';
+  get agentDid(): BearerDid {
+    if (this._agentDid === undefined) {
+      throw new Error(
+        'Web5UserAgent: The "agentDid" property is not set. Ensure the agent is properly ' +
+        'initialized and a DID is assigned.'
+      );
     }
+    return this._agentDid;
+  }
 
-    if (appData === undefined) {
-      // A custom AppDataStore implementation was not specified, so
-      // instantiate a LevelDB backed secure AppDataVault.
-      appData = new AppDataVault({
-        store: new LevelStore({ location: 'data/agent/vault' })
-      });
-    }
+  set agentDid(did: BearerDid) {
+    this._agentDid = did;
+  }
 
-    if (didManager === undefined) {
-      // A custom DidManager implementation was not specified, so
-      // instantiate a default with in-memory store.
-      didManager = new DidManager({
-        didMethods : [DidIonMethod, DidKeyMethod],
-        store      : new DidStoreDwn()
-      });
-    }
+  /**
+   * If any of the required agent components are not provided, instantiate default implementations.
+   */
+  public static async create({
+    dataPath = 'DATA/AGENT',
+    agentDid, agentVault, cryptoApi, didApi, dwnApi, identityApi, keyManager, rpcClient, syncApi
+  }: Partial<AgentParams> = {}
+  ): Promise<Web5IdentityAgent> {
+
+    agentVault ??= new HdIdentityVault({
+      keyDerivationWorkFactor : 210_000,
+      store                   : new LevelStore<string, string>({ location: `${dataPath}/VAULT_STORE` })
+    });
 
-    if (didResolver === undefined) {
-      // A custom DidManager implementation was not specified, so
-      // instantiate a default with in-memory store.
-      didResolver = new DidResolver({ didResolvers: [DidIonMethod, DidKeyMethod] });
-    }
+    cryptoApi ??= new AgentCryptoApi();
 
-    if (dwnManager === undefined) {
-      // A custom DwnManager implementation was not specified, so
-      // instantiate a default.
-      dwnManager = await DwnManager.create({ didResolver });
-    }
+    didApi ??= new AgentDidApi({
+      didMethods    : [DidDht, DidJwk],
+      resolverCache : new DidResolverCacheLevel({ location: `${dataPath}/DID_RESOLVERCACHE` }),
+      store         : new DwnDidStore()
+    });
 
-    if (identityManager === undefined) {
-      // A custom IdentityManager implementation was not specified, so
-      // instantiate a default that uses a DWN store.
-      identityManager = new IdentityManager({
-        store: new IdentityStoreDwn()
-      });
-    }
+    dwnApi ??= new AgentDwnApi({
+      dwn: await AgentDwnApi.createDwn({ dataPath, didResolver: didApi })
+    });
 
-    if (keyManager === undefined) {
-      // A custom KeyManager implementation was not specified, so
-      // instantiate a default with KMSs.
-      const localKmsDwn = new LocalKms({
-        kmsName         : 'local',
-        keyStore        : new KeyStoreDwn({ schema: 'https://identity.foundation/schemas/web5/kms-key' }),
-        privateKeyStore : new PrivateKeyStoreDwn()
-      });
-      const localKmsMemory = new LocalKms({
-        kmsName: 'memory'
-      });
-      keyManager = new KeyManager({
-        kms: {
-          local  : localKmsDwn,
-          memory : localKmsMemory
-        },
-        store: new KeyStoreDwn({ schema: 'https://identity.foundation/schemas/web5/managed-key' })
-      });
-    }
+    identityApi ??= new AgentIdentityApi({ store: new DwnIdentityStore() });
 
-    if (rpcClient === undefined) {
-      // A custom RPC Client implementation was not specified, so
-      // instantiate a default.
-      rpcClient = new Web5RpcClient();
-    }
+    keyManager ??= new LocalKeyManager({ keyStore: new DwnKeyStore() });
 
-    if (syncManager === undefined) {
-      // A custom SyncManager implementation was not specified, so
-      // instantiate a LevelDB-backed default.
-      syncManager = new SyncManagerLevel();
-    }
+    rpcClient ??= new Web5RpcClient();
 
-    // Instantiate the Identity Agent.
-    const agent = new IdentityAgent({
+    syncApi ??= new AgentSyncApi({ syncEngine: new SyncEngineLevel({ dataPath }) });
+
+    // Instantiate the Agent using the provided or default components.
+    return new Web5IdentityAgent({
       agentDid,
-      appData,
-      didManager,
-      didResolver,
-      dwnManager,
-      identityManager,
+      agentVault,
+      cryptoApi,
+      didApi,
+      dwnApi,
       keyManager,
+      identityApi,
       rpcClient,
-      syncManager
+      syncApi
     });
-
-    return agent;
   }
 
-  async firstLaunch(): Promise<boolean> {
-    // Check whether data vault is already initialized.
-    const { initialized } = await this.appData.getStatus();
-    return initialized === false;
+  public async firstLaunch(): Promise<boolean> {
+    // Check whether data vault is already initialize
+    return await this.vault.isInitialized() === false;
   }
 
   /**
-   * Executed once the first time the Identity Agent is launched.
-   * The passphrase should be input by the end-user.
+   * Initializes the User Agent with a password, and optionally a recovery phrase.
+   *
+   * This method is typically called once, the first time the Agent is launched, and is responsible
+   * for setting up the agent's operational environment, cryptographic key material, and readiness
+   * for processing Web5 requests.
+   *
+   * The password is used to secure the Agent vault, and the recovery phrase is used to derive the
+   * cryptographic keys for the vault. If a recovery phrase is not provided, a new recovery phrase
+   * will be generated and returned. The password should be chosen and entered by the end-user.
    */
-  async initialize(options: { passphrase: string }) {
-    const { passphrase } = options;
-
-    // Generate an Ed25519 key pair for the Identity Agent.
-    const agentKeyPair = await new EdDsaAlgorithm().generateKey({
-      algorithm   : { name: 'EdDSA', namedCurve: 'Ed25519' },
-      extractable : true,
-      keyUsages   : ['sign', 'verify']
-    });
+  public async initialize({ password, recoveryPhrase }: AgentInitializeParams): Promise<string> {
+    // Initialize the Agent vault.
+    recoveryPhrase = await this.vault.initialize({ password, recoveryPhrase });
 
-    /** Initialize the AppDataStore with the Identity Agent's
-     * private key and passphrase, which also unlocks the data vault. */
-    await this.appData.initialize({
-      passphrase : passphrase,
-      keyPair    : agentKeyPair,
-    });
+    return recoveryPhrase;
   }
 
-  async processDidRequest(request: DidRequest): Promise<DidResponse> {
-    switch (request.messageType) {
-      case DidMessage.Resolve: {
-        const { didUrl, resolutionOptions } = request.messageOptions;
-        const result = await this.didResolver.resolve(didUrl, resolutionOptions);
-        return { result };
-      }
-
-      default: {
-        return this.didManager.processRequest(request);
-      }
-    }
+  async processDidRequest<T extends DidInterface>(
+    request: DidRequest<T>
+  ): Promise<DidResponse<T>> {
+    return this.did.processRequest(request);
   }
 
-  async processDwnRequest(request: ProcessDwnRequest): Promise<DwnResponse> {
-    return this.dwnManager.processRequest(request);
+  public async processDwnRequest<T extends DwnInterface>(
+    request: ProcessDwnRequest<T>
+  ): Promise<DwnResponse<T>> {
+    return this.dwn.processRequest(request);
   }
 
-  async processVcRequest(_request: ProcessVcRequest): Promise<VcResponse> {
+  public async processVcRequest(_request: ProcessVcRequest): Promise<VcResponse> {
     throw new Error('Not implemented');
   }
 
-  async sendDidRequest(_request: DidRequest): Promise<DidResponse> {
+  public async sendDidRequest<T extends DidInterface>(
+    _request: DidRequest<T>
+  ): Promise<DidResponse<T>> {
     throw new Error('Not implemented');
   }
 
-  async sendDwnRequest(request: SendDwnRequest): Promise<DwnResponse> {
-    return this.dwnManager.sendRequest(request);
+  public async sendDwnRequest<T extends DwnInterface>(
+    request: SendDwnRequest<T>
+  ): Promise<DwnResponse<T>> {
+    return this.dwn.sendRequest(request);
   }
 
-  async sendVcRequest(_request: SendVcRequest): Promise<VcResponse> {
+  public async sendVcRequest(_request: SendVcRequest): Promise<VcResponse> {
     throw new Error('Not implemented');
   }
 
-  async start(options: { passphrase: string }) {
-    const { passphrase } = options;
-
-    if (await this.firstLaunch()) {
-      // 1A. Agent's first launch so initialize.
-      await this.initialize({ passphrase });
-    } else {
-      // 1B. Agent was previously initialized.
-      // Unlock the data vault and cache the vault unlock key (VUK) in memory.
-      await this.appData.unlock({ passphrase });
+  public async start({ password }: AgentInitializeParams): Promise<void> {
+    // If the Agent vault is locked, unlock it.
+    if (this.vault.isLocked()) {
+      await this.vault.unlock({ password });
     }
 
-    // 2. Set the Identity Agent's root did:key identifier.
-    this.agentDid = await this.appData.getDid();
-
-    // 3. Import the Identity Agent's signing key pair to KeyManager.
-    const defaultSigningKey = cryptoToPortableKeyPair({
-      cryptoKeyPair: {
-        privateKey : await this.appData.getPrivateKey(),
-        publicKey  : await this.appData.getPublicKey()
-      },
-      keyData: {
-        alias : await this.didManager.getDefaultSigningKey({ did: this.agentDid }),
-        kms   : 'memory'
-      }
-    });
-    await this.keyManager.setDefaultSigningKey({ key: defaultSigningKey });
+    // Set the Agent's DID.
+    this.agentDid = await this.vault.getDid();
   }
 }
\ No newline at end of file
diff --git a/packages/identity-agent/tests/identity-agent.spec.ts b/packages/identity-agent/tests/identity-agent.spec.ts
index 4debdcf52..b71aa7739 100644
--- a/packages/identity-agent/tests/identity-agent.spec.ts
+++ b/packages/identity-agent/tests/identity-agent.spec.ts
@@ -1,11 +1,12 @@
-import * as sinon from 'sinon';
-import chai, { expect } from 'chai';
-import chaiAsPromised from 'chai-as-promised';
-import { TestManagedAgent } from '@web5/agent';
+import type { BearerIdentity, PortableIdentity } from '@web5/agent';
 
-import { IdentityAgent } from '../src/identity-agent.js';
+import { expect } from 'chai';
+import { DidDht } from '@web5/dids';
+import { Convert } from '@web5/common';
+import { DidInterface, DwnInterface, PlatformAgentTestHarness } from '@web5/agent';
 
-chai.use(chaiAsPromised);
+import { testDwnUrl } from './utils/test-config.js';
+import { Web5IdentityAgent } from '../src/identity-agent.js';
 
 // NOTE: @noble/secp256k1 requires globalThis.crypto polyfill for node.js <=18: https://github.com/paulmillr/noble-secp256k1/blob/main/README.md#usage
 // Remove when we move off of node.js v18 to v20, earliest possible time would be Oct 2023: https://github.com/nodejs/release#release-schedule
@@ -15,163 +16,415 @@ import { webcrypto } from 'node:crypto';
 // @ts-ignore
 if (!globalThis.crypto) globalThis.crypto = webcrypto;
 
-describe('IdentityAgent', () => {
+let testDwnUrls: string[] = [testDwnUrl];
+
+describe('Web5IdentityAgent', () => {
+
+  describe('agentDid', () => {
+    it('throws an error if accessed before the Agent is initialized', async () => {
+      // @ts-expect-error - Initializing with empty object to test error.
+      const identityAgent = new Web5IdentityAgent({ didApi: {}, dwnApi: {}, identityApi: {}, keyManager: {}, syncApi: {} });
+      try {
+        identityAgent.agentDid;
+        throw new Error('Expected an error');
+      } catch (error: any) {
+        expect(error.message).to.include('"agentDid" property is not set');
+      }
+    });
+  });
+
+  describe('create()', () => {
+    it('should create an instance with default parameters when none are provided', async () => {
+      const identityAgent = await Web5IdentityAgent.create({ dataPath: '__TESTDATA__/USERAGENT' });
+
+      expect(identityAgent).to.be.an.instanceof(Web5IdentityAgent);
+      expect(identityAgent.crypto).to.exist;
+      expect(identityAgent.did).to.exist;
+      expect(identityAgent.dwn).to.exist;
+      expect(identityAgent.identity).to.exist;
+      expect(identityAgent.keyManager).to.exist;
+      expect(identityAgent.rpc).to.exist;
+      expect(identityAgent.sync).to.exist;
+      expect(identityAgent.vault).to.exist;
+    });
+  });
 
   const agentStoreTypes = ['dwn', 'memory'] as const;
   agentStoreTypes.forEach((agentStoreType) => {
 
     describe(`with ${agentStoreType} data stores`, () => {
-      let testAgent: TestManagedAgent;
+      let testHarness: PlatformAgentTestHarness;
 
       before(async () => {
-        testAgent = await TestManagedAgent.create({
-          agentClass  : IdentityAgent,
+        testHarness = await PlatformAgentTestHarness.setup({
+          agentClass  : Web5IdentityAgent,
           agentStores : agentStoreType
         });
       });
 
       beforeEach(async () => {
-        await testAgent.clearStorage();
+        await testHarness.clearStorage();
+        await testHarness.createAgentDid();
       });
 
       after(async () => {
-        await testAgent.clearStorage();
-        await testAgent.closeStorage();
+        await testHarness.clearStorage();
+        await testHarness.closeStorage();
       });
 
       describe('firstLaunch()', () => {
         it('returns true the first time the Identity Agent runs', async () => {
-          await expect(testAgent.agent.firstLaunch()).to.eventually.be.true;
+          const result = await testHarness.agent.firstLaunch();
+          expect(result).to.be.true;
         });
 
         it('returns false after Identity Agent initialization', async () => {
-          await expect(testAgent.agent.firstLaunch()).to.eventually.be.true;
+          let result = await testHarness.agent.firstLaunch();
+          expect(result).to.be.true;
 
-          await testAgent.agent.start({ passphrase: 'test' });
-          await expect(testAgent.agent.firstLaunch()).to.eventually.be.false;
+          await testHarness.agent.initialize({ password: 'test' });
+
+          result = await testHarness.agent.firstLaunch();
+          expect(result).to.be.false;
         });
       });
 
       describe('initialize()', () => {
-        it('initializes the AppData store and stores the vault key set', async () => {
-          await testAgent.agent.initialize({ passphrase: 'test' });
+        it('generates and returns a 12-word mnenomic if one is not provided', async () => {
+          // Initialize the vault.
+          const generatedRecoveryPhrase = await testHarness.agent.initialize({
+            password: 'dumbbell-krakatoa-ditty'
+          });
+
+          // Verify that the vault is initialized and is unlocked.
+          expect(generatedRecoveryPhrase).to.be.a('string');
+          if (typeof generatedRecoveryPhrase !== 'string') throw new Error('type guard');
+          expect(generatedRecoveryPhrase.split(' ')).to.have.lengthOf(12);
+        });
+
+        it('accepts a recovery phrase', async () => {
+          const predefinedRecoveryPhrase = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
 
-          // Confirm the AppData store was initialized.
-          const { initialized } = await testAgent.agent.appData.getStatus();
-          expect(initialized).to.be.true;
+          // Initialize the vault with a recovery phrase.
+          const returnedRecoveryPhrase = await testHarness.agent.initialize({
+            password       : 'dumbbell-krakatoa-ditty',
+            recoveryPhrase : predefinedRecoveryPhrase
+          });
 
-          // Confirm the vault key set was stored.
-          const storedVaultKeySet = await testAgent.appDataStore.get('vaultKeySet');
-          expect(storedVaultKeySet).to.exist;
-          expect(storedVaultKeySet).to.be.a.string;
+          // Verify that the vault is initialized and is unlocked.
+          expect(returnedRecoveryPhrase).to.equal(predefinedRecoveryPhrase);
         });
       });
 
-      describe('start()', () => {
-        it('initializes the AppData store the first time the Identity Agent runs', async () => {
-          // const agent = await IdentityAgent.create({ appData, dwnManager });
-          let initializeSpy = sinon.spy(testAgent.agent, 'initialize');
-          let unlockSpy = sinon.spy(testAgent.agent.appData, 'unlock');
-
-          // Execute agent.start() for the first time.
-          await testAgent.agent.start({ passphrase: 'test' });
-
-          // Confirm agent.initialize() was called.
-          expect(initializeSpy.called).to.be.true;
-
-          // Confirm agent.appData.unlock() was not called.
-          expect(unlockSpy.called).to.be.false;
-
-          // Confirm the AppData store was initialized.
-          const { initialized } = await testAgent.agent.appData.getStatus();
-          expect(initialized).to.be.true;
-
-          // Confirm the vault key set was stored.
-          const storedVaultKeySet = await testAgent.appDataStore.get('vaultKeySet');
-          expect(storedVaultKeySet).to.exist;
-          expect(storedVaultKeySet).to.be.a.string;
-
-          // Confirm the AppData store was unlocked.
-          // @ts-expect-error because a private variable is being intentionally accessed.
-          const vaultUnlockKey = testAgent.agent.appData._vaultUnlockKey;
-          expect(vaultUnlockKey).to.exist;
-          expect(vaultUnlockKey).to.be.a('Uint8Array');
-
-          // Confirm the Agent's key pair was stored in KeyManager.
-          if (testAgent.agent.agentDid === undefined) throw new Error(); // Type guard.
-          const signingKeyId = await testAgent.agent.didManager.getDefaultSigningKey({ did: testAgent.agent.agentDid });
-          if (!signingKeyId) throw new Error('Type guard');
-          const agentKeyPair = await testAgent.agent.keyManager.getKey({ keyRef: signingKeyId });
-          expect(agentKeyPair).to.exist;
-          expect(agentKeyPair).to.have.property('privateKey');
-          expect(agentKeyPair).to.have.property('publicKey');
-
-          initializeSpy.restore();
-          unlockSpy.restore();
+      describe('processDidRequest()', () => {
+        it('processes a DID Create request', async () => {
+          const didCreateResponse = await testHarness.agent.processDidRequest({
+            messageType   : DidInterface.Create,
+            messageParams : { method: 'jwk' }
+          });
+
+          expect(didCreateResponse).to.exist;
+          expect(didCreateResponse).to.have.property('ok', true);
+          expect(didCreateResponse).to.have.property('status');
+          expect(didCreateResponse.status).to.have.property('code', 201);
+          expect(didCreateResponse.status).to.have.property('message', 'Created');
+          expect(didCreateResponse).to.have.property('result');
+          expect(didCreateResponse.result).to.have.property('uri');
+          expect(didCreateResponse.result).to.have.property('document');
+          expect(didCreateResponse.result).to.have.property('metadata');
         });
 
-        it('unlocks the AppData store on subsequent Identity Agent runs', async () => {
-          // Execute agent.start() for the first time.
-          await testAgent.agent.start({ passphrase: 'test' });
-
-          let initializeSpy = sinon.spy(testAgent.agent, 'initialize');
-          let unlockSpy = sinon.spy(testAgent.agent.appData, 'unlock');
-
-          // Execute agent.start() for the second time.
-          await testAgent.agent.start({ passphrase: 'test' });
-
-          // Confirm agent.initialize() was not called.
-          expect(initializeSpy.called).to.be.false;
-
-          // Confirm agent.appData.unlock() was called.
-          expect(unlockSpy.called).to.be.true;
-
-          // Confirm the vault key set was stored.
-          const storedVaultKeySet = await testAgent.appDataStore.get('vaultKeySet');
-          expect(storedVaultKeySet).to.exist;
-          expect(storedVaultKeySet).to.be.a.string;
-
-          // Confirm the AppData store was unlocked.
-          // @ts-expect-error because a private variable is being intentionally accessed.
-          const vaultUnlockKey = testAgent.agent.appData._vaultUnlockKey;
-          expect(vaultUnlockKey).to.exist;
-          expect(vaultUnlockKey).to.be.a('Uint8Array');
+        it('processes a DID Resolve request', async () => {
+          const didResolveResponse = await testHarness.agent.processDidRequest({
+            messageType   : DidInterface.Resolve,
+            messageParams : { didUri: testHarness.agent.agentDid.uri }
+          });
+
+          expect(didResolveResponse).to.exist;
+          expect(didResolveResponse).to.have.property('ok', true);
+          expect(didResolveResponse).to.have.property('status');
+          expect(didResolveResponse.status).to.have.property('code', 200);
+          expect(didResolveResponse.status).to.have.property('message', 'OK');
+          expect(didResolveResponse).to.have.property('result');
+          expect(didResolveResponse.result).to.have.property('didDocument');
+          expect(didResolveResponse.result).to.have.property('didDocumentMetadata');
+          expect(didResolveResponse.result).to.have.property('didResolutionMetadata');
+        });
+      });
 
-          // Confirm the Agent's key pair was stored in KeyManager.
-          if (testAgent.agent.agentDid === undefined) throw new Error(); // Type guard.
-          const signingKeyId = await testAgent.agent.didManager.getDefaultSigningKey({ did: testAgent.agent.agentDid });
-          if (!signingKeyId) throw new Error('Type guard');
-          const agentKeyPair = await testAgent.agent.keyManager.getKey({ keyRef: signingKeyId });
-          expect(agentKeyPair).to.exist;
-          expect(agentKeyPair).to.have.property('privateKey');
-          expect(agentKeyPair).to.have.property('publicKey');
+      if (agentStoreType === 'dwn') {
+        let alice: BearerIdentity;
 
-          initializeSpy.restore();
-          unlockSpy.restore();
+        beforeEach(async () => {
+          alice = await testHarness.agent.identity.create({
+            metadata  : { name: 'Alice' },
+            didMethod : 'jwk'
+          });
         });
 
-        it('unlocks the AppData store', async () => {
-          // const agent = await IdentityAgent.create({ appData, dwnManager });
-          let initializeSpy = sinon.spy(testAgent.agent, 'initialize');
-          let unlockSpy = sinon.spy(testAgent.agent.appData, 'unlock');
+        describe('processDwnRequest()', () => {
+          it('processes a Records Write request', async () => {
+            // Create test data to write.
+            const dataBytes = Convert.string('Hello, world!').toUint8Array();
+
+            // Attempt to process the RecordsWrite
+            let writeResponse = await testHarness.agent.processDwnRequest({
+              author        : alice.did.uri,
+              target        : alice.did.uri,
+              messageType   : DwnInterface.RecordsWrite,
+              messageParams : {
+                dataFormat: 'text/plain'
+              },
+              dataStream: new Blob([dataBytes])
+            });
+
+            // Verify the response.
+            expect(writeResponse).to.have.property('message');
+            expect(writeResponse).to.have.property('messageCid');
+            expect(writeResponse).to.have.property('reply');
+
+            const writeMessage = writeResponse.message;
+            expect(writeMessage).to.have.property('authorization');
+            expect(writeMessage).to.have.property('descriptor');
+            expect(writeMessage).to.have.property('recordId');
+
+            const writeReply = writeResponse.reply;
+            expect(writeReply).to.have.property('status');
+            expect(writeReply.status.code).to.equal(202);
+          });
+        });
 
-          await testAgent.agent.start({ passphrase: 'test' });
+        describe('processVcRequest()', () => {
+          it('throws an error', async () => {
+            try {
+              await testHarness.agent.processVcRequest({});
+              throw new Error('Expected an error');
+            } catch (error) {
+              expect(error).to.have.property('message', 'Not implemented');
+            }
+          });
+        });
 
-          // Confirm agent.initialize() was called.
-          expect(initializeSpy.called).to.be.true;
+        describe('sendDidRequest()', () => {
+          it('throws an error', async () => {
+            try {
+              await testHarness.agent.sendDidRequest({
+                messageType   : DidInterface.Create,
+                messageParams : { method: 'jwk' }
+              });
+              throw new Error('Expected an error');
+            } catch (error) {
+              expect(error).to.have.property('message', 'Not implemented');
+            }
+          });
+        });
 
-          // Confirm agent.appData.unlock() was not called.
-          expect(unlockSpy.called).to.be.false;
+        describe('sendDwnRequest()', () => {
+          beforeEach(async () => {
+            const testPortableIdentity: PortableIdentity = {
+              portableDid: {
+                uri      : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy',
+                document : {
+                  id                 : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy',
+                  verificationMethod : [
+                    {
+                      id           : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#0',
+                      type         : 'JsonWebKey',
+                      controller   : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy',
+                      publicKeyJwk : {
+                        crv : 'Ed25519',
+                        kty : 'OKP',
+                        x   : 'mZXKvarfofrcrdTYzes2YneEsrbJFc1kE0O-d1cJPEw',
+                        kid : 'EAlW6h08kqdLGEhR_o6hCnZpYpQ8QJavMp3g0BJ35IY',
+                        alg : 'EdDSA',
+                      },
+                    },
+                    {
+                      id           : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#sig',
+                      type         : 'JsonWebKey',
+                      controller   : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy',
+                      publicKeyJwk : {
+                        crv : 'Ed25519',
+                        kty : 'OKP',
+                        x   : 'iIWijzQnfb_Jk4yRjISV6ci8EtyHn0fIxg0TVCh7wkE',
+                        kid : '8QSlw4ct9taIgh23EUGLM0ELaukQ1VogIuBGrQ_UIsk',
+                        alg : 'EdDSA',
+                      },
+                    },
+                    {
+                      id           : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#enc',
+                      type         : 'JsonWebKey',
+                      controller   : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy',
+                      publicKeyJwk : {
+                        kty : 'EC',
+                        crv : 'secp256k1',
+                        x   : 'P5FoqXk9W11i8FWyTpIvltAjV09FL9Q5o76wEHcxMtI',
+                        y   : 'DgoLVlLKbjlaUja4RTjdxzqAy0ITOEFlCXGKSpu8XQs',
+                        kid : 'hXXhIgfXRVIYqnKiX0DIL7ZGy0CBJrFQFIYxmRkAB-A',
+                        alg : 'ES256K',
+                      },
+                    },
+                  ],
+                  authentication: [
+                    'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#0',
+                    'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#sig',
+                  ],
+                  assertionMethod: [
+                    'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#0',
+                    'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#sig',
+                  ],
+                  capabilityDelegation: [
+                    'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#0',
+                  ],
+                  capabilityInvocation: [
+                    'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#0',
+                  ],
+                  keyAgreement: [
+                    'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#enc',
+                  ],
+                  service: [
+                    {
+                      id              : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy#dwn',
+                      type            : 'DecentralizedWebNode',
+                      serviceEndpoint : testDwnUrls,
+                      enc             : '#enc',
+                      sig             : '#sig',
+                    },
+                  ],
+                },
+                metadata: {
+                  published : true,
+                  versionId : '1708160454',
+                },
+                privateKeys: [
+                  {
+                    crv : 'Ed25519',
+                    d   : 'gXu7HmJgvZFWgNf_eqF-eDAFegd0OLe8elAIXXGMgoc',
+                    kty : 'OKP',
+                    x   : 'mZXKvarfofrcrdTYzes2YneEsrbJFc1kE0O-d1cJPEw',
+                    kid : 'EAlW6h08kqdLGEhR_o6hCnZpYpQ8QJavMp3g0BJ35IY',
+                    alg : 'EdDSA',
+                  },
+                  {
+                    crv : 'Ed25519',
+                    d   : 'SiUL1QDp6X2QnvJ1Q7hRlpo3ZhiVjRlvINocOzYPaBU',
+                    kty : 'OKP',
+                    x   : 'iIWijzQnfb_Jk4yRjISV6ci8EtyHn0fIxg0TVCh7wkE',
+                    kid : '8QSlw4ct9taIgh23EUGLM0ELaukQ1VogIuBGrQ_UIsk',
+                    alg : 'EdDSA',
+                  },
+                  {
+                    kty : 'EC',
+                    crv : 'secp256k1',
+                    d   : 'b2gb-OfB5X4G3xd16u19MXNkamDP5lsT6bVsDN4aeuY',
+                    x   : 'P5FoqXk9W11i8FWyTpIvltAjV09FL9Q5o76wEHcxMtI',
+                    y   : 'DgoLVlLKbjlaUja4RTjdxzqAy0ITOEFlCXGKSpu8XQs',
+                    kid : 'hXXhIgfXRVIYqnKiX0DIL7ZGy0CBJrFQFIYxmRkAB-A',
+                    alg : 'ES256K',
+                  },
+                ],
+              },
+              metadata: {
+                name   : 'Alice',
+                tenant : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy',
+                uri    : 'did:dht:ugkhixpk56o9izfp4ucc543scj5ajcis3rkh43yueq98qiaj8tgy'
+              }
+            };
+
+            await testHarness.preloadResolverCache({
+              didUri           : testPortableIdentity.portableDid.uri,
+              resolutionResult : {
+                didDocument           : testPortableIdentity.portableDid.document,
+                didDocumentMetadata   : testPortableIdentity.portableDid.metadata,
+                didResolutionMetadata : {}
+              }
+            });
+
+            alice = await testHarness.agent.identity.import({
+              portableIdentity: testPortableIdentity
+            });
+
+            // Ensure the DID is published to the DHT. This step is necessary while the DHT Gateways
+            // operated by TBD are regularly restarted and DIDs are no longer persisted.
+            await DidDht.publish({ did: alice.did });
+          });
+
+          it('processes a Records Write request', async () => {
+            // Create test data to write.
+            const dataBytes = Convert.string('Hello, world!').toUint8Array();
+
+            // Attempt to process the RecordsWrite
+            let writeResponse = await testHarness.agent.sendDwnRequest({
+              author        : alice.did.uri,
+              target        : alice.did.uri,
+              messageType   : DwnInterface.RecordsWrite,
+              messageParams : {
+                dataFormat: 'text/plain'
+              },
+              dataStream: new Blob([dataBytes])
+            });
+
+            // Verify the response.
+            expect(writeResponse).to.have.property('message');
+            expect(writeResponse).to.have.property('messageCid');
+            expect(writeResponse).to.have.property('reply');
+
+            const writeMessage = writeResponse.message;
+            expect(writeMessage).to.have.property('authorization');
+            expect(writeMessage).to.have.property('descriptor');
+            expect(writeMessage).to.have.property('recordId');
+
+            const writeReply = writeResponse.reply;
+            expect(writeReply).to.have.property('status');
+            expect(writeReply.status.code).to.equal(202);
+          });
+        });
 
-          // @ts-expect-error because a private variable is being intentionally accessed.
-          const vaultUnlockKey = testAgent.agent.appData._vaultUnlockKey;
-          expect(vaultUnlockKey).to.exist;
-          expect(vaultUnlockKey).to.be.a('Uint8Array');
+        describe('sendVcRequest()', () => {
+          it('throws an error', async () => {
+            try {
+              await testHarness.agent.sendVcRequest({});
+              throw new Error('Expected an error');
+            } catch (error) {
+              expect(error).to.have.property('message', 'Not implemented');
+            }
+          });
+        });
 
-          initializeSpy.restore();
-          unlockSpy.restore();
+        describe('subsequent launches', () => {
+          it('can access stored identifiers after second launch', async () => {
+            // First launch and initialization.
+            await testHarness.agent.initialize({ password: 'test' });
+
+            // Start the Agent, which will decrypt and load the Agent's DID from the vault.
+            await testHarness.agent.start({ password: 'test' });
+
+            // Create and persist a new Identity (with DID and Keys).
+            const socialIdentity = await testHarness.agent.identity.create({
+              metadata  : { name: 'Social' },
+              didMethod : 'jwk'
+            });
+
+            // Simulate terminating and restarting an app.
+            await testHarness.closeStorage();
+            testHarness = await PlatformAgentTestHarness.setup({
+              agentClass  : Web5IdentityAgent,
+              agentStores : 'dwn'
+            });
+            await testHarness.agent.start({ password: 'test' });
+
+            // Try to get the identity and verify it exists.
+            const storedIdentity = await testHarness.agent.identity.get({
+              didUri : socialIdentity.did.uri,
+              tenant : socialIdentity.did.uri
+            });
+
+            expect(storedIdentity).to.exist;
+            expect(storedIdentity!.did).to.have.property('uri', socialIdentity.did.uri);
+          });
         });
-      });
+      }
     });
   });
+
 });
\ No newline at end of file
diff --git a/packages/identity-agent/tests/managing-identities.spec.ts b/packages/identity-agent/tests/managing-identities.spec.ts
index 92258ceca..616fbe5ad 100644
--- a/packages/identity-agent/tests/managing-identities.spec.ts
+++ b/packages/identity-agent/tests/managing-identities.spec.ts
@@ -1,14 +1,8 @@
-import { Web5 } from '@web5/api';
-import chai, { expect } from 'chai';
-import chaiAsPromised from 'chai-as-promised';
-import { TestManagedAgent } from '@web5/agent';
+import { expect } from 'chai';
+import { PlatformAgentTestHarness } from '@web5/agent';
 
-import { IdentityAgent } from '../src/identity-agent.js';
+import { Web5IdentityAgent } from '../src/identity-agent.js';
 
-chai.use(chaiAsPromised);
-
-// NOTE: @noble/secp256k1 requires globalThis.crypto polyfill for node.js <=18: https://github.com/paulmillr/noble-secp256k1/blob/main/README.md#usage
-// Remove when we move off of node.js v18 to v20, earliest possible time would be Oct 2023: https://github.com/nodejs/release#release-schedule
 // NOTE: @noble/secp256k1 requires globalThis.crypto polyfill for node.js <=18: https://github.com/paulmillr/noble-secp256k1/blob/main/README.md#usage
 // Remove when we move off of node.js v18 to v20, earliest possible time would be Oct 2023: https://github.com/nodejs/release#release-schedule
 import { webcrypto } from 'node:crypto';
@@ -21,201 +15,123 @@ describe('Managing Identities', () => {
   agentStoreTypes.forEach((agentStoreType) => {
 
     describe(`with ${agentStoreType} data stores`, () => {
-      let testAgent: TestManagedAgent;
+      let testHarness: PlatformAgentTestHarness;
 
       before(async () => {
-        testAgent = await TestManagedAgent.create({
-          agentClass  : IdentityAgent,
+        testHarness = await PlatformAgentTestHarness.setup({
+          agentClass  : Web5IdentityAgent,
           agentStores : agentStoreType
         });
       });
 
       beforeEach(async () => {
-        await testAgent.clearStorage();
+        await testHarness.clearStorage();
+        await testHarness.createAgentDid();
       });
 
       after(async () => {
-        await testAgent.clearStorage();
-        await testAgent.closeStorage();
+        await testHarness.clearStorage();
+        await testHarness.closeStorage();
       });
 
       describe('initial identity creation', () => {
         it('can create three identities', async () => {
-          // Start agent for the first time.
-          await testAgent.agent.start({ passphrase: 'test' });
+          // First launch and initialization.
+          await testHarness.agent.initialize({ password: 'test' });
+
+          // Start the Agent, which will decrypt and load the Agent's DID from the vault.
+          await testHarness.agent.start({ password: 'test' });
 
           // Create three identities, each of which is stored in a new tenant.
-          const careerIdentity = await testAgent.agent.identityManager.create({
-            name      : 'Social',
-            didMethod : 'key',
-            kms       : 'local'
+          const careerIdentity = await testHarness.agent.identity.create({
+            metadata  : { name: 'Social' },
+            didMethod : 'jwk'
           });
 
-          const familyIdentity = await testAgent.agent.identityManager.create({
-            name      : 'Social',
-            didMethod : 'key',
-            kms       : 'local'
+          const familyIdentity = await testHarness.agent.identity.create({
+            metadata  : { name: 'Social' },
+            didMethod : 'jwk'
           });
 
-          const socialIdentity = await testAgent.agent.identityManager.create({
-            name      : 'Social',
-            didMethod : 'key',
-            kms       : 'local'
+          const socialIdentity = await testHarness.agent.identity.create({
+            metadata  : { name: 'Social' },
+            didMethod : 'jwk'
           });
 
           // Verify the Identities were stored in each new Identity's tenant.
-          const storedCareerIdentity = await testAgent.agent.identityManager.get({ did: careerIdentity.did, context: careerIdentity.did });
-          const storedFamilyIdentity = await testAgent.agent.identityManager.get({ did: familyIdentity.did, context: familyIdentity.did });
-          const storedSocialIdentity = await testAgent.agent.identityManager.get({ did: socialIdentity.did, context: socialIdentity.did });
-          expect(storedCareerIdentity).to.have.property('did', careerIdentity.did);
-          expect(storedFamilyIdentity).to.have.property('did', familyIdentity.did);
-          expect(storedSocialIdentity).to.have.property('did', socialIdentity.did);
+          const storedCareerIdentity = await testHarness.agent.identity.get({ didUri: careerIdentity.did.uri, tenant: careerIdentity.did.uri });
+          const storedFamilyIdentity = await testHarness.agent.identity.get({ didUri: familyIdentity.did.uri, tenant: familyIdentity.did.uri });
+          const storedSocialIdentity = await testHarness.agent.identity.get({ didUri: socialIdentity.did.uri, tenant: socialIdentity.did.uri });
+          expect(storedCareerIdentity!.did).to.have.property('uri', careerIdentity.did.uri);
+          expect(storedFamilyIdentity!.did).to.have.property('uri', familyIdentity.did.uri);
+          expect(storedSocialIdentity!.did).to.have.property('uri', socialIdentity.did.uri);
         }).timeout(30000);
 
         // Tests that should only run for DWN-backed stores that provide multi-tenancy.
         if (agentStoreType === 'dwn') {
           it('supports tenant isolation between Identity Agent and Identities under management', async () => {
-            // Start agent for the first time.
-            await testAgent.agent.start({ passphrase: 'test' });
+            // First launch and initialization.
+            await testHarness.agent.initialize({ password: 'test' });
+
+            // Start the Agent, which will decrypt and load the Agent's DID from the vault.
+            await testHarness.agent.start({ password: 'test' });
 
             // Create three identities, each of which is stored in a new tenant.
-            const careerIdentity = await testAgent.agent.identityManager.create({
-              name      : 'Career',
-              didMethod : 'key',
-              kms       : 'local'
+            const careerIdentity = await testHarness.agent.identity.create({
+              metadata  : { name: 'Social' },
+              didMethod : 'jwk'
             });
 
-            const familyIdentity = await testAgent.agent.identityManager.create({
-              name      : 'Family',
-              didMethod : 'key',
-              kms       : 'local'
+            const familyIdentity = await testHarness.agent.identity.create({
+              metadata  : { name: 'Social' },
+              didMethod : 'jwk'
             });
 
-            const socialIdentity = await testAgent.agent.identityManager.create({
-              name      : 'Social',
-              didMethod : 'key',
-              kms       : 'local'
+            const socialIdentity = await testHarness.agent.identity.create({
+              metadata  : { name: 'Social' },
+              didMethod : 'jwk'
             });
 
-            // Import just the Identity metadata for the new identities to the Identity Agent's tenant.
-            await testAgent.agent.identityManager.import({ identity: careerIdentity, context: testAgent.agent.agentDid });
-            await testAgent.agent.identityManager.import({ identity: familyIdentity, context: testAgent.agent.agentDid });
-            await testAgent.agent.identityManager.import({ identity: socialIdentity, context: testAgent.agent.agentDid });
-
-            // Verify the Identities were stored in each new Identity's tenant.
-            const storedCareerIdentity = await testAgent.agent.identityManager.get({ did: careerIdentity.did, context: careerIdentity.did });
-            const storedFamilyIdentity = await testAgent.agent.identityManager.get({ did: familyIdentity.did, context: familyIdentity.did });
-            const storedSocialIdentity = await testAgent.agent.identityManager.get({ did: socialIdentity.did, context: socialIdentity.did });
-            expect(storedCareerIdentity).to.have.property('did', careerIdentity.did);
-            expect(storedFamilyIdentity).to.have.property('did', familyIdentity.did);
-            expect(storedSocialIdentity).to.have.property('did', socialIdentity.did);
+            // Manage the newly created identities with the Identity Agent.
+            await testHarness.agent.identity.manage({ portableIdentity: await careerIdentity.export() });
+            await testHarness.agent.identity.manage({ portableIdentity: await familyIdentity.export() });
+            await testHarness.agent.identity.manage({ portableIdentity: await socialIdentity.export() });
 
             // Verify the Identities were ALSO stored in the Identity Agent's tenant.
-            const storedIdentities = await testAgent.agent.identityManager.list();
+            const storedIdentities = await testHarness.agent.identity.list();
             expect(storedIdentities).to.have.length(3);
 
             // Verify the DIDs were only stored in the new Identity's tenant.
-            let storedCareerDid = await testAgent.agent.didManager.get({ didRef: careerIdentity.did, context: careerIdentity.did });
+            let storedCareerDid = await testHarness.agent.did.get({ didUri: careerIdentity.did.uri, tenant: careerIdentity.did.uri });
             expect(storedCareerDid).to.exist;
-            storedCareerDid = await testAgent.agent.didManager.get({ didRef: careerIdentity.did });
+            storedCareerDid = await testHarness.agent.did.get({ didUri: careerIdentity.did.uri });
             expect(storedCareerDid).to.not.exist;
-            let storedFamilyDid = await testAgent.agent.didManager.get({ didRef: familyIdentity.did, context: familyIdentity.did });
+            let storedFamilyDid = await testHarness.agent.did.get({ didUri: familyIdentity.did.uri, tenant: familyIdentity.did.uri });
             expect(storedFamilyDid).to.exist;
-            storedFamilyDid = await testAgent.agent.didManager.get({ didRef: familyIdentity.did });
+            storedFamilyDid = await testHarness.agent.did.get({ didUri: familyIdentity.did.uri });
             expect(storedFamilyDid).to.not.exist;
-            let storedSocialDid = await testAgent.agent.didManager.get({ didRef: socialIdentity.did, context: socialIdentity.did });
+            let storedSocialDid = await testHarness.agent.did.get({ didUri: socialIdentity.did.uri, tenant: socialIdentity.did.uri });
             expect(storedSocialDid).to.exist;
-            storedSocialDid = await testAgent.agent.didManager.get({ didRef: socialIdentity.did });
+            storedSocialDid = await testHarness.agent.did.get({ didUri: socialIdentity.did.uri });
             expect(storedSocialDid).to.not.exist;
 
             // Verify keys were stored in Identity Agent's DWN.
-            const careerKey = await testAgent.agent.keyManager.getKey({
-              keyRef: await testAgent.agent.didManager.getDefaultSigningKey({ did: careerIdentity.did }) ?? '' // Type guard.
-            });
-            expect(careerKey).to.exist;
-            const familyKey = await testAgent.agent.keyManager.getKey({
-              keyRef: await testAgent.agent.didManager.getDefaultSigningKey({ did: familyIdentity.did }) ?? '' // Type guard.
-            });
-            expect(familyKey).to.exist;
-            const socialKey = await testAgent.agent.keyManager.getKey({
-              keyRef: await testAgent.agent.didManager.getDefaultSigningKey({ did: socialIdentity.did }) ?? '' // Type guard.
-            });
-            expect(socialKey).to.exist;
-          }).timeout(30000);
-        }
-      });
-
-      describe('Using Web5 API', async () => {
-        it('should instantiate Web5 API with provided Web5Agent and DID', async () => {
-          // Start agent for the first time.
-          await testAgent.agent.start({ passphrase: 'test' });
-
-          // Create two identities, each of which is stored in a new tenant.
-          const careerIdentity = await testAgent.agent.identityManager.create({
-            name      : 'Career',
-            didMethod : 'key',
-            kms       : 'local'
-          });
-          const socialIdentity = await testAgent.agent.identityManager.create({
-            name      : 'Social',
-            didMethod : 'key',
-            kms       : 'local'
+            const careerVm = await testHarness.agent.did.getSigningMethod({ didUri: careerIdentity.did.uri });
+            const careerKeyUri = await testHarness.agent.keyManager.getKeyUri({ key: careerVm!.publicKeyJwk! });
+            const careerPublicKey = await testHarness.agent.keyManager.getPublicKey({ keyUri: careerKeyUri });
+            expect(careerPublicKey).to.exist;
+
+            const familyVm = await testHarness.agent.did.getSigningMethod({ didUri: familyIdentity.did.uri });
+            const familyKeyUri = await testHarness.agent.keyManager.getKeyUri({ key: familyVm!.publicKeyJwk! });
+            const familyPublicKey = await testHarness.agent.keyManager.getPublicKey({ keyUri: familyKeyUri });
+            expect(familyPublicKey).to.exist;
+
+            const socialVm = await testHarness.agent.did.getSigningMethod({ didUri: socialIdentity.did.uri });
+            const socialKeyUri = await testHarness.agent.keyManager.getKeyUri({ key: socialVm!.publicKeyJwk! });
+            const socialPublicKey = await testHarness.agent.keyManager.getPublicKey({ keyUri: socialKeyUri });
+            expect(socialPublicKey).to.exist;
           });
-
-          // Instantiate a Web5 instance with the "Career" Identity, write a record, and verify the result.
-          const web5Career = new Web5({ agent: testAgent.agent, connectedDid: careerIdentity.did });
-          expect(web5Career).to.exist;
-
-          // Instantiate a Web5 instance with the "Social" Identity, write a record, and verify the result.
-          const web5Social = new Web5({ agent: testAgent.agent, connectedDid: socialIdentity.did });
-          expect(web5Social).to.exist;
-        }).timeout(30000);
-
-        it('Can write records using an Identity under management', async () => {
-          // Start agent for the first time.
-          await testAgent.agent.start({ passphrase: 'test' });
-
-          // Create two identities, each of which is stored in a new tenant.
-          const careerIdentity = await testAgent.agent.identityManager.create({
-            name      : 'Social',
-            didMethod : 'key',
-            kms       : 'local'
-          });
-          const socialIdentity = await testAgent.agent.identityManager.create({
-            name      : 'Social',
-            didMethod : 'key',
-            kms       : 'local'
-          });
-
-          // Instantiate a Web5 instance with the "Career" Identity, write a record, and verify the result.
-          const web5Career = new Web5({ agent: testAgent.agent, connectedDid: careerIdentity.did });
-          const careerResult = await web5Career.dwn.records.write({
-            data    : 'Hello, world!',
-            message : {
-              schema     : 'foo/bar',
-              dataFormat : 'text/plain'
-            }
-          });
-          expect(careerResult.status.code).to.equal(202);
-          expect(careerResult.record).to.exist;
-          expect(careerResult.record?.author).to.equal(careerIdentity.did);
-          expect(await careerResult.record?.data.text()).to.equal('Hello, world!');
-
-          // Instantiate a Web5 instance with the "Social" Identity, write a record, and verify the result.
-          const web5Social = new Web5({ agent: testAgent.agent, connectedDid: socialIdentity.did });
-          const socialResult = await web5Social.dwn.records.write({
-            data    : 'Hello, everyone!',
-            message : {
-              schema     : 'foo/bar',
-              dataFormat : 'text/plain'
-            }
-          });
-          expect(socialResult.status.code).to.equal(202);
-          expect(socialResult.record).to.exist;
-          expect(socialResult.record?.author).to.equal(socialIdentity.did);
-          expect(await socialResult.record?.data.text()).to.equal('Hello, everyone!');
-        }).timeout(30000);
-
+        }
       });
     });
   });
diff --git a/packages/identity-agent/tests/utils/test-config.ts b/packages/identity-agent/tests/utils/test-config.ts
new file mode 100644
index 000000000..11c53fa07
--- /dev/null
+++ b/packages/identity-agent/tests/utils/test-config.ts
@@ -0,0 +1,5 @@
+const DEFAULT_TEST_DWN_URL = 'http://localhost:3000';
+
+const getTestDwnUrl = () => process.env.TEST_DWN_URL || DEFAULT_TEST_DWN_URL;
+
+export const testDwnUrl = getTestDwnUrl();
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ed6594ab7..6df744d06 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -157,6 +157,9 @@ importers:
       '@playwright/test':
         specifier: 1.40.1
         version: 1.40.1
+      '@tbd54566975/dwn-sdk-js':
+        specifier: 0.2.18
+        version: 0.2.18
       '@types/chai':
         specifier: 4.3.6
         version: 4.3.6
@@ -670,17 +673,17 @@ importers:
   packages/identity-agent:
     dependencies:
       '@web5/agent':
-        specifier: 0.2.6
-        version: 0.2.6
+        specifier: 0.3.0
+        version: link:../agent
       '@web5/common':
-        specifier: 0.2.3
-        version: 0.2.3
+        specifier: 0.2.4
+        version: link:../common
       '@web5/crypto':
-        specifier: 0.2.2
-        version: 0.2.2
+        specifier: 0.4.0
+        version: link:../crypto
       '@web5/dids':
-        specifier: 0.2.4
-        version: 0.2.4
+        specifier: 0.4.1
+        version: link:../dids
     devDependencies:
       '@playwright/test':
         specifier: 1.40.1
@@ -715,9 +718,6 @@ importers:
       '@web/test-runner-playwright':
         specifier: 0.11.0
         version: 0.11.0
-      '@web5/api':
-        specifier: 0.8.4
-        version: 0.8.4
       c8:
         specifier: 9.0.0
         version: 9.0.0
@@ -1426,15 +1426,6 @@ packages:
     resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
     dev: true
 
-  /@decentralized-identity/ion-pow-sdk@1.0.17:
-    resolution: {integrity: sha512-vk7DTDM8aKDbFyu1ad/qkoRrGL4q+KvNeL/FNZXhkWPaDhVExBN/qGEoRLf1YSfFe+myto3+4RYTPut+riiqnw==}
-    dependencies:
-      buffer: 6.0.3
-      cross-fetch: 3.1.5
-      hash-wasm: 4.9.0
-    transitivePeerDependencies:
-      - encoding
-
   /@decentralized-identity/ion-sdk@1.0.1:
     resolution: {integrity: sha512-+P+DXcRSFjsEsI5KIqUmVjpzgUT28B2lWpTO+IxiBcfibAN/1Sg20NebGTO/+serz2CnSZf95N2a1OZ6eXypGQ==}
     dependencies:
@@ -1806,20 +1797,12 @@ packages:
       multiformats: 13.1.0
       murmurhash3js-revisited: 3.0.0
 
-  /@noble/ciphers@0.1.4:
-    resolution: {integrity: sha512-d3ZR8vGSpy3v/nllS+bD/OMN5UZqusWiQqkyj7AwzTnhXFH72pF5oB4Ach6DQ50g5kXxC28LdaYBEpsyv9KOUQ==}
-
   /@noble/ciphers@0.3.0:
     resolution: {integrity: sha512-ldbrnOjmNRwFdXcTM6uXDcxpMIFrbzAWNnpBPp4oTJTFF0XByGD6vf45WrehZGXRQTRVV+Zm8YP+EgEf+e4cWA==}
 
   /@noble/ciphers@0.4.1:
     resolution: {integrity: sha512-QCOA9cgf3Rc33owG0AYBB9wszz+Ul2kramWN8tXG44Gyciud/tbkEqvxRF/IpqQaBpRBNi9f4jdNxqB2CQCIXg==}
 
-  /@noble/curves@1.1.0:
-    resolution: {integrity: sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==}
-    dependencies:
-      '@noble/hashes': 1.3.1
-
   /@noble/curves@1.3.0:
     resolution: {integrity: sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==}
     dependencies:
@@ -1828,10 +1811,6 @@ packages:
   /@noble/ed25519@2.0.0:
     resolution: {integrity: sha512-/extjhkwFupyopDrt80OMWKdLgP429qLZj+z6sYJz90rF2Iz0gjZh2ArMKPImUl13Kx+0EXI2hN9T/KJV0/Zng==}
 
-  /@noble/hashes@1.3.1:
-    resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==}
-    engines: {node: '>= 16'}
-
   /@noble/hashes@1.3.3:
     resolution: {integrity: sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==}
     engines: {node: '>= 16'}
@@ -2539,37 +2518,6 @@ packages:
       jwt-decode: 3.1.2
     dev: true
 
-  /@tbd54566975/dwn-sdk-js@0.2.10:
-    resolution: {integrity: sha512-CoKO8+NciwWNzD4xRoAAgeElqQCXKM4Fc+zEHsUWD0M3E9v67hRWiTHI6AenUfQv1RSEB2H4GHUeUOHuEV72uw==}
-    engines: {node: '>= 18'}
-    dependencies:
-      '@ipld/dag-cbor': 9.0.3
-      '@js-temporal/polyfill': 0.4.4
-      '@noble/ed25519': 2.0.0
-      '@noble/secp256k1': 2.0.0
-      abstract-level: 1.0.3
-      ajv: 8.12.0
-      blockstore-core: 4.2.0
-      cross-fetch: 4.0.0
-      eciesjs: 0.4.5
-      interface-blockstore: 5.2.3
-      interface-store: 5.1.2
-      ipfs-unixfs-exporter: 13.1.5
-      ipfs-unixfs-importer: 15.1.5
-      level: 8.0.0
-      lodash: 4.17.21
-      lru-cache: 9.1.2
-      ms: 2.1.3
-      multiformats: 11.0.2
-      randombytes: 2.1.0
-      readable-stream: 4.4.2
-      ulidx: 2.1.0
-      uuid: 8.3.2
-      varint: 6.0.0
-    transitivePeerDependencies:
-      - encoding
-      - supports-color
-
   /@tbd54566975/dwn-sdk-js@0.2.18:
     resolution: {integrity: sha512-71K8izqimtsforMBISl4s8VR5El6VtZd6xjs/s7eYsOww3H/wbdVC5KBXeCvGDlfBoTOk62R5eEUm+WEgaOMrw==}
     engines: {node: '>= 18'}
@@ -2602,39 +2550,6 @@ packages:
       - encoding
       - supports-color
 
-  /@tbd54566975/dwn-sdk-js@0.2.8:
-    resolution: {integrity: sha512-oiKk+ekAQO94bUkt6yk+xkDY8uCGmNC+rKaYQLhAoTrhYrczeRSuDT04F5/vPBT5K6NfAoRcQsIyBmvgRCUvgA==}
-    engines: {node: '>= 18'}
-    dependencies:
-      '@ipld/dag-cbor': 9.0.3
-      '@js-temporal/polyfill': 0.4.4
-      '@noble/ed25519': 2.0.0
-      '@noble/secp256k1': 2.0.0
-      abstract-level: 1.0.3
-      ajv: 8.12.0
-      blockstore-core: 4.2.0
-      cross-fetch: 4.0.0
-      eciesjs: 0.4.5
-      flat: 5.0.2
-      interface-blockstore: 5.2.3
-      interface-store: 5.1.2
-      ipfs-unixfs-exporter: 13.1.5
-      ipfs-unixfs-importer: 15.1.5
-      level: 8.0.0
-      lodash: 4.17.21
-      lru-cache: 9.1.2
-      ms: 2.1.3
-      multiformats: 11.0.2
-      randombytes: 2.1.0
-      readable-stream: 4.4.2
-      ulidx: 2.1.0
-      uuid: 8.3.2
-      varint: 6.0.0
-    transitivePeerDependencies:
-      - encoding
-      - supports-color
-    dev: true
-
   /@tbd54566975/dwn-sql-store@0.2.10:
     resolution: {integrity: sha512-x3tTosQFBwn2Ltqj2FZ84Ma297ekUFeXSyOSfhO6rMHexU1RNmSvuV3YBLfDpBWZUr0ffF1GiJ6zMHKXdoF2jw==}
     engines: {node: '>=18'}
@@ -3258,73 +3173,6 @@ packages:
       - utf-8-validate
     dev: true
 
-  /@web5/agent@0.2.5:
-    resolution: {integrity: sha512-Z9JY/43Yrg0xKK26y/iZFdHNtVf/k9XLxw8mXP5zfYidrqfAgVR0i4LKA7qZKfUSxC7/uaD/STYYIKpNByd/cw==}
-    engines: {node: '>=18.0.0'}
-    dependencies:
-      '@tbd54566975/dwn-sdk-js': 0.2.8
-      '@web5/common': 0.2.2
-      '@web5/crypto': 0.2.2
-      '@web5/dids': 0.2.3
-      level: 8.0.0
-      readable-stream: 4.4.2
-      readable-web-to-node-stream: 3.0.2
-    transitivePeerDependencies:
-      - encoding
-      - supports-color
-    dev: true
-
-  /@web5/agent@0.2.6:
-    resolution: {integrity: sha512-qBbFH7b08Sw8JCpPQ+ClbblL++hkSR25e1XNF46ToVTJJldLspLTcXjr2Oui1bpJZ3jppnZ9nvcQPfymtdM5fg==}
-    engines: {node: '>=18.0.0'}
-    dependencies:
-      '@tbd54566975/dwn-sdk-js': 0.2.10
-      '@web5/common': 0.2.3
-      '@web5/crypto': 0.2.2
-      '@web5/dids': 0.2.4
-      level: 8.0.0
-      readable-stream: 4.4.2
-      readable-web-to-node-stream: 3.0.2
-      ulidx: 2.1.0
-    transitivePeerDependencies:
-      - encoding
-      - supports-color
-    dev: false
-
-  /@web5/api@0.8.4:
-    resolution: {integrity: sha512-3kqh7KQeeffsOBi+K5Vhpa59+ZgdI1em16drrRd7h59Jj4m8a09Enhw7WjWNLre7rTG84hQqB9+rpS1VHnOaKQ==}
-    engines: {node: '>=18.0.0'}
-    dependencies:
-      '@tbd54566975/dwn-sdk-js': 0.2.10
-      '@web5/agent': 0.2.5
-      '@web5/common': 0.2.2
-      '@web5/crypto': 0.2.2
-      '@web5/dids': 0.2.4
-      '@web5/user-agent': 0.2.5
-      level: 8.0.0
-      ms: 2.1.3
-      readable-stream: 4.4.2
-      readable-web-to-node-stream: 3.0.2
-    transitivePeerDependencies:
-      - encoding
-      - supports-color
-    dev: true
-
-  /@web5/common@0.2.1:
-    resolution: {integrity: sha512-Tt5P17HgQCx+Epw0IHnhRKqp5UU3E4xtsE8PkdghOBnvntBB0op5P6efvR1WqmJft5+VunDHt3yZAZstuqQkNg==}
-    engines: {node: '>=18.0.0'}
-    dependencies:
-      level: 8.0.0
-      multiformats: 11.0.2
-
-  /@web5/common@0.2.2:
-    resolution: {integrity: sha512-dRn6SmALExeTLMTK/W5ozGarfaddK+Lraf5OjuIGLAaLfcX1RWx3oDMoY5Hr9LjfxHJC8mGXB8DnKflbeYJRgA==}
-    engines: {node: '>=18.0.0'}
-    dependencies:
-      level: 8.0.0
-      multiformats: 11.0.2
-      readable-stream: 4.4.2
-
   /@web5/common@0.2.3:
     resolution: {integrity: sha512-WTbIS6l5inrQTS5cwOoQP5KEuUHZqOWCKCDAA62qGUn4QyySolog9Gt2HCNUEClIzzk/d5DHcpBgLCadHoJ6rQ==}
     engines: {node: '>=18.0.0'}
@@ -3333,15 +3181,6 @@ packages:
       multiformats: 11.0.2
       readable-stream: 4.4.2
 
-  /@web5/crypto@0.2.2:
-    resolution: {integrity: sha512-vHFg0wXQSQXrwuBNQyDHnmSZchfTfO6/Sv+7rDsNkvofs+6lGTE8CZ02cwUYMeIwTRMLer12c+fMfzYrXokEUQ==}
-    engines: {node: '>=18.0.0'}
-    dependencies:
-      '@noble/ciphers': 0.1.4
-      '@noble/curves': 1.1.0
-      '@noble/hashes': 1.3.1
-      '@web5/common': 0.2.1
-
   /@web5/crypto@0.4.0:
     resolution: {integrity: sha512-GIKn2CizQKeATvHQqmC4ky26b3q2pJOd2GjIYsOSw/3Y3QIEm3holDGqu9FHs6kacmr6u0Pv5ELvccs58+cKEg==}
     engines: {node: '>=18.0.0'}
@@ -3351,44 +3190,6 @@ packages:
       '@noble/hashes': 1.3.3
       '@web5/common': 0.2.3
 
-  /@web5/dids@0.2.3:
-    resolution: {integrity: sha512-Y3PHOavNkSyjBxZQEpKE6XueaqemBO5w0UMOnFh4xH6+5B43ENEE4LHIqVyn2bCpfEBGLXENgDZYqyJphBu0pA==}
-    engines: {node: '>=18.0.0'}
-    deprecated: Upgrade to @latest
-    dependencies:
-      '@decentralized-identity/ion-pow-sdk': 1.0.17
-      '@decentralized-identity/ion-sdk': 1.0.1
-      '@web5/common': 0.2.2
-      '@web5/crypto': 0.2.2
-      did-resolver: 4.1.0
-      dns-packet: 5.6.1
-      level: 8.0.0
-      ms: 2.1.3
-      pkarr: 1.1.1
-      z32: 1.0.1
-    transitivePeerDependencies:
-      - encoding
-      - supports-color
-    dev: true
-
-  /@web5/dids@0.2.4:
-    resolution: {integrity: sha512-e+m+xgpiM8ydTJgWcPdwmjILLMZYdl2kwahlO22mK0azSKVrg1klpGrUODzqkrWrQ5O0tnOyqEy39FcD5Sy11w==}
-    engines: {node: '>=18.0.0'}
-    dependencies:
-      '@decentralized-identity/ion-pow-sdk': 1.0.17
-      '@decentralized-identity/ion-sdk': 1.0.1
-      '@web5/common': 0.2.2
-      '@web5/crypto': 0.2.2
-      did-resolver: 4.1.0
-      dns-packet: 5.6.1
-      level: 8.0.0
-      ms: 2.1.3
-      pkarr: 1.1.1
-      z32: 1.0.1
-    transitivePeerDependencies:
-      - encoding
-      - supports-color
-
   /@web5/dids@0.4.0:
     resolution: {integrity: sha512-e+QcrKWlWPJVBbpz4QKrdcgPoj+uC8YUXt4tGKj1mC4kV0rCtIioMLw9Djg+54pp3VXl3eGKxju98UEddyZwqA==}
     engines: {node: '>=18.0.0'}
@@ -3446,19 +3247,6 @@ packages:
       - utf-8-validate
     dev: true
 
-  /@web5/user-agent@0.2.5:
-    resolution: {integrity: sha512-qv5M698C5HSvq30xUgLWtcsbZppjfOH5qZthpTRx4ItL5UWA/eQ9DsQiQeb4vet3uIUy3NHRDIQezclOdwYErw==}
-    engines: {node: '>=18.0.0'}
-    dependencies:
-      '@web5/agent': 0.2.5
-      '@web5/common': 0.2.2
-      '@web5/crypto': 0.2.2
-      '@web5/dids': 0.2.3
-    transitivePeerDependencies:
-      - encoding
-      - supports-color
-    dev: true
-
   /@webassemblyjs/ast@1.11.6:
     resolution: {integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==}
     dependencies:
@@ -3834,6 +3622,7 @@ packages:
 
   /b4a@1.6.6:
     resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==}
+    dev: true
 
   /balanced-match@1.0.2:
     resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
@@ -3857,15 +3646,6 @@ packages:
     engines: {node: '>=10.0.0'}
     dev: true
 
-  /bencode@2.0.3:
-    resolution: {integrity: sha512-D/vrAD4dLVX23NalHwb8dSvsUsxeRPO8Y7ToKA015JQYq69MLDOMkC0uGZYA/MPpltLO8rt8eqFC2j8DxjTZ/w==}
-
-  /bencode@3.1.1:
-    resolution: {integrity: sha512-btsxX9201yoWh45TdqYg6+OZ5O1xTYKTYSGvJndICDFtznE/9zXgow8yjMvvhOqKKuzuL7h+iiCMpfkG8+QuBA==}
-    engines: {node: '>=12.20.0'}
-    dependencies:
-      uint8-util: 2.2.4
-
   /bencode@4.0.0:
     resolution: {integrity: sha512-AERXw18df0pF3ziGOCyUjqKZBVNH8HV3lBxnx5w0qtgMIk4a1wb9BkcCQbkp9Zstfrn/dzRwl7MmUHHocX3sRQ==}
     engines: {node: '>=12.20.0'}
@@ -3895,21 +3675,6 @@ packages:
     resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==}
     dev: true
 
-  /bittorrent-dht@11.0.5:
-    resolution: {integrity: sha512-R09D6uNaziRqsc+B/j5QzkjceTak+wH9vcNLnkmt8A52EWF9lQwBP0vvCKgSA3AJOYYl+41n3osA2KYYn/z5uQ==}
-    engines: {node: '>=12.20.0'}
-    dependencies:
-      bencode: 4.0.0
-      debug: 4.3.4(supports-color@8.1.1)
-      k-bucket: 5.1.0
-      k-rpc: 5.1.0
-      last-one-wins: 1.0.4
-      lru: 3.1.0
-      randombytes: 2.1.0
-      record-cache: 1.2.0
-    transitivePeerDependencies:
-      - supports-color
-
   /bl@4.1.0:
     resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
     dependencies:
@@ -3925,18 +3690,6 @@ packages:
       inherits: 2.0.4
       readable-stream: 3.6.2
 
-  /blake2b-wasm@2.4.0:
-    resolution: {integrity: sha512-S1kwmW2ZhZFFFOghcx73+ZajEfKBqhP82JMssxtLVMxlaPea1p9uoLiUZ5WYyHn0KddwbLc+0vh4wR0KBNoT5w==}
-    dependencies:
-      b4a: 1.6.6
-      nanoassert: 2.0.0
-
-  /blake2b@2.1.4:
-    resolution: {integrity: sha512-AyBuuJNI64gIvwx13qiICz6H6hpmjvYS5DGkG6jbXMOT8Z3WUJ3V1X0FlhIoT1b/5JtHE3ki+xjtMvu1nn+t9A==}
-    dependencies:
-      blake2b-wasm: 2.4.0
-      nanoassert: 2.0.0
-
   /blockstore-core@4.2.0:
     resolution: {integrity: sha512-F8BCobc75D+9/+hUD+5cixbU6zmZA+lBgNiuBkNlJqRgmAaBBvLOQF6Ad9Jei0Nvmy2a1jaF4CiN76W1apIghA==}
     engines: {node: '>=16.0.0', npm: '>=7.0.0'}
@@ -4222,11 +3975,6 @@ packages:
     resolution: {integrity: sha512-xAuZbCDUOZxCe/ZJuIrnlG1Bk1R0qhwCXdnPYxVmqBSqm9M3BeE3G6Qoj5Zq+8epas36bT3vjiInDTJ6BVH6Rg==}
     hasBin: true
 
-  /chacha20-universal@1.0.4:
-    resolution: {integrity: sha512-/IOxdWWNa7nRabfe7+oF+jVkGjlr2xUL4J8l/OvzZhj+c9RpMqoo3Dq+5nU1j/BflRV4BKnaQ4+4oH1yBpQG1Q==}
-    dependencies:
-      nanoassert: 2.0.0
-
   /chai-as-promised@7.1.1(chai@4.3.10):
     resolution: {integrity: sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==}
     peerDependencies:
@@ -4273,10 +4021,6 @@ packages:
       supports-color: 7.2.0
     dev: true
 
-  /chalk@5.3.0:
-    resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==}
-    engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
-
   /charenc@0.0.2:
     resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==}
     dev: true
@@ -4321,17 +4065,6 @@ packages:
     resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
     dev: true
 
-  /chrome-dgram@3.0.6:
-    resolution: {integrity: sha512-bqBsUuaOiXiqxXt/zA/jukNJJ4oaOtc7ciwqJpZVEaaXwwxqgI2/ZdG02vXYWUhHGziDlvGMQWk0qObgJwVYKA==}
-    dependencies:
-      inherits: 2.0.4
-      run-series: 1.1.9
-
-  /chrome-dns@1.0.1:
-    resolution: {integrity: sha512-HqsYJgIc8ljJJOqOzLphjAs79EUuWSX3nzZi2LNkzlw3GIzAeZbaSektC8iT/tKvLqZq8yl1GJu5o6doA4TRbg==}
-    dependencies:
-      chrome-net: 3.3.4
-
   /chrome-launcher@0.15.2:
     resolution: {integrity: sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==}
     engines: {node: '>=12.13.0'}
@@ -4345,11 +4078,6 @@ packages:
       - supports-color
     dev: true
 
-  /chrome-net@3.3.4:
-    resolution: {integrity: sha512-Jzy2EnzmE+ligqIZUsmWnck9RBXLuUy6CaKyuNMtowFG3ZvLt8d+WBJCTPEludV0DHpIKjAOlwjFmTaEdfdWCw==}
-    dependencies:
-      inherits: 2.0.4
-
   /chrome-trace-event@1.0.3:
     resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==}
     engines: {node: '>=6.0'}
@@ -4560,13 +4288,6 @@ packages:
     resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
     dev: true
 
-  /cross-fetch@3.1.5:
-    resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==}
-    dependencies:
-      node-fetch: 2.6.7
-    transitivePeerDependencies:
-      - encoding
-
   /cross-fetch@4.0.0:
     resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==}
     dependencies:
@@ -4762,9 +4483,6 @@ packages:
     resolution: {integrity: sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==}
     dev: true
 
-  /did-resolver@4.1.0:
-    resolution: {integrity: sha512-S6fWHvCXkZg2IhS4RcVHxwuyVejPR7c+a4Go0xbQ9ps5kILa8viiYQgrM4gfTyeTjJ0ekgJH9gk/BawTpmkbZA==}
-
   /diff@5.0.0:
     resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==}
     engines: {node: '>=0.3.1'}
@@ -4790,12 +4508,6 @@ packages:
       path-type: 4.0.0
     dev: true
 
-  /dns-packet@5.6.1:
-    resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==}
-    engines: {node: '>=6'}
-    dependencies:
-      '@leichtgewicht/ip-codec': 2.0.4
-
   /doctrine@3.0.0:
     resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
     engines: {node: '>=6.0.0'}
@@ -5629,11 +5341,6 @@ packages:
     resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
     dev: true
 
-  /graceful-goodbye@1.3.0:
-    resolution: {integrity: sha512-hcZOs20emYlTM7MmUE0FpuZcjlk2GPsR+UYTHDeWxtGjXcbh2CawGi8vlzqsIvspqAbot7mRv3sC/uhgtKc4hQ==}
-    dependencies:
-      safety-catch: 1.0.2
-
   /graphemer@1.4.0:
     resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
     dev: true
@@ -5685,9 +5392,6 @@ packages:
       safe-buffer: 5.2.1
     dev: true
 
-  /hash-wasm@4.9.0:
-    resolution: {integrity: sha512-7SW7ejyfnRxuOc7ptQHSf4LDoZaWOivfzqw+5rpcQku0nHfmicPKE51ra9BiRLAmT8+gGLestr1XroUkqdjL6w==}
-
   /hash.js@1.1.7:
     resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==}
     dependencies:
@@ -6313,26 +6017,6 @@ packages:
   /jwt-decode@3.1.2:
     resolution: {integrity: sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==}
 
-  /k-bucket@5.1.0:
-    resolution: {integrity: sha512-Fac7iINEovXIWU20GPnOMLUbjctiS+cnmyjC4zAUgvs3XPf1vo9akfCHkigftSic/jiKqKl+KA3a/vFcJbHyCg==}
-    dependencies:
-      randombytes: 2.1.0
-
-  /k-rpc-socket@1.11.1:
-    resolution: {integrity: sha512-8xtA8oqbZ6v1Niryp2/g4GxW16EQh5MvrUylQoOG+zcrDff5CKttON2XUXvMwlIHq4/2zfPVFiinAccJ+WhxoA==}
-    dependencies:
-      bencode: 2.0.3
-      chrome-dgram: 3.0.6
-      chrome-dns: 1.0.1
-      chrome-net: 3.3.4
-
-  /k-rpc@5.1.0:
-    resolution: {integrity: sha512-FGc+n70Hcjoa/X2JTwP+jMIOpBz+pkRffHnSl9yrYiwUxg3FIgD50+u1ePfJUOnRCnx6pbjmVk5aAeB1wIijuQ==}
-    dependencies:
-      k-bucket: 5.1.0
-      k-rpc-socket: 1.11.1
-      randombytes: 2.1.0
-
   /keygrip@1.1.0:
     resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==}
     engines: {node: '>= 0.6'}
@@ -6421,9 +6105,6 @@ packages:
     engines: {node: '>=14.0.0'}
     dev: true
 
-  /last-one-wins@1.0.4:
-    resolution: {integrity: sha512-t+KLJFkHPQk8lfN6WBOiGkiUXoub+gnb2XTYI2P3aiISL+94xgZ1vgz1SXN/N4hthuOoLXarXfBZPUruyjQtfA==}
-
   /layerr@2.0.1:
     resolution: {integrity: sha512-z0730CwG/JO24evdORnyDkwG1Q7b7mF2Tp1qRQ0YvrMMARbt1DFG694SOv439Gm7hYKolyZyaB49YIrYIfZBdg==}
 
@@ -6564,12 +6245,6 @@ packages:
     resolution: {integrity: sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==}
     engines: {node: 14 || >=16.14}
 
-  /lru@3.1.0:
-    resolution: {integrity: sha512-5OUtoiVIGU4VXBOshidmtOsvBIvcQR6FD/RzWSvaeHyxCGB+PCUCu+52lqMfdc0h/2CLvHhZS4TwUmMQrrMbBQ==}
-    engines: {node: '>= 0.4.0'}
-    dependencies:
-      inherits: 2.0.4
-
   /make-dir@4.0.0:
     resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
     engines: {node: '>=10'}
@@ -6861,9 +6536,6 @@ packages:
       lru-cache: 7.18.3
     dev: true
 
-  /nanoassert@2.0.0:
-    resolution: {integrity: sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==}
-
   /nanocolors@0.2.13:
     resolution: {integrity: sha512-0n3mSAQLPpGLV9ORXT5+C/D4mwew7Ebws69Hx4E2sgz2ZA5+32Q80B9tL8PbL7XHnRDiAxH/pnrUJ9a4fkTNTA==}
     dev: true
@@ -6932,17 +6604,6 @@ packages:
       lodash: 4.17.21
     dev: true
 
-  /node-fetch@2.6.7:
-    resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
-    engines: {node: 4.x || >=6.0.0}
-    peerDependencies:
-      encoding: ^0.1.0
-    peerDependenciesMeta:
-      encoding:
-        optional: true
-    dependencies:
-      whatwg-url: 5.0.0
-
   /node-fetch@2.7.0:
     resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
     engines: {node: 4.x || >=6.0.0}
@@ -7389,20 +7050,6 @@ packages:
     engines: {node: '>=8.6'}
     dev: true
 
-  /pkarr@1.1.1:
-    resolution: {integrity: sha512-X27LKqf83X3WuJd2Z9qdfVxkmfOu6HUbY0pm11LqeBbFmgmZRPgOxJG8bKiIsmmD6Vjc25j45KHYflF2lfodyQ==}
-    hasBin: true
-    dependencies:
-      bencode: 3.1.1
-      bittorrent-dht: 11.0.5
-      chalk: 5.3.0
-      dns-packet: 5.6.1
-      graceful-goodbye: 1.3.0
-      sodium-universal: 4.0.0
-      z32: 1.0.1
-    transitivePeerDependencies:
-      - supports-color
-
   /pkg-dir@5.0.0:
     resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==}
     engines: {node: '>=10'}
@@ -7742,6 +7389,7 @@ packages:
     engines: {node: '>=8'}
     dependencies:
       readable-stream: 3.6.2
+    dev: false
 
   /readdirp@3.6.0:
     resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
@@ -7750,11 +7398,6 @@ packages:
       picomatch: 2.3.1
     dev: true
 
-  /record-cache@1.2.0:
-    resolution: {integrity: sha512-kyy3HWCez2WrotaL3O4fTn0rsIdfRKOdQQcEJ9KpvmKmbffKVvwsloX063EgRUlpJIXHiDQFhJcTbZequ2uTZw==}
-    dependencies:
-      b4a: 1.6.6
-
   /regexp.prototype.flags@1.5.2:
     resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==}
     engines: {node: '>= 0.4'}
@@ -7878,9 +7521,6 @@ packages:
       queue-microtask: 1.2.3
     dev: true
 
-  /run-series@1.1.9:
-    resolution: {integrity: sha512-Arc4hUN896vjkqCYrUXquBFtRZdv1PfLbTYP71efP6butxyQ0kWpiNJyAgsxscmQg1cqvHY32/UCBzXedTpU2g==}
-
   /rxjs@7.8.1:
     resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
     dependencies:
@@ -7917,9 +7557,6 @@ packages:
     resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
     dev: true
 
-  /safety-catch@1.0.2:
-    resolution: {integrity: sha512-C1UYVZ4dtbBxEtvOcpjBaaD27nP8MlvyAQEp2fOTOEe6pfUpk1cDUxij6BR1jZup6rSyUTaBBplK7LanskrULA==}
-
   /schema-utils@3.3.0:
     resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==}
     engines: {node: '>= 10.13.0'}
@@ -8027,30 +7664,6 @@ packages:
       safe-buffer: 5.2.1
     dev: true
 
-  /sha256-universal@1.2.1:
-    resolution: {integrity: sha512-ghn3muhdn1ailCQqqceNxRgkOeZSVfSE13RQWEg6njB+itsFzGVSJv+O//2hvNXZuxVIRyNzrgsZ37SPDdGJJw==}
-    dependencies:
-      b4a: 1.6.6
-      sha256-wasm: 2.2.2
-
-  /sha256-wasm@2.2.2:
-    resolution: {integrity: sha512-qKSGARvao+JQlFiA+sjJZhJ/61gmW/3aNLblB2rsgIxDlDxsJPHo8a1seXj12oKtuHVgJSJJ7QEGBUYQN741lQ==}
-    dependencies:
-      b4a: 1.6.6
-      nanoassert: 2.0.0
-
-  /sha512-universal@1.2.1:
-    resolution: {integrity: sha512-kehYuigMoRkIngCv7rhgruLJNNHDnitGTBdkcYbCbooL8Cidj/bS78MDxByIjcc69M915WxcQTgZetZ1JbeQTQ==}
-    dependencies:
-      b4a: 1.6.6
-      sha512-wasm: 2.3.4
-
-  /sha512-wasm@2.3.4:
-    resolution: {integrity: sha512-akWoxJPGCB3aZCrZ+fm6VIFhJ/p8idBv7AWGFng/CZIrQo51oQNsvDbTSRXWAzIiZJvpy16oIDiCCPqTe21sKg==}
-    dependencies:
-      b4a: 1.6.6
-      nanoassert: 2.0.0
-
   /shebang-command@2.0.0:
     resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
     engines: {node: '>=8'}
@@ -8104,11 +7717,6 @@ packages:
       supports-color: 7.2.0
     dev: true
 
-  /siphash24@1.3.1:
-    resolution: {integrity: sha512-moemC3ZKiTzH29nbFo3Iw8fbemWWod4vNs/WgKbQ54oEs6mE6XVlguxvinYjB+UmaE0PThgyED9fUkWvirT8hA==}
-    dependencies:
-      nanoassert: 2.0.0
-
   /slash@3.0.0:
     resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
     engines: {node: '>=8'}
@@ -8147,36 +7755,6 @@ packages:
       smart-buffer: 4.2.0
     dev: true
 
-  /sodium-javascript@0.8.0:
-    resolution: {integrity: sha512-rEBzR5mPxPES+UjyMDvKPIXy9ImF17KOJ32nJNi9uIquWpS/nfj+h6m05J5yLJaGXjgM72LmQoUbWZVxh/rmGg==}
-    dependencies:
-      blake2b: 2.1.4
-      chacha20-universal: 1.0.4
-      nanoassert: 2.0.0
-      sha256-universal: 1.2.1
-      sha512-universal: 1.2.1
-      siphash24: 1.3.1
-      xsalsa20: 1.2.0
-
-  /sodium-native@4.0.10:
-    resolution: {integrity: sha512-vrJQt4gASntDbnltRRk9vN4rks1SehjM12HkqQtu250JtWT+/lo8oEOa1HvSq3+8hzJdYcCJuLR5qRGxoRDjAg==}
-    requiresBuild: true
-    dependencies:
-      node-gyp-build: 4.8.0
-
-  /sodium-universal@4.0.0:
-    resolution: {integrity: sha512-iKHl8XnBV96k1c75gwwzANFdephw/MDWSjQAjPmBE+du0y3P23Q8uf7AcdcfFsYAMwLg7WVBfSAIBtV/JvRsjA==}
-    dependencies:
-      blake2b: 2.1.4
-      chacha20-universal: 1.0.4
-      nanoassert: 2.0.0
-      sha256-universal: 1.2.1
-      sha512-universal: 1.2.1
-      siphash24: 1.3.1
-      sodium-javascript: 0.8.0
-      sodium-native: 4.0.10
-      xsalsa20: 1.2.0
-
   /source-map-js@1.0.2:
     resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
     engines: {node: '>=0.10.0'}
@@ -9051,9 +8629,6 @@ packages:
     resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==}
     dev: true
 
-  /xsalsa20@1.2.0:
-    resolution: {integrity: sha512-FIr/DEeoHfj7ftfylnoFt3rAIRoWXpx2AoDfrT2qD2wtp7Dp+COajvs/Icb7uHqRW9m60f5iXZwdsJJO3kvb7w==}
-
   /xtend@4.0.2:
     resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
     engines: {node: '>=0.4'}
@@ -9143,8 +8718,3 @@ packages:
     resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
     engines: {node: '>=10'}
     dev: true
-
-  /z32@1.0.1:
-    resolution: {integrity: sha512-Uytfqf6VEVchHKZDw0NRdCViOARHP84uzvOw0CXCMLOwhgHZUL9XibpEPLLQN10mCVLxOlGCQWbkV7km7yNYcw==}
-    dependencies:
-      b4a: 1.6.6
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index d9b7946c8..bf5d0d166 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -7,5 +7,5 @@ packages:
   - "packages/agent"
   - "packages/user-agent"
   - "packages/proxy-agent"
-  - "packages/api"
   - "packages/identity-agent"
+  - "packages/api"