# Agent Management

The **Edge Agent SDK** revolves around instances of `SudoDIEdgeAgent`s. Agent instances provide access to modules for accessing data or DI protocol engagements. As well as APIs for managing the agent's configuration, managing the message receiver loop, and hooks for subscribing to agent events.

## Initializing an Agent

### Configuration

Before initializing an agent, an `AgentConfiguration` object must be created to dictate some of the agent's behavior. Using the fields in `AgentConfiguration`, the following agent behavior can be configured:

#### Required:

* `networkConfiguration` controls how the agent should handle network interactions and which ledgers the agent should opt-in to. This configuration allows tweaking of general network settings (such as ledger re-try attempts), and ledger-specific configurations for the Edge Agent's supported ledger types: **Cheqd** and **Sovrin-based** (did:sov + unqualified indy DIDs). The configurations for these ledgers will be used when resolving DIDs or Anoncreds objects from those ledgers during protocols. For ledger-specific configurations, `null` can be explicitly provided to opt-out of that ledger type if it's not needed by the agent.

#### Optional:

* `storageDirectory` can be used to specify a directory location on the device where agent data will be stored. If `null` is provided (or if unprovided), a persistent data directory within the application's environment will be used.
* `peerConnectionConfiguration` is the default configuration for how the agent should present itself to peers it connects with. It includes a `label` field for a self-assigned unverified label or nickname.
* `exchangePreservationConfiguration` controls preservation of exchange records in the wallet after a protocol (connection, credential or proof presentation protocols) is complete.
* `cacheConfiguration` controls how the agent should cache different data it uses (such as immutable data it receives from the ledger it uses).
* *+ more*. The configurable parameters on the agent are growing over time. To see the full set of configurable fields, please refer to the [API Reference](https://docs.sudoplatform.com/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/sdk-releases).

### Builder

To initialize a `SudoDIEdgeAgent`, the associated `Builder` class can be used along with the configuration you wish to use ([see above](#configuration)).

{% tabs %}
{% tab title="Swift" %}
An `AgentConfiguration` must be set in the builder. A logger can also optionally be set.

The following example demonstrates how to build an agent using a default configuration.&#x20;

```swift
let genesisFile: URL // a URL pointing to a indy-ledger genesis file on disk

let networkConfiguration = NetworkConfiguration(
    sovConfiguration: NetworkConfiguration.Sov(
        genesisFiles: [genesisFile],
        namespace: "testnet"
    ),
    cheqdConfiguration: NetworkConfiguration.Cheqd()
)
let agentConfiguration = AgentConfiguration(networkConfiguration: networkConfiguration)
agent = try SudoDIEdgeAgentBuilder()
            .setAgentConfiguration(agentConfiguration: agentConfiguration)
            .build()
```

{% endtab %}

{% tab title="Kotlin" %}
Note that only `agentConfiguration` and `context` are **required** to be set in the Builder. A custom `logger` and `dispatcher` for coroutine methods can be passed into the builder if desired as well.

```kotlin
val appContext: Context // Android application context
val genesisFile: File // a File reference to a indy-ledger genesis file on disk

val networkConfiguration = NetworkConfiguration(
    sovConfiguration = NetworkConfiguration.Sov(
        genesisFiles = listOf(genesisFile),
        namespace = "testnet"
    ),
    cheqdConfiguration = NetworkConfiguration.Cheqd()
)

val agentConfiguration = AgentConfiguration(
    networkConfiguration = networkConfiguration
)

val agent: SudoDIEdgeAgent = SudoDIEdgeAgent.builder()
    .setContext(appContext)
    .setAgentConfiguration(agentConfiguration)
    .build()
```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
This builder also notably allows for custom cryptography providers to be injected into the agent, such that the agent can utilize this provider during operations such as DID creation and presentation signing. For high assurance use cases, we also provide default cryptography providers that utilize iOS and Android Hardware security.&#x20;

Please see [utilize-alternative-cryptography-providers](https://docs.sudoplatform.com/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/utilize-alternative-cryptography-providers "mention") for guidance.
{% endhint %}

## Changing Agent Configuration

After an [agent is initialized](#initializing-an-agent), the configuration can be changed on the fly if desired. This can be achieved via the `setAgentConfiguration` API. This will allow certain behavior, such as credential auto acceptance, agent connection appearance, and more to be changed.

{% tabs %}
{% tab title="Swift" %}

```swift
let agent: SudoDIEdgeAgent // agent to change configuration for
let newConfiguration: AgentConfiguration // the new configuration to set the agent to

try agent.setAgentConfiguration(agentConfiguration: newConfiguration)
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val agent: SudoDIEdgeAgent // agent to change configuration for
val newConfiguration: AgentConfiguration // the new configuration to set the agent to

agent.setAgentConfiguration(newConfiguration)
```

{% endtab %}
{% endtabs %}

{% hint style="danger" %}
Some configuration fields should **not** be changed whilst an agent has a wallet open. The SDK inline code documentation notes these fields. These fields include `storageDirectory` and `networkConfiguration`. To reconfigure these fields on an agent, it is recommended to `stop` the agent's run loop, close the wallet and re-initialize the agent before setting a new config for these values.
{% endhint %}

## Receiving a URL Request

As part of the protocols supported by the Edge Agent, external requests (from Issuers, Verifiers, etc) are often made to the Wallet App in the form of a URL request. For instance, a deep link to the app or a QR Code scanned by the app. The Edge Agent API `receiveUrl` can be used to process these incoming URL requests.&#x20;

The following request types are supported:

* [OpenID4VCI Credential Offers](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-10.1.1):  via the `openid-credential-offer://` URL scheme
* [OpenID4VP Authorization Requests](https://openid.net/specs/openid-4-verifiable-presentations-1_0-21.html#section-11.1.2): via the `openid4vp://` or `openid://` URL scheme
* [Out of Band DIDComm message](https://github.com/hyperledger/aries-rfcs/tree/main/features/0434-outofband#standard-out-of-band-message-encoding): explicitly via the `didcomm://` URL scheme, or as a fallback for when other known schemes are not caught

When a URL is successfully processed and received by the agent, the agent's data state is updated, and a corresponding event may trigger (see [#subscribe-to-events](#subscribe-to-events "mention")). After this, and depending on the type of request received, the agent may be ready to respond to the request via protocol interactions (refer to later relevant sections in this documentation such as [establishing-connections](https://docs.sudoplatform.com/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/establishing-connections "mention"), [accepting-new-credentials](https://docs.sudoplatform.com/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/accepting-new-credentials "mention") & [present-credentials-for-verification](https://docs.sudoplatform.com/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/present-credentials-for-verification "mention")).

{% tabs %}
{% tab title="Swift" %}

```swift
let agent: SudoDIEdgeAgent
let requestUrl: String // string URL received from some channel (QR Code, deep link)

do {
    try await agent.receiveUrl(url: requestUrl)
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val agent: SudoDIEdgeAgent
val requestUrl: String // string URL received from some channel (QR Code, deep link)

launch {
    try {
        agent.receiveUrl(requestUrl)
    } catch (e: ReceiveUrlException) {
        // handle exception
    }
}
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
Most URLs will only be processable if the agent has their wallet storage open before receiving. Please see the [documentation](https://docs.sudoplatform.com/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/manage-wallets) on wallet management.
{% endhint %}

{% hint style="info" %}
How URL requests are captured before being passed into the  `receiveUrl` API is a responsibility of the application itself. The application may wish to register known deep link schemes for the application, or implement a QR Code scanner.

See our [sample apps](https://docs.sudoplatform.com/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/sdk-releases) as a reference.
{% endhint %}

## Receiving a DIDComm Message

With an instantiated `SudoDIEdgeAgent`, you are ready to begin receiving and processing DIDComm messages. The agent API `receiveMessage` can be used to receive supported Aries RFC DIDComm messages. Typically, the messages passed into `receiveMessage` are encrypted (Aries RFC 0019) and come from some communication channel with a peer (e.g. via a Relay or Mediator source). However, some non-encrypted messages are supported as well, most notably Connection invitations received through some other channel (e.g. out of band channels).

{% tabs %}
{% tab title="Swift" %}

```swift
let agent: SudoDIEdgeAgent
let rawMessage: Data // bytes of a raw DIDComm message received from some channel
let metadata: ReceivedMessageMetadata // metadata of the received DIDComm message. Usually generated by the message source

do {
    try await agent.receiveMessage(
        rawMessage: rawMessage,
        metadata: metadata
    )
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val agent: SudoDIEdgeAgent
val rawMessage: ByteArray // bytes of a raw DIDComm message received from some channel 
val metadata: ReceivedMessageMetadata // metadata of the received DIDComm message. Usually generated by the message source

launch {
    try {
        agent.receiveMessage(rawMessage, metadata)
    } catch (e: ReceiveMessageException) {
        // handle exception
    }
}
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
Most messages will only be processable if the agent has their wallet storage open before receiving a message. Please see the [documentation](https://docs.sudoplatform.com/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/manage-wallets) on wallet management.
{% endhint %}

The `receiveMessage` API allows for manual processing of messages to be achieved. If automatic message receiving and processing is desired, configuring a `MessageSource` and using the agent `run` method will be more fitting for this use case (see below).

## Running DIDComm Agent Management

In addition to [manually receiving DIDComm messages](#receiving-a-didcomm-message), the agent can be run in a loop to poll for and process messages that come in via a `MessageSource`. Information on `MessageSource`s and using the default provided implementations can be [found below](#message-sources).

Note that similar to `receiveMessage`, the agent must have a wallet open before starting the `run` loop. Additionally, only one `run` loop for an agent can be run at once.

The run loop performs any pre-checks and then begins the loop (polling the `MessageSource` in a background task). The API can be called like so:

{% tabs %}
{% tab title="Swift" %}

<pre class="language-swift"><code class="lang-swift">let agent: SudoDIEdgeAgent
let messageSource: MessageSource // some implementation of a MessageSource

<strong>do {
</strong>    try agent.run(messageSource: messageSource)
} catch {
    // handle error
}
</code></pre>

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val agent: SudoDIEdgeAgent
val messageSource: MessageSource // some implementation of a MessageSource

try {
    agent.run(messageSource)
} catch (e: AgentRunException) {
    // handle exception
}
```

{% endtab %}
{% endtabs %}

After an agent is running, several APIs can be used to manage the run loop. These APIs are:

* `isRunning(): Boolean` - reports whether the agent is actively running and not attempting to stop. False indicates that the loop is stopped.
* `stop()` - triggers the agent to begin its stop sequence (finishing whatever message it was processing, if any)
* `isStopping(): Boolean` - reports whether the agent is in the stop sequence (as requested via `stop()`).

## DIDComm Message Sources

The agent's `run` loop is powered by implementations of the `MessageSource` interface. As a consumer, you can choose to implement your own message source, or use our provided implementations.

To implement your own `MessageSource`, all you need to implement is 2 methods - `getMessage(): Message?` and `finalizeMessage(id: String)`. Quite simply, your implementation of `getMessage` should attempt to fetch the next available `Message` from your message source channel, and then `finalizeMessage` should take the provided `id` (from `Message.id`) and make the necessary action to 'finalize' or remove the message from the message source). The message body returned by the `getMessage` call should encrypted or unencrypted Aries DIDComm messages.

Our provided `MessageSource`s, particularly [Relay Message Source](#relay-message-source), can be used as a reference for designing your own `MessageSource`.

### Relay Message Source

To integrate the **Edge Agent SDK** with our [Sudo DI Relay SDK](https://docs.sudoplatform.com/guides/decentralized-identity/decentralized-identity/relay-sdk) , you can use the `SudoDIRelayMessageSource` implementation. The `SudoDIRelayMessageSource` can be created using the constructor:

{% tabs %}
{% tab title="Swift" %}

```swift
let relayClient: SudoDIRelayClient
let logger: Logger

let messageSource = SudoDIRelayMessageSource(relayClient: relayClient, logger: logger)

do {
    try agent.run(messageSource: messageSource)
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val relayClient: SudoDIRelayClient
val logger: SudoLogger

val messageSource = SudoDIRelayMessageSource(relayClient, logger)

try {
    agent.run(messageSource)
} catch (e: AgentRunException) {
    // handle exception
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
To get a `SudoDIRelayClient` instance, please refer to the [Sudo DI Relay SDK integration documentation](https://docs.sudoplatform.com/guides/decentralized-identity/decentralized-identity/relay-sdk/integrate-the-relay-sdk)
{% endhint %}

### Other Provided Message Sources

In addition to the relay message source, you may use a URL DIDComm message processing message source (`UrlMessageSource`) and a simple aggregator message source (`RoundRobinMultiMessageSource`).

The `UrlMessageSource` can be used to parse and queue DIDComm messages that are encoded in a URL (i.e. an [Out of Band 0434 Message](https://github.com/hyperledger/aries-rfcs/tree/main/features/0434-outofband#standard-out-of-band-message-encoding), or a [Connection Protocol 0160 Invitation](https://github.com/hyperledger/aries-rfcs/tree/main/features/0160-connection-protocol#standard-invitation-encoding)). These messages can be passed into the `queueUrlMessage` method to queue them for usage in the agent:

{% tabs %}
{% tab title="Swift" %}

```swift
let logger: Logger 
let urlEncodedMessage: String // (e.g. from scanning an invitation QR code)

let messageSource = UrlMessageSource(logger: logger)

do {
    try agent.run(messageSource: messageSource)
} catch {
    // handle error
}

do {
    try await messageSource.queueUrlMessage(url: urlEncodedMessage)
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val logger: SudoLogger 
val urlEncodedMessage: String // (e.g. from scanning an invitation QR code)

val messageSource = UrlMessageSource(logger)

try {
    agent.run(messageSource)
} catch (e: AgentRunException) {
    // handle exception
}

launch {
    try {
        messageSource.queueUrlMessage(urlEncodedMessage)
    } catch (e: QueueUrlMessageException) {
        // handle exception
    }
}
```

{% endtab %}
{% endtabs %}

The `RoundRobinMultiMessageSource` can be used to aggregate together multiple message sources. As the name implies, the implementation simply polls the `MessageSource`s it has been given in a fair round-robin approach. For more use-case specific aggregations of `MessageSource`s, a custom implementation may be more suitable.

The `RoundRobinMultiMessageSource` can be used like so:

{% tabs %}
{% tab title="Swift" %}

```swift
let urlMessageSource: UrlMessageSource
let relayMessageSource: SudoDIRelayMessageSource
let logger: Logger

let multiMessageSource = RoundRobinMultiMessageSource(logger: logger)
multiMessageSource.addMessageSource(messageSource: urlMessageSource)
multiMessageSource.addMessageSource(messageSource: relayMessageSource)

do {
    try agent.run(messageSource: multiMessageSource)
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val urlMessageSource: UrlMessageSource
val relayMessageSource: SudoDIRelayMessageSource
val logger: SudoLogger

val multiMessageSource = RoundRobinMultiMessageSource(logger)
multiMessageSource.addMessageSource(urlMessageSource)
multiMessageSource.addMessageSource(relayMessageSource)

try {
    // agent.run will call `getMessage` on the `multiMessageSource`, which will
    // alternate between calling the `urlMessageSource` and `relayMessageSource`
    // message sources it was provided in this example.
    agent.run(multiMessageSource)
} catch (e: AgentRunException) {
    // handle exception
}
```

{% endtab %}
{% endtabs %}

## Subscribe to Events

It is important for consuming applications to be aware of updates to the agent's protocol states in real-time, rather than polling for updates. Important protocol state updates and events include:

* A new incoming Connection establishment request, or an update to an existing Connection establishment protocol
* A new incoming Credential offer from a credential Issuer, or an update to an existing Credential exchange protocol
* A new incoming Credential Presentation request from a credential Verifier, or an update to an existing Presentation exchange protocol
* A new incoming basic message from a connection

These state changes can be subscribed to via the `subscribeToAgentEvents` API. This API takes an implementation of `AgentEventSubscriber`, which has methods that will be invoked on different event types.&#x20;

The `subscribeToAgentEvents` API can be called multiple times to attach many subscribers. Each call to the API will return a unique `String` identifier, which can be passed into `unsubscribeToAgentEvents` in order to remove the subscriber from receiving event updates. `unsubscribeAll` is an API that can also be used to quickly remove **all** subscribers from receiving event updates.

{% tabs %}
{% tab title="Swift" %}

```swift
class CustomSubscriber : AgentEventSubscriber {
    func connectionExchangeStateChanged(connectionExchange: ConnectionExchange) {
        // the following will be invoked whenever a connection protocol state change occurs.
        // ConnectionExchange will contain the updated state
        print("connection update: \(connectionExchange)")
    }
    func credentialExchangeStateChanged(credentialExchange: CredentialExchange) {
        // the following will be invoked whenever a credential issuance protocol state change occurs.
        // CredentialExchange will contain the updated state
        print("credential update: \(credentialExchange)")
    }
    func proofExchangeStateChanged(proofExchange: ProofExchange) {
        // the following will be invoked whenever a proof presentation protocol state change occurs.
        // ProofExchange will contain the updated state
        print("proof update: \(proofExchange)")
    }
    func inboundBasicMessage(basicMessage: BasicMessage.Inbound) {
        // the following will be invoked whenever a new basic message is received.
        print("new message: \(basicMessage)")
    }
    func messageProcessed(messageId: String) {
        // the following will be invoked whenever the agent run loop processes a new message.
        print("message processed: \(messageId)")
    }
}


let customSubscriber = CustomSubscriber()
let subscriberId = agent.subscribeToAgentEvents(subscriber: customSubscriber)
```

```swift
let subscriberId: String // captured from `subscribeToAgentEvents`
agent.unsubscribeToAgentEvents(subscriptionId: subscriberId)
```

```swift
agent.unsubscribeAll()
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val subscriber = object : AgentEventSubscriber {
    override fun connectionExchangeStateChanged(connectionExchange: ConnectionExchange) {
        // the following will be invoked whenever a connection protocol state change occurs.
        // ConnectionExchange will contain the updated state
        println("connection update: $connectionExchange")
    }
    override fun credentialExchangeStateChanged(credentialExchange: CredentialExchange) {
        // the following will be invoked whenever a credential issuance protocol state change occurs.
        // CredentialExchange will contain the updated state
        println("credential update: $credentialExchange")
    }
    override fun proofExchangeStateChanged(proofExchange: ProofExchange) {
        // the following will be invoked whenever a proof presentation protocol state change occurs.
        // ProofExchange will contain the updated state
        println("proof update: $proofExchange")
    }
    
    override fun inboundBasicMessage(basicMessage: BasicMessage.Inbound) {
        // the following will be invoked whenever a new basic message is received.
        println("new message: $basicMessage")
    }
}

val subscriberId = agent.subscribeToAgentEvents(subscriber)
```

```kotlin
val subscriberId: String // captured from `subscribeToAgentEvents`
agent.unsubscribeToAgentEvents(subscriberId)
```

```kotlin
agent.unsubscribeAll()
```

{% endtab %}
{% endtabs %}

## Managing Ledger Cache

As a decentralized identity agent, lots of read requests to ledgers to retrieve immutable data are performed. These read requests can be costly depending on the reception of nodes on the ledger network. To help reduce this cost, the agent features APIs to help management the caching of these ledger read requests. As mentioned [above](#configuration), the agent configuration contains a field to turn on or off caching of immutable ledger data. By default, caching is turned on.

If caching is being used, you can clear the cache at your own discretion via the `clearLedgerCache` API. This API will succeed regardless of whether any cache was cleared or not.

{% tabs %}
{% tab title="Swift" %}

```swift
let agent: SudoDIEdgeAgent

do {
    try await agent.clearLedgerCache()
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val agent: SudoDIEdgeAgent

launch {
    try {
        agent.clearLedgerCache()
    } catch (e: ClearLedgerCacheException) {
        // handle exception
    }
}
```

{% endtab %}
{% endtabs %}
