1. Introduction
The Sub Apps API allows a parent application to programmatically install, list, and remove auxiliary applications (Sub Apps) that:
-
Appear to the operating system and user as fully distinct applications (separate launcher icons, distinct taskbar/shelf windows, and individual OS integrations).
-
Share the underlying resources, origin identification, storage, permissions, and update lifecycle of the parent application.
This API is restricted to isolated contexts to ensure security and data integrity.
2. Extensions to the Window interface
[Exposed =Window ,SecureContext ,IsolatedContext ]partial interface Window { [SameObject ]readonly attribute SubApps ; };subApps
2.1. subApps attribute
When getting, the subApps attribute always returns the same
instance of the SubApps object.
3. SubApps interface
dictionary {SubAppsAddResponse record <USVString ,USVString >;installedApps record <USVString ,DOMException >; };failedApps dictionary {SubAppsRemoveResponse sequence <USVString >;removedApps record <USVString ,DOMException >; };failedApps dictionary {SubAppsListResult required DOMString ; }; [appName Exposed =Window ,SecureContext ,IsolatedContext ]interface {SubApps Promise <SubAppsAddResponse >(add sequence <USVString >);install_paths Promise <SubAppsRemoveResponse >(remove sequence <USVString >);manifest_ids Promise <record <USVString ,SubAppsListResult >>(); };list
Each application has a primary application context (the parent app). A Document is a sub-app document if it is not loaded within the primary application context of the parent app.
A string path is a valid relative path for a Document document if all of the following conditions are met:
-
path is not a valid absolute URL.
-
path starts with
"/". -
path is not empty.
-
path does not start with
"//". -
The result of parsing path with document’s origin as the base URL is not failure.
3.1. add() method
The add(install_paths) method steps are:
-
Let promise be a new promise.
-
If the relevant global object’s associated Document is not allowed to use the policy-controlled feature named "sub-apps", reject promise with a "
SecurityError"DOMExceptionand return promise. -
If the relevant global object’s associated Document is a sub-app document, reject promise with a "
NotSupportedError"DOMExceptionand return promise. -
Let parsedUrls be an empty list.
-
For each installPath in
install_paths:-
If installPath is not a valid relative path for the relevant global object’s associated Document, reject promise with a "
TypeError"DOMExceptionand return promise. -
Let absoluteUrl be the result of parsing installPath with the parent app’s origin as the base URL.
-
If absoluteUrl is failure, reject promise with a "
TypeError"DOMExceptionand return promise. -
Append absoluteUrl to parsedUrls.
-
-
Let parentApp be the parent app.
-
Let currentSubAppsCount be the number of sub-apps currently installed for parentApp.
-
If currentSubAppsCount +
install_paths’s size is greater than 20, reject promise with a "QuotaExceededError"DOMExceptionand return promise. -
Let userConsent be the result of requesting user consent for installing the sub-apps in
install_paths(e.g. by presenting a unified installation dialog). -
If userConsent is denied, reject promise with a "
NotAllowedError"DOMExceptionand return promise. -
Run the following steps in parallel:
-
Let installedApps be an empty map.
-
Let failedApps be an empty map.
-
For each absoluteUrl in parsedUrls:
-
Let installPath be the relative path portion of absoluteUrl.
-
Fetch the resource at absoluteUrl and parse its web manifest [APPMANIFEST].
-
If fetching or parsing the web manifest fails, perform the following steps:
-
Add a mapping from installPath to a new "
DataError"DOMExceptionto failedApps. -
Continue.
-
-
Let manifest be the parsed web manifest.
-
Let manifestId be manifest’s id. If it is not defined, fall back to manifest’s start_url (without reference/hash fragment).
-
If parentApp already has a sub-app with manifestId installed, perform the following steps:
-
Add a mapping from installPath to a new "
InvalidStateError"DOMExceptionto failedApps. -
Continue.
-
-
If the scope of manifest overlaps with the scope of parentApp, or the scope of manifest overlaps with any currently installed sub-app’s scope under parentApp, or the absoluteUrl points to parentApp’s manifest itself, perform the following steps:
-
Add a mapping from installPath to a new "
ConstraintError"DOMExceptionto failedApps. -
Continue.
-
-
Try to install the sub-app on the platform’s application launcher.
-
If the installation fails due to a system or database error:
-
Add a mapping from installPath to a new "
OperationError"DOMExceptionto failedApps. -
Continue.
-
-
Add a mapping from installPath to manifestId to installedApps.
-
-
Let response be a new
SubAppsAddResponsedictionary with:-
installedAppsset to installedApps. -
failedAppsset to failedApps.
-
-
Queue a global task on the relevant global object of this to resolve promise with response.
-
-
Return promise.
3.2. remove() method
The remove(manifest_ids) method steps are:
-
Let promise be a new promise.
-
If the relevant global object’s associated Document is not allowed to use the policy-controlled feature named "sub-apps", reject promise with a "
SecurityError"DOMExceptionand return promise. -
If the relevant global object’s associated Document is a sub-app document, reject promise with a "
NotSupportedError"DOMExceptionand return promise. -
Let parsedManifestIds be an empty list.
-
For each manifestId in
manifest_ids:-
If manifestId is not a valid relative path for the relevant global object’s associated Document, reject promise with a "
TypeError"DOMExceptionand return promise. -
Let parsedUrl be the result of parsing manifestId with the parent app’s origin as the base URL.
-
If parsedUrl is failure, reject promise with a "
TypeError"DOMExceptionand return promise. -
Append parsedUrl to parsedManifestIds.
-
-
Run the following steps in parallel:
-
Let removedApps be an empty sequence.
-
Let failedApps be an empty map.
-
For each parsedUrl in parsedManifestIds:
-
Let manifestId be the relative path portion of parsedUrl.
-
If manifestId does not exist under the parent app, perform the following steps:
-
Add a mapping from manifestId to a new "
NotFoundError"DOMExceptionto failedApps. -
Continue.
-
-
Attempt to uninstall the sub-app with ID manifestId from the system launcher and registry.
-
If uninstallation fails due to a system error, perform the following steps:
-
Add a mapping from manifestId to a new "
OperationError"DOMExceptionto failedApps. -
Continue.
-
-
Append manifestId to removedApps.
-
-
Let response be a new
SubAppsRemoveResponsedictionary with:-
removedAppsset to removedApps. -
failedAppsset to failedApps.
-
-
Queue a global task on the relevant global object of this to resolve promise with response.
-
-
Return promise.
3.3. list() method
The list() method steps are:
-
Let promise be a new promise.
-
If the relevant global object’s associated Document is not allowed to use the policy-controlled feature named "sub-apps", reject promise with a "
SecurityError"DOMExceptionand return promise. -
If the relevant global object’s associated Document is a sub-app document, reject promise with a "
NotSupportedError"DOMExceptionand return promise. -
Run the following steps in parallel:
-
Let listResult be an empty map.
-
Retrieve the list of all currently installed sub-apps for the parent app from the platform registry.
-
If retrieving the list fails due to a platform error, Queue a global task on the relevant global object of this to reject promise with an "
OperationError"DOMException, and abort these steps. -
For each installed sub-app subApp:
-
Let manifestId be subApp’s manifest ID.
-
Let appName be the name of the sub-app as extracted from its web manifest.
-
Let resultEntry be a new
SubAppsListResultdictionary withappNameset to appName. -
Add a mapping from manifestId to resultEntry to listResult.
-
-
Queue a global task on the relevant global object of this to resolve promise with listResult.
-
-
Return promise.
4. Security and Privacy Considerations
Installing and managing auxiliary applications is a powerful feature. A user agent MUST NOT allow a web application to install or manage sub-apps without express permission.
This section outlines the threats considered and the normative requirements for user agents to mitigate them.
4.1. Shared Origin Identity
A sub-app does not possess a separate security origin. It shares the exact same origin and local data stores (such as Cookies, IndexedDB, LocalStorage, and Cache Storage) with its parent app. Standard web security boundaries (such as the Same-Origin Policy) treat the parent and all its sub-apps as a single entity.4.2. Permission Inheritance
All permissions are shared across the parent app and its sub-apps. Granting a permission (e.g., camera, file system access, USB) to a sub-app automatically grants it to the parent, and vice versa.To access the Sub Apps API, the parent app’s document must explicitly declare the permission policy sub-apps. Permission policies declared for a sub-app have no effect.
4.3. Explicit User Consent
User consent MUST be obtained for a specific origin. Whenadd() is called, the user agent MUST present a unified installation dialog to the user displaying all requested sub-apps. If multiple sub-apps are added at once, they should be presented inside a single prompt to avoid dialog spam.
The user agent MUST display a permission prompt that clearly indicates which origin is requesting access and provides the user with enough information to make an informed decision (for example, by displaying the names and icons of the sub-apps being installed).
4.4. Identity Spoofing Risks
Since developers can customize the names and icons of sub-apps, there is a risk that a malicious application could create a sub-app that mimics system dialogs or trusted third-party applications. To mitigate this risk, the Sub Apps API is restricted to isolated contexts, which guarantee integrity and signature verification.4.5. OS Integration Extension Risk
Sub-apps have the ability to register their own OS integrations (such as protocol handlers or file type associations). This means an application could potentially extend its reach into the OS far beyond what was declared in the parent app’s primary manifest. This is mitigated by the fact that most operating system integrations require explicit user approval (for example, choosing the sub-app as the default application for a file type) before they become active.4.6. Quota and Limits
To protect the host operating system and the user’s application launcher from potential exhaustion or abuse, the platform enforces a hard limit of 20 installed sub-apps per parent application. If a batch installation call exceeds the platform limit, the entireadd() call rejects with a "QuotaExceededError" DOMException.
5. Integrations
5.1. Permissions Policy
This specification defines a feature that controls whether the methods exposed by the subApps attribute on the Window object may be used.
The feature name for this feature is "sub-apps".
The default allowlist for this feature is 'self'.