Skip to main content

Augmenting FDC3: selectConnect

After several years of experience working with FDC3, and in response to requests from customers, Finsemble provides a set of features that go beyond what the FDC3 Standard defines known as selectConnect. This feature set includes the ability to:

  • Controlling information flow between apps.
  • Allow apps to join multiple User channels.
  • Make Finsemble's interop easier to use by auto-associating applications with channels.
  • Allow apps external to Finsemble to connect, be authenticated and participate in FDC3 interop.
  • Allow remote, virtualized apps to interoperate with local apps.

Control Information Flow: authorize

In Finsemble information flows along channels. Any app that is connected to a channel can see anything that appears on it. This works well when you want to broadcast indiscriminately to all the apps on the channel.

In some situations you don’t want to share information or context with all the apps on the channel. Instead, you want to pick specific apps that you trust, and send that information only to them. For example, a bank might want to send some market information only to specific clients, and keep it away from the general client population. In this case, you want to deliberately allow some apps to receive restricted information while excluding others. Similarly, in some situations you don’t want to receive information from all sources. Instead, you want to specify which apps you want to hear from. To achieve this level of control over the flow of information, you must specify which apps you trust.

selectConnect is a mechanism that provides a powerful declarative interface for this kind of selective integration without coding. Whenever an app broadcasts context, raises an intent, or listens for either, selectConnect redirects the flow of information, so that only approved destinations receive the information. Context and intents can be routed through modules that act upon the interop message flow based on the trust relationships that you can define.

Important

If you don’t explicitly specify trust, you trust everyone. If you specify even one trust relationship, this automatically means that you don’t trust anyone else.

You can use selectConnect to authorize an app exchange data with a specific client application. You configure this access in your config file, no coding required. Instead, you specify rules that you want the communication to obey.

Whenever an app broadcasts a context or raises an intent, it places this information on the channel so that other apps can grab it. Similarly, when an app listens for a context or an intent and then finds one, it grabs that data for its own use. The information flows through the channel, and anyone can access it.

selectConnect restricts this free flow of information. Context and intents can be routed through modules that act upon the interop message flow, while others on the channel don’t even know the information is there.

How to specify trusted flows of communication

Add trust declarations to your config file. You can use selectConnect to specify the trusted flow of information by using the authorize rule.

authorize restricts delivery and receipt of messages to authorized endpoints only. An app using the authorize rule can both send and receive data from the apps identified by the rule; it can't send or receive data from any other app. Authorization is by app name.

Authorization restricts communication in both directions.

Example:

Consider 2 apps on the same channel, Blue and Green. You can restrict the order contexts to authorized apps. In our example, app Blue authorizes app Green so that both can exchange privileged information.

"selectConnect": [
{
"contextType": "order",
"authorize": [{"name":"Green"}]
}
]
note

The authorize rule defines the trust relationship from the perspective of one app, the one for which you specify this rule. But just because one app wants to communicate, it doesn’t necessarily follow that the other apps must allow this communication. The rule in one app is not binding for the other apps listed in that rule. They can have their own rules, or no rule at all. For example, if we specified an authorize rule for Green that doesn’t list Blue, the communication wouldn’t work.

Properties of trust relationships

The trust relationships have two very important properties. First, trust is not a two-way street. When you specify that app A trusts app B, it doesn’t automatically follow that now app B also trusts app A. If you want the apps to trust each other, you must specify this for each app separately. Also, because all trust relationships must be explicitly declared, trust is not transitive. In other words, if app A can send messages to app B, and app B can send messages to app C, it doesn’t follow that now app A can send messages to app C. If you do want app A to be able to send messages to app C, you must say so explicitly.

When to use

Think of selectConnect as a mechanism to limit authorization. By specifying that you trust an app, you authorize this app to receive restricted information. This is helpful in situations where a company has two different sets of customers. For example, one set includes everyone, and another includes most valued customers only. If you establish trust with the most valued customers, you can send specific information to those customers only. The general public doesn’t get to see this valuable information.

Pitfalls

The trust mechanism is powerful, but you need to be aware of it at all times. If you forget that an app now trusts (or doesn’t trust) another app, you can spend many hours debugging, thinking you have a channel problem. The biggest source of headaches is forgetting to update or check your trust declarations.

Make sure your trust is set up correctly. It’s easy to define that A authorizes B and then forget to check if B authorizes A. Don’t expect this trust relationship to run both ways and verify that you defined it correctly. If you raise an intent for an unauthorized app, that app won’t get the intent. There is no error message. If you have an app that doesn’t receive intents and you expect that it should, verify the trust specification for that app.

Join multiple channels

The FDC3 Standard currently states that an app can only be joined to one User channel at a time.

However, a number of Finsemble clients have shared with us use cases that are most easily solved by allowing applications to join multiple channels.

For example, a sales trader is working multiple orders and has multiple copies of several apps open, which are set up to work together as a group. The trader has joined thesse groups of apps to different channels so that they can share context within their group, but not across groups. The user also has other tools such as visualisations or a market data apps or terminals that can only run a single instance at a time or that they can't spare the screen real-estate to run multiple instances of. These tools need to react to multiple other groups of components. To do so, they need to be able to join multiple channels.

Finsemble allows apps to join multiple channels, which makes it possible to receive and broadcast context to and from all of the channels they are connected to. This is a non-standard behavior, which you can disable in configuration.

To disable multiple channel membership for a single application, set hostManifests.Finsemble.interop.joinMultipleChannels in its AppD record to false.

Connect automatically: autoAssociate

Apps can automatically form or join an association, which we define as a set of apps that are joined to the same FDC3 channel. You typically specify an association though AppD config, so you don't need to do much coding. But you can also specify it programmatically. Associations are usually dynamically created at run-time. This means that multiple instances of an app, and all other apps spawned by an instance, will join different associations unless you specify otherwise.

Using AppD config, the selectConnect property specifies an association for a set of apps that are typically children of an app instance. You normally control auto-associating apps though AppD config using the autoAssociate property of selectConnect. The supported values are:

  • "allChildren": Set this property to true if you want all apps spawned or opened by this app to automatically join this app's association.
  • "selectChildren": The apps in this list automatically join this app’s association when this app spawns or opens them. The apps not on the list will not join the association even though they were spawned by the same parent app.
  • "usingConnectId": specifies a fixed association for this app based on the connectId . All apps with this specific connectId will automatically join the same association. You rarely need this property because Finsemble dynamically sets the connectId for each instance of an app and its children, but you can specify it to create fixed associations. You can use it alone, and you can also use it together with "allChildren" or "selectChildren" to establish a fixed association for all instances of an app and its children.

Here's an example of an AppD config for an app that sets autoAssociate for all children the app spawns:

{
...
"hostManifests": {
"Finsemble": {
...
"interop": {
"selectConnect": [
{
"autoAssociate": {
"allChildren": true
}
}
]
},
...
},
...

Here's an example of an AppD config for an app that sets autoAssociate for specific children that the app spawned. In this case we have only "ChartIQ Example App", but you can have however many you want.

{
...
"hostManifests": {
"Finsemble": {
...
"interop": {
"selectConnect": [
{
"autoAssociate": {
"selectChildren": [
"ChartIQ Example App"
]
}
}
]
},
...
},
...

Here's an example of an AppD config for an app that specifics a static connectId with usingConnectId. Other apps specifying this same connectId ("connectId-A") will be associated together.

{
...
"hostManifests": {
"Finsemble": {
...
"interop": {
"selectConnect": [
{
"autoAssociate": {
"usingConnectId": "connectId-A"
}
}
]
},
...
},
...

Here's an example of an AppD config for an app that specifies particular children and a static connectId with usingConnectId. This config specifies all "Chartiq Example App" apps that this app spawns will be associated with this app. Also, because we specified a static connectId, all instances of this app and selected children will be in the same association and share the same channel.

{
...
"hostManifests": {
"Finsemble": {
...
"interop": {
"selectConnect": [
{
"autoAssociate": {
"selectChildren": [
"ChartIQ Example App"
],
"usingConnectId": "connectId-A"
}
}
]
},
...
},
...

If a parent's selectConnect config conflicts with a child's selectConnect config, the parent's config takes precedence.

Launching multiple instances of an app

Consider an app, App-A, that has its AppD selectConnect config set to automatically associate all children. The first instance of App-A, and all of its children will be joined to Channel-1. But when we launch this app again, the second instance of App-A and all of its children will be joined to another channel, Channel-2. We can of course launch such an app multiple times, and each time it will join a different unused channel.

You can launch many instances of an app and the association works as expected as long as there are channels available. If there are no more channels, no more associations can form. When this happens, Finsemble logs an error and displays a user notification.

Let's change our configuration a bit and have App-A specify a fixed connectId with "usingConnectId". Now, all instances of App-A and all the children of those instances will be joined to the same channel, Channel-1 in our example.

Here's how to create an associations by specifying a connectId as a spawn parameter. This approach to creating associations between apps is not as straightforward as using the AppD selectConnect option, but it provides more flexibility.

await FSBL.Clients.LauncherClient.spawn("ChartIQ Example App", { connectId: "connectId-B" );

App associations apply to apps started from the Finsemble menu, apps defined in a LaunchGroup, apps programmatically spawned, or apps opened though FDC3 by using fdc3.open or fdc3.raiseIntent. App associations also apply to native apps known to Finsemble.

When to use which association

Use "allChildren" for a LaunchGroup or high-level app when you want all the children to communicate through the common channel. One example is when the set of associated apps coordinate within a workflow.

Use"selectChildren" when you want to exclude some children from the communication. This might be useful when you want to exclude some apps from communicating with each other. Or perhaps the unselected apps communicate outside the association with usingConnectId. For example, you can have an app launch an app that sends info to a client one time only. You don't want such an app to participate in the entire communication on the channel.

You will rarely need to use "usingConnectId". Normally Finsemble dynamically creates a connectId, but you can provide your own. You can use this parameter to form static associations independent from any parent app. For example, if two singleton apps communicate through a common channel, setting usingConnectId to the same value will result in a shared channel. Another use is when you want multiple apps to use the same channel. You can specify "usingConnectId" together with "allChildren" or "selectChildren".

Authenticate external apps

The Supported Platforms page of the FDC3 Standard states:

For a (web) application to be FDC3-enabled, it needs to run in the context of an environment or Platform Provider that makes the FDC3 API available to the application. This environment could be a browser extension, a web or native app, or fully-fledged desktop container framework.

In other words, you can write applications to expect a copy of the FDC3 Desktop Agent API to be injected onto the window global for them to use, as described under API Access. This requires the application to reside within the relevant container or browser extension etc. This is a very important requirement within the Standard. It negates the need to include vendor-specific libraries or code within an app, ensuring that it targets the Standard rather than the specific vendor, and it can be expected to work in the same way on any FDC3-compliant Destkop Agent.

note

The FDC3 standard doesn't define wire formats for communication, nor does it define language-specific API bindings other than JavaScript and TypeScript. Hence, for a native application to be FDC3-enabled, it needs to make use of a shared library (such as a .NET DLL or JAR file) that provides it with an implementation of the FDC3 API. Therefore, an FDC3-enabled native application is currently specific to a particular desktop container framework (or other suitable environment) that provides the necessary library.

Despite this limitation, implementing support for FDC3 in a native application can allow such apps to interact with a wide variety of FDC3-enabled web applications.

Although Finsemble can launch and manage a wide variety of applications and technologies, running within it's container is not always possible or desirable. For example:

  • A legacy, monolithic native application might need to interact with newer tools. But the team responsible for it wants to keep changes to the application minimal so that they can focus on creating newer tools to replace it in the near future.
  • A native application embeds a WebView that loads a JavaScript application. The application is runnig within the WebView rather than within the container.

To handle these situations, Finsemble offers support for 'freestanding applications', which are apps that Finsemble didn't start, but that want to connect into Finsemble to share context by using FDC3.

You can connect freestanding apps to Finsemble by using an adapter such as the JavaScript adapter or one of the Finsemble native adapters for .NET or Java. When you use an adapter to connect an app to Finsemble, it identifies itself with an appId which is used to retrieve a configuration in the form of an AppD record. To avoid arbitrary or unauthorized apps from using a configuration to connect, it can contain a signatureKey, or a URL from which one may be retrieved, representing the public key portion of a key pair. If this key is present in the appD record, the application connecting must also provide an authentication token, signed with the private portion of the key pair, thereby authenticating itself.

For more details about how to authenticate apps, see the Authenticating apps tutorial.

Virtual app interop

Most applications on your user's desktop are running locally. However, some applications run in a virtual environment hosted elsewhere on premises or in the cloud, completely separate from the local system and facilitated by technologies such as Citrix Workspace. Images of their application windows are streamed to the local device and details of user interactions with them sent back to be applied. Virtualized apps have a number of advantages, such as reducing the load on the local system or allowing a vendor to manage the installation without access to the local machine.

Whatever the reason for virtualization, it presents a problem for application interop by the front-end integration style enabled by FDC3. This is because the application is not running locally and instead it is primarily runnig within the context of the remote virtualization environment rather than the desktop agent that provides interop services.

Finsemble solves this problem by using a Microsoft Terminal Services plug-in that allows remote applications to connect to Finsemble and to participate in FDC3 Interop.

For details of how to connect your virtualized applications to Finsemble and participate in FDC3 interop, see Setting up the virtual app interop.

See also