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, storage, permissions, and update lifecycle of the parent application.
This API is restricted to isolated contexts to ensure security and data integrity.
2. Concepts
An installed web application has an associated parent app, which is either null or an installed web application.
An installed web application has an associated sub-apps set, which is a set of installed web applications.
A Document has an associated installed web application, which is the installed web application that the Document is presented as part of. The exact mechanism for this association is implementation-defined.
A Document is a sub-app document if it has an associated installed web application and its associated installed web application’s parent app is not null.
3. Extensions to the Window interface
[Exposed =Window ,SecureContext ,IsolatedContext ]partial interface Window { [SameObject ]readonly attribute SubApps subApps ; };
3.1. subApps attribute
Each Window object has an associated subApps, which is a SubApps instance created alongside the Window.
4. SubApps interface
// Represents the https://w3c.github.io/manifest/#id-membertypedef USVString ;ManifestId dictionary {SubAppsAddResponse record <USVString ,ManifestId >;installedApps record <USVString ,DOMException >; };failedApps dictionary {SubAppsRemoveResponse sequence <ManifestId >;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 <ManifestId >);manifest_ids Promise <record <USVString ,SubAppsListResult >>(); };list
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 document base URL as the base URL is not failure.
4.1. add() method
The add(install_paths) method steps are:
The install_paths argument is a list of relative paths pointing to the sub-apps' start HTML pages.
-
Let promise be a new promise.
-
Let document be the relevant global object’s associated Document.
-
If document is not allowed to use the policy-controlled feature named "sub-apps", reject promise with a "
SecurityError"DOMExceptionand return promise. -
If 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 document, reject promise with a "
TypeError"DOMExceptionand return promise. -
Let absoluteUrl be the result of parsing installPath with document’s document base URL as the base URL.
-
If absoluteUrl is failure, reject promise with a "
TypeError"DOMExceptionand return promise. -
Append absoluteUrl to parsedUrls.
-
-
Let parentApp be document’s associated installed web application.
-
Let currentSubAppsCount be the size of parentApp’s sub-apps set.
-
If currentSubAppsCount +
install_paths’s size is greater than 20, reject promise with a "QuotaExceededError"DOMExceptionand return promise. -
Let subApps be this.
-
Run the following steps in parallel:
-
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, queue a global task on the relevant global object of subApps to reject promise with a "
NotAllowedError"DOMException, and abort these steps. -
Let installedApps be an empty map.
-
Let failedApps be an empty map.
-
For each absoluteUrl in parsedUrls:
-
Let installPath be absoluteUrl’s path.
-
Let manifest be the result of fetch and process the manifest given absoluteUrl.
-
If manifest is failure, perform the following steps:
-
Set failedApps[installPath] to a new "
DataError"DOMException.
-
-
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:
-
Set failedApps[installPath] to a new "
InvalidStateError"DOMException.
-
-
If the scope of parentApp’s manifest is a prefix of the manifest’s scope, or the manifest’s scope is a prefix of the scope of any currently installed sub-app in parentApp’s sub-apps set, or the scope of any currently installed sub-app in parentApp’s sub-apps set is a prefix of the manifest’s scope, or the absoluteUrl points to parentApp’s manifest itself, perform the following steps:
-
Set failedApps[installPath] to a new "
ConstraintError"DOMException.
-
-
Try to install the sub-app on the platform’s application launcher.
-
If the installation fails due to a system or database error:
-
Set failedApps[installPath] to a new "
OperationError"DOMException.
-
-
Set installedApps[installPath] to manifestId.
-
-
Let response be a new
SubAppsAddResponsedictionary with:-
installedAppsset to installedApps. -
failedAppsset to failedApps.
-
-
Queue a global task on the relevant global object of subApps to resolve promise with response.
-
-
Return promise.
4.2. remove() method
The manifest_ids argument is a list of ids of the sub-apps to remove.
The remove(manifest_ids) method steps are:
-
Let promise be a new promise.
-
Let document be the relevant global object’s associated Document.
-
If document is not allowed to use the policy-controlled feature named "sub-apps", reject promise with a "
SecurityError"DOMExceptionand return promise. -
If document is a sub-app document, reject promise with a "
NotSupportedError"DOMExceptionand return promise. -
Let parentApp be document’s associated installed web application.
-
Let parsedManifestIds be an empty list.
-
For each manifestId in
manifest_ids:-
If manifestId is not a valid relative path for document, reject promise with a "
TypeError"DOMExceptionand return promise. -
Let parsedUrl be the result of parsing manifestId with document’s document base URL as the base URL.
-
If parsedUrl is failure, reject promise with a "
TypeError"DOMExceptionand return promise. -
Append parsedUrl to parsedManifestIds.
-
-
Let subApps be this.
-
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 parsedUrl’s path.
-
If there is no installed web application in parentApp’s sub-apps set whose id is manifestId, perform the following steps:
-
Set failedApps[manifestId] to a new "
NotFoundError"DOMException.
-
-
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:
-
Set failedApps[manifestId] to a new "
OperationError"DOMException.
-
-
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 subApps to resolve promise with response.
-
-
Return promise.
4.3. list() method
The list() method steps are:
-
Let promise be a new promise.
-
Let document be the relevant global object’s associated Document.
-
If document is not allowed to use the policy-controlled feature named "sub-apps", reject promise with a "
SecurityError"DOMExceptionand return promise. -
If document is a sub-app document, reject promise with a "
NotSupportedError"DOMExceptionand return promise. -
Let parentApp be document’s associated installed web application.
-
Let subApps be this.
-
Run the following steps in parallel:
-
Let listResult be an empty map.
-
Retrieve the list of all currently installed sub-apps for parentApp from the platform registry.
-
If retrieving the list fails due to a platform error, Queue a global task on the relevant global object of subApps to reject promise with an "
OperationError"DOMException, and abort these steps. -
For each installed sub-app subApp:
-
Let manifestId be subApp’s 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. -
Set listResult[manifestId] to resultEntry.
-
-
Queue a global task on the relevant global object of subApps to resolve promise with listResult.
-
-
Return promise.
4.4. Fetch and process the manifest
write the "fetch and process the manifest" algorithm. [Issue #2]
To fetch and process the manifest given a url (a URL), run the following steps:
-
Return failure.
5. 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.
5.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.5.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.
5.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).
5.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.5.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.5.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.
6. Integrations
6.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 'none'. User agents MAY override this
to 'self' for particular origins (for example based on user decision).