Support for Multiple Application Scopes (Agents) within a single window process
Enhancement Request
This enhancement is looking at the problem from a web perspective but could be extended to other implementing technologies.
Use Case:
As more web applications adopt micro-frontend architecture, users often encounter scenarios where multiple application contexts need to be loaded into a single window process through dynamic code resolution techniques like module federation. Currently, the FDC spec assumes a one-to-one mapping between Window, Agent, and Application, which limits the ability to handle multiple application contexts within a single window. This creates challenges for users who need to interact with different application scopes seamlessly within the same window environment.
Workflow Description
The proposed enhancement involves updating the FDC spec to support the creation of Agents with different application scopes within a single window process. This would allow multiple micro-frontends applications to coexist and interact within the same window, providing a more flexible and efficient user experience. The workflow should enable dynamic resolution and management of multiple Agents, each corresponding to different applications, within a single window context.
Workflow Examples
Multi-tool Dashboard
- A user accesses a dashboard that integrates multiple tools (e.g., a calendar, email client, and task manager) using micro-frontend architecture.
- Each tool operates as a separate application deployed remotely from the container however will share the context within the same window.
- The enhanced FDC spec allows each tool to create and manage its own Agent within the same window, enabling seamless interaction and communication between the tools.
Additional Information
Supporting the creation of Agents with different application scopes within a single window process would significantly enhance the flexibility and usability of applications using micro-frontend architecture. This enhancement would reduce the need for redundant window processes, streamline resource management, and improve the overall user experience by enabling more dynamic and integrated interactions between multiple application contexts.
Many thanks for raising the issue @Davidhanson90.
There is a proposal to resolve this use case (in both Electron-style containers and on the web) in the FDC3 for Web Browsers proposal: https://tick42-my.sharepoint.com/:w:/g/personal/finsemble_datastore_interop_io/EZ0dfTCdRlJCnIF3C_1Oit0BF3fsXyvlMbisXp722DC9Kg?e=dRVqQm (under the heading 'Sub-application API Proposal (use case 4)').
To give a basic overview, it proposes that the following function is added to the FDC3 API:
/**
* @param {string} params Required parameters object, which must include
* at least a fully qualified appId or appD URL for the application to
* indicate the app's identity.
* @param {string} params.appId The fully qualified appId
* (in the form appid@<appd origin> for the application
* @param {URL} params.appDUrl The URL to the appD record providing the
* app's identity, used as an alternative to a fully qualified appId.
*
* @return A promise that resolves to a DesktopAgent implementation or
* rejects if the app identity could not be validated.
*/
type getSubAgent = (
params: GetSubAgentParams,
): Promise<DesktopAgent>
type GetSubAgentParams = {
appId?: string,
appDUrl?: URL
}
and that the window acquires a copy of the API as if it were itself an application, perhaps making it available globally at window.fdc3 if not already present, then the sub-applications call this function to identify themselves with details of an appD record (which might express intents that it can resolve) and then retrieve their own copy.
Please feel free to comment on the proposal or to propose changes - it hasn't had as much attention as the other parts of the proposal, although it builds on some of the same approaches (for example the need for additional identity validation steps).
Ok thanks for this, it does indeed look like the solution to the problem stated.
I very much want to also bring this into fdc3-dotnet as I feel it will solve alot of problems with native apps built through composition to address appid/instance at the module or widget level.
If I'm reading this right, we're basically saying that one application can exist in multiple windows?
What are the implications for delivering intents or broadcast contexts?
If I'm reading this right, we're basically saying that one application can exist in multiple windows?
@robmoffat No, multiple applications in the same window and allowing them to communicate with each other over FDC3 in the same way as they would with apps running in other windows.
Some thoughts on this. Some questions that we will have to answer when we come to implement this:
- Will a sub agents appear to the other applications as separate instances? In other words will sub agents be listed separately in an app resolver ui?
- Related to the above will subAgentOne AND subAgentTwo be able to register the same intent listener (to do this they would have to be listed separately in the app resolver UI)
Hi @Roaders, my 2p below:
- Will a sub agents appear to the other applications as separate instances? In other words will sub agents be listed separately in an app resolver ui?
I believe this to be one of two key requirements of the proposal:
- Sub instances should be able to register their own intent listeners (and as only one listener can answer a raised Intent they need to be individually addressable)
- Messages broadcast by a sub-instance within a window receive broadcasts sent by other sub-instances (but not their own as we do not deliver back to the sender).
If you can't route intents to sub instances-specifically then they could just be using the window's FDC3 instance and would not need their own...
- Related to the above will subAgentOne AND subAgentTwo be able to register the same intent listener (to do this they would have to be listed separately in the app resolver UI)
They would have to be able to in order to achieve the first point. You can only register a single listener for each intent through each instance of the DesktopAgent interface (which this open PR will clarify in the documentation https://github.com/finos/FDC3/pull/1394 - this has always been the case, we're just making it clearer in the documentation. We'd be allowing the creation of multiple interfaces in a window but each could only register a single listener per intent type.
Ok, that all makes sense. It seems that sub agents are fully self contained agents that have all the features of a normal agent. The only difference is that there can be multiple in one window.
A couple more questions:
- Does a normal agent have to be created in the window using
getAgent()before a sub agent is created - I would guess not. - When a new window is opened with
desktopAgent.open()and if the window has multiple sub agents registered (with or without a normal agent) which instance id is returned from thewindow.open()? - It is now possible to have one "app" with multiple agents in. This means that one app can be joined to several different user channels. I assume there is nothing proposed to keep all sub agents joined to the same user channel?
@Roaders if you look further up in this thread theres a proposal to add a function to the DesktopAgent interface above that would create a sub-instance. Hence:
Does a normal agent have to be created in the window using getAgent() before a sub agent is created - I would guess not.
As proposed above, yes you would need to create a window-level instance and then use it to create sub-instances. I don't see a way around that without breaking both types of Desktop Agent interface (Desktop Agent Preload and Desktop Agent Proxy).
That proposal is a little out of date as FDC3 for the web has moved away from apps providing details of their own identity via appId, they now pass a URL and ask the DesktopAgent to assign the appId (a change that I believe was short-sighted). However, for a sub-agent they would either have to pass an appId or appD URL (as per the original fdc3 for web proposal) to connect themselves to a configuration to provide at least metadata on intent/context pairs they listen for.
If they don't work this way (calling a new standard function on the window-level DesktopAgent), a new interface will need to established in the Standard to accomplish this without tieing apps to a particular implementation - and I suspect that this would add significant complexity.
It is now possible to have one "app" with multiple agents in. This means that one app can be joined to several different user channels. I assume there is nothing proposed to keep all sub agents joined to the same user channel?
Again if this were the case you would need sub-agents - you'd just listen at the window level and farm the events out to each sub-application. Looks at @Davidhanson90's terminology above, each sub-agent is relating to a specific app scope and identity. Hence, there is a parent 'app' that owns the main instance and might be joined to a particular channel, and 'sub-apps' each with their 'sub-instance' that could each be joined to a different channel? Perhaps thats an area of the proposal that needs development.
I had missed that the getSubAgent function was to be added to DesktopAgent. I had thought that it would be a function at the same level as getAgent()
That could be an alternative proposal - but its going to add a complexity to the Desktop Agent Proxy interface (as multiple interfaces would be trying to message over the same window.postMessage - not impossible however) and would need a wholesale alternative to the Desktop Agent Preload interface used in containers (as it only injects one interface)... That would necessarily be a much bigger set of changes.
I might have missed some discussion on this then. Are there any documents on this?
It sounds like only 1 interface (the desktop agent created by getAgent()) will communicate from a given window. Sub agents will then be created by the DesktopAgentProxy (or root DesktopAgent in the case of the root window) and the communication for those will go through the same channel setup by the DesktopAgentProxy. Is that correct?
This could cause some headaches. We have assumed in our implementation that all messages received over 1 message channel are from a single interface instance. If we now have multiple instances communicating over the same channel we will have to use a different method to determine the source of the message.
I might have missed some discussion on this then. Are there any documents on this?
There was a small amount of discussion in FDC3 for Web meetings and a proposal for web-based DA's worked up in the shared doc (starting on Page 18): https://tick42-my.sharepoint.com/:w:/g/personal/finsemble_datastore_interop_io/EZ0dfTCdRlJCnIF3C_1Oit0BF3fsXyvlMbisXp722DC9Kg?e=e0e53o
It sounds like only 1 interface (the desktop agent created by getAgent()) will communicate from a given window. Sub agents will then be created by the DesktopAgentProxy (or root DesktopAgent in the case of the root window) and the communication for those will go through the same channel setup by the DesktopAgentProxy. Is that correct?
Not quite, the window-level agent would be created and connected by something. A sub-application (or parent app creating said sub-app) would then request an additional instance via an API call - directly using the the first interface. The DA returns an additional MessagePort in its response, which it then communicates with like it does any other message port. I.e. a second connection gets created through the first connection, thereafter they are independent.
Hopefully, that solved your issue above. However, its just a proposal at this stage - but it did seem the least complex solution at the time!
ok, so there is no additional handshake process, just a message to return a new channel. That makes the communication easier... How does this work if agent => proxy communication is done over an iframe?
Basically, the WCP connection flow (started by the app calling getAgent) is replaced by an API call - otherwise everything works the same way as it would for a normal app connecting with getAgent.
In both cases (normal connection and subagent connection) the Desktop Agent needs to send over a MessagePort for further communication. That happens in WCP3Handshake for normal connections, it would be in the response to the API call for subagent connections. We'd need to decide if the identity validation step for subagents happens as part of the API call and first message exchange or to have it happen in the same way as the first message exchange on the MessagePort for normal connections - I suspect the latter would make for simpler implementations. From there, the Desktop Agent can communicate with the sub-app via the MessagePort as it would any normal app, it should look no different despite being in the same window as other apps.
Hence, the main app and sub-app never actually share a messaging channel/MessagePort. The window-level app's connection was just used to retrieve a separate MessagePort, which the sub-app uses to communicate thereafter.
Container-based agent (Desktop Agent preload) can implement this however they need to - they'd just have to support the ability to retrieve another instance of the API interface and have it be considered separate to the first one.
Does that answer the question? This is all just based on my own view of how to do this from past meetings and the associated proposal. It seemed the simplest/most elegant approach.
yes thanks very much @kriswest
I've been thinking about this over the last few weeks and I wanted to throw in this curveball. This is currently a half-baked idea but hopefully this audience can either whip it into shape or dismiss it completely.
I don't know if just being able to create a subagent will be enough - the DA won't know what to call sub-applications, won't know whether to route intents to them and the application itself won't be able to change this stuff in response to the behaviour of the user.
So, here's a suggestion:
interface DesktopAgent {
registerAppDEntry(e: AppDEntry): AppDId // tell the DA about a new sub-app definition
unregisterAppDEntry(id: AppDId) // only for apps you've defined
connectSubAgent(appId: string) : DesktopAgent // should be one of your 'known' app IDs only (?)
disconnectSubAgent(instanceId: string) // only available for an app you've connected
disconnect() // you can call this on from a sub-agent if you want, does similar to the above.
}
Hopefully it's clear where I'm going with this?
@robmoffat If you read the proposals above we're already asking them to supply an appId as a parameter to getSubAgent to give it an identity - although we noted that may also need to be able to send an identityUrl to help validate it. We can discuss at the next meeting
We're also talking about adding a disconnect function to the Desktop Agent API (I think you already have it in DesktopAgentProxy and its called when the page is unloaded) for subAgents - but could consider it for all agents...
Hence, I'd reduce your proposal above to down to whether we need calls to programmatically register appD entries with the Desktop Agent. This has been avoided up until now and perhaps because it open attack vectors on the DAs (with dynamic content able to add and start arbitrary apps, which would be bad).
@kriswest @robmoffat great points above about connecting, disconnecting and appD. Just to flag that I think the subAgent concept does need a good solution to handle opening apps e.g. via open / raiseIntent / raiseIntentForContext methods.
In particular, we need to deal with the fact that unlike the isolated web apps that FDC3 has dealt with up until now (which can be opened simply via a url to use with a window or iframe) apps using subAgents are unlikely to have standard mechanism for loading and bootstrapping. That is to say, they could use module federation, or they could use import maps to load ES modules.
So I think there's the question of whether instructions (script bundle url + more) specified in an appD record can be flexible enough to handle a load/bootstrap which is by definition not standardized. And whether any other mechanisms also need to be considered.
Again, there is of course the issue of attack vectors here as well :)
So I think there's the question of whether instructions (script bundle url + more) specified in an appD record can be flexible enough to handle a load/bootstrap which is by definition not standardized. And whether any other mechanisms also need to be considered.
Can you elaborate on this? I'm not sure I understand.
Again, there is of course the issue of attack vectors here as well :)
Yes, it might be a good idea to do some threat modelling here and figure out what the threats might be... but I think our proposal might need to be a bit clearer first.
@robmoffat
Can you elaborate on this? I'm not sure I understand.
What I was getting at is that up until now, when an FDC3 DA needs to open a web app (e.g. when an originating app invokes open / raiseIntent / raiseIntentForContext methods) the DA has been able to simply look up the app's url from the appD e.g.:
"details": {
"url": "https://someproduct.somevendor.com/someroute"
}
... with that url representing an HTML page (provided using any tech stack) which can simply be used in a window.open(..) call (or indeed for setting the src attribute of an iframe element if opening the target app inside a layout inside a window). This is all that is required to load a traditional/isolated web app. The DA does not need to get involved in loading/bootstrapping the target app, and nor does it care what technology the target app was built with - because the browser takes care of loading and bootstrapping.
By contrast, web apps utilizing the new FDC3 SubAgent concept are intended to load and run in the same DOM as other apps. They don't tend to be loaded/bootstrapped by means of an url representing an HTML page. Rather, they tend to be loaded by dynamically importing a script bundle via a url (e.g. https://somevendor.com/someapp/bundle.xyz.esm.js). Once loaded, they may also need to be separately bootstrapped. This could be using some orchestration that is typically used in a module federation setup. But depending what toolchain was used to build and package the target app, the bootstrapping might even involve creating a <some-app></some-app> element underneath an existing div element on the page the app is being created inside (I've seen this technique used to load React apps that have been wrapped in a Web Component and exposed as an ES module). Note also that, depending on the build/packaging approach used, a primary script bundle may also require an import map to have previously been setup on the page in order to reference (or load) dependency bundles e.g. (react, react-dom, etc.) which are shared between all the apps on the page.
Hence, we're no longer simply dealing with apps that all conform to a single standard. One way of dealing with this might be to allow the appD records for these types of apps to specify more than just a url property. But the DAs would then need to know how to use the additional properties to load/bootstrap a given app. This seems troublesome.
Another technique might be to use callback functions. This would relieve the DA of the burden of loading/bootstrapping apps using n different mechanisms. But this could also lead to problems if not thought out properly e.g. is it reasonable that n originating apps have all need to have a detailed understanding of how to load/bootstrap a target app? It feels like the functionality to load/bootstrap a target should exist at platform level, not baked into n originating apps (which would involve n updates if the target app tech stack was subsequently changed).
One thing that is worth noting when dealing with SubAgents is that the originating and target apps using are far more likely to be from the same vendor as one another - because loading a target app into the same DOM as an originating app requires a high degree of trust. This might imply that originating and target apps have some common understanding around loading and bootstrapping. But this is by no means guaranteed.
Ultimately, I think we cannot pollute the FDC3 Standard with some of the complexity mentioned above. But we do still need a clean way to allow that complexity to be handled by the place best suited e.g. by the app ecosystem. This is of course assuming that we do want SubAgent-based apps to be spawnable via open / raiseIntent / raiseIntentForContext (like traditional/isolated web apps can be today).
btw I'd be very happy if someone can say I'm overthinking some of the above, and that there's an easier way to support what's needed!
@robmoffat
Yes, it might be a good idea to do some threat modelling here and figure out what the threats might be... but I think our proposal might need to be a bit clearer first.
Agreed. I think we need to start by defining the scope, and then it will become clearer what threat modelling is required.
That said, if SubAgent-based apps are indeed going to support spawning via open / raiseIntent / raiseIntentForContext (like regular Agent-based apps) then we need to be really clear that the level of risk is an order of magnitude higher than the current scenario where apps are loaded in separate windows and/or iframes. Because apps loaded in the same DOM are not isolated - they have permission to execute scripts, blat each other's DOM elements, tamper with local/session storage, monkey patch built-in JS APIs, etc.
Playing devil's advocate for a moment, it could be argued that unless a secure solution can be designed then (existing running) SubAgent-based apps should be able to use intents and channels, but that the ability to launch these types of apps should be reserved to the platform (and not available via FDC3 methods). In practice, this would mean no ambiguous intents, and no open method - for these types of app. This would effectively mean there would be two different classes of FDC3-enabled app.
I would add that making FDC3 support SubAgents at all - even with limitations around app launching - would still be a big win for some app vendors.
As a starting point for further discussion of this issue in the FDC3 for Web Browsers & Bridging Discussion group, I am providing some analysis below of the main points to consider. This is based on the work originally undertaken for the 'FDC3 Sub-agents for App Composition' talk at OSFF London 2025 (https://static.sched.com/hosted_files/osfflondon2025/e7/OSFF-London-2025-FDC3-Sub-agents-2025.06.23-17.15.pdf).
Existing FDC3 Support for Web Apps
- FDC3 2.2 dramatically improved the situation for web apps by enabling dynamic runtime discovery and acquisition of the FDC3 DA which opened them
- But there still remains a restriction that web apps must be hosted in separate windows or iframes
- this stems from FDC3's origins in desktop containers
Rationale for Sub-agents
- Support multiple FDC3-enabled apps running in the same window / DOM
- this allows fewer browser processes, and lower overhead via shared runtime dependencies
- Make it easier to retrofit FDC3 support to existing web-based platforms, without requiring a major restructure
- many organizations have large investments in existing web-based platforms, and restructuring to use iframes can often be undesirable / prohibitive
- The expectation is the Sub-agents proposal will be particularly useful for multiple apps owned by the same organization
- code isolation / sandboxing is much less of an issue for apps within the same organization
- for interop crossing organizational boundaries (e.g. mixing in-house and 3rd party vendor apps), security considerations would typically make the existing FDC3 DA solution with an iframe-based approach would the most likely / most desirable implementation pattern
Sub-agents Proposal
- Additive change to FDC3 standard - does not alter the way FDC3 is used in existing web apps and platforms
- Child apps get their own copy of the FDC3 API interface with their identity
- Original proposal was for a new getSubAgent(..) method on the existing FDC3 API
- note that to reduce boilerplate code in child apps it might also be instructive to consider a top-level getSubAgent(..) function e.g. which could call getAgent(..) internally and then create a sub-agent under the FDC3 API returned by that getAgent(..) call
- any alternative proposals for acquiring a reference to a sub-agent should also be discussed and considered
- Expectation is that all messages sent/received by child apps would be routed via the main FDC3 DA (in the same manner that messages by regular apps in separate iframes/windows would be). Although this does involve extra hops when sharing context between two child apps running in the same DOM, keeping the comms pattern consistent:
- ensures child apps can interop with any currently-running app (e.g. including regular apps in other iframes/windows) as opposed to only being able to interop with their own parent app and sibling apps
- moreover, it is important that messages are routed via the main FDC3 DA (as opposed to having Sub-agents talk directly to one another) because not doing so would bypass any observability/tracing or context translation middleware that existed in the main FDC3 DA.
Open Questions - Relationship & Scope
- Child app identification for handshake
- does child app self-identify with its own appId and parent app url?
- Where should a child app's sub-agent be instantiated?
- logically it would probably make sense for the sub-agent to be instantiated within the child app code, but the solution should not prescribe this
- it is important not to confuse the instantiation of a child app with the instantiation of a sub-agent (creating a sub-agent does not create an instance of a child app; rather, a sub-agent is used by a child app)
- Parent app to child app relationship:
- can a child app only live under one parent app?
- for future-proofing, it might be worth designing in such a way so as not to preclude the possibility of instances of a given child app being created under different parent apps at runtime e.g. because different parent apps may all wish to embed a particularly useful/generic child app
- Should child app records exist in the App Directory?
- if not, this might imply a child app with any appId can register at runtime under a given parent app?
- would need to consider any security implications here
- on the other hand, if the app directory was in fact used then it would need to specify relationships between parent and child appIds - this could involve either:
- nesting child app records under a parent app record; or
- a flattened structure with either (a) child app records referencing one or more parent app appIds; or (b) parent app records referencing child app appIds
- if not, this might imply a child app with any appId can register at runtime under a given parent app?
Open Questions - Child App Launch
- Launching regular apps is an HTML5 standard - FDC3 DA can simply use url from appD
- But there is no standard for loading / bootstrapping child apps because:
- different JS frameworks may be used
- different loading mechanisms may be used e.g. Webpack module federation, ES modules with import maps, etc.
- Is it feasible - and is it an MVP requirement - for FDC3 to support launching child apps?
- if not, what about ambiguous intents and fdc3.open? does this effectively lead to two different classes of FDC3 app?
- if so, this might require dynamic registration of an 'open handler' callback function (which accepted an appId argument) in a parent app e.g. so that the DA could delegate the launch of a given child app to the parent app which will host it
- a parent app would need to pre-register an 'open handler' with the FDC3 DA, to handle loading / bootstrapping / positioning of a child app within the parent app window
Open Questions - Child App Lifecycle
- DA will need to track currently-running child apps (in the same manner as it does for regular apps)
- For teardown of regular apps, FDC3 benefits from standard browser functionality like pageHide event and port.close() method
- but there is no single standard for teardown of child apps - it is framework-specific
- When a child app is destroyed, how should it dispose of its sub-agent?