> For the complete documentation index, see [llms.txt](https://docs.sudoplatform.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.sudoplatform.com/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/establishing-connections.md).

# Establishing Connections

In Decentralized Identity, establishing an end-to-end encrypted communication channels with a peer is an essential step before receiving or proving credentials via DIDComm/Aries. As such, the agent's `ConnectionExchangeModule` provides all functionality needed to establish those connections with peers, and manage pending connections.

The functionality of the `ConnectionExchangeModule` is accessed via the `agent`'s fields: `agent.connections.exchange`. The functionality provided is described below.

## Receive a Connection Invitation

A `ConnectionExchange`, representing a pending connection, will be created by the agent once a [connection invitation](https://github.com/hyperledger/aries-rfcs/tree/main/features/0434-outofband) is received. To have the agent 'receive' an invitation message, this can be achieved in multiple ways. An invitation JSON payload message can be pre-processed and passed to the agent via the `receiveMessage` [API](/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/agent-management.md#receiving-a-message). Or, if the invitation is in its URL form (as it typically is when scanned from a QR Code), our [URL Message Source](/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/agent-management.md#other-provided-message-sources) can be used to process and receive that payload.

After an invitation is received by the agent, a `ConnectionExchange` is created in the agent's wallet and can be accessed and used by the other APIs mentioned below. If the agent is [subscribed to agent events](/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/agent-management.md#subscribe-to-events), a new `ConnectionExchange` will invoke a connection update event.

A `ConnectionExchange` created as a result of receiving an invitation will have a role of `INVITEE`.

## Create a Connection Invitation

Alternatively, the Edge Agent can *create* connection invitations to be received by other agents. The module's `createInvitation` API can be used to create connection invitations. This API takes a configuration, which has variants depending on the format of the invitation which should be created.

After calling the `createInvitation` API, a `CreatedInvitation` data structure will be returned, which contains the created invitation URL encoding, and the newly created `ConnectionExchange` in the `INVITATION` state. The ID of this `ConnectionExchange` object can be used to track updates in it's state (e.g. an incoming connection request from a peer who receives the invitation).

A `ConnectionExchange` created as a result of creating an invitation will have a role of `INVITER`.

### Creating an Out of Band Invitation

To create a pairwise single-use Aries Out of Band Connection Invitation ([RFC 0434](https://github.com/hyperledger/aries-rfcs/tree/main/features/0434-outofband)), the `createInvitation` API should be used with the "pairwise" variant of the `CreateInvitationConfiguration` input configuration. This configuration takes the following input:

* A `Routing` object, which is an item that the agent will use to inform the peer about how this agent should be contacted. This object notably includes a `serviceEndpoint` (a HTTP endpoint where the peer should send DIDComm messages to), and an optional list of `routingVerkeys`, which (if using a mediator) describes what layers of encryption the peer should wrap their messages in. If the agent is being used with the [Sudo DI Relay](/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/agent-management.md#relay-message-source), then a [relay postbox](/guides/decentralized-identity/decentralized-identity/relay-sdk/manage-relay-postboxes.md) can be used to create a `Routing` for that `Postbox` ([see below](#creating-a-connection-route-from-the-sudo-relay-sdk)). If a custom message source is being used, `Routing` will need to be manually constructed.
* `overrideLabel` - a connection label which the inviter should present to the peer as a nickname for themself. If this input is provided, it will override the globally configured label.
* `overrideBaseUrl` - the base URL for the encoded invitation URL (see[ RFC 0160](https://github.com/hyperledger/aries-rfcs/tree/main/features/0434-outofband)). If this input is provided, it will override the globally configured invitation base URL.

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

```swift
let agent: SudoDIEdgeAgent
let routing: Routing
let label = "Alice"
let baseUrl = "https://example.com"
    
do {
    let createdInvitation = try await agent.connections.exchange.createInvitation(
        configuration: .pairwise(
            routing: routing,
            overrideLabel: label,
            overrideBaseUrl: baseUrl
        )
    )
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val agent: SudoDIEdgeAgent
val routing: Routing
val label = "Alice"
val baseUrl = "https://example.com"

try {
    val createdInvitation = agent.connections.exchange.createInvitation(
        CreateInvitationConfiguration.Pairwise(
            routing = routing,
            overrideLabel = label,
            overrideBaseUrl = baseUrl
        )
    )
} catch (e: ConnectionExchangeModule.CreateInvitationException) {
    // handle error
}
```

{% endtab %}
{% endtabs %}

{% hint style="danger" %}
Aries AIP1 Connection Invitations ([RFC 0160](https://github.com/hyperledger/aries-rfcs/tree/main/features/0160-connection-protocol)) have been superseded by AIP2 invitations and are now deprecated. However they can still be created using the `.LegacyPairwise` variant of the configuration used above. Deprecated APIs will be removed eventually, so please migrate to use AIP2 invitations as seen above.
{% endhint %}

## Accepting a Connection

Once a connection invitation is received (`INVITATION` state in the `INVITEE` role), or a connection request is received in response to a connection created by the Edge Agent (`REQUEST` state in the `INVITER` role) the agent has different options for how they can "accept" that connection.

The agent may choose to create a new connection ([#establish-a-new-connection](#establish-a-new-connection "mention")) or, in certain situations as an Invitee, the connection may be *reused* to avoid creating a duplicate connection with a peer ([#reusing-an-existing-connection](#reusing-an-existing-connection "mention")).

### Establish a New Connection

The agent can "accept" an exchange by creating a new connection. This will begin the next steps of the Aries protocol to establish an end-to-end encrypted channel with that peer.

{% hint style="info" %}
Note that after calling the `acceptConnection` API as an invitee, the connection is not fully established immediately, further messages between the peer and agent are sent in the background. The status updates of the establishment process can be tracked via [agent events](/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/agent-management.md#subscribe-to-events).
{% endhint %}

To accept a new connection, the `acceptConnection` API is used to with a `AcceptConnectionConfiguration.NewConnection` configuration variant. The configuration takes the following:

* A `Routing` object, which is an item that the agent will use to inform the peer about how this agent should be contacted. This object notably includes a `serviceEndpoint` (a HTTP endpoint where the peer should send DIDComm messages to), and an optional list of `routingVerkeys`, which (if using a mediator) describes what layers of encryption the peer should wrap their messages in. If the agent is being used with the [Sudo DI Relay](/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/agent-management.md#relay-message-source), then a [relay postbox](/guides/decentralized-identity/decentralized-identity/relay-sdk/manage-relay-postboxes.md) can be used to create a `Routing` for that `Postbox` ([see below](#creating-a-connection-route-from-the-sudo-relay-sdk)). If a custom message source is being used, `Routing` will need to be manually constructed.
* Optionally, a `PeerConnectionConfiguration` object. Which includes configuration for how the agent should present itself to the peer. Any provided configuration here will override the global setting for this specific connection.

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

```swift
let connectionExchangeId: String // The identifier of the [ConnectionExchange]
let routingVerkeys: [String] // any additional routing verkeys that 
// the peer should encrypt their messages with before sending.
// In the case of the sudo relay, this is an emptyList(), whereas mediator 
// services may specify routing keys.

let routing = Routing(serviceEndpoint: "https://sample.endpoint.com/abc", routingVerkeys: routingVerkeys)

do {
    try await agent.connections.exchange.acceptConnection(
        connectionExchangeId: connectionExchangeId,
        configuration: .newConnection(
            routing: routing,
            peerConfiguration: PeerConnectionConfiguration()
        )
    )
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val connectionExchangeId: String // The identifier of the [ConnectionExchange]
val routingVerkeys: List<String> // any additional routing verkeys that 
// the peer should encrypt their messages with before sending.
// In the case of the sudo relay, this is an emptyList(), whereas mediator 
// services may specify routing keys.

val routing = Routing("https://sample.endpoint.com/abc", routingVerkeys)

try {
    agent.connections.exchange.acceptConnection(
        connectionExchangeId,
        AcceptConnectionConfiguration.NewConnection(
            routing, 
            PeerConnectionConfiguration()
        )
    )
} catch (e: ConnectionExchangeModule.AcceptConnectionException) {
    // Handle exception
}
```

{% endtab %}
{% endtabs %}

### Reusing an Existing Connection

If a `ConnectionExchange` is in the invitation state as an invitee (recipient of an invitation), the agent SDK will determine if there are already established connections with the inviter. If there are, then the `reusableConnectionIds` field of the `ConnectionExchange` will be populated with a list of established `Connection` IDs (see [Manage Connections](/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/manage-connections.md)) that were detected to originate from the same Inviter.

When a `ConnectionExchange` is populated with `reusableConnectionIds`, then the desired ID from this list may be used to attempt a "connection reuse". Doing so will request that the inviter use the desired original connection instead of trying to establish a new connection, the exchange now enters the "reuse request" state. Once/if the peer responds, the exchange will complete; successfully re-using the connection. If the peer does not respond, a new connection can be established with this exchange instead ([#establish-a-new-connection](#establish-a-new-connection "mention")).

To accept a connection via reuse, the `acceptConnection` API is used to with a `AcceptConnectionConfiguration.ReuseExistingConnection` configuration variant. The configuration takes the following:

* `connectionId`, the ID of the `Connection` to request reuse from

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

```swift
let connectionExchangeId: String // The identifier of the [ConnectionExchange]
let connectionId: String // the identifier of the [Connection] to request reuse

do {
    try await agent.connections.exchange.acceptConnection(
        connectionExchangeId: connectionExchangeId,
        configuration: .reuseExistingConnection(connectionId: connectionId)
    )
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val connectionExchangeId: String // The identifier of the [ConnectionExchange]
val connectionId: String // the identifier of the [Connection] to request reuse

try {
    agent.connections.exchange.acceptConnection(
        connectionExchangeId,
        AcceptConnectionConfiguration.ReuseExistingConnection(connectionId)
    )
} catch (e: ConnectionExchangeModule.AcceptConnectionException) {
    // Handle exception
}
```

{% endtab %}
{% endtabs %}

### Creating a Connection Route from the Sudo Relay SDK

When integrating with the [Sudo Relay SDK](/guides/decentralized-identity/decentralized-identity/relay-sdk.md) (i.e. via the [relay message source](/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/agent-management.md#relay-message-source)), the `Routing` for the `acceptConnection` API can be easily created with the `routingFromPostbox` helper API on the relay message source.

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

```swift
let connectionExchangeId: String // The identifier of the [ConnectionExchange]
let postbox: Postbox // A new postbox created by a sudo relay client.

let relayRouting = SudoDIRelayMessageSource.routingFromPostbox(postbox: postbox)

do {
    try await agent.connections.exchange.acceptConnection(
        connectionExchangeId: connectionExchangeId,
        configuration: .newConnection(routing: relayRouting)
    )
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val connectionExchangeId: String // The identifier of the [ConnectionExchange]
val postbox: Postbox // A new postbox created by a sudo relay client.

val relayRouting = SudoDIRelayMessageSource.routingFromPostbox(postbox)

launch {
    try {
        agent.connections.exchange.acceptConnection(
            connectionExchangeId,
            AcceptConnectionConfiguration.NewConnection(relayRouting)
        )
    } catch (e: ConnectionExchangeModule.AcceptConnectionException) {
        // Handle exception
    }
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Please refer to the [Sudo Relay SDK](/guides/decentralized-identity/decentralized-identity/relay-sdk.md) documentation for more information about creating Postboxes and more.
{% endhint %}

{% hint style="warning" %}
To avoid any chance of peers correlating different connections to a specific relay endpoint, it is recommended that a new `Postbox` (and therefore new `Postbox.serviceEndpoint` and corresponding `Routing`) is created per connection.
{% endhint %}

## Get a Pending Connection by ID

As mentioned above, a pending connection is represented by `ConnectionExchange` objects. To retrieve the current state of a specific `ConnectionExchange` in the agent's wallet, the `getById` API can be used. If a connection exchange cannot be found by the given ID, then `null` is returned:

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

```swift
let id: String // id of the ConnectionExchange

do {
    let connectionExchange = try await agent.connections.exchange.getById(connectionExchangeId: id)
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val id: String // ID of the connection exchange to get ([ConnectionExchange.connectionExchangeId])

launch {
    try {
        val connectionExchange = agent.connections.exchange.getById(id)
    } catch (e: ConnectionExchangeModule.GetException) {
        // Handle exception
    }
}
```

{% endtab %}
{% endtabs %}

## Delete a Pending Connection by ID

Similarly, a `ConnectionExchange` in the wallet can be easily deleted via the `deleteById` API:

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

```swift
let id: String // id of the ConnectionExchange

do {
    try await agent.connections.exchange.deleteById(connectionExchangeId: id)
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val id: String // ID of the connection exchange to delete ([ConnectionExchange.connectionExchangeId])

launch {
    try {
        agent.connections.exchange.deleteById(id)
    } catch (e: ConnectionExchangeModule.DeleteException) {
        // Handle exception
    }
}
```

{% endtab %}
{% endtabs %}

## Updating the Metadata of a Pending Connection

`ConnectionExchange` objects contain some metadata that can be controlled by SDK consumers, allowing custom information to be attached to each `ConnectionExchange`, and allowing custom [listing functionality](#listing-pending-connections) to be leveraged.

Each `ConnectionExchange` contains a list of `RecordTag` (`ConnectionExchange.tags`) attached to it, where a `RecordTag` is simply a name-value pair stored with the record. By default, some tags are attached to a new `ConnectionExchange`, this includes:

* tag-name: `~started_timestamp`
* tag-value: *The UNIX epoch seconds which this connection began establishment*

The tags on a `ConnectionExchange` can be replaced or updated by using the `updateConnectionExchange` API, and providing a new set to update. This will replace whatever the current set of tags is:

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

```swift
let id: String // ID of the connection exchange to update

// add a 'category' of 'work' to this connection, and a 'priority' of '1'
let update = ConnectionExchangeUpdate(tags: [
    RecordTag(name: "category", value: "work"),
    RecordTag(name: "~priority", value: "1")
])

do {
    try await agent.connections.exchange.updateConnectionExchange(connectionExchangeId: id, connectionExchangeUpdate: update)
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val id: String // ID of the connection exchange to update ([ConnectionExchange.connectionExchangeId])

// add a 'category' of 'work' to this connection, and a 'priority' of '1'
val update = ConnectionExchangeUpdate(
    tags = listOf(
         RecordTag("category", "work"),
         RecordTag("~priority", "1")   
    )
)

launch {
    try {
        agent.connections.exchange.updateConnectionExchange(id, update)
    } catch (e: ConnectionExchangeModule.UpdateException) {
        // Handle exception
    }
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Like most data in the wallet, `RecordTag` will be stored encrypted. **Unless**, the tag name is prefixed with `~`, then the tag value will be stored unencrypted. Storing a tag value as unencrypted will allow some additional listing queries to be performed ([see below](#listing-pending-connections)).
{% endhint %}

## Listing Pending Connections

To list all pending connections in the agent's wallet, the `listAll` API can be used:

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

```swift
do {
    let conns = try await agent.connections.exchange.listAll(options: nil)
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
launch {
    try {
        val conns: List<ConnectionExchange> = agent.connections.exchange.listAll(null)
    } catch (e: ConnectionExchangeModule.ListException) {
        // Handle exception
    }
}
```

{% endtab %}
{% endtabs %}

### Filtered Listing

More complicated `ConnectionExchange` list queries can also be achieved by utilizing the `ListConnectionExchangeFilters`.

These filters allow for the list of `ConnectionExchange` to be filtered by their `state`, `tags`, or both together.

Filtering by `state` can be achieved as follows:

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

```swift
// get all the pending connections which are waiting for their invitation to be accepted
let filters = ListConnectionExchangeFilters(state: ConnectionExchangeState.invitation)
let options = ListConnectionExchangeOptions(filters: filters)
do {
    let conns = try await agent.connections.exchange.listAll(options: options)
} catch {
    // handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// get all the pending connections which are waiting for their invitation to be accepted
val filters = ListConnectionExchangeFilters(state = ConnectionExchangeState.Invitation)
val options = ListConnectionExchangeOptions(filters)
        
launch {
    try {
        val connectionsPendingInvitationAcceptance = agent.connections.exchange.listAll(options)
    } catch (e: ConnectionExchangeModule.ListException) {
        // Handle exception
    }
}
```

{% endtab %}
{% endtabs %}

To filter by `tags` applied to the `ConnectionExchange` (i.e. applied [via the update API](/guides/decentralized-identity/decentralized-identity/edge-agent-sdk/establishing-connections.md#updating-the-metadata-of-a-pending-connection)), the `tagFilter` field of `ListConnectionExchangeFilters` should be used. This field takes a `String` in compliance with a [Wallet Query Language (WQL)](https://hyperledger-indy.readthedocs.io/projects/sdk/en/latest/docs/design/011-wallet-query-language/README.html) Query.

Continuing from the example in the [Update section](#updating-the-metadata-of-a-pending-connection):&#x20;

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

```swift
// WQL Query, filter for 'category' == 'work'
let wqlQuery = "{ \"category\": \"work\" }"
let filters = ListConnectionExchangeFilters(tagFilter: wqlQuery)
let options = ListConnectionExchangeOptions(filters: filters)
do {
    let pendingWorkConnections = try await agent.connections.exchange.listAll(options: options)
} catch {
    // handle error
}
```

```swift
// WQL Query, filter for priority < 2 (e.g. 'high' priority items)
let wqlQuery = "{ \"~priority\": { \"$lt\": \"2\" } }"
let filters = ListConnectionExchangeFilters(tagFilter: wqlQuery)
let options = ListConnectionExchangeOptions(filters: filters)

do {
    let highPriorityPendingConnections = try await agent.connections.exchange.listAll(options: options)
} catch {
    // Handle error
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// WQL Query, filter for 'category' == 'work'
val wqlQuery = """{ "category": "work" }"""
val filters = ListConnectionExchangeFilters(tagFilter = wqlQuery)
val options = ListConnectionExchangeOptions(filters)

launch {
    try {
        val pendingWorkConnections = agent.connections.exchange.listAll(options)
    } catch (e: ConnectionExchangeModule.ListException) {
        // Handle exception
    }
}
```

```kotlin
// WQL Query, filter for priority < 2 (e.g. 'high' priority items)
val wqlQuery = """{ "~priority": { "${"$"}lt": "2" } }"""
val filters = ListConnectionExchangeFilters(tagFilter = wqlQuery)
val options = ListConnectionExchangeOptions(filters)

launch {
    try {
        val highPriorityPendingConnections = agent.connections.exchange.listAll(options)
    } catch (e: ConnectionExchangeModule.ListException) {
        // Handle exception
    }
}
```

{% endtab %}
{% endtabs %}
